// ============================================================
// TaskHistory — 任务记录列表（分页 + 列头筛选 + 排序 + 滚动）
//
// 筛选策略（混合模式）：
//   - 服务端筛选（传 API）：status、model_id、user_id（企业版）
//   - 客户端筛选（antd onFilter）：output_type、provider_name
//   - 服务端不支持日期查询，时间范围筛选暂用 sorter 排序替代
//   - 企业版才会在"用户"列显示筛选菜单
//
// 使用 antd Table onChange 统一管理 pagination + filters + sorter，
// 参考 table-test.tsx 示例 4 的模式。
// ============================================================

import { useState, useRef, useEffect, useCallback, useMemo } from 'react';
import { Table, Tag, Typography, theme as antTheme, Popover, Checkbox, Button, Space } from 'antd';
import type { ColumnsType, TableProps } from 'antd/es/table';
import type { FilterValue, SorterResult } from 'antd/es/table/interface';
import { HistoryOutlined, SearchOutlined, SettingOutlined } from '@ant-design/icons';

import { useTaskList, useModelList } from '@/hooks/use-api';
import { useAppContext } from '@/contexts/app-context';
import { useHomeContext } from '@/contexts/home-context';
import { useTheme } from '@/hooks/use-theme';
import { on, off, EVENTS } from '@/utils/event-bus';
import type {
   TaskInfoItemResponseBody,
   TaskListRequestParams,
   TaskStatusString,
} from '@/services/modules';
import { TaskStatusMap, OutputTypeMap } from '@/services/modules';

const { Text } = Typography;

// ---------- 辅助类型 ----------

/** antd Table onChange 中 filters 的类型 */
type TableFilters = Record<string, FilterValue | null>;

/** antd Table onChange 中 sorter 的类型（单列排序） */
interface TableSorter {
   field?: React.Key | readonly React.Key[];
   order?: 'ascend' | 'descend';
}

/** 表格参数（缓存 antd onChange 的结果，用于构建 API 请求） */
interface TableParams {
   pagination: { current: number; pageSize: number };
   filters: TableFilters;
   sorter: TableSorter;
}

// ---------- 常量 ----------

/** 状态筛选选项（来自 API 枚举） */
const STATUS_FILTERS = Object.entries(TaskStatusMap).map(([value, label]) => ({
   text: label,
   value,
}));

/** 输出类型筛选选项（从 OutputTypeMap 派生） */
const OUTPUT_TYPE_FILTERS = Object.entries(OutputTypeMap).map(([value, text]) => ({
   text,
   value,
}));

/** 状态标签配置（渲染用，含未来未知状态的降级处理） */
const STATUS_CONFIG: Record<string, { color: string; label: string }> = {
   pending: { color: 'blue', label: '排队中' },
   submitted: { color: 'cyan', label: '已提交' },
   processing: { color: 'orange', label: '处理中' },
   success: { color: 'green', label: '已完成' },
   failed: { color: 'red', label: '失败' },
};

/** 未知状态降级：后端新增状态时自动回退显示 key 原文 */
function resolveStatus(s: string): { color: string; label: string } {
   return STATUS_CONFIG[s] || { color: 'default', label: s };
}

// ---------- 列可见性持久化 ----------

/** localStorage 键名（遵循 ele-heixiu-* 规范） */
const COLUMN_VISIBILITY_KEY = 'ele-heixiu-table-columns';

/** 列元数据（定义所有可用列 + 默认可见性） */
interface ColumnMeta { key: string; title: string; defaultVisible: boolean }

