<template>
  <div class="task-content">
    <a-spin :spinning="props.loading" tip="加载中...">
    <div class="content-header">
      <template v-if="showListGroupView">
        <div class="content-header-row">
          <div class="content-header-row-left">
            <h2 class="content-title">计划列表</h2>
            <a-tooltip title="新建清单">
              <a-button type="text" size="small" class="add-list-btn" @click="onCreateListClick">
                <Icon icon="ant-design:plus-outlined" />
              </a-button>
            </a-tooltip>
            <a-input-search
              v-model:value="searchKeyword"
              placeholder="搜索计划名称"
              class="list-search-input"
              allow-clear
            />
          </div>
          <a-tooltip title="使用帮助">
            <a-button type="text" size="small" class="help-btn" @click="helpModalOpen = true">
              <Icon icon="ant-design:question-circle-outlined" />
            </a-button>
          </a-tooltip>
        </div>
      </template>
      <template v-else>
        <div class="content-header-left">
          <div v-if="headerRenaming" class="content-header-rename-row">
            <a-input
              ref="headerRenameInputRef"
              v-model:value="headerRenameValue"
              size="small"
              style="width: 200px"
              @press-enter="confirmHeaderRename"
              @blur="confirmHeaderRename"
            />
          </div>
          <h2 v-else class="content-title">{{ currentList?.name || '请选择计划' }}</h2>
          <a-dropdown v-if="currentList && canShowHeaderMore" :trigger="['click']">
            <a-button type="text" size="small" class="header-more-btn">
              <Icon icon="ant-design:ellipsis-outlined" />
            </a-button>
            <template #overlay>
              <a-menu @click="onHeaderMenuClick">
                <a-menu-item v-if="canEdit" key="rename">
                  <Icon icon="ant-design:edit-outlined" class="mr-1" />
                  重命名
                </a-menu-item>
                <a-menu-item v-if="_isOwner" key="delete" danger>
                  <Icon icon="ant-design:delete-outlined" class="mr-1" />
                  删除
                </a-menu-item>
              </a-menu>
            </template>
          </a-dropdown>
        </div>
        <div v-if="currentList" class="content-header-actions">
          <span v-if="currentList.secretText" class="header-secret-badge" :style="{ color: SECRET_LEVEL_COLOR[currentList.secretLevel || 1] || '#8c8c8c', borderColor: SECRET_LEVEL_COLOR[currentList.secretLevel || 1] || '#8c8c8c' }">
            {{ currentList.secretText }}
          </span>
          <div class="collaborator-bar" :class="{ disabled: !_isOwner }" @click="_isOwner && emit('open-collaborator')">
            <AvatarDisplay
              v-if="collaboratorDisplayNames"
              :names="collaboratorDisplayNames"
              :size="24"
              :show-name="showCollaboratorName"
              :max-show="5"
            />
            <a-button size="small" type="text" class="collab-btn">
              <Icon icon="ant-design:user-add-outlined" style="margin-right: 2px" />
              <span>协作人</span>
            </a-button>
          </div>
        </div>
      </template>
    </div>

    <div v-if="!showListGroupView" class="content-toolbar">
      <a-dropdown>
        <template #overlay>
          <a-menu @click="onStatusFilterChange">
            <a-menu-item key="all" :class="{ 'ant-dropdown-menu-item-selected': statusFilter === 'all' }">全部</a-menu-item>
            <a-menu-item key="completed" :class="{ 'ant-dropdown-menu-item-selected': statusFilter === 'completed' }">已完成</a-menu-item>
            <a-menu-item key="uncompleted" :class="{ 'ant-dropdown-menu-item-selected': statusFilter === 'uncompleted' }">未完成</a-menu-item>
          </a-menu>
        </template>
        <a-button size="small">
          <Icon icon="ant-design:check-circle-outlined" class="mr-1" />
          {{ statusFilterLabel }}
          <Icon icon="ant-design:down-outlined" class="ml-1" />
        </a-button>
      </a-dropdown>

      <a-popover v-model:open="filterVisible" placement="bottomLeft" trigger="click" :width="480">
        <template #content>
          <div class="filter-panel">
            <div class="filter-panel-header">
              <span class="filter-panel-title">筛选条件</span>
              <a-button v-if="filterConditions.length > 0" type="link" size="small" @click="clearAllFilters">清空全部</a-button>
            </div>

            <div v-for="(cond, idx) in filterConditions" :key="cond.id" class="filter-condition-row">
              <a-select
                :value="cond.field"
                size="small"
                class="filter-field-select"
                placeholder="选择字段"
                @change="(v: FilterFieldType) => onConditionFieldChange(idx, v)"
              >
                <a-select-option v-for="fc in FILTER_FIELD_CONFIGS" :key="fc.value" :value="fc.value">{{ fc.label }}</a-select-option>
              </a-select>

              <a-select
                v-if="cond.field"
                :value="cond.operator"
                size="small"
                class="filter-op-select"
                @change="(v: FilterOperator) => onConditionOperatorChange(idx, v)"
              >
                <a-select-option v-for="op in getOperatorsForField(cond.field)" :key="op" :value="op">
                  {{ FILTER_OPERATOR_LABELS[op] }}
                </a-select-option>
              </a-select>

              <template v-if="cond.field && needsValueInput(cond)">
                <a-input
                  v-if="getFieldType(cond.field) === 'text'"
                  :value="cond.value"
                  size="small"
                  class="filter-value-input"
                  placeholder="输入关键词"
                  @change="(e: any) => onConditionValueChange(idx, e.target.value)"
                />
                <a-select
                  v-if="getFieldType(cond.field) === 'select'"
                  :value="cond.value"
                  size="small"
                  class="filter-value-input"
                  placeholder="请选择"
                  @change="(v: string) => onConditionValueChange(idx, v)"
                >
                  <a-select-option value="1">高</a-select-option>
                  <a-select-option value="2">中</a-select-option>
                  <a-select-option value="3">低</a-select-option>
                </a-select>
                <a-date-picker
                  v-if="getFieldType(cond.field) === 'date' && cond.operator !== 'between'"
                  :value="cond.value"
                  size="small"
                  class="filter-value-input"
                  placeholder="选择日期"
                  @change="(v: any) => onConditionValueChange(idx, v)"
                />
                <template v-if="getFieldType(cond.field) === 'date' && cond.operator === 'between'">
                  <a-date-picker
                    :value="cond.value"
                    size="small"
                    class="filter-value-input-short"
                    placeholder="开始日期"
                    @change="(v: any) => onConditionValueChange(idx, v)"
                  />
                  <span class="filter-range-sep">~</span>
                  <a-date-picker
                    :value="cond.valueEnd"
                    size="small"
                    class="filter-value-input-short"
                    placeholder="结束日期"
                    @change="(v: any) => onConditionValueEndChange(idx, v)"
                  />
                </template>
              </template>

              <span class="filter-condition-remove" @click="removeCondition(idx)">
                <Icon icon="ant-design:close-outlined" />
              </span>
            </div>

            <a-button type="dashed" size="small" block class="filter-add-btn" @click="addCondition">
              <Icon icon="ant-design:plus-outlined" class="mr-1" />
              添加条件
            </a-button>

            <div class="filter-panel-footer">
              <a-button size="small" @click="filterVisible = false">取消</a-button>
              <a-button type="primary" size="small" @click="applyConditions">筛选</a-button>
            </div>
          </div>
        </template>
        <a-button size="small" :type="activeFilterCount > 0 ? 'primary' : 'default'" :ghost="activeFilterCount > 0">
          <Icon icon="ant-design:filter-outlined" class="mr-1" />
          筛选
          <span v-if="activeFilterCount > 0" class="filter-badge">{{ activeFilterCount }}</span>
        </a-button>
      </a-popover>

      <a-dropdown>
        <template #overlay>
          <a-menu @click="onSortChange">
            <a-menu-item v-for="opt in SORT_OPTIONS" :key="opt.value" :class="{ 'ant-dropdown-menu-item-selected': sortField === opt.value }">
              <div class="sort-menu-item">
                <span>{{ opt.label }}</span>
                <span v-if="opt.value !== 'sortOrder'" class="sort-arrows-row">
                  <span
                    class="sort-arrow-btn"
                    :class="{ active: sortField === opt.value && sortDirection === 'asc' }"
                    @click.stop="onSortDirectionChange(opt.value, 'asc')"
                  >
                    <Icon icon="ant-design:arrow-up-outlined" />
                  </span>
                  <span
                    class="sort-arrow-btn"
                    :class="{ active: sortField === opt.value && sortDirection === 'desc' }"
                    @click.stop="onSortDirectionChange(opt.value, 'desc')"
                  >
                    <Icon icon="ant-design:arrow-down-outlined" />
                  </span>
                </span>
              </div>
            </a-menu-item>
          </a-menu>
        </template>
        <a-button size="small">
          <Icon icon="ant-design:sort-ascending-outlined" class="mr-1" />
          排序: {{ currentSortLabel }}
          <Icon icon="ant-design:down-outlined" class="ml-1" />
        </a-button>
      </a-dropdown>

      <a-dropdown>
        <template #overlay>
          <a-menu @click="onGroupChange">
            <a-menu-item v-for="opt in GROUP_OPTIONS" :key="opt.value" :class="{ 'ant-dropdown-menu-item-selected': groupDimension === opt.value }">
              {{ opt.label }}
            </a-menu-item>
          </a-menu>
        </template>
        <a-button size="small">
          <Icon icon="ant-design:group-outlined" class="mr-1" />
          分组: {{ currentGroupLabel }}
          <Icon icon="ant-design:down-outlined" class="ml-1" />
        </a-button>
      </a-dropdown>

      <a-popover v-model:open="fieldConfigVisible" placement="bottomRight" trigger="click">
        <template #content>
          <div class="field-config-panel">
            <div class="field-config-title">字段配置</div>
            <div v-for="field in taskContentFieldConfigs" :key="field.key" class="field-config-item" @click="toggleField(field.key)">
              <span>{{ field.label }}</span>
              <Icon
                :icon="visibleFields.includes(field.key) ? 'ant-design:eye-outlined' : 'ant-design:eye-invisible-outlined'"
                :class="visibleFields.includes(field.key) ? 'field-visible' : 'field-hidden'"
              />
            </div>
          </div>
        </template>
        <a-button size="small">
          <Icon icon="ant-design:setting-outlined" class="mr-1" />
          字段配置
        </a-button>
      </a-popover>
    </div>

    <div class="content-body">
      <template v-if="showListGroupView">
        <div class="list-group-view">
          <div class="list-group-table-header">
            <div class="list-group-col list-group-col-name">名称</div>
            <div class="list-group-col list-group-col-owner">所有者</div>
            <div class="list-group-col list-group-col-collaborators">协作者</div>
            <div class="list-group-col list-group-col-secret">密级</div>
            <div class="list-group-col list-group-col-time">创建时间</div>
          </div>
          <div v-for="list in allTaskLists" :key="list.id" class="list-group-row" @click="emit('list-select', list.id)">
            <div class="list-group-col list-group-col-name">
              <Icon icon="ant-design:file-text-outlined" class="list-icon" size="16" />
              <span class="list-name-text">{{ list.name }}</span>
              <a-tooltip :title="isFavorited(list.id) ? '取消收藏' : '收藏'">
                <span class="pin-btn-wrap" @click.stop="toggleFavorite(list.id)">
                  <Icon v-if="!isFavorited(list.id)" icon="tasklist-star|svg" class="pin-svg pin-svg-outlined" />
                  <Icon v-else icon="tasklist-star-filled|svg" class="pin-svg pin-svg-filled" />
                </span>
              </a-tooltip>
            </div>
            <div class="list-group-col list-group-col-owner">
              <template v-if="getFirstAssignee(list)">
                <a-tooltip :title="getFirstAssignee(list)" placement="top"><AvatarDisplay :names="getFirstAssignee(list)" :size="24" /></a-tooltip>
              </template>
              <span v-else class="field-placeholder">-</span>
            </div>
            <div class="list-group-col list-group-col-collaborators clickable" @click.stop="emit('open-collaborator-for-list', list.id)">
              <template v-if="getAllCollaborators(list)">
                <a-tooltip title="点击管理协作者" placement="top">
                  <AvatarDisplay :names="getAllCollaborators(list)" :size="24" :show-name="getCollaboratorCount(list) === 1" />
                </a-tooltip>
              </template>
              <Icon v-else icon="tasklist-add-user|svg" class="collab-add-icon" />
            </div>
            <div class="list-group-col list-group-col-secret">
              <span
                v-if="list.secretText"
                class="secret-badge-inline"
                :style="{ color: SECRET_LEVEL_COLOR[(list as any).secretLevel || 1] || '#8c8c8c', borderColor: SECRET_LEVEL_COLOR[(list as any).secretLevel || 1] || '#8c8c8c' }"
              >
                {{ list.secretText }}
              </span>
              <span v-else class="field-placeholder">-</span>
            </div>
            <div class="list-group-col list-group-col-time">
              <span class="field-value">{{ getFormattedCreateTime(list) }}</span>
            </div>
          </div>
          <div v-if="allTaskLists.length === 0" class="empty-state">
            <Icon icon="ant-design:inbox-outlined" class="empty-icon" />
            <p>暂无可见计划</p>
          </div>
        </div>
      </template>

      <template v-else>
        <div class="content-table-header" :style="{ minWidth: totalFieldsWidth + 'px' }">
          <div class="table-col table-col-drag-handle"></div>
          <div class="table-col table-col-title">
            <Icon icon="tasklist-title|svg" class="header-icon" />
            <span class="header-label">事项标题</span>
          </div>
          <template v-for="field in visibleFieldConfigs" :key="field.key">
            <div class="table-col header-col" :style="{ width: fieldWidthMap[field.key] || '100px' }">
              <Icon v-if="field.key === 'priority'" icon="tasklist-priority|svg" class="header-icon" />
              <Icon
                v-else-if="
                  field.key === 'assigneeName' ||
                  field.key === 'assignName' ||
                  field.key === 'participantName' ||
                  field.key === 'followersName' ||
                  field.key === 'createBy'
                "
                icon="tasklist-user|svg"
                class="header-icon"
              />
              <Icon v-else-if="field.key === 'startTime' || field.key === 'endTime'" icon="tasklist-time|svg" class="header-icon" />
              <Icon v-else :icon="fieldIconMap[field.key]" class="header-icon" />
              <span class="header-label">{{ field.label }}</span>
            </div>
          </template>
        </div>

        <template v-if="currentList">
          <draggable
            :list="localGroups"
            item-key="id"
            group="taskGroups"
            handle=".group-drag-handle"
            :animation="200"
            :disabled="groupDimension !== 'custom' || !canEdit"
            @end="onGroupDragEnd"
          >
            <template #item="{ element: group }">
              <div :data-group-id="group.id">
                <div
                  v-if="groupDimension !== 'none'"
                  class="task-group-header"
                  :style="{ minWidth: totalFieldsWidth + 'px' }"
                  @click="toggleGroupCollapse(group.id)"
                >
                  <span v-if="canEdit && groupDimension === 'custom'" class="group-drag-handle table-col-drag-handle">
                    <Icon icon="tasklist-drag-handle|svg" />
                  </span>
                  <span v-else class="table-col-drag-handle"></span>
                  <span class="table-col table-col-title">
                    <div class="task-indent" style="margin-left: 0">
                      <span class="expand-arrow">
                        <Icon :icon="group.collapsed ? 'ant-design:caret-right-outlined' : 'ant-design:caret-down-outlined'" />
                      </span>
                    </div>
                    <template v-if="groupDimension === 'assignee' || groupDimension === 'createBy'">
                      <template v-if="group.name && group.name !== '未分配' && group.name !== '未知'">
                        <AvatarDisplay :names="group.name" :size="24" :show-name="true" />
                      </template>
                      <span v-else class="group-name">{{ group.name }}</span>
                    </template>
                    <template v-else-if="groupDimension === 'priority'">
                      <span
                        v-if="group.name && group.name !== '无优先级'"
                        class="priority-tag"
                        :style="{ backgroundColor: getPriorityBg(group.name), color: '#1f2329' }"
                      >
                        {{ getPriorityLabel(group.name) }}
                      </span>
                      <span v-else class="group-name">{{ group.name }}</span>
                    </template>
                    <template v-else-if="renamingGroupId === group.id">
                      <a-input
                        ref="renamingGroupInputRef"
                        v-model:value="renamingGroupName"
                        size="small"
                        style="width: 160px"
                        @press-enter="confirmRenameGroup"
                        @blur="confirmRenameGroup"
                        @click.stop
                      />
                    </template>
                    <span v-else class="group-name">{{ group.name }}</span>
                    <span class="group-count">({{ countTopLevelTasks(group.tasks) }})</span>
                    <span class="group-actions" @click.stop>
                      <span
                        v-if="canEdit && groupDimension === 'custom'"
                        class="group-action-btn group-action-add"
                        title="新建事项"
                        @click.stop="showNewTaskInput = group.id"
                      >
                        <Icon icon="ant-design:plus-outlined" />
                      </span>
                      <a-dropdown v-if="canEdit && groupDimension === 'custom'">
                        <span class="group-action-btn group-action-more" title="更多操作" @click.stop>
                          <Icon icon="ant-design:ellipsis-outlined" />
                        </span>
                        <template #overlay>
                          <a-menu @click="({ key }) => onGroupMenuClick(key, group.id, group.name)">
                            <a-menu-item key="rename">重命名</a-menu-item>
                            <a-tooltip v-if="group.id === '__default__'" title="系统内置分组不可删除" placement="right">
                              <a-menu-item key="delete" disabled style="color: #ccc; cursor: not-allowed">删除</a-menu-item>
                            </a-tooltip>
                            <a-menu-item v-else key="delete" danger>删除</a-menu-item>
                          </a-menu>
                        </template>
                      </a-dropdown>
                    </span>
                  </span>
                </div>

                <div v-show="!group.collapsed">
                  <draggable
                    :model-value="flattenGroupTasks(group.tasks)"
                    :item-key="(ft: any) => ft.task.id"
                    group="tasks"
                    handle=".drag-handle"
                    :animation="200"
                    ghost-class="sortable-ghost"
                    chosen-class="sortable-chosen"
                    @start="onTaskDragStart"
                    @end="(evt: any) => onTaskDragEnd(evt)"
                    @move="onTaskMove"
                  >
                    <template #item="{ element: ft }">
                      <div class="task-row" :data-task-id="ft.task.id" :style="{ minWidth: totalFieldsWidth + 'px' }" v-show="!isChildHidden(ft)">
                        <div class="table-col table-col-drag-handle" v-if="_canEditTask(ft.task)">
                          <span class="drag-handle">
                            <Icon icon="tasklist-drag-handle|svg" />
                          </span>
                        </div>
                        <div class="table-col table-col-drag-handle" v-else></div>
                        <div class="table-col table-col-title" @click.stop="emit('task-click', ft.task.id)">
                          <div class="task-indent" :style="{ marginLeft: Math.min(ft.level, 4) * 20 + 'px' }">
                            <span v-if="ft.task.childCount > 0" class="expand-arrow" @click.stop="toggleTaskExpand(ft.task.id)">
                              <Icon :icon="expandedTaskIds.has(ft.task.id) ? 'ant-design:caret-down-outlined' : 'ant-design:caret-right-outlined'" />
                            </span>
                            <span v-else class="expand-arrow-placeholder"></span>
                            <button
                              class="task-checkbox"
                              :class="{ checked: ft.task.completed }"
                              :disabled="!_canEditTask(ft.task)"
                              @click.stop="_canEditTask(ft.task) && emit('toggle-task', ft.task.id)"
                            >
                              <Icon v-if="ft.task.completed" icon="ant-design:check-outlined" />
                            </button>
                          </div>
                          <span class="task-title" :class="{ completed: ft.task.completed }">{{ ft.task.taskName }}</span>
                          <span v-if="ft.task.childCount > 0" class="task-child-count">
                            <Icon icon="tasklist-subtask|svg" class="child-count-icon" />
                            {{ ft.task.completedChildCount }}/{{ ft.task.childCount }}
                          </span>
                        </div>
                        <template v-for="field in visibleFieldConfigs" :key="field.key">
                          <div class="table-col task-field" :style="{ width: fieldWidthMap[field.key] || '100px' }">
                            <template v-if="field.key === 'subTaskProgress'">
                              <div class="cell-inner readonly-cell">
                                <div class="progress-cell" v-if="ft.task.childCount > 0">
                                  <div class="progress-bar">
                                    <div
                                      class="progress-bar-fill"
                                      :style="{
                                        width: (ft.task.completedChildCount / ft.task.childCount) * 100 + '%',
                                        background: ft.task.completedChildCount > 0 ? '#52c41a' : '#1677ff',
                                      }"
                                    ></div>
                                  </div>
                                </div>
                                <span v-else :class="{ 'task-field-completed': ft.task.completed }">-</span>
                              </div>
                            </template>
                            <template v-else-if="field.key === 'priority'">
                              <a-dropdown
                                v-if="_canEditTask(ft.task)"
                                :trigger="['click']"
                                :get-popup-container="getPopupContainer"
                                :overlay-style="{ minWidth: '100px' }"
                              >
                                <div class="cell-inner editable-cell dropdown-cell">
                                  <span
                                    v-if="ft.task.priority"
                                    class="priority-tag"
                                    :class="{ 'task-field-completed': ft.task.completed }"
                                    :style="{ backgroundColor: getPriorityBg(ft.task.priority), color: '#1f2329' }"
                                    >{{ getPriorityLabel(ft.task.priority) }}</span
                                  >
                                  <span v-else class="field-placeholder">-</span>
                                </div>
                                <template #overlay>
                                  <a-menu @click="({ key }) => onPriorityChange(ft.task.id, key)">
                                    <a-menu-item v-for="opt in priorityOptions" :key="opt.value" class="priority-menu-item">
                                      <span class="priority-tag" :style="{ backgroundColor: getPriorityBg(opt.value), color: '#1f2329' }">{{
                                        opt.label
                                      }}</span>
                                    </a-menu-item>
                                  </a-menu>
                                </template>
                              </a-dropdown>
                              <div v-else class="cell-inner">
                                <span
                                  v-if="ft.task.priority"
                                  class="priority-tag"
                                  :class="{ 'task-field-completed': ft.task.completed }"
                                  :style="{ backgroundColor: getPriorityBg(ft.task.priority), color: '#1f2329' }"
                                  >{{ getPriorityLabel(ft.task.priority) }}</span
                                >
                                <span v-else class="field-placeholder">-</span>
                              </div>
                            </template>
                            <template v-else-if="field.key === 'assigneeName'">
                              <div v-if="_canEditTask(ft.task)" class="cell-inner editable-cell" @click.stop="openUserSelectModal('assignee', ft.task.id)">
                                <template v-if="ft.task.assigneeName">
                                  <a-tooltip :title="ft.task.assigneeName" placement="top"
                                    ><AvatarDisplay :names="ft.task.assigneeName" :size="24" :completed="ft.task.completed"
                                  /></a-tooltip>
                                </template>
                                <template v-else>
                                  <span class="field-icon-placeholder" :class="{ 'task-field-completed': ft.task.completed }">
                                    <Icon icon="tasklist-add-user|svg" />
                                  </span>
                                </template>
                              </div>
                              <div v-else class="cell-inner">
                                <template v-if="ft.task.assigneeName">
                                  <a-tooltip :title="ft.task.assigneeName" placement="top"
                                    ><AvatarDisplay :names="ft.task.assigneeName" :size="24" :completed="ft.task.completed"
                                  /></a-tooltip>
                                </template>
                                <span v-else class="field-placeholder">-</span>
                              </div>
                            </template>
                            <template v-else-if="field.key === 'startTime' || field.key === 'endTime'">
                              <div
                                class="cell-inner date-cell-wrapper"
                                :class="{ 'editable-cell': _canEditTask(ft.task) }"
                                @click.stop="_canEditTask(ft.task) && !datePickerVisible && startEditCell(ft.task.id, field.key)"
                              >
                                <a-date-picker
                                  v-if="editingCell?.taskId === ft.task.id && editingCell?.field === field.key"
                                  :value="field.key === 'startTime' ? editingDateDayjs[0] : editingDateDayjs[1]"
                                  :open="datePickerVisible"
                                  placeholder="选择日期"
                                  format="YYYY-MM-DD"
                                  :allow-clear="true"
                                  :disabled-date="field.key === 'endTime' ? getDisabledEndDate(ft.task.id) : undefined"
                                  class="inline-date-picker"
                                  @change="(date) => onDateCellChange(date, field.key)"
                                  @open-change="onCellPickerOpenChange"
                                >
                                  <template #renderExtraFooter>
                                    <div style="text-align: right">
                                      <a-button type="link" size="small" danger @click.stop="clearDateCell(field.key)">清除</a-button>
                                    </div>
                                  </template>
                                </a-date-picker>
                                <template v-else>
                                  <template v-if="(field.key === 'startTime' && ft.task.startTime) || (field.key === 'endTime' && ft.task.endTime)">
                                    <span
                                      class="field-value"
                                      :class="{
                                        'task-field-completed': ft.task.completed,
                                        'overdue-date': field.key === 'endTime' && isOverdue(ft.task),
                                      }"
                                    >
                                      {{ field.key === 'startTime' ? ft.task.startTime : ft.task.endTime }}
                                    </span>
                                  </template>
                                  <template v-else>
                                    <span class="field-icon-placeholder" :class="{ 'task-field-completed': ft.task.completed }">
                                      <Icon icon="tasklist-calendar|svg" />
                                    </span>
                                  </template>
                                </template>
                              </div>
                            </template>
                            <template v-else-if="field.key === 'followersName'">
                              <div
                                v-if="_canEditTask(ft.task)"
                                class="cell-inner editable-cell"
                                @click.stop="openUserSelectModal('followers', ft.task.id)"
                              >
                                <template v-if="ft.task.followersName">
                                  <a-tooltip :title="ft.task.followersName" placement="top"
                                    ><AvatarDisplay :names="ft.task.followersName" :size="24" :completed="ft.task.completed"
                                  /></a-tooltip>
                                </template>
                                <template v-else>
                                  <span class="field-icon-placeholder" :class="{ 'task-field-completed': ft.task.completed }">
                                    <Icon icon="tasklist-add-user|svg" />
                                  </span>
                                </template>
                              </div>
                              <div v-else class="cell-inner">
                                <template v-if="ft.task.followersName">
                                  <a-tooltip :title="ft.task.followersName" placement="top"
                                    ><AvatarDisplay :names="ft.task.followersName" :size="24" :completed="ft.task.completed"
                                  /></a-tooltip>
                                </template>
                                <span v-else class="field-placeholder">-</span>
                              </div>
                            </template>
                            <template v-else-if="field.key === 'participantName'">
                              <div
                                v-if="_canEditTask(ft.task)"
                                class="cell-inner editable-cell"
                                @click.stop="openUserSelectModal('participant', ft.task.id)"
                              >
                                <template v-if="ft.task.participantName">
                                  <a-tooltip :title="ft.task.participantName" placement="top"
                                    ><AvatarDisplay :names="ft.task.participantName" :size="24" :completed="ft.task.completed"
                                  /></a-tooltip>
                                </template>
                                <template v-else>
                                  <span class="field-icon-placeholder" :class="{ 'task-field-completed': ft.task.completed }">
                                    <Icon icon="tasklist-add-user|svg" />
                                  </span>
                                </template>
                              </div>
                              <div v-else class="cell-inner">
                                <template v-if="ft.task.participantName">
                                  <a-tooltip :title="ft.task.participantName" placement="top"
                                    ><AvatarDisplay :names="ft.task.participantName" :size="24" :completed="ft.task.completed"
                                  /></a-tooltip>
                                </template>
                                <span v-else class="field-placeholder">-</span>
                              </div>
                            </template>
                            <template v-else-if="field.key === 'completeTime'">
                              <div class="cell-inner readonly-cell">
                                <template v-if="ft.task.completeTime">
                                  <span class="field-value" :class="{ 'task-field-completed': ft.task.completed }">
                                    {{ ft.task.completeTime.substring(0, 10) }}
                                  </span>
                                </template>
                                <span v-else class="field-placeholder">-</span>
                              </div>
                            </template>
                            <template v-else-if="field.key === 'remark'">
                              <div
                                class="cell-inner"
                                :class="{ 'editable-cell': canEdit }"
                                @click.stop="canEdit && startEditTextCell(ft.task.id, field.key)"
                              >
                                <a-input
                                  v-if="editingCell?.taskId === ft.task.id && editingCell?.field === field.key"
                                  v-model:value="editingValue"
                                  size="small"
                                  :maxlength="200"
                                  class="inline-text-editor"
                                  :ref="
                                    (el: any) => {
                                      if (el) textEditInputRef = el;
                                    }
                                  "
                                  @press-enter="confirmEditTextCell"
                                  @blur="confirmEditTextCell"
                                  @click.stop
                                />
                                <template v-else>
                                  <span
                                    v-if="ft.task.remark"
                                    class="field-value"
                                    :class="{ 'task-field-completed': ft.task.completed }"
                                    :title="ft.task.remark"
                                  >
                                    {{ ft.task.remark.length > 20 ? ft.task.remark.substring(0, 20) + '...' : ft.task.remark }}
                                  </span>
                                  <span v-else class="field-icon-placeholder" :class="{ 'task-field-completed': ft.task.completed }">
                                    <Icon icon="ant-design:edit-outlined" />
                                  </span>
                                </template>
                              </div>
                            </template>
                            <template v-else-if="field.key === 'assignName' || field.key === 'createBy'">
                              <div class="cell-inner readonly-cell">
                                <template v-if="getFieldValue(ft.task, field.key)">
                                  <a-tooltip :title="getFieldValue(ft.task, field.key)" placement="top"
                                    ><AvatarDisplay :names="getFieldValue(ft.task, field.key)" :size="24" :completed="ft.task.completed"
                                  /></a-tooltip>
                                </template>
                                <span v-else class="field-placeholder">-</span>
                              </div>
                            </template>
                            <template v-else-if="field.key === 'taskDesc'">
                              <div
                                class="cell-inner"
                                :class="{ 'editable-cell': canEdit }"
                                @click.stop="canEdit && startEditTextCell(ft.task.id, field.key)"
                              >
                                <a-input
                                  v-if="editingCell?.taskId === ft.task.id && editingCell?.field === field.key"
                                  v-model:value="editingValue"
                                  size="small"
                                  :maxlength="200"
                                  class="inline-text-editor"
                                  :ref="
                                    (el: any) => {
                                      if (el) textEditInputRef = el;
                                    }
                                  "
                                  @press-enter="confirmEditTextCell"
                                  @blur="confirmEditTextCell"
                                  @click.stop
                                />
                                <template v-else>
                                  <span
                                    v-if="ft.task.taskDesc"
                                    class="field-value"
                                    :class="{ 'task-field-completed': ft.task.completed }"
                                    :title="ft.task.taskDesc"
                                  >
                                    {{ ft.task.taskDesc.length > 20 ? ft.task.taskDesc.substring(0, 20) + '...' : ft.task.taskDesc }}
                                  </span>
                                  <span v-else class="field-icon-placeholder" :class="{ 'task-field-completed': ft.task.completed }">
                                    <Icon icon="ant-design:edit-outlined" />
                                  </span>
                                </template>
                              </div>
                            </template>
                            <template v-else>
                              <div class="cell-inner">
                                <span :class="{ 'task-field-completed': ft.task.completed }">{{ getFieldValue(ft.task, field.key) }}</span>
                              </div>
                            </template>
                          </div>
                        </template>
                      </div>
                    </template>
                  </draggable>

                  <div
                    v-if="showNewTaskInput === group.id && canEdit"
                    class="task-row new-task-input-row"
                    :style="{ minWidth: totalFieldsWidth + 'px' }"
                  >
                    <div class="table-col table-col-drag-handle"></div>
                    <div class="table-col table-col-title">
                      <div class="task-indent">
                        <span class="expand-arrow-placeholder"></span>
                        <div class="task-checkbox"></div>
                      </div>
                      <a-input
                        ref="newTaskInputRef"
                        v-model:value="newTaskTitle"
                        size="small"
                        placeholder="输入事项标题，按回车确认..."
                        autofocus
                        @press-enter="submitNewTask(group.id)"
                        @blur="submitNewTask(group.id)"
                      />
                    </div>
                  </div>

                  <div
                    v-if="showNewTaskInput !== group.id && canEdit"
                    class="task-row new-task-btn"
                    :style="{ minWidth: totalFieldsWidth + 'px' }"
                    @click="showNewTaskInput = group.id"
                  >
                    <div class="table-col table-col-drag-handle"></div>
                    <div class="table-col table-col-title">
                      <div class="task-indent">
                        <span class="expand-arrow-placeholder"></span>
                      </div>
                      <Icon icon="ant-design:plus-outlined" class="new-task-plus" />
                      <span class="new-task-text">新建事项</span>
                    </div>
                  </div>
                </div>
              </div>
            </template>
          </draggable>

          <div v-if="showNewGroupInput && canEdit" class="new-group-row">
            <Icon icon="ant-design:plus-outlined" class="new-task-plus" />
            <a-input
              ref="newGroupInputRef"
              v-model:value="newGroupName"
              size="small"
              placeholder="输入分组名称，按回车确认..."
              autofocus
              style="flex: 1; max-width: 300px"
              @press-enter="submitNewGroup"
              @blur="submitNewGroup"
            />
          </div>
          <div v-else-if="canEdit" class="new-group-row new-group-btn" @click="showNewGroupInput = true">
            <Icon icon="ant-design:plus-outlined" class="new-task-plus" />
            <span class="new-task-text">新建分组</span>
          </div>
        </template>

        <div v-else class="empty-state">
          <Icon icon="ant-design:inbox-outlined" class="empty-icon" />
          <p>请从左侧选择一个计划</p>
        </div>
      </template>
    </div>

    <SzDeptUserModal
      ref="userSelectModalRef"
      hide-trigger
      :title="userSelectModalType === 'assignee' ? '选择负责人' : userSelectModalType === 'participant' ? '选择参与人' : '选择关注人'"
      :value="userSelectModalCurrentIds"
      value-key="id"
      :multi="true"
      :sec-level="currentList?.secretLevel || 1"
      :role="1"
      @confirm="onUserSelectConfirm"
    />
    <HelpModal v-model:open="helpModalOpen" />
    </a-spin>
  </div>