const ALL_COLUMNS: ColumnMeta[] = [
   { key: 'id', title: '任务 ID', defaultVisible: true },
   { key: 'created_at', title: '创建时间', defaultVisible: true },
   { key: 'model_name', title: '模型', defaultVisible: true },
   { key: 'output_type', title: '输出类型', defaultVisible: true },
   { key: 'status', title: '状态', defaultVisible: true },
   { key: 'prompt', title: '提示词', defaultVisible: true },
   { key: 'duration', title: '时长', defaultVisible: true },
   { key: 'cost_cent', title: '费用', defaultVisible: true },
   { key: 'user_display_name', title: '用户', defaultVisible: true },
   { key: 'provider_name', title: '供应商', defaultVisible: false },
   { key: 'fps', title: '帧率', defaultVisible: false },
   { key: 'file_size', title: '文件大小', defaultVisible: false },
   { key: 'tags', title: '标签', defaultVisible: false },
   { key: 'error_message', title: '错误信息', defaultVisible: false },
];

function readStoredColumns(): Set<string> | null {
   try {
      const raw = localStorage.getItem(COLUMN_VISIBILITY_KEY);
      if (raw) {
         const arr: unknown = JSON.parse(raw);
         if (Array.isArray(arr) && arr.every((v) => typeof v === 'string')) return new Set(arr);
      }
   } catch { /* ignore */ }
   return null;
}

function writeStoredColumns(visible: Set<string>): void {
   try { localStorage.setItem(COLUMN_VISIBILITY_KEY, JSON.stringify([...visible])); } catch { /* ignore */ }
}

// ---------- 工具函数 ----------

/** 从 filters 中提取单值筛选（如 status 单选） */
function pickOne(filters: TableFilters, key: string): string | undefined {
   const v = filters[key];
   if (Array.isArray(v) && v.length > 0) return v[0] as string;
   return undefined;
}

/** 从 filters 中提取多值筛选（如 model_ids、user_ids） */
function pickMany(filters: TableFilters, key: string): string[] | undefined {
   const v = filters[key];
   if (Array.isArray(v) && v.length > 0) return v as string[];
   return undefined;
}

// ---------- 组件 ----------

export function TaskHistory() {
   const { token } = antTheme.useToken();
   const { edition } = useAppContext();
   const {
      selectedTask,
      setSelectedTask,
      taskPage,
      taskPageSize,
      setTaskPage,
      setTaskPageSize,
   } = useHomeContext();

   const isEnterprise = edition === 'enterprise';

   // -------- 列可见性 --------

   const [visibleColumns, setVisibleColumns] = useState<Set<string>>(() => {
      const stored = readStoredColumns();
      if (stored) return stored;
      return new Set(ALL_COLUMNS.filter((c) => c.defaultVisible).map((c) => c.key));
   });

   const handleColumnToggle = useCallback((key: string, checked: boolean) => {
      setVisibleColumns((prev) => {
         const next = new Set(prev);
         if (checked) next.add(key); else next.delete(key);
         writeStoredColumns(next);
         return next;
      });
   }, []);

   // -------- 主题感知斑马纹 / hover / 选中行 CSS --------

   const { isDark } = useTheme();

   const tableCss = useMemo(() => {
      const searchIconColor = token.colorPrimary;
      if (isDark) {
         return `
            .task-table .task-row-odd > td { background: rgba(255,255,255,0.02) !important; }
            .task-table .task-row-even > td { background: transparent !important; }
            .task-table .task-row-selected > td { background: ${token.blue}1A !important; }
            .task-table .task-row-selected.task-row-odd > td { background: ${token.blue}22 !important; }
            .task-table .ant-table-row:hover > td { background: rgba(255,255,255,0.05) !important; }
            .task-table .task-row-selected:hover > td { background: ${token.blue}28 !important; }
            .task-table .prompt-search-icon.filtered { color: ${searchIconColor} !important; }
         `;
      }
      return `
         .task-table .task-row-odd > td { background: #fafafa !important; }
         .task-table .task-row-even > td { background: #ffffff !important; }
         .task-table .task-row-selected > td { background: #e6f4ff !important; }
         .task-table .task-row-selected.task-row-odd > td { background: #dceeff !important; }
         .task-table .ant-table-row:hover > td { background: #f0f5ff !important; }
         .task-table .task-row-selected:hover > td { background: #d6ebff !important; }
         .task-table .prompt-search-icon.filtered { color: ${searchIconColor} !important; }
      `;
   }, [isDark, token.blue, token.colorPrimary]);

   // -------- antd Table onChange 参数缓存 --------

   const [tableParams, setTableParams] = useState<TableParams>({
      pagination: { current: taskPage, pageSize: taskPageSize },
      filters: {},
      sorter: {},
   });

   // 同步 context 分页 ↔ tableParams
   useEffect(() => {
      setTableParams((prev:TableParams) => ({
         ...prev,
         pagination: { current: taskPage, pageSize: taskPageSize },
      }));
   }, [taskPage, taskPageSize]);

   // -------- 模型列表（用于模型列筛选选项 + 模型名→ID 映射）--------


   const { data: allModels } = useModelList();
   const modelFilters:{text:string, value:string}[] = useMemo(() => {
      if (!allModels) return [];
      return allModels.map((m) => ({ text: m.name, value: m.id }));
   }, [allModels]);

   // -------- 供应商列表（从模型数据中提取）--------

   const providerFilters = useMemo(() => {
      if (!allModels) return [];
      const seen = new Set<string>();
      allModels.forEach((m) => {
         if (m.provider_name) seen.add(m.provider_name);
      });
      return Array.from(seen).sort().map((name) => ({ text: name, value: name }));
   }, [allModels]);

   // -------- 用户列表（从当前任务数据中提取，仅企业版使用）--------
   // TODO: 替换为独立的用户列表 API

   const [userFilters, setUserFilters] = useState<Array<{ text: string; value: string }>>([]);

   // -------- 构建 API 请求参数 --------

   const requestParams = useMemo<TaskListRequestParams>(() => {
      const req: TaskListRequestParams = {
         page: tableParams.pagination.current,
         page_size: tableParams.pagination.pageSize,
      };
      const { filters } = tableParams;

      // 服务端筛选字段
      const status = pickOne(filters, 'status');
      if (status) req.status = status as TaskStatusString;

      const modelIds = pickMany(filters, 'model_name');
      if (modelIds) req.model_ids = modelIds;

      // user_ids：仅企业版传入
      if (isEnterprise) {
         const userIds = pickMany(filters, 'user_id');
         if (userIds) req.user_ids = userIds;
      }

      return req;
   }, [tableParams, isEnterprise]);

   // -------- 数据获取 --------

   const [refreshTick, setRefreshTick] = useState(0);
   const handleTaskSubmitted = useCallback(() => {
      setRefreshTick((t) => t + 1);
   }, []);

   useEffect(() => {
      on(EVENTS.TASK_SUBMITTED, handleTaskSubmitted);
      return () => { off(EVENTS.TASK_SUBMITTED, handleTaskSubmitted); };
   }, [handleTaskSubmitted]);

   const {
      data: taskData,
      loading,
      errorMessage,
   } = useTaskList({
      requestParams,
      refreshSignal: refreshTick,
   });

   const tasks = taskData?.items ?? [];
   const total = taskData?.total ?? 0;

   // 从任务数据中提取用户列表（企业版筛选用）
   useEffect(() => {
      if (!isEnterprise || !taskData?.items) return;
      const seen = new Set<string>();
      taskData.items.forEach((t) => {
         if (t.user_id && t.user_display_name) {
            seen.add(JSON.stringify({ text: t.user_display_name, value: t.user_id }));
         }
      });
      const newList = Array.from(seen).map((s) => JSON.parse(s) as { text: string; value: string });
      setUserFilters((prev) => {
         if (prev.length === newList.length) return prev; // 避免不必要的重渲染
         return newList;
      });
   }, [isEnterprise, taskData]);

   // -------- antd Table onChange --------

   const handleTableChange: TableProps<TaskInfoItemResponseBody>['onChange'] = (
      pagination,
      filters,
      sorter,
   ) => {
      // 同步分页到 Context
      if (pagination.current && pagination.current !== taskPage) setTaskPage(pagination.current);
      if (pagination.pageSize && pagination.pageSize !== taskPageSize) setTaskPageSize(pagination.pageSize);

      // 缓存筛选和排序参数
      const s = sorter as SorterResult<TaskInfoItemResponseBody>;
      setTableParams({
         pagination: {
            current: pagination.current || taskPage,
            pageSize: pagination.pageSize || taskPageSize,
         },
         filters: filters as TableFilters,
         sorter: !Array.isArray(s) && s.field
            ? { field: s.field, order: s.order as 'ascend' | 'descend' | undefined }
            : {},
      });
   };

   // -------- 表格列定义（含列头筛选/排序 + 可见性过滤）--------
   //
   // 列顺序：任务 ID(固定) → 创建时间 → 模型 → 输出类型 → 状态 → 提示词 →
   //         时长(视频) → 费用 → 供应商 → 帧率/文件大小/标签/错误信息 → 用户(企业版)
   //
   // 可见性：通过标题栏齿轮图标控制，持久化到 localStorage
   //
   // ⚠️ 重要：columns 依赖中不出 token.colorPrimary，否则主题切换时 columns 重新生成
   //   会导致 Table 内部状态重置（页码、筛选、排序等丢失）。
   //   主题色通过 searchIconColorRef 传递，不触发 columns 重建。

   /** 提示词搜索图标颜色（通过 CSS class 承载，避免 columns 依赖 token 导致主题切换重置状态） */

   /** 提取视频时长（秒），非视频输出类型返回 null → 渲染 "-" */
   function getVideoDuration(record: TaskInfoItemResponseBody): number | null {
      if (record.output_type === 'video') {
         return record.ui_params.duration ?? null;
      }
      return null;
   }

   /** 格式化时长：<60s 显示秒，≥60s 显示分秒 */
   function formatDuration(seconds: number): string {
      if (seconds < 60) return `${seconds} 秒`;
      const m = Math.floor(seconds / 60);
      const s = seconds % 60;
      return s > 0 ? `${m} 分 ${s} 秒` : `${m} 分钟`;
   }

   const allColumns = useMemo<ColumnsType<TaskInfoItemResponseBody>>(() => {
      const cols: ColumnsType<TaskInfoItemResponseBody> = [
         // ---- 任务 ID（固定列） ----
         {
            title: '任务 ID',
            dataIndex: 'id',
            key: 'id',
            width: 140,
            ellipsis: true,
            fixed: 'left',
            render: (id: string) => (
               <Text copyable={{ text: id }} style={{ fontSize: 12, fontFamily: 'monospace' }}>
                  {id.slice(0, 10)}...
               </Text>
            ),
         },
         // ---- 创建时间 ----
         {
            title: '创建时间',
            dataIndex: 'created_at',
            key: 'created_at',
            width: 155,
            sorter: (a, b) => {
               const da = a.created_at ? new Date(a.created_at).getTime() : 0;
               const db = b.created_at ? new Date(b.created_at).getTime() : 0;
               return da - db;
            },
            sortOrder: tableParams.sorter.field === 'created_at' ? tableParams.sorter.order : null,
            render: (v: Date) => (
               <Text style={{ fontSize: 11 }}>
                  {v ? new Date(v).toLocaleString('zh-CN', {
                     month: '2-digit', day: '2-digit',
                     hour: '2-digit', minute: '2-digit', second: '2-digit',
                  }) : '-'}
               </Text>
            ),
         },
         // ---- 模型 ----
         {
            title: '模型',
            dataIndex: 'model_name',
            key: 'model_name',
            width: 130,
            ellipsis: true,
            filters: modelFilters,
            filteredValue: (tableParams.filters.model_name as string[]) || null,
            onFilter: () => true, // 服务端筛选
            filterSearch: true,
            filterMode: 'menu' as const,
         },
         // ---- 输出类型 ----
         {
            title: '输出类型',
            dataIndex: 'output_type',
            key: 'output_type',
            width: 90,
            filters: OUTPUT_TYPE_FILTERS,
            filteredValue: (tableParams.filters.output_type as string[]) || null,
            onFilter: (value, record) => record.output_type === value, // 客户端筛选
            render: (t: string) => (
               <Tag style={{ fontSize: 10 }}>
                  {OutputTypeMap[t as keyof typeof OutputTypeMap] || t}
               </Tag>
            ),
         },
         // ---- 状态 ----
         {
            title: '状态',
            dataIndex: 'status',
            key: 'status',
            width: 80,
            filters: STATUS_FILTERS,
            filteredValue: (tableParams.filters.status as string[]) || null,
            onFilter: () => true, // 服务端筛选
            render: (s: string) => {
               const cfg = resolveStatus(s);
               return <Tag color={cfg.color} style={{ fontSize: 10 }}>{cfg.label}</Tag>;
            },
         },
         // ---- 提示词 ----
         {
            title: '提示词',
            dataIndex: 'prompt',
            key: 'prompt',
            width: 160,
            ellipsis: true,
            filterIcon: (filtered: boolean) => (
               <SearchOutlined className={`prompt-search-icon${filtered ? ' filtered' : ''}`} style={{ fontSize: 12 }} />
            ),
            filterDropdown: undefined, // TODO: 后续加自定义搜索框
            render: (p: string | undefined) => (
               <Text style={{ fontSize: 11 }} title={p}>{p || '-'}</Text>
            ),
         },
         // ---- 时长（仅视频有数据，否则 "-"） ----
         {
            title: '时长',
            key: 'duration',
            width: 75,
            sorter: (a, b) => {
               const da = getVideoDuration(a) ?? -1;
               const db = getVideoDuration(b) ?? -1;
               return da - db;
            },
            sortOrder: tableParams.sorter.field === 'duration' ? tableParams.sorter.order : null,
            render: (_: unknown, record: TaskInfoItemResponseBody) => {
               const sec = getVideoDuration(record);
               return <Text style={{ fontSize: 11 }}>{sec !== null ? formatDuration(sec) : '-'}</Text>;
            },
         },
         // ---- 费用 ----
         {
            title: '费用',
            dataIndex: 'cost_cent',
            key: 'cost_cent',
            width: 75,
            sorter: (a, b) => a.cost_cent - b.cost_cent,
            sortOrder: tableParams.sorter.field === 'cost_cent' ? tableParams.sorter.order : null,
            render: (c: number) => (
               <Text style={{ fontSize: 11 }}>¥{(c / 100).toFixed(2)}</Text>
            ),
         },
         // ---- 供应商（默认隐藏） ----
         {
            title: '供应商',
            dataIndex: 'provider_name',
            key: 'provider_name',
            width: 100,
            ellipsis: true,
            filters: providerFilters,
            filteredValue: (tableParams.filters.provider_name as string[]) || null,
            onFilter: (value, record) => record.provider_name === value, // 客户端筛选
            filterSearch: true,
         },
         // ---- 帧率（占位列，待后端支持） ----
         {
            title: '帧率',
            key: 'fps',
            width: 70,
            render: () => <Text type="secondary" style={{ fontSize: 11 }}>-</Text>,
         },
         // ---- 文件大小（占位列，待后端支持） ----
         {
            title: '文件大小',
            key: 'file_size',
            width: 85,
            render: () => <Text type="secondary" style={{ fontSize: 11 }}>-</Text>,
         },
         // ---- 标签 ----
         {
            title: '标签',
            dataIndex: 'tags',
            key: 'tags',
            width: 100,
            ellipsis: true,
            render: (tags: Array<string | null>) => {
               const filtered = tags?.filter((t): t is string => t !== null) ?? [];
               if (filtered.length === 0) return <Text type="secondary" style={{ fontSize: 11 }}>-</Text>;
               return (
                  <Space size={2} wrap>
                     {filtered.slice(0, 2).map((t) => (
                        <Tag key={t} style={{ fontSize: 9, lineHeight: '14px', margin: 0 }}>{t}</Tag>
                     ))}
                     {filtered.length > 2 && (
                        <Text type="secondary" style={{ fontSize: 9 }}>+{filtered.length - 2}</Text>
                     )}
                  </Space>
               );
            },
         },
         // ---- 错误信息（失败任务时展示） ----
         {
            title: '错误信息',
            dataIndex: 'error_message',
            key: 'error_message',
            width: 140,
            ellipsis: true,
            render: (msg: string) => msg
               ? <Text type="danger" style={{ fontSize: 11 }} title={msg}>{msg}</Text>
               : <Text type="secondary" style={{ fontSize: 11 }}>-</Text>,
         },
      ];

      // 用户列：仅企业版可筛选
      if (isEnterprise) {
         cols.push({
            title: '用户',
            dataIndex: 'user_display_name',
            key: 'user_id',
            width: 100,
            ellipsis: true,
            filters: userFilters,
            filteredValue: (tableParams.filters.user_id as string[]) || null,
            onFilter: () => true, // 服务端筛选（传 user_ids）
            render: (n: string | null) => (
               <Text style={{ fontSize: 11 }}>{n || '-'}</Text>
            ),
         });
      } else {
         cols.push({
            title: '用户',
            dataIndex: 'user_display_name',
            key: 'user_id',
            width: 100,
            ellipsis: true,
            render: (n: string | null) => (
               <Text style={{ fontSize: 11 }}>{n || '-'}</Text>
            ),
         });
      }

      return cols;
   }, [
      modelFilters,
      providerFilters,
      userFilters,
      tableParams.filters,
      tableParams.sorter,
      isEnterprise,
   ]);

   // 按 visibleColumns 过滤实际展示的列
   const columns = useMemo(
      () => allColumns.filter((c) => visibleColumns.has(c.key as string)),
      [allColumns, visibleColumns],
   );

   // -------- 表格高度 --------
   //
   // 核心策略：固定预留 pagination 区域高度，确保无论数据加载时机如何，
   // 分页器始终有空间渲染，不会溢出可视区。
   //
   //   scroll.y = wrapper高度 - thead高度 - PAGINATION_RESERVE - 水平滚动条高度
   //
   // 为什么固定预留 pagination 而不是动态测量？
   //   - 无数据/加载中时 pagination DOM 尚不存在 → offsetHeight=0 → scroll.y 偏大
   //   - 翻页/切换 pageSize 时 pagination 异步渲染 → 测量时机不可控
   //   - 固定预留值（48px 覆盖 small-size pagination + 上下间距）避免上述所有时序问题

   /** 预留 pagination 高度（small-size: ~32px + 上下 padding/margin 8px × 2） */
   const PAGINATION_RESERVE = 48;

   const tableWrapperRef = useRef<HTMLDivElement>(null);
   const [tableBodyHeight, setTableBodyHeight] = useState(400);

   useEffect(() => {
      const wrapper = tableWrapperRef.current;
      if (!wrapper) return;

      const calcHeight = () => {
         const thead = wrapper.querySelector('.ant-table-thead') as HTMLElement | null;
         const body = wrapper.querySelector('.ant-table-body') as HTMLElement | null;
         const theadH = thead?.offsetHeight ?? 0;

         // 水平滚动条占用高度（antd Table scroll.x 触发时出现）
         let hScrollH = 0;
         if (body && body.scrollWidth > body.clientWidth + 1) {
            hScrollH = body.offsetHeight - body.clientHeight;
         }

         const available = wrapper.clientHeight - theadH - PAGINATION_RESERVE - hScrollH;
         setTableBodyHeight(Math.max(120, available));
      };

      // ResizeObserver：容器尺寸变化（窗口拉伸、面板拖拽）
      const resizeObserver = new ResizeObserver(calcHeight);
      resizeObserver.observe(wrapper);

      // MutationObserver：内部 DOM 变化（thead 高度变化如筛选菜单弹出、
      // 数据加载后水平滚动条出现等）
      const mutationObserver = new MutationObserver(calcHeight);
      mutationObserver.observe(wrapper, { childList: true, subtree: true });

      calcHeight();
      return () => {
         resizeObserver.disconnect();
         mutationObserver.disconnect();
      };
   }, [taskPageSize]);

   // -------- 行样式：斑马纹 + 选中行 --------

   const rowClassName = (_record: TaskInfoItemResponseBody, index: number) => {
      const base = index % 2 === 1 ? 'task-row-odd' : 'task-row-even';
      return selectedTask?.id === _record.id ? `${base} task-row-selected` : base;
   };

   // -------- 是否有活跃筛选 --------

   const hasActiveFilters = Object.values(tableParams.filters).some(
      (v) => Array.isArray(v) && v.length > 0,
   );

   // ======== 渲染 ========

   return (
      <div style={{ flex: 1, display: 'flex', flexDirection: 'column', overflow: 'hidden', minHeight: 0 }}>
         {/* 标题栏 */}
         <div
            style={{
               padding: '6px 12px',
               borderBottom: `1px solid ${token.colorBorderSecondary}`,
               display: 'flex',
               alignItems: 'center',
               gap: 6,
               flexShrink: 0,
            }}
         >
            <HistoryOutlined style={{ fontSize: 14 }} />
            <Text strong style={{ fontSize: 13 }}>任务记录</Text>
            {total > 0 && (
               <Text type="secondary" style={{ fontSize: 11 }}>共 {total} 条</Text>
            )}
            {hasActiveFilters && (
               <Tag color="blue" style={{ fontSize: 10, lineHeight: '16px', padding: '0 4px' }}>
                  已筛选
               </Tag>
            )}
            {errorMessage && (
               <Text type="danger" style={{ fontSize: 11, marginLeft: 'auto' }}>{errorMessage}</Text>
            )}

            {/* 列可见性控制 — 齿轮图标 */}
            <Popover
               trigger="click"
               placement="bottomRight"
               title="显示/隐藏列"
               content={
                  <Checkbox.Group
                     value={[...visibleColumns]}
                     style={{ display: 'flex', flexDirection: 'column', gap: 2 }}
                  >
                     {ALL_COLUMNS.map((col) => (
                        <Checkbox
                           key={col.key}
                           value={col.key}
                           disabled={col.key === 'id'}
                           onChange={(e) => handleColumnToggle(col.key, e.target.checked)}
                        >
                           {col.title}
                        </Checkbox>
                     ))}
                  </Checkbox.Group>
               }
            >
               <Button
                  type="text"
                  size="small"
                  icon={<SettingOutlined style={{ fontSize: 13 }} />}
                  style={{ marginLeft: 'auto' }}
                  title="列设置"
               />
            </Popover>
         </div>

         {/* 表格 — 横向 + 纵向滚动，筛选/排序通过列头操作 */}
         <div ref={tableWrapperRef} style={{ flex: 1, overflow: 'hidden', minHeight: 0 }}>
            <style>{tableCss}</style>
            <Table<TaskInfoItemResponseBody>
               className="task-table"
               columns={columns}
               dataSource={tasks}
               rowKey="id"
               size="small"
               loading={loading}
               showHeader={true}
               scroll={{ x: isEnterprise ? 1350 : 1250, y: tableBodyHeight }}
               rowClassName={rowClassName}
               onRow={(record) => ({
                  onClick: () => setSelectedTask(record),
                  style: { cursor: 'pointer' },
               })}
               onChange={handleTableChange}
               pagination={{
                  current: tableParams.pagination.current,
                  pageSize: tableParams.pagination.pageSize,
                  total,
                  size: 'small',
                  showSizeChanger: true,
                  pageSizeOptions: ['10', '20', '50'],
                  placement: ['bottomCenter'],
                  style: { marginBottom: 0 },
               }}
               locale={{ emptyText: '暂无任务记录', filterReset: '重置', filterConfirm: '确定' }}
            />
         </div>
      </div>
   );
}