</template>

<script lang="ts" setup>
  import { ref, computed, watch, nextTick } from 'vue';
  import HelpModal from './HelpModal.vue';
  import dayjs from 'dayjs';
  import Icon from '/@/components/Icon/index';
  import AvatarDisplay from './AvatarDisplay.vue';
  import draggable from 'vuedraggable';
  import SzDeptUserModal from '/@/components/semri/deptUserComponent/SzDeptUserModal.vue';
  import { useUserStore } from '/@/store/modules/user';
  import { useMessage } from '/@/hooks/web/useMessage';
  import { FIELD_CONFIG, GROUP_OPTIONS, SORT_OPTIONS, MAX_TASK_DEPTH, FILTER_FIELD_CONFIGS, FILTER_OPERATOR_LABELS, SECRET_LEVEL_COLOR } from '../types';
  import { PRIORITY_OPTIONS, getPriorityBg, getPriorityLabel, isOverdue } from '../utils/taskUtils';
  import type {
    TaskList,
    TaskGroup,
    TaskItem,
    StatusFilter,
    GroupDimension,
    SortField,
    SortDirection,
    FieldKey,
    FlatTaskItem,
    TaskListGroup,
    FilterCondition,
    FilterFieldType,
    FilterOperator,
  } from '../types';

  const props = defineProps<{
    currentList: TaskList | null;
    taskListGroups: TaskListGroup[];
    statusFilter: StatusFilter;
    groupDimension: GroupDimension;
    sortField: SortField;
    sortDirection: SortDirection;
    visibleFields: FieldKey[];
    showListGroupView?: boolean;
    favoriteIdMap?: Map<string, string>;
    loading?: boolean;
  }>();

  const emit = defineEmits<{
    'create-task': [groupId: string, taskName: string];
    'toggle-task': [taskId: string];
    'task-click': [taskId: string];
    'add-group': [listId: string, groupName: string];
    'update-task-field': [taskId: string, field: string, value: any];
    'update-task-fields': [taskId: string, fields: Record<string, any>];
    'update:statusFilter': [value: StatusFilter];
    'update:groupDimension': [value: GroupDimension];
    'update:sortField': [value: SortField];
    'update:sortDirection': [value: SortDirection];
    'update:visibleFields': [value: FieldKey[]];
    'update:list-group-tab': [value: string];
    'list-select': [listId: string];
    'open-collaborator': [];
    'open-collaborator-for-list': [listId: string];
    'add-to-favorites': [taskListId: string];
    'remove-from-favorites': [favoriteId: string];
    'move-task': [taskId: string, targetPid: string, targetSortOrder: number];
    'move-task-group': [taskGroupId: string, targetSortOrder: number];
    'rename-list': [listId: string, newName: string];
    'delete-list': [listId: string];
    'rename-group': [groupId: string, newName: string];
    'delete-group': [groupId: string];
    'create-list': [];
  }>();

  const userStore = useUserStore();
  const { createConfirm } = useMessage();

  const helpModalOpen = ref(false);

  function onCreateListClick() {
    emit('create-list');
  }

  const showNewTaskInput = ref<string | null>(null);
  const newTaskTitle = ref('');
  const showNewGroupInput = ref(false);
  const newGroupName = ref('');
  const renamingGroupId = ref<string | null>(null);
  const renamingGroupName = ref('');
  const renamingGroupInputRef = ref<HTMLInputElement | null>(null);

  watch(renamingGroupId, async (val) => {
    if (val) {
      await nextTick();
      renamingGroupInputRef.value?.focus();
    }
  });
  const submittingGroup = ref(false);
  const submittingRename = ref(false);
  const searchKeyword = ref('');
  const filterVisible = ref(false);
  const fieldConfigVisible = ref(false);
  const collapsedGroups = ref<Set<string>>(new Set());
  const expandedTaskIds = ref<Set<string>>(new Set());
  const draggingTaskId = ref('');
  const dragBlockedOnce = ref(false);
  const newTaskInputRef = ref<HTMLInputElement | null>(null);
  const newGroupInputRef = ref<HTMLInputElement | null>(null);

  watch(showNewTaskInput, async (val) => {
    if (val) {
      await nextTick();
      newTaskInputRef.value?.focus();
    }
  });

  watch(showNewGroupInput, async (val) => {
    if (val) {
      await nextTick();
      newGroupInputRef.value?.focus();
    }
  });

  const localGroups = ref<TaskGroup[]>([]);

  function showDragBlockedMessage() {
    import('ant-design-vue').then(({ message }) => {
      message.warning('子事项只能在当前父事项下移动');
    });
  }

  const priorityOptions = PRIORITY_OPTIONS;

  const editingCell = ref<{ taskId: string; field: string } | null>(null);
  const editingValue = ref<any>('');
  const textEditInputRef = ref<any>(null);
  const datePickerVisible = ref(false);
  const pickerEpoch = ref(0);
  const editingDateDayjs = computed(() => {
    if (!editingCell.value) return [null, null] as [any, any];
    const task = findTaskById(editingCell.value.taskId);
    return [task?.startTime ? dayjs(task.startTime.substring(0, 10)) : null, task?.endTime ? dayjs(task.endTime.substring(0, 10)) : null] as [
      any,
      any,
    ];
  });
  const userSelectModalRef = ref();
  const userSelectModalType = ref<'assignee' | 'followers' | 'participant'>('assignee');
  const userSelectModalTaskId = ref('');
  const userSelectModalCurrentIds = ref('');
  const userSelectModalCurrentNames = ref('');

  function getPopupContainer() {
    return document.body;
  }

  function getFirstAssignee(list: TaskList): string {
    if (list.ownerName) {
      return list.ownerName;
    }
    for (const group of list.groups) {
      for (const task of group.tasks) {
        if (task.assigneeName) return task.assigneeName.split(/[,，、]/)[0].trim();
      }
    }
    return '';
  }

  function getAllCollaborators(list: TaskList): string {
    if (list.collaboratorNames) {
      return list.collaboratorNames;
    }
    const names: string[] = [];
    for (const group of list.groups) {
      for (const task of group.tasks) {
        if (task.assigneeName) {
          for (const n of task.assigneeName
            .split(/[,，、]/)
            .map((s) => s.trim())
            .filter(Boolean)) {
            if (!names.includes(n)) names.push(n);
          }
        }
      }
    }
    return names.join(',');
  }

  function getCollaboratorCount(list: TaskList): number {
    const names = getAllCollaborators(list);
    if (!names) return 0;
    return names.split(/[,，、]/).filter(Boolean).length;
  }

  function getFormattedCreateTime(list: TaskList): string {
    if (list.createTimeStr) {
      return list.createTimeStr;
    }
    if (!list.groups.length || !list.groups[0].tasks.length) return '-';
    const task = list.groups[0].tasks[0];
    if (task.createTime) {
      const d = new Date(task.createTime);
      const month = d.getMonth() + 1;
      const day = d.getDate();
      const hours = String(d.getHours()).padStart(2, '0');
      const minutes = String(d.getMinutes()).padStart(2, '0');
      return `${month}月${day}日 ${hours}:${minutes}`;
    }
    return '-';
  }

  const filterConditions = ref<FilterCondition[]>([]);
  const appliedConditions = ref<FilterCondition[]>([]);

  const activeFilterCount = computed(() => {
    return appliedConditions.value.filter((c) => {
      if (c.operator === 'isEmpty' || c.operator === 'isNotEmpty') return true;
      return !!c.value;
    }).length;
  });

  let condIdCounter = 0;

  function addCondition() {
    const fieldConfig = FILTER_FIELD_CONFIGS[0];
    filterConditions.value.push({
      id: `cond-${++condIdCounter}`,
      field: fieldConfig.value,
      operator: fieldConfig.operators[0],
      value: '',
      valueEnd: undefined,
    });
  }

  function removeCondition(idx: number) {
    filterConditions.value.splice(idx, 1);
  }

  function getOperatorsForField(field: FilterFieldType): FilterOperator[] {
    const config = FILTER_FIELD_CONFIGS.find((c) => c.value === field);
    return config ? config.operators : [];
  }

  function getFieldType(field: FilterFieldType): 'text' | 'select' | 'date' {
    const config = FILTER_FIELD_CONFIGS.find((c) => c.value === field);
    return config ? config.type : 'text';
  }

  function needsValueInput(cond: FilterCondition): boolean {
    return cond.operator !== 'isEmpty' && cond.operator !== 'isNotEmpty';
  }

  function onConditionFieldChange(idx: number, field: FilterFieldType) {
    const config = FILTER_FIELD_CONFIGS.find((c) => c.value === field);
    const cond = filterConditions.value[idx];
    if (cond && config) {
      cond.field = field;
      cond.operator = config.operators[0];
      cond.value = '';
      cond.valueEnd = undefined;
    }
  }

  function onConditionOperatorChange(idx: number, operator: FilterOperator) {
    const cond = filterConditions.value[idx];
    if (cond) {
      cond.operator = operator;
      cond.value = '';
      cond.valueEnd = undefined;
    }
  }

  function onConditionValueChange(idx: number, value: any) {
    const cond = filterConditions.value[idx];
    if (cond) {
      cond.value = value;
    }
  }

  function onConditionValueEndChange(idx: number, value: any) {
    const cond = filterConditions.value[idx];
    if (cond) {
      cond.valueEnd = value;
    }
  }

  function applyConditions() {
    appliedConditions.value = filterConditions.value.map((c) => ({ ...c }));
    filterVisible.value = false;
  }

  function clearAllFilters() {
    filterConditions.value = [];
    appliedConditions.value = [];
  }

  const allTaskLists = computed(() => {
    const lists: TaskList[] = [];
    const keyword = searchKeyword.value.trim().toLowerCase();
    for (const group of props.taskListGroups) {
      for (const list of group.taskLists) {
        if (list.delFlag !== 1) {
          if (!keyword || list.name.toLowerCase().includes(keyword)) {
            lists.push(list);
          }
        }
      }
    }
    return lists;
  });

  const collaboratorDisplayNames = computed(() => {
    if (!props.currentList) return '';
    const names: string[] = [];
    if (props.currentList.ownerName) {
      names.push(props.currentList.ownerName);
    }
    if (props.currentList.collaboratorNames) {
      const collabNames = props.currentList.collaboratorNames
        .split(/[,，、]/)
        .map((s) => s.trim())
        .filter(Boolean);
      for (const n of collabNames) {
        if (!names.includes(n)) names.push(n);
      }
    }
    return names.join(',');
  });

  const collaboratorCount = computed(() => {
    if (!collaboratorDisplayNames.value) return 0;
    return collaboratorDisplayNames.value.split(/[,，、]/).filter(Boolean).length;
  });

  const showCollaboratorName = computed(() => collaboratorCount.value === 1);

  function isFavorited(listId: string): boolean {
    return !!props.favoriteIdMap?.has(listId);
  }

  function toggleFavorite(listId: string) {
    if (isFavorited(listId)) {
      const favId = props.favoriteIdMap!.get(listId)!;
      emit('remove-from-favorites', favId);
    } else {
      emit('add-to-favorites', listId);
    }
  }

  const _isOwner = computed(() => props.currentList?.myPermission === '1');
  const canEdit = computed(() => props.currentList?.myPermission === '1' || props.currentList?.myPermission === '2');
  const isReadOnly = computed(() => props.currentList?.myPermission === '3');

  function isListOwner(list: TaskList): boolean {
    return list.myPermission === '1';
  }

  const canShowHeaderMore = computed(() => {
    if (!props.currentList) return false;
    const perm = props.currentList.myPermission;
    return perm === '1' || perm === '2';
  });

  const headerRenaming = ref(false);
  const headerRenameValue = ref('');
  const headerRenameInputRef = ref<HTMLInputElement | null>(null);

  watch(headerRenaming, async (val) => {
    if (val) {
      await nextTick();
      headerRenameInputRef.value?.focus();
    }
  });

  function onHeaderMenuClick({ key }: { key: string }) {
    if (key === 'rename') {
      headerRenaming.value = true;
      headerRenameValue.value = props.currentList?.name || '';
    } else if (key === 'delete') {
      createConfirm({
        iconType: 'warning',
        title: '确认删除',
        content: '删除计划后，其下所有事项也将被删除，确定要删除吗？',
        okText: '确认',
        cancelText: '取消',
        onOk: () => {
          if (props.currentList) {
            emit('delete-list', props.currentList.id);
          }
        },
      });
    }
  }

  function confirmHeaderRename() {
    const name = headerRenameValue.value.trim();
    if (name && props.currentList && name !== props.currentList.name) {
      emit('rename-list', props.currentList.id, name);
    }
    headerRenaming.value = false;
  }

  function _canEditTask(task: TaskItem): boolean {
    if (canEdit.value) return true;
    if (isReadOnly.value && task.assigneeId) {
      const userId = userStore.getUserInfo?.id || '';
      return task.assigneeId.includes(userId);
    }
    return false;
  }

  const taskContentFieldConfigs = computed(() => {
    return FIELD_CONFIG.filter((f) => f.key !== 'listName');
  });

  const visibleFieldConfigs = computed(() => {
    return taskContentFieldConfigs.value.filter((f) => props.visibleFields.includes(f.key));
  });

  const totalFieldsWidth = computed(() => {
    let w = 400 + 24;
    for (const f of visibleFieldConfigs.value) {
      w += parseInt(fieldWidthMap[f.key] || '100', 10);
    }
    return w + 48;
  });

  const fieldWidthMap: Record<string, string> = {
    taskDesc: '140px',
    priority: '120px',
    assigneeName: '120px',
    assignName: '120px',
    participantName: '120px',
    followersName: '120px',
    createBy: '120px',
    startTime: '180px',
    endTime: '180px',
    completeTime: '140px',
    createTime: '160px',
    updateTime: '160px',
    remark: '160px',
    subTaskProgress: '110px',
  };

  const fieldIconMap: Record<string, string> = {
    taskDesc: 'ant-design:align-left-outlined',
    priority: 'ant-design:flag-outlined',
    assigneeName: 'inline-user',
    assignName: 'inline-user',
    participantName: 'inline-user',
    followersName: 'inline-user',
    createBy: 'inline-user',
    startTime: 'ant-design:calendar-outlined',
    endTime: 'ant-design:clock-circle-outlined',
    completeTime: 'ant-design:check-circle-outlined',
    createTime: 'ant-design:plus-circle-outlined',
    updateTime: 'ant-design:sync-outlined',
    remark: 'ant-design:file-text-outlined',
    subTaskProgress: 'ant-design:pie-chart-outlined',
  };

  const statusFilterLabel = computed(() => {
    const map: Record<StatusFilter, string> = { all: '全部', completed: '已完成', uncompleted: '未完成' };
    return map[props.statusFilter];
  });

  const currentSortLabel = computed(() => {
    const opt = SORT_OPTIONS.find((o) => o.value === props.sortField);
    return opt ? opt.label : '';
  });

  const currentGroupLabel = computed(() => {
    const opt = GROUP_OPTIONS.find((o) => o.value === props.groupDimension);
    return opt ? opt.label : '';
  });

  function startEditCell(taskId: string, field: string) {
    if (field === 'startTime' || field === 'endTime') {
      pickerEpoch.value++;
      editingCell.value = { taskId, field };
      datePickerVisible.value = true;
      return;
    }

    cancelEdit();
    editingCell.value = { taskId, field };
  }

  function cancelEdit() {
    editingCell.value = null;
    editingValue.value = '';
    datePickerVisible.value = false;
  }

  function startEditTextCell(taskId: string, field: string) {
    cancelEdit();
    const task = findTaskById(taskId);
    editingValue.value = (task as any)?.[field] || '';
    editingCell.value = { taskId, field };
    nextTick(() => {
      textEditInputRef.value?.focus();
    });
  }

  function confirmEditTextCell() {
    if (!editingCell.value) return;
    const { taskId, field } = editingCell.value;
    const value = editingValue.value?.trim() ?? '';
    emit('update-task-field', taskId, field, value);
    cancelEdit();
  }

  function onPriorityChange(taskId: string, value: any) {
    if (value !== undefined && value !== null) {
      emit('update-task-field', taskId, 'priority', value);
    }
    nextTick(() => cancelEdit());
  }

  function findTaskById(taskId: string): TaskItem | undefined {
    for (const group of props.currentList.groups) {
      for (const t of group.tasks) {
        if (t.id === taskId) return t;
      }
    }
    return undefined;
  }

  function openUserSelectModal(type: 'assignee' | 'followers' | 'participant', taskId: string) {
    userSelectModalType.value = type;
    userSelectModalTaskId.value = taskId;

    const task = findTaskById(taskId);
    if (type === 'assignee') {
      userSelectModalCurrentIds.value = task?.assigneeId || '';
      userSelectModalCurrentNames.value = task?.assigneeName || '';
    } else if (type === 'participant') {
      userSelectModalCurrentIds.value = task?.participantId || '';
      userSelectModalCurrentNames.value = task?.participantName || '';
    } else {
      userSelectModalCurrentIds.value = task?.followersId || '';
      userSelectModalCurrentNames.value = task?.followersName || '';
    }

    nextTick(() => {
      userSelectModalRef.value?.open();
    });
  }

  function onUserSelectConfirm(userIds: string, userNames: string) {
    const type = userSelectModalType.value;
    const taskId = userSelectModalTaskId.value;
    if (taskId) {
      if (type === 'assignee') {
        emit('update-task-fields', taskId, { assigneeId: userIds, assigneeName: userNames });
      } else if (type === 'participant') {
        emit('update-task-fields', taskId, { participantId: userIds, participantName: userNames });
      } else {
        emit('update-task-fields', taskId, { followersId: userIds, followersName: userNames });
      }
    }
  }

  function getDisabledEndDate(taskId: string) {
    return (current: any) => {
      if (!current) return false;
      const task = findTaskById(taskId);
      if (!task?.startTime) return false;
      const startDate = dayjs(task.startTime.substring(0, 10));
      return current.isBefore(startDate, 'day');
    };
  }

  function clearDateCell(field: string) {
    const taskId = editingCell.value?.taskId;
    if (!taskId) return;
    const task = findTaskById(taskId);
    const updates: Record<string, string> = {};
    if (field === 'startTime') {
      updates.startTime = '';
      updates.endTime = normalizeDate(task?.endTime);
    } else if (field === 'endTime') {
      updates.startTime = normalizeDate(task?.startTime);
      updates.endTime = '';
    }
    emit('update-task-fields', taskId, updates);
    pickerEpoch.value++;
    datePickerVisible.value = false;
    editingCell.value = null;
  }

  function onDateCellChange(date: any, field: string) {
    const taskId = editingCell.value?.taskId;
    if (!taskId) return;
    const task = findTaskById(taskId);
    const value = date ? dayjs(date).format('YYYY-MM-DD') + ' 00:00:00' : '';
    const updates: Record<string, string> = {};
    if (field === 'startTime') {
      updates.startTime = value;
      updates.endTime = normalizeDate(task?.endTime);
    } else if (field === 'endTime') {
      updates.startTime = normalizeDate(task?.startTime);
      updates.endTime = value;
    }
    emit('update-task-fields', taskId, updates);
    cancelEdit();
  }

  function onCellPickerOpenChange(open: boolean) {
    if (!open) {
      const epoch = pickerEpoch.value;
      setTimeout(() => {
        if (pickerEpoch.value === epoch && editingCell.value) {
          cancelEdit();
        }
      }, 100);
    }
  }

  function normalizeDate(val: string | null | undefined): string {
    if (!val) return '';
    if (val.length >= 19) return val.substring(0, 19);
    return val.substring(0, 10) + ' 00:00:00';
  }

  function onTaskDragStart(evt: any) {
    dragBlockedOnce.value = false;
    const taskId = evt.item?.dataset?.taskId;
    if (taskId) draggingTaskId.value = taskId;
    const task = findTaskById(taskId);
    if (task && task.childCount > 0) {
      expandedTaskIds.value.delete(taskId);
    }
  }

  function onTaskDragEnd(evt: any) {
    const { oldIndex, newIndex } = evt;
    draggingTaskId.value = '';

    if (dragBlockedOnce.value) {
      dragBlockedOnce.value = false;
      showDragBlockedMessage();
      return;
    }

    if (oldIndex === newIndex && evt.from === evt.to) {
      return;
    }

    const movedElement = evt.item;
    const taskId = movedElement?.dataset?.taskId;
    if (!taskId) {
      return;
    }

    const toContainer = evt.to;
    const targetGroupEl = toContainer?.closest('[data-group-id]');
    const targetGroupId = targetGroupEl?.dataset?.groupId || '';

    const targetGroup = computedGroups.value.find((g) => g.id === targetGroupId);
    if (!targetGroup) {
      return;
    }

    const rawFlatList = flattenGroupTasks(targetGroup.tasks);
    const groupIds = new Set(computedGroups.value.map((g) => g.id));

    const draggedIdx = rawFlatList.findIndex((f) => f.task.id === taskId);
    let flatList: FlatTaskItem[];
    if (draggedIdx !== -1) {
      flatList = [...rawFlatList];
      const [item] = flatList.splice(draggedIdx, 1);
      flatList.splice(newIndex, 0, item);
    } else {
      flatList = rawFlatList;
    }

    let draggedTask: TaskItem | null = null;
    if (draggedIdx !== -1) {
      draggedTask = flatList[newIndex]?.task || null;
    } else {
      for (const g of computedGroups.value) {
        const found = flattenGroupTasks(g.tasks).find((f) => f.task.id === taskId);
        if (found) {
          draggedTask = found.task;
          break;
        }
      }
    }
    if (!draggedTask) {
      return;
    }

    const isSubtask = !!(draggedTask.pid && !groupIds.has(draggedTask.pid));

    let targetPid: string;
    let targetSortOrder = 0;

    if (isSubtask) {
      targetPid = draggedTask.pid;
      for (let i = 0; i < newIndex; i++) {
        if (flatList[i].task.pid === targetPid) {
          targetSortOrder++;
        }
      }
      targetSortOrder += 1;
    } else {
      targetPid = targetGroupId && targetGroupId !== '__default__' ? targetGroupId : '';
      for (let i = 0; i < newIndex; i++) {
        const ft = flatList[i];
        const isTopLevel = !ft.task.pid || ft.task.pid === ft.task.groupId || groupIds.has(ft.task.pid);
        if (isTopLevel) {
          targetSortOrder++;
        }
      }
      targetSortOrder += 1;
    }

    emit('move-task', taskId, targetPid, targetSortOrder);
  }

  function onTaskMove(evt: any) {
    const relatedEl = evt.related as HTMLElement;
    const draggedTaskId = draggingTaskId.value;
    if (!draggedTaskId) {
      return true;
    }

    const draggedTask = findTaskById(draggedTaskId);
    if (!draggedTask) {
      return true;
    }

    const groupIds = new Set(computedGroups.value.map((g) => g.id));
    const isSubtask = !!(draggedTask.pid && !groupIds.has(draggedTask.pid));

    if (!isSubtask) {
      return true;
    }

    if (!relatedEl) {
      dragBlockedOnce.value = true;
      return false;
    }

    const relatedTaskId = relatedEl.dataset?.taskId;
    if (!relatedTaskId) {
      dragBlockedOnce.value = true;
      return false;
    }

    const relatedTask = findTaskById(relatedTaskId);
    if (!relatedTask) {
      dragBlockedOnce.value = true;
      return false;
    }

    if (relatedTask.pid !== draggedTask.pid) {
      dragBlockedOnce.value = true;
      return false;
    }

    return true;
  }

  function isChildHidden(ft: FlatTaskItem): boolean {
    if (!draggingTaskId.value) return false;
    return ft.task.pid === draggingTaskId.value;
  }

  function onGroupDragEnd(evt: any) {
    const { oldIndex, newIndex } = evt;
    if (oldIndex === newIndex) return;

    const groupId = localGroups.value[newIndex]?.id;
    if (!groupId) return;

    // 拖拽结束后收起分组，防止展开跳动
    collapsedGroups.value.add(groupId);

    emit('move-task-group', groupId, newIndex + 1);
  }

  const computedGroups = computed(() => {
    if (!props.currentList || !Array.isArray(props.currentList.groups)) return [];

    let allTasks: TaskItem[] = [];
    for (const g of props.currentList.groups) {
      allTasks = allTasks.concat(g.tasks || []);
    }

    if (props.statusFilter === 'completed') {
      allTasks = allTasks.filter((t) => t.completed);
    } else if (props.statusFilter === 'uncompleted') {
      allTasks = allTasks.filter((t) => !t.completed);
    }

    allTasks = applyFilterToTasks(allTasks);

    if (props.groupDimension === 'custom') {
      const groups: TaskGroup[] = props.currentList.groups.map((g) => ({
        ...g,
        tasks: allTasks.filter((t) => t.groupId === g.id).sort((a, b) => (a.sortOrder ?? 0) - (b.sortOrder ?? 0)),
        collapsed: collapsedGroups.value.has(g.id),
      }));
      groups.sort((a, b) => (a.sortOrder ?? 0) - (b.sortOrder ?? 0));
      return groups;
    }

    allTasks = applySortToTasks(allTasks);

    if (props.groupDimension === 'none') {
      return [
        {
          id: 'none-group',
          name: '',
          taskListId: props.currentList.id,
          collapsed: false,
          tasks: allTasks,
        },
      ];
    }

    return computeDynamicGroups(allTasks);
  });

  watch(
    computedGroups,
    (newGroups) => {
      localGroups.value = [...newGroups];
    },
    { immediate: true }
  );

  function flattenGroupTasks(tasks?: TaskItem[]): FlatTaskItem[] {
    const sourceTasks = tasks || [];
    const result: FlatTaskItem[] = [];
    const topTasks = sourceTasks.filter((t) => !t.pid || t.pid === t.groupId).sort((a, b) => (a.sortOrder ?? 0) - (b.sortOrder ?? 0));
    for (const task of topTasks) {
      result.push({ task, level: 0 });
      if (expandedTaskIds.value.has(task.id)) {
        collectChildren(sourceTasks, task.id, 1, result);
      }
    }
    return result;
  }

  function collectChildren(allTasks: TaskItem[], parentId: string, level: number, result: FlatTaskItem[]) {
    if (level >= MAX_TASK_DEPTH) return;
    const children = allTasks.filter((t) => t.pid === parentId).sort((a, b) => (a.sortOrder ?? 0) - (b.sortOrder ?? 0));
    for (const child of children) {
      result.push({ task: child, level });
      if (expandedTaskIds.value.has(child.id)) {
        collectChildren(allTasks, child.id, level + 1, result);
      }
    }
  }

  function toggleTaskExpand(taskId: string) {
    if (expandedTaskIds.value.has(taskId)) {
      expandedTaskIds.value.delete(taskId);
    } else {
      expandedTaskIds.value.add(taskId);
    }
  }

  function countTopLevelTasks(tasks: TaskItem[]): number {
    return tasks.filter((t) => !t.pid || t.pid === t.groupId).length;
  }

  function applyFilterToTasks(tasks: TaskItem[]): TaskItem[] {
    let result = tasks;
    for (const cond of appliedConditions.value) {
      if (cond.operator !== 'isEmpty' && cond.operator !== 'isNotEmpty' && !cond.value) continue;
      const fieldName = cond.field as keyof TaskItem;
      if (cond.operator === 'isEmpty') {
        result = result.filter((t) => !t[fieldName]);
      } else if (cond.operator === 'isNotEmpty') {
        result = result.filter((t) => !!t[fieldName]);
      } else if (cond.operator === 'contains') {
        result = result.filter((t) => String(t[fieldName] || '').includes(String(cond.value)));
      } else if (cond.operator === 'equals') {
        result = result.filter((t) => String(t[fieldName] || '') === String(cond.value));
      } else if (cond.operator === 'before') {
        const target = cond.value ? cond.value.format('YYYY-MM-DD') : '';
        result = result.filter((t) => {
          const v = String(t[fieldName] || '').substring(0, 10);
          return v && v < target;
        });
      } else if (cond.operator === 'after') {
        const target = cond.value ? cond.value.format('YYYY-MM-DD') : '';
        result = result.filter((t) => {
          const v = String(t[fieldName] || '').substring(0, 10);
          return v && v > target;
        });
      } else if (cond.operator === 'between') {
        const start = cond.value ? cond.value.format('YYYY-MM-DD') : '';
        const end = cond.valueEnd ? cond.valueEnd.format('YYYY-MM-DD') : '';
        if (start && end) {
          result = result.filter((t) => {
            const v = String(t[fieldName] || '').substring(0, 10);
            return v && v >= start && v <= end;
          });
        }
      }
    }
    return result;
  }

  function applySortToTasks(tasks: TaskItem[]): TaskItem[] {
    const field = props.sortField;
    if (field === 'sortOrder') {
      return [...tasks].sort((a, b) => (a.sortOrder ?? 0) - (b.sortOrder ?? 0));
    }
    const dir = props.sortDirection === 'asc' ? 1 : -1;
    return [...tasks].sort((a, b) => {
      const va = (a as any)[field] || '';
      const vb = (b as any)[field] || '';
      if (va < vb) return -1 * dir;
      if (va > vb) return 1 * dir;
      return 0;
    });
  }

  function computeDynamicGroups(tasks: TaskItem[]): TaskGroup[] {
    const groupMap = new Map<string, TaskItem[]>();
    for (const task of tasks) {
      const key = getGroupKey(task, props.groupDimension);
      if (!groupMap.has(key)) groupMap.set(key, []);
      groupMap.get(key)!.push(task);
    }
    const groups: TaskGroup[] = [];
    groupMap.forEach((groupTasks, key) => {
      groups.push({
        id: `dynamic-${key}`,
        name: key || '未分组',
        taskListId: props.currentList!.id,
        collapsed: collapsedGroups.value.has(`dynamic-${key}`),
        tasks: groupTasks,
      });
    });
    return groups;
  }

  function getGroupKey(task: TaskItem, dimension: GroupDimension): string {
    switch (dimension) {
      case 'assignee':
        return task.assigneeName || '未分配';
      case 'startTime':
        return task.startTime ? task.startTime.substring(0, 10) : '无日期';
      case 'endTime':
        return task.endTime ? task.endTime.substring(0, 10) : '无日期';
      case 'createBy':
        return task.createBy || '未知';
      case 'priority':
        return task.priority || '无优先级';
      default:
        return '默认';
    }
  }

  function getFieldValue(task: TaskItem, key: FieldKey): string {
    return (task as any)[key] || '';
  }

  function toggleGroupCollapse(groupId: string) {
    if (collapsedGroups.value.has(groupId)) {
      collapsedGroups.value.delete(groupId);
    } else {
      collapsedGroups.value.add(groupId);
    }
  }

  function submitNewTask(groupId: string) {
    const title = newTaskTitle.value.trim();
    newTaskTitle.value = '';
    showNewTaskInput.value = null;
    if (title) {
      emit('create-task', groupId, title);
    }
  }

  function submitNewGroup() {
    if (submittingGroup.value) return;
    submittingGroup.value = true;
    const name = newGroupName.value.trim();
    newGroupName.value = '';
    showNewGroupInput.value = false;
    if (name && props.currentList) {
      emit('add-group', props.currentList.id, name);
    }
    setTimeout(() => {
      submittingGroup.value = false;
    }, 300);
  }

  function onGroupMenuClick(key: string, groupId: string, groupName: string) {
    if (key === 'rename') {
      renamingGroupId.value = groupId;
      renamingGroupName.value = groupName;
    } else if (key === 'delete') {
      createConfirm({
        iconType: 'warning',
        title: '确认删除',
        content: '删除分组后，分组内的事项将合并到默认分组，确定要删除吗？',
        okText: '确认',
        cancelText: '取消',
        onOk: () => {
          emit('delete-group', groupId);
        },
      });
    }
  }

  function confirmRenameGroup() {
    if (submittingRename.value) return;
    submittingRename.value = true;
    const name = renamingGroupName.value.trim();
    const groupId = renamingGroupId.value;
    renamingGroupId.value = null;
    renamingGroupName.value = '';
    if (name && groupId) {
      emit('rename-group', groupId, name);
    }
    setTimeout(() => {
      submittingRename.value = false;
    }, 300);
  }

  function onStatusFilterChange({ key }: { key: string }) {
    emit('update:statusFilter', key as StatusFilter);
  }

  function onSortChange({ key }: { key: string }) {
    if (props.sortField === key) {
      if (key !== 'sortOrder') {
        emit('update:sortDirection', props.sortDirection === 'asc' ? 'desc' : 'asc');
      }
    } else {
      emit('update:sortField', key as SortField);
      emit('update:sortDirection', 'desc');
    }
  }

  function onSortDirectionChange(field: string, dir: string) {
    emit('update:sortField', field as SortField);
    emit('update:sortDirection', dir as SortDirection);
  }

  function onGroupChange({ key }: { key: string }) {
    emit('update:groupDimension', key as GroupDimension);
  }

  function toggleField(fieldKey: FieldKey) {
    const fields = [...props.visibleFields];
    const idx = fields.indexOf(fieldKey);
    if (idx >= 0) {
      fields.splice(idx, 1);
    } else {
      fields.push(fieldKey);
    }
    emit('update:visibleFields', fields);
  }
</script>

<style lang="less" scoped>
  .task-content {
    position: relative;
    flex: 1;
    height: 100%;
    background: #fff;
    display: flex;
    flex-direction: column;
    overflow: hidden;
  }

  .content-header {
    padding: 16px 24px;
    border-bottom: 1px solid #f0f0f0;
    display: flex;
    align-items: center;
    justify-content: space-between;
  }

  .content-header-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    width: 100%;
  }

  .content-header-row-left {
    display: flex;
    align-items: center;
    gap: 4px;
  }

  .add-list-btn {
    color: #888;
    font-size: 16px;
    padding: 0 4px;
    &:hover {
      color: #1677ff;
    }
  }

  .list-search-input {
    width: 220px;
    margin-left: 16px;
  }

  .help-btn {
    color: #888;
    font-size: 18px;
    &:hover {
      color: #1677ff;
    }
  }

  .content-header-actions {
    display: flex;
    align-items: center;
    gap: 8px;
  }

  .header-secret-badge {
    font-size: 11px;
    padding: 0 6px;
    border-radius: 3px;
    border: 1px solid;
    line-height: 18px;
    flex-shrink: 0;
  }

  .collaborator-bar {
    display: flex;
    align-items: center;
    gap: 4px;
    cursor: pointer;
    padding: 2px 4px 2px 8px;
    border-radius: 6px;
    border: 1px solid #e8e8e8;
    transition: all 0.15s;

    &:hover {
      border-color: #d0d0d0;
      background: #fafafa;
    }

    &.disabled {
      cursor: not-allowed;
      opacity: 0.5;

      &:hover {
        border-color: #e8e8e8;
        background: transparent;
      }
    }
  }

  .collab-btn {
    color: #595959 !important;
    font-size: 13px;
    padding: 0 4px;
    height: 28px;
  }

  .content-title {
    margin: 0;
    font-size: 18px;
    font-weight: 600;
    color: #1f1f1f;
  }

  .header-more-btn {
    color: #1f1f1f;
    font-size: 18px;
    margin-left: 6px;
    &:hover {
      color: #3370ff;
      background: #f0f0f0;
    }
  }

  .content-header-left {
    display: flex;
    align-items: center;
    gap: 0;
  }

  .content-header-rename-row {
    flex-shrink: 0;
  }

  .content-toolbar {
    padding: 8px 24px;
    border-bottom: 1px solid #f0f0f0;
    display: flex;
    gap: 8px;
    align-items: center;
    flex-wrap: wrap;
  }

  .filter-badge {
    display: inline-block;
    margin-left: 4px;
    padding: 0 6px;
    font-size: 12px;
    line-height: 18px;
    background: #1677ff;
    color: #fff;
    border-radius: 9px;
  }

  .filter-panel {
    .filter-panel-header {
      display: flex;
      justify-content: space-between;
      align-items: center;
      margin-bottom: 12px;
    }
    .filter-panel-title {
      font-weight: 600;
      font-size: 14px;
    }
    .filter-panel-footer {
      display: flex;
      justify-content: flex-end;
      gap: 8px;
      margin-top: 12px;
      padding-top: 12px;
      border-top: 1px solid #f0f0f0;
    }
  }

  .filter-condition-row {
    display: flex;
    align-items: center;
    gap: 6px;
    margin-bottom: 8px;
  }

  .filter-field-select {
    width: 100px;
    flex-shrink: 0;
  }

  .filter-op-select {
    width: 80px;
    flex-shrink: 0;
  }

  .filter-value-input {
    flex: 1;
    min-width: 100px;
  }

  .filter-value-input-short {
    width: 100px;
  }

  .filter-range-sep {
    color: #999;
    font-size: 12px;
    flex-shrink: 0;
  }

  .filter-condition-remove {
    width: 24px;
    height: 24px;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    color: #999;
    border-radius: 4px;
    flex-shrink: 0;
    transition:
      color 0.15s,
      background 0.15s;
    &:hover {
      color: #ff4d4f;
      background: #fff1f0;
    }
  }

  .filter-add-btn {
    margin-bottom: 4px;
    color: #1677ff;
    border-color: #1677ff;
  }

  .sort-menu-item {
    display: flex;
    align-items: center;
    justify-content: space-between;
    width: 160px;
  }

  .sort-arrows-row {
    display: flex;
    align-items: center;
    gap: 4px;
  }

  .sort-arrow-btn {
    width: 24px;
    height: 24px;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    border-radius: 4px;
    color: #bfbfbf;
    font-size: 14px;
    transition:
      color 0.15s,
      background 0.15s;
    &.active {
      color: #1677ff;
      background: #e6f4ff;
    }
    &:hover {
      color: #1677ff;
    }
  }

  .field-config-panel {
    .field-config-title {
      font-weight: 600;
      margin-bottom: 8px;
      padding-bottom: 8px;
      border-bottom: 1px solid #f0f0f0;
    }
    .field-config-item {
      display: flex;
      align-items: center;
      justify-content: space-between;
      padding: 6px 4px;
      cursor: pointer;
      border-radius: 4px;
      &:hover {
        background: #f5f5f5;
      }
    }
    .field-visible {
      color: #1677ff;
    }
    .field-hidden {
      color: #d9d9d9;
    }
  }

  .content-table-header {
    padding: 0 8px;
    height: 40px;
    background: #fafafa;
    border-bottom: 1px solid #e5e6eb;
    display: flex;
    align-items: stretch;
    font-size: 14px;
    color: #1f2329;
    font-weight: 500;
    flex-shrink: 0;
    position: sticky;
    top: 0;
    z-index: 10;

    .header-col {
      padding: 0 12px;
      gap: 4px;
      box-sizing: border-box;
    }

    .header-icon {
      color: #8f959e;
      font-size: 14px;
      flex-shrink: 0;
    }

    .header-label {
      white-space: nowrap;
      overflow: visible;
    }
  }

  .table-col {
    flex-shrink: 0;
    display: flex;
    align-items: center;
    height: 100%;
  }

  .table-col-drag-handle {
    width: 24px;
    min-width: 24px;
    justify-content: center;
  }

  .drag-handle {
    cursor: grab;
    color: transparent;
    transition: color 0.15s;
    font-size: 14px;
    display: flex;
    align-items: center;
    justify-content: center;

    &:active {
      cursor: grabbing;
    }
  }

  .task-row:hover .drag-handle {
    color: #c0c4cc;
  }

  .drag-handle:hover {
    color: #3370ff;
  }

  .sortable-chosen {
    background: #e6f4ff !important;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
  }

  .sortable-ghost {
    opacity: 0.3;
  }

  .table-col-title {
    width: 400px;
    min-width: 400px;
    height: 100%;
    display: flex;
    align-items: center;
    gap: 8px;
    cursor: pointer;
  }

  .content-body {
    flex: 1;
    overflow: auto;
    display: flex;
    flex-direction: column;
    min-height: 0;
  }

  .task-group-header {
    display: flex;
    align-items: center;
    padding: 6px 8px;
    cursor: pointer;
    border-bottom: 1px solid #f0f0f0;
    font-size: 14px;
    font-weight: 600;
    color: #1f2329;
    &:hover {
      background: #f5f6f7;
    }
    .group-drag-handle {
      cursor: grab;
      font-size: 14px;
      color: transparent;
      transition: color 0.15s;
      display: flex;
      align-items: center;
      justify-content: center;
      width: 24px;
      min-width: 24px;
      flex-shrink: 0;
      &:active {
        cursor: grabbing;
      }
    }
    &:hover .group-drag-handle {
      color: #c0c4cc;
    }
    .group-drag-handle:hover {
      color: #8c8c8c;
    }
    .table-col-title {
      font-weight: 600;
      color: #1f2329;
      .expand-arrow {
        color: #8f959e;
      }
    }
    .group-name {
      color: #1f2329;
      font-weight: 600;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
      flex-shrink: 1;
      min-width: 0;
    }
    .group-count {
      color: #8f959e;
      font-weight: 400;
      font-size: 13px;
      flex-shrink: 0;
      margin-left: 4px;
    }
    .group-actions {
      display: flex;
      align-items: center;
      gap: 2px;
      flex-shrink: 0;
      opacity: 0;
      transition: opacity 0.15s;
    }
    &:hover .group-actions {
      opacity: 1;
    }
    .group-action-btn {
      width: 24px;
      height: 24px;
      display: flex;
      align-items: center;
      justify-content: center;
      border-radius: 4px;
      cursor: pointer;
      color: #8f959e;
      font-size: 14px;
      transition: all 0.15s;
      &:hover {
        background: #e8e8e8;
        color: #595959;
      }
    }
  }

  .task-row {
    display: flex;
    align-items: stretch;
    height: 46px;
    padding: 0 8px;
    border-bottom: 1px solid #f0f0f0;
    font-size: 14px;
    box-sizing: border-box;
    &:hover {
      background: #f5f6f7;
    }
  }

  .task-indent {
    display: flex;
    align-items: center;
    gap: 4px;
    flex-shrink: 0;
  }

  .expand-arrow {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 16px;
    height: 16px;
    font-size: 11px;
    color: #999;
    cursor: pointer;
    border-radius: 2px;
    &:hover {
      color: #1677ff;
      background: #e6f4ff;
    }
  }

  .expand-arrow-placeholder {
    width: 16px;
    height: 16px;
  }

  .task-checkbox {
    width: 18px;
    height: 18px;
    border-radius: 50%;
    border: 2px solid #d9d9d9;
    background: none;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: all 0.2s;
    padding: 0;
    flex-shrink: 0;
    &:hover {
      border-color: #52c41a;
    }
    &.checked {
      background: #52c41a;
      border-color: #52c41a;
      color: #fff;
      font-size: 11px;
    }
    &:disabled {
      cursor: not-allowed;
      opacity: 0.5;
      &:hover {
        border-color: #d9d9d9;
      }
    }
  }

  .task-title {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    color: #1f2329;
    flex-shrink: 1;
    min-width: 0;
    padding: 2px 6px;
    border-radius: 4px;
    transition: background 0.15s;
    &.completed {
      text-decoration: line-through;
      color: #c0c4cc;
    }
  }

  .table-col-title:hover .task-title {
    background: #fff;
  }

  .task-child-count {
    font-size: 11px;
    color: #8c8c8c;
    margin-left: 6px;
    white-space: nowrap;
    flex-shrink: 0;
    display: inline-flex;
    align-items: center;
    gap: 2px;
  }

  .child-count-icon {
    font-size: 12px;
    color: #8f959e;
  }

  .task-field {
    color: #646a73;
    overflow: visible;
    padding: 0;
    box-sizing: border-box;
  }

  .task-field-completed {
    color: #c0c4cc;
  }

  .overdue-date {
    color: #f5222d !important;
    font-weight: 500;
  }

  .cell-inner {
    display: flex;
    align-items: center;
    width: 100%;
    height: 100%;
    padding: 0 12px;
    border: 1px solid transparent;
    border-radius: 4px;
    transition: border-color 0.15s;
    box-sizing: border-box;
    &:hover {
      border-color: #d0d3d6;
    }
  }

  .date-cell-wrapper {
    position: relative;
  }

  .inline-date-picker {
    width: 100% !important;

    :deep(.ant-picker) {
      width: 100%;
    }

    :deep(.ant-picker-footer-extra) {
      display: flex;
      justify-content: flex-end;
    }
  }

  .editable-cell {
    cursor: pointer;
    &:hover {
      border-color: #d0d3d6;
    }

    .inline-editor {
      width: 100%;
      :deep(.ant-select) {
        min-width: 80px;
      }
    }

    .inline-text-editor {
      width: 100%;
      :deep(.ant-input) {
        font-size: 13px;
      }
    }
  }

  .dropdown-cell {
    position: relative;
    &::after {
      content: '';
      position: absolute;
      right: 6px;
      top: 50%;
      transform: translateY(-50%);
      border: 4px solid transparent;
      border-top-color: #999;
      opacity: 0;
      transition: opacity 0.15s;
      pointer-events: none;
    }
    &:hover::after {
      opacity: 1;
    }
  }

  .readonly-cell {
    &:hover {
      border-color: transparent;
    }
  }

  .date-picker-footer {
    display: flex;
    justify-content: flex-end;
    padding: 0 4px;
  }

  .field-value {
    color: #646a73;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }

  .field-placeholder {
    color: #c0c4cc;
    font-size: 14px;
  }

  .priority-tag {
    display: inline-block;
    padding: 1px 8px;
    border-radius: 4px;
    font-size: 14px;
    line-height: 20px;
    white-space: nowrap;
    font-weight: 500;
    &.task-field-completed {
      opacity: 0.5;
    }
  }

  .priority-menu-item {
    display: flex;
    align-items: center;
    min-height: 32px;
  }

  .field-icon-placeholder {
    color: #8f959e;
    font-size: 16px;
    display: flex;
    align-items: center;
    transition: color 0.15s;
    &:hover {
      color: #3370ff;
    }
  }

  .assignee-single-wrap {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 2px 8px 2px 2px;
    background: #f5f6f7;
    border-radius: 14px;
    max-width: 100%;
    overflow: hidden;

    &.task-field-completed {
      opacity: 0.5;
    }
  }

  .assignee-display {
    display: flex;
    align-items: center;
    gap: 0;
    overflow: hidden;
  }

  .assignee-avatar {
    width: 24px;
    height: 24px;
    border-radius: 50%;
    background: #3370ff;
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    position: relative;
    box-shadow: 0 0 0 2px #fff;

    .avatar-text {
      font-size: 12px;
      color: #fff;
      font-weight: 500;
      line-height: 1;
    }

    &.task-field-completed {
      opacity: 0.5;
    }
  }

  .assignee-name {
    color: #646a73;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }

  .assignee-overflow {
    width: 24px;
    height: 24px;
    border-radius: 50%;
    background: #f0f0f0;
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    font-size: 10px;
    color: #8c8c8c;
    font-weight: 500;
    margin-left: -8px;
    position: relative;
    z-index: 0;
    box-shadow: 0 0 0 2px #fff;
  }

  .progress-cell {
    display: flex;
    align-items: center;
  }

  .progress-bar {
    width: 60px;
    height: 6px;
    background: #f0f0f0;
    border-radius: 3px;
    overflow: hidden;
  }

  .progress-bar-fill {
    height: 100%;
    border-radius: 3px;
    transition: width 0.3s ease;
  }

  .new-task-btn,
  .new-task-input-row {
    cursor: pointer;
    color: #999;
  }

  .new-task-plus {
    color: #bbb;
    margin-right: 8px;
    font-size: 12px;
  }

  .new-task-text {
    font-size: 14px;
  }

  .new-group-row {
    display: flex;
    align-items: center;
    padding: 12px 8px;
    font-size: 14px;
    &.new-group-btn {
      cursor: pointer;
      color: #999;
      &:hover {
        background: #fafafa;
      }
    }
  }

  .list-group-view {
    flex: 1;
    overflow: auto;
  }

  .list-group-table-header {
    display: flex;
    align-items: center;
    height: 40px;
    padding: 0 24px;
    background: #fafafa;
    border-bottom: 1px solid #e5e6eb;
    font-size: 14px;
    color: #1f2329;
    font-weight: 500;
    position: sticky;
    top: 0;
    z-index: 10;
  }

  .list-group-col {
    flex-shrink: 0;
    display: flex;
    align-items: center;
    height: 100%;
    overflow: hidden;
  }

  .list-group-col-name {
    flex: 1;
    min-width: 200px;
    gap: 8px;
  }

  .list-group-col-owner {
    width: 120px;
    padding: 0 12px;
  }

  .list-group-col-collaborators {
    width: 120px;
    padding: 0 12px;
    display: flex;
    align-items: center;
    gap: 4px;

    &.clickable {
      cursor: pointer;
      border-radius: 4px;
      transition: background 0.15s;

      &:hover {
        background: #f0f5ff;
      }
    }
  }

  .collab-add-icon {
    font-size: 20px;
    color: #bfbfbf;
    transition: color 0.15s;

    .clickable:hover & {
      color: #3370ff;
    }
  }

  .list-group-col-secret {
    width: 80px;
    padding: 0 12px;
    text-align: center;
  }

  .secret-badge-inline {
    font-size: 11px;
    padding: 1px 6px;
    border-radius: 3px;
    border: 1px solid;
    line-height: 16px;
    white-space: nowrap;
  }

  .list-group-col-time {
    width: 160px;
    padding: 0 12px;
  }

  .pin-btn-wrap {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    padding: 2px;
    border-radius: 4px;
    margin-left: 4px;
    flex-shrink: 0;
    transition: background 0.12s;

    &:hover {
      background: #e8e8e8;
    }
  }

  .pin-svg {
    font-size: 14px;
    transition: color 0.12s;
  }

  .pin-svg-outlined {
    color: #bfbfbf;
    opacity: 0;
    transition: all 0.15s;

    .pin-btn-wrap:hover & {
      color: #3370ff;
    }
  }

  .pin-svg-filled {
    color: #3370ff;
  }

  .list-group-row:hover .pin-svg-outlined {
    opacity: 1;
  }

  .list-group-row {
    display: flex;
    align-items: center;
    height: 46px;
    padding: 0 24px;
    border-bottom: 1px solid #f0f0f0;
    font-size: 14px;
    cursor: pointer;
    &:hover {
      background: #f5f6f7;
    }
  }

  .list-icon {
    color: #8f959e;
    flex-shrink: 0;
  }

  .list-name-text {
    color: #1f2329;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .empty-state {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    padding: 60px 24px;
    color: #bbb;
    .empty-icon {
      font-size: 48px;
      margin-bottom: 12px;
    }
    p {
      margin: 0;
      font-size: 14px;
    }
  }
</style>
