@robot-admin/naive-ui-components 0.6.31 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/README.md +5 -3
- package/dist/C_OrgChart.js.map +1 -1
- package/dist/C_OrgChart2.js.map +1 -1
- package/dist/C_Skeleton.js.map +1 -1
- package/dist/C_Skeleton2.js.map +1 -1
- package/dist/{C_Table-B8hpzOcR.css.map → C_Table-BcM5LqAM.css.map} +1 -1
- package/dist/{C_Table-CGP3n_u0.css.map → C_Table-HATVoq1F.css.map} +1 -1
- package/dist/C_Table.cjs +1 -1
- package/dist/C_Table.css +140 -97
- package/dist/C_Table.d.cts +2 -2
- package/dist/C_Table.d.ts +2 -2
- package/dist/C_Table.js +1 -1
- package/dist/C_Table.js.map +1 -1
- package/dist/C_Table2.js +3 -1
- package/dist/C_Table2.js.map +1 -1
- package/dist/constants.d.ts +4 -4
- package/dist/constants2.d.ts +3 -3
- package/dist/constants3.d.ts +3 -3
- package/dist/constants4.d.ts +2 -2
- package/dist/constants5.d.ts +2 -2
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/index.vue.d.ts +1 -1
- package/dist/index10.vue.d.ts +1 -1
- package/dist/index11.vue.d.ts +2 -2
- package/dist/index13.vue.d.ts +3 -3
- package/dist/index14.vue.d.ts +1 -1
- package/dist/index15.vue.d.ts +4 -4
- package/dist/index16.vue.d.ts +2 -2
- package/dist/index18.vue.d.ts +4 -4
- package/dist/index2.vue.d.ts +6 -6
- package/dist/index20.vue.d.ts +1 -1
- package/dist/index21.vue.d.ts +1 -1
- package/dist/index22.vue.d.ts +2 -2
- package/dist/index23.vue.d.ts +1 -1
- package/dist/index24.vue.d.ts +1 -1
- package/dist/index26.vue.d.ts +4 -4
- package/dist/index27.vue.d.ts +2 -2
- package/dist/index28.vue.d.ts +3 -3
- package/dist/index3.vue.d.ts +3 -3
- package/dist/index4.vue.d.ts +5 -5
- package/dist/index6.vue.d.ts +2 -2
- package/dist/index7.vue.d.ts +2 -2
- package/dist/index9.vue.d.ts +2 -2
- package/dist/resolver.cjs.map +1 -1
- package/dist/resolver.js.map +1 -1
- package/dist/style.css +142 -99
- package/dist/useCalendarEvents.d.ts +1 -1
- package/dist/useCollapsePanel.d.ts +5 -5
- package/dist/useCropperCore.d.ts +4 -4
- package/dist/useDraggableLayout.d.ts +5 -5
- package/dist/useDynamicFormState.d.ts +124 -124
- package/dist/useEdgeInteraction.d.ts +1 -1
- package/dist/useGlobalSearch.d.ts +1 -1
- package/dist/useInfiniteScroll.d.ts +4 -4
- package/dist/useModalEdit.d.ts +513 -240
- package/dist/useQRCode.d.ts +3 -3
- package/dist/useSearchState.d.ts +2 -2
- package/dist/useSignatureHistory.d.ts +3 -3
- package/dist/useSplitResize.d.ts +5 -5
- package/dist/useTimeSelection.d.ts +2 -2
- package/dist/useTreeOperations.d.ts +4 -4
- package/dist/useWorkflowValidation.d.ts +7 -7
- package/package.json +1 -1
package/dist/C_Table2.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"C_Table2.js","names":["NSpin","_useNaiveMessage","useMessage","NButton","C_Icon","NTooltip","NButtonGroup","NSpace","NModal","C_Icon","C_Icon","$attrs","rowKey"],"sources":["../src/components/C_Table/composables/useTableConfig.ts","../src/components/C_Table/composables/useRowEdit.ts","../src/components/C_Table/composables/useCellEdit.ts","../src/components/C_Table/composables/useModalEdit.ts","../src/components/C_Table/composables/useTableExpand.ts","../src/components/C_Table/composables/usePrintWatermark.ts","../src/components/C_Table/composables/useDynamicRow.ts","../src/components/C_Table/composables/useTableManager.ts","../src/components/C_Table/composables/usePagination.ts","../src/components/C_Table/composables/useTableActions.ts","../src/components/C_Table/data.ts","../src/components/C_Table/composables/useTableColumns.ts","../src/components/C_Table/components/ColumnSettings/data.ts","../src/components/C_Table/components/ColumnSettings/index.vue","../src/components/C_Table/components/ColumnSettings/index.vue","../src/components/C_Table/components/ColumnSettings/index.vue","../src/components/C_Table/index.vue","../src/components/C_Table/index.vue","../src/components/C_Table/index.vue"],"sourcesContent":["/*\r\n * @Description: 表格配置构建 — 从 config 单一入口合并为内部统一配置对象\r\n * @Migration: naive-ui-components 组件库迁移版本\r\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\r\n */\r\n\r\nimport type { VNodeChild, Ref, ComputedRef } from 'vue'\r\nimport type { DataTableRowKey } from 'naive-ui/es'\r\nimport type {\r\n DataRecord,\r\n PaginationConfig,\r\n ParentChildLinkMode,\r\n SimpleTableActions,\r\n TableColumn,\r\n} from '../types'\r\nimport type { DynamicRowsOptions } from './useDynamicRow'\r\n\r\n/* ================= CRUD 绑定类型 ================= */\r\n\r\n/**\r\n * 可绑定到 C_Table 的 CRUD 对象接口(桥接类型)\r\n *\r\n * C_Table 内部已将 data/columns 规范化为 DataRecord/TableColumn,\r\n * 此接口对 columns/actions 使用 any[] 避免 TableColumn<any> 结构展开异常,\r\n * 同时允许 useTableCrud<T>() 任意子类型直接传入而无需类型断言。\r\n */\r\nexport interface CrudBinding {\r\n data: Ref<any[]>\r\n loading: Ref<boolean>\r\n\r\n columns: ComputedRef<any[]>\r\n\r\n actions?: ComputedRef<any>\r\n\r\n pagination?: ComputedRef<any>\r\n\r\n tableRef?: Ref<any>\r\n\r\n save?: (...args: any[]) => any\r\n handleCancel?: () => void\r\n handlePaginationChange?: (page: number, pageSize: number) => void\r\n\r\n handleRowDelete?: (...args: any[]) => any\r\n\r\n detail?: { show: (...args: any[]) => void }\r\n}\r\n\r\n/* ================= 使用侧配置类型(对外 API) ================= */\r\n\r\n/** C_Table :config prop 的类型 — 所有功能配置收拢在此 */\r\nexport interface TableConfig<T extends DataRecord = DataRecord> {\r\n /** 编辑配置 */\r\n edit?: EditConfig | boolean\r\n /** 操作按钮 */\r\n actions?: SimpleTableActions<T>\r\n /** 分页配置 */\r\n pagination?: PaginationConfig | boolean\r\n /** 展开行配置 */\r\n expand?: ExpandConfig<T> | boolean\r\n /** 选择配置 */\r\n selection?: SelectionConfig<T> | boolean\r\n /** 动态行配置 */\r\n dynamicRows?: DynamicRowsOptions<T> | boolean\r\n /** 工具栏 */\r\n toolbar?: ToolbarConfig\r\n /** 表格显示属性 */\r\n display?: DisplayConfig\r\n /** 虚拟滚动配置(大数据量) */\r\n virtualScroll?: VirtualScrollConfig | boolean\r\n /** 合计行配置(财务场景) */\r\n summary?: SummaryConfig<T>\r\n /** 列拖拽排序 */\r\n columnDrag?: ColumnDragConfig | boolean\r\n}\r\n\r\nexport interface EditConfig {\r\n enabled?: boolean\r\n mode?: 'row' | 'cell' | 'modal' | 'both' | 'none'\r\n showRowActions?: boolean\r\n modalTitle?: string\r\n modalWidth?: number\r\n}\r\n\r\nexport interface ExpandConfig<T extends DataRecord = DataRecord> {\r\n enabled?: boolean\r\n defaultExpandedKeys?: DataTableRowKey[]\r\n onLoadData?: (row: T) => Promise<unknown[]> | unknown[]\r\n renderContent?: (\r\n row: T,\r\n expandData: unknown[],\r\n loading: boolean,\r\n childSelection?: unknown\r\n ) => VNodeChild\r\n rowExpandable?: (row: T) => boolean\r\n}\r\n\r\nexport interface SelectionConfig<T extends DataRecord = DataRecord> {\r\n enabled?: boolean\r\n defaultCheckedKeys?: import('naive-ui/es').DataTableRowKey[]\r\n rowCheckable?: (row: T) => boolean\r\n maxSelection?: number\r\n childSelection?: {\r\n enabled?: boolean\r\n childRowCheckable?: (childRow: unknown, parentRow: T) => boolean\r\n }\r\n parentChildLink?: {\r\n enabled?: boolean\r\n mode?: ParentChildLinkMode\r\n }\r\n}\r\n\r\nexport interface ToolbarConfig {\r\n /** 是否显示工具栏 */\r\n show?: boolean\r\n /** 是否启用列设置 */\r\n columnSettings?: boolean\r\n}\r\n\r\nexport interface DisplayConfig {\r\n striped?: boolean\r\n bordered?: boolean\r\n singleLine?: boolean\r\n size?: 'small' | 'medium' | 'large'\r\n maxHeight?: number | string\r\n scrollX?: number | string\r\n columnWidth?: number\r\n}\r\n\r\n/** 虚拟滚动配置 */\r\nexport interface VirtualScrollConfig {\r\n enabled?: boolean\r\n /** 每行高度(px) */\r\n itemHeight?: number\r\n /** 最大高度(px),用于滚动容器 */\r\n maxHeight?: number\r\n}\r\n\r\n/** 合计行配置 */\r\nexport interface SummaryConfig<T extends DataRecord = DataRecord> {\r\n /** 合计行位置 */\r\n position?: 'top' | 'bottom'\r\n /** 自定义合计函数,每列返回 { value, colSpan? } */\r\n render?: (data: T[]) => Record<string, { value: string | number; colSpan?: number }>\r\n}\r\n\r\n/** 列拖拽排序配置 */\r\nexport interface ColumnDragConfig {\r\n enabled?: boolean\r\n /** 拖拽手柄类名(默认拖拽头部区域) */\r\n handleClass?: string\r\n /** 拖拽动画时长(ms) */\r\n animationDuration?: number\r\n}\r\n\r\n/* ================= 内部解析后的扁平配置 ================= */\r\n\r\nexport interface ResolvedConfig {\r\n editable: boolean\r\n editMode: string\r\n showRowActions: boolean\r\n modalTitle: string\r\n modalWidth: number\r\n expandable: boolean\r\n defaultExpandedKeys: DataTableRowKey[] | undefined\r\n onLoadExpandData:\r\n | ((row: DataRecord) => Promise<unknown[]> | unknown[])\r\n | undefined\r\n renderExpandContent:\r\n | ((\r\n row: DataRecord,\r\n expandData: unknown[],\r\n loading: boolean,\r\n childSelection?: unknown\r\n ) => VNodeChild)\r\n | undefined\r\n rowExpandable: ((row: DataRecord) => boolean) | undefined\r\n enableSelection: boolean\r\n defaultCheckedKeys: DataTableRowKey[] | undefined\r\n rowCheckable: ((row: DataRecord) => boolean) | undefined\r\n maxSelection: number | undefined\r\n enableChildSelection: boolean\r\n childRowCheckable:\r\n | ((childRow: unknown, parentRow: DataRecord) => boolean)\r\n | undefined\r\n enableParentChildLink: boolean\r\n parentChildLinkMode: ParentChildLinkMode\r\n pagination: PaginationConfig | null\r\n dynamicRows: DynamicRowsOptions<DataRecord> | undefined\r\n striped: boolean\r\n bordered: boolean\r\n singleLine: boolean\r\n size: 'small' | 'medium' | 'large'\r\n maxHeight: number | string | undefined\r\n scrollX: number | string | undefined\r\n columnWidth: number\r\n showToolbar: boolean\r\n enableColumnSettings: boolean\r\n /** 虚拟滚动 */\r\n virtualScroll: boolean\r\n virtualItemHeight: number\r\n virtualMaxHeight: number\r\n /** 合计行 */\r\n summaryPosition: 'top' | 'bottom' | undefined\r\n summaryRender:\r\n | ((\r\n data: DataRecord[]\r\n ) => Record<string, { value: string | number; colSpan?: number }>)\r\n | undefined\r\n /** 列拖拽 */\r\n enableColumnDrag: boolean\r\n columnDragHandleClass: string\r\n columnDragAnimationDuration: number\r\n}\r\n\r\n/* ================= 解析逻辑 ================= */\r\n\r\nconst EDIT_DISABLED: Pick<\r\n ResolvedConfig,\r\n 'editable' | 'editMode' | 'showRowActions' | 'modalTitle' | 'modalWidth'\r\n> = {\r\n editable: false,\r\n editMode: 'none',\r\n showRowActions: false,\r\n modalTitle: '编辑数据',\r\n modalWidth: 600,\r\n}\r\n\r\nconst resolveEdit = (\r\n edit: EditConfig | boolean | undefined\r\n): Pick<\r\n ResolvedConfig,\r\n 'editable' | 'editMode' | 'showRowActions' | 'modalTitle' | 'modalWidth'\r\n> => {\r\n if (edit === false || edit === undefined) return EDIT_DISABLED\r\n if (edit === true)\r\n return {\r\n editable: true,\r\n editMode: 'modal',\r\n showRowActions: true,\r\n modalTitle: '编辑数据',\r\n modalWidth: 600,\r\n }\r\n return {\r\n editable: edit.enabled !== false && edit.mode !== 'none',\r\n editMode: edit.mode || 'modal',\r\n showRowActions: edit.showRowActions !== false && edit.mode !== 'none',\r\n modalTitle: edit.modalTitle || '编辑数据',\r\n modalWidth: edit.modalWidth || 600,\r\n }\r\n}\r\n\r\nconst resolveExpand = (expand: ExpandConfig | boolean | undefined) => {\r\n if (!expand)\r\n return {\r\n expandable: false,\r\n defaultExpandedKeys: undefined,\r\n onLoadExpandData: undefined,\r\n renderExpandContent: undefined,\r\n rowExpandable: undefined,\r\n }\r\n if (expand === true)\r\n return {\r\n expandable: true,\r\n defaultExpandedKeys: undefined,\r\n onLoadExpandData: undefined,\r\n renderExpandContent: undefined,\r\n rowExpandable: undefined,\r\n }\r\n return {\r\n expandable: expand.enabled !== false,\r\n defaultExpandedKeys: expand.defaultExpandedKeys,\r\n onLoadExpandData: expand.onLoadData,\r\n renderExpandContent: expand.renderContent,\r\n rowExpandable: expand.rowExpandable,\r\n }\r\n}\r\n\r\nconst resolveSelection = (selection: SelectionConfig | boolean | undefined) => {\r\n if (!selection)\r\n return {\r\n enableSelection: false,\r\n defaultCheckedKeys: undefined,\r\n rowCheckable: undefined,\r\n maxSelection: undefined,\r\n enableChildSelection: false,\r\n childRowCheckable: undefined,\r\n enableParentChildLink: false,\r\n parentChildLinkMode: 'loose' as ParentChildLinkMode,\r\n }\r\n if (selection === true)\r\n return {\r\n enableSelection: true,\r\n defaultCheckedKeys: undefined,\r\n rowCheckable: undefined,\r\n maxSelection: undefined,\r\n enableChildSelection: false,\r\n childRowCheckable: undefined,\r\n enableParentChildLink: false,\r\n parentChildLinkMode: 'loose' as ParentChildLinkMode,\r\n }\r\n const child = selection.childSelection || {}\r\n const link = selection.parentChildLink || {}\r\n return {\r\n enableSelection: selection.enabled !== false,\r\n defaultCheckedKeys: selection.defaultCheckedKeys,\r\n rowCheckable: selection.rowCheckable,\r\n maxSelection: selection.maxSelection,\r\n enableChildSelection: child.enabled || false,\r\n childRowCheckable: child.childRowCheckable,\r\n enableParentChildLink: link.enabled || false,\r\n parentChildLinkMode: (link.mode || 'loose') as ParentChildLinkMode,\r\n }\r\n}\r\n\r\nconst resolvePagination = (\r\n pagination: PaginationConfig | boolean | undefined\r\n): { pagination: PaginationConfig | null } => {\r\n const defaults: PaginationConfig = {\r\n enabled: true,\r\n page: 1,\r\n pageSize: 10,\r\n showSizePicker: true,\r\n showQuickJumper: true,\r\n pageSizes: [10, 20, 50, 100],\r\n simple: false,\r\n size: 'medium',\r\n }\r\n\r\n if (pagination === false) return { pagination: null }\r\n if (pagination === true || pagination === undefined)\r\n return { pagination: defaults }\r\n return { pagination: { ...defaults, ...pagination } }\r\n}\r\n\r\nconst resolveDynamicRows = (\r\n dynamicRows: DynamicRowsOptions | boolean | undefined\r\n) => {\r\n if (!dynamicRows) return { dynamicRows: undefined }\r\n if (dynamicRows === true) {\r\n return {\r\n dynamicRows: {\r\n enableRadioSelection: true,\r\n enableAdd: true,\r\n enableInsert: true,\r\n enableDelete: true,\r\n enableCopy: true,\r\n enableMove: true,\r\n enablePrint: true,\r\n },\r\n }\r\n }\r\n return { dynamicRows }\r\n}\r\n\r\nconst DISPLAY_DEFAULTS: Pick<\r\n ResolvedConfig,\r\n | 'striped'\r\n | 'bordered'\r\n | 'singleLine'\r\n | 'size'\r\n | 'maxHeight'\r\n | 'scrollX'\r\n | 'columnWidth'\r\n> = {\r\n striped: true,\r\n bordered: true,\r\n singleLine: true,\r\n size: 'medium',\r\n maxHeight: undefined,\r\n scrollX: undefined,\r\n columnWidth: 180,\r\n}\r\n\r\n/** 解析 display 配置,合并默认值 */\r\nfunction resolveDisplay(display: DisplayConfig | undefined) {\r\n if (!display) return { ...DISPLAY_DEFAULTS }\r\n return {\r\n striped: display.striped ?? DISPLAY_DEFAULTS.striped,\r\n bordered: display.bordered ?? DISPLAY_DEFAULTS.bordered,\r\n singleLine: display.singleLine ?? DISPLAY_DEFAULTS.singleLine,\r\n size: display.size ?? DISPLAY_DEFAULTS.size,\r\n maxHeight: display.maxHeight,\r\n scrollX: display.scrollX,\r\n columnWidth: display.columnWidth ?? DISPLAY_DEFAULTS.columnWidth,\r\n }\r\n}\r\n\r\nconst resolveToolbar = (toolbar: ToolbarConfig | undefined) => ({\r\n showToolbar: toolbar?.show !== false,\r\n enableColumnSettings: toolbar?.columnSettings !== false,\r\n})\r\n\r\nconst resolveVirtualScroll = (\r\n vs: VirtualScrollConfig | boolean | undefined\r\n) => {\r\n if (!vs)\r\n return {\r\n virtualScroll: false,\r\n virtualItemHeight: 34,\r\n virtualMaxHeight: 600,\r\n }\r\n if (vs === true)\r\n return { virtualScroll: true, virtualItemHeight: 34, virtualMaxHeight: 600 }\r\n return {\r\n virtualScroll: vs.enabled !== false,\r\n virtualItemHeight: vs.itemHeight ?? 34,\r\n virtualMaxHeight: vs.maxHeight ?? 600,\r\n }\r\n}\r\n\r\nconst resolveSummary = (summary: SummaryConfig | undefined) => {\r\n if (!summary) return { summaryPosition: undefined, summaryRender: undefined }\r\n return {\r\n summaryPosition: summary.position ?? 'bottom',\r\n summaryRender: summary.render,\r\n }\r\n}\r\n\r\nconst resolveColumnDrag = (cd: ColumnDragConfig | boolean | undefined) => {\r\n if (!cd)\r\n return {\r\n enableColumnDrag: false,\r\n columnDragHandleClass: 'drag-handle',\r\n columnDragAnimationDuration: 150,\r\n }\r\n if (cd === true)\r\n return {\r\n enableColumnDrag: true,\r\n columnDragHandleClass: 'drag-handle',\r\n columnDragAnimationDuration: 150,\r\n }\r\n return {\r\n enableColumnDrag: cd.enabled !== false,\r\n columnDragHandleClass: cd.handleClass ?? 'drag-handle',\r\n columnDragAnimationDuration: cd.animationDuration ?? 150,\r\n }\r\n}\r\n\r\n/* ================= 主配置函数 ================= */\r\n\r\n/**\r\n * 将 config 单一对象解析为内部扁平化配置\r\n */\r\nexport function resolveConfig(config: TableConfig = {}): ResolvedConfig {\r\n return {\r\n ...resolveEdit(config.edit),\r\n ...resolveExpand(config.expand),\r\n ...resolveSelection(config.selection),\r\n ...resolvePagination(config.pagination),\r\n ...resolveDynamicRows(config.dynamicRows),\r\n ...resolveDisplay(config.display),\r\n ...resolveToolbar(config.toolbar),\r\n ...resolveVirtualScroll(config.virtualScroll),\r\n ...resolveSummary(config.summary),\r\n ...resolveColumnDrag(config.columnDrag),\r\n }\r\n}\r\n\r\n/* ================= 编辑模式检查器 ================= */\r\n\r\nexport const createEditModeChecker = (config: ResolvedConfig) => ({\r\n isNonEditable: (column: TableColumn) =>\r\n !config.editable || column.editable === false || config.editMode === 'none',\r\n isRowEditMode: () => ['row', 'both'].includes(config.editMode),\r\n isCellEditMode: () => ['cell', 'both'].includes(config.editMode),\r\n})\r\n","/*\r\n * @Description: 可编辑行组合函数,提供表格整行的编辑功能\r\n * @Migration: naive-ui-components 组件库迁移版本\r\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\r\n */\r\n\r\nimport { ref } from \"vue\";\r\nimport type { DataTableRowKey } from \"naive-ui/es\";\r\nimport type { DataRecord } from \"../types\";\r\n\r\n/**\r\n * 行编辑配置选项\r\n */\r\nexport interface RowEditOptions {\r\n data: () => DataRecord[];\r\n rowKey: (row: DataRecord) => DataTableRowKey;\r\n onSave?: (rowData: DataRecord, rowIndex: number) => void | Promise<void>;\r\n onCancel?: (rowData: DataRecord, rowIndex: number) => void;\r\n}\r\n\r\n/**\r\n * 可编辑行组合函数,提供表格整行的编辑功能\r\n */\r\nexport function useRowEdit(options: RowEditOptions) {\r\n const editingRowKey = ref<DataTableRowKey | null>(null);\r\n const editingData = ref<Record<string, DataRecord>>({});\r\n\r\n /**\r\n * 检查指定行是否正在编辑状态\r\n */\r\n const isEditingRow = (rowKey: DataTableRowKey) => {\r\n return editingRowKey.value === rowKey;\r\n };\r\n\r\n /**\r\n * 根据rowKey实时查找最新的行数据\r\n */\r\n const findRowData = (rowKey: DataTableRowKey) => {\r\n const currentData = options.data();\r\n if (!currentData || !Array.isArray(currentData)) {\r\n return null;\r\n }\r\n return currentData.find((row) => options.rowKey(row) === rowKey);\r\n };\r\n\r\n /**\r\n * 开始编辑指定行,将原始数据复制到编辑缓存中\r\n */\r\n const startEditRow = (rowKey: DataTableRowKey) => {\r\n const rowData = findRowData(rowKey);\r\n if (!rowData) return;\r\n\r\n editingRowKey.value = rowKey;\r\n editingData.value[rowKey as string] = { ...rowData };\r\n };\r\n\r\n /**\r\n * 取消当前行编辑,调用取消回调并清理编辑状态\r\n */\r\n const cancelEditRow = async () => {\r\n if (!editingRowKey.value) return;\r\n\r\n const currentData = options.data();\r\n if (!currentData || !Array.isArray(currentData)) return;\r\n\r\n const rowIndex = currentData.findIndex(\r\n (row) => options.rowKey(row) === editingRowKey.value,\r\n );\r\n\r\n if (rowIndex > -1) {\r\n await options.onCancel?.(currentData[rowIndex], rowIndex);\r\n }\r\n\r\n editingRowKey.value = null;\r\n editingData.value = {};\r\n };\r\n\r\n /**\r\n * 保存当前行编辑,调用保存回调并清理编辑状态\r\n */\r\n const saveEditRow = async () => {\r\n if (!editingRowKey.value) return;\r\n\r\n const rowKey = editingRowKey.value;\r\n const currentData = options.data();\r\n if (!currentData || !Array.isArray(currentData)) return;\r\n\r\n const rowIndex = currentData.findIndex(\r\n (row) => options.rowKey(row) === rowKey,\r\n );\r\n\r\n if (rowIndex === -1) return;\r\n\r\n const updatedData = editingData.value[rowKey as string];\r\n if (!updatedData) return;\r\n\r\n await options.onSave?.(updatedData, rowIndex);\r\n\r\n editingRowKey.value = null;\r\n editingData.value = {};\r\n\r\n return { updatedData, rowIndex };\r\n };\r\n\r\n /**\r\n * 获取指定行的编辑中数据\r\n */\r\n const getEditingRowData = (rowKey: DataTableRowKey) => {\r\n return editingData.value[rowKey as string];\r\n };\r\n\r\n /**\r\n * 更新指定行编辑中的字段值\r\n */\r\n const updateEditingRowData = (\r\n rowKey: DataTableRowKey,\r\n field: string,\r\n value: unknown,\r\n ) => {\r\n if (!editingData.value[rowKey as string]) return;\r\n editingData.value[rowKey as string][field] = value;\r\n };\r\n\r\n return {\r\n editingRowKey,\r\n isEditingRow,\r\n startEditRow,\r\n cancelEditRow,\r\n saveEditRow,\r\n getEditingRowData,\r\n updateEditingRowData,\r\n findRowData,\r\n };\r\n}\r\n","/*\r\n * @Description: 可编辑单元格组合函数\r\n * @Migration: naive-ui-components 组件库迁移版本\r\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\r\n */\r\n\r\nimport { ref } from \"vue\";\r\nimport type { DataTableRowKey } from \"naive-ui/es\";\r\nimport type { DataRecord } from \"../types\";\r\n\r\n/**\r\n * 单元格编辑配置选项\r\n */\r\nexport interface CellEditOptions {\r\n data: () => DataRecord[];\r\n rowKey: (row: DataRecord) => DataTableRowKey;\r\n onSave?: (\r\n rowData: DataRecord,\r\n rowIndex: number,\r\n columnKey: string,\r\n ) => void | Promise<void>;\r\n}\r\n\r\n/**\r\n * 可编辑单元格组合函数,提供表格单元格的编辑功能\r\n */\r\nexport function useCellEdit(options: CellEditOptions) {\r\n const editingCell = ref<{\r\n rowKey: DataTableRowKey | null;\r\n columnKey: string | null;\r\n }>({\r\n rowKey: null,\r\n columnKey: null,\r\n });\r\n const editingData = ref<Record<string, unknown>>({});\r\n\r\n /**\r\n * 检查指定单元格是否正在编辑状态\r\n */\r\n const isEditingCell = (rowKey: DataTableRowKey, columnKey: string) => {\r\n return (\r\n editingCell.value.rowKey === rowKey &&\r\n editingCell.value.columnKey === columnKey\r\n );\r\n };\r\n\r\n /**\r\n * 根据rowKey实时查找最新的行数据\r\n */\r\n const findRowData = (rowKey: DataTableRowKey) => {\r\n const currentData = options.data();\r\n if (!currentData || !Array.isArray(currentData)) {\r\n return null;\r\n }\r\n return currentData.find((row) => options.rowKey(row) === rowKey);\r\n };\r\n\r\n /**\r\n * 开始编辑指定单元格,将原值存储到编辑缓存中\r\n */\r\n const startEditCell = (rowKey: DataTableRowKey, columnKey: string) => {\r\n const rowData = findRowData(rowKey);\r\n if (!rowData) return;\r\n\r\n editingCell.value = { rowKey, columnKey };\r\n editingData.value[`${rowKey}-${columnKey}`] = rowData[columnKey];\r\n };\r\n\r\n /**\r\n * 保存当前编辑的单元格,调用保存回调并清理编辑状态\r\n */\r\n const saveEditCell = async () => {\r\n const { rowKey, columnKey } = editingCell.value;\r\n if (!rowKey || !columnKey) return;\r\n\r\n const currentData = options.data();\r\n if (!currentData || !Array.isArray(currentData)) return;\r\n\r\n const rowIndex = currentData.findIndex(\r\n (row) => options.rowKey(row) === rowKey,\r\n );\r\n if (rowIndex === -1) return;\r\n\r\n const cellKey = `${rowKey}-${columnKey}`;\r\n const newValue = editingData.value[cellKey];\r\n\r\n const updatedData = {\r\n ...currentData[rowIndex],\r\n [columnKey]: newValue,\r\n };\r\n\r\n await options.onSave?.(updatedData, rowIndex, columnKey);\r\n\r\n editingCell.value = { rowKey: null, columnKey: null };\r\n delete editingData.value[cellKey];\r\n\r\n return { updatedData, rowIndex, columnKey };\r\n };\r\n\r\n /**\r\n * 取消当前单元格编辑,清理编辑状态和缓存数据\r\n */\r\n const cancelEditCell = () => {\r\n const { rowKey, columnKey } = editingCell.value;\r\n if (rowKey && columnKey) {\r\n delete editingData.value[`${rowKey}-${columnKey}`];\r\n }\r\n editingCell.value = { rowKey: null, columnKey: null };\r\n };\r\n\r\n /**\r\n * 获取指定单元格的编辑中数值\r\n */\r\n const getEditingCellValue = (rowKey: DataTableRowKey, columnKey: string) => {\r\n return editingData.value[`${rowKey}-${columnKey}`];\r\n };\r\n\r\n /**\r\n * 更新指定单元格的编辑中数值\r\n */\r\n const updateEditingCellValue = (\r\n rowKey: DataTableRowKey,\r\n columnKey: string,\r\n value: unknown,\r\n ) => {\r\n editingData.value[`${rowKey}-${columnKey}`] = value;\r\n };\r\n\r\n return {\r\n editingCell,\r\n isEditingCell,\r\n startEditCell,\r\n saveEditCell,\r\n cancelEditCell,\r\n getEditingCellValue,\r\n updateEditingCellValue,\r\n findRowData,\r\n };\r\n}\r\n","/*\r\n * @Description: 模态框编辑组合函数\r\n * @Migration: naive-ui-components 组件库迁移版本\r\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\r\n */\r\n\r\nimport { ref, computed } from \"vue\";\r\nimport type { DataTableRowKey } from \"naive-ui/es\";\r\n\r\n/**\r\n * 模态框编辑配置选项\r\n */\r\nexport interface ModalEditOptions<T = Record<string, any>> {\r\n data: () => T[];\r\n rowKey: (row: T) => DataTableRowKey;\r\n onSave?: (\r\n editingData: Record<string, any>,\r\n rowIndex: number,\r\n ) => void | Promise<void>;\r\n onCancel?: (originalData: T, rowIndex: number) => void | Promise<void>;\r\n}\r\n\r\n/**\r\n * 模态框编辑组合函数,提供弹窗形式的数据编辑功能\r\n */\r\nexport function useModalEdit<T = Record<string, any>>(\r\n options: ModalEditOptions<T>,\r\n) {\r\n const isModalVisible = ref(false);\r\n const editingRowKey = ref<DataTableRowKey | null>(null);\r\n const editingData = ref<Record<string, any>>({});\r\n\r\n /**\r\n * 计算属性:获取当前编辑行的索引\r\n */\r\n const editingRowIndex = computed(() => {\r\n if (!editingRowKey.value) return -1;\r\n const currentData = options.data();\r\n if (!currentData || !Array.isArray(currentData)) return -1;\r\n\r\n return currentData.findIndex(\r\n (row) => options.rowKey(row) === editingRowKey.value,\r\n );\r\n });\r\n\r\n /**\r\n * 重置编辑状态\r\n */\r\n const resetEditingState = () => {\r\n editingRowKey.value = null;\r\n editingData.value = {};\r\n };\r\n\r\n /**\r\n * 开始编辑指定行\r\n */\r\n const startEdit = (rowKey: DataTableRowKey) => {\r\n console.log(\"🚀 开始编辑:\", rowKey);\r\n\r\n const currentData = options.data();\r\n if (!currentData || !Array.isArray(currentData)) {\r\n console.warn(\"数据源为空或不是数组\");\r\n return;\r\n }\r\n\r\n const rowIndex = currentData.findIndex(\r\n (row) => options.rowKey(row) === rowKey,\r\n );\r\n\r\n if (rowIndex === -1) {\r\n console.warn(\"未找到对应的行数据:\", rowKey);\r\n return;\r\n }\r\n\r\n const rowData = currentData[rowIndex];\r\n console.log(\"找到行数据:\", rowData);\r\n\r\n editingRowKey.value = rowKey;\r\n editingData.value = JSON.parse(JSON.stringify(rowData));\r\n\r\n console.log(\"编辑数据已准备:\", editingData.value);\r\n\r\n isModalVisible.value = true;\r\n };\r\n\r\n /**\r\n * 保存编辑\r\n */\r\n const saveEdit = async (formData?: Record<string, any>) => {\r\n const dataToSave = formData || editingData.value;\r\n const currentIndex = editingRowIndex.value;\r\n\r\n console.log(\"💾 保存编辑:\", {\r\n rowKey: editingRowKey.value,\r\n index: currentIndex,\r\n data: dataToSave,\r\n });\r\n\r\n if (currentIndex === -1) {\r\n console.warn(\"无法找到编辑行的索引\");\r\n return;\r\n }\r\n\r\n try {\r\n await options.onSave?.(dataToSave, currentIndex);\r\n\r\n isModalVisible.value = false;\r\n resetEditingState();\r\n\r\n console.log(\"✅ 保存成功\");\r\n } catch (error) {\r\n console.error(\"❌ 保存失败:\", error);\r\n throw error;\r\n }\r\n };\r\n\r\n /**\r\n * 取消编辑\r\n */\r\n const cancelEdit = async () => {\r\n console.log(\"🚫 取消编辑\");\r\n\r\n try {\r\n const currentIndex = editingRowIndex.value;\r\n if (editingRowKey.value && currentIndex > -1) {\r\n const currentData = options.data();\r\n if (currentData && currentData[currentIndex]) {\r\n await options.onCancel?.(currentData[currentIndex], currentIndex);\r\n }\r\n }\r\n } finally {\r\n isModalVisible.value = false;\r\n resetEditingState();\r\n }\r\n };\r\n\r\n /**\r\n * 更新编辑数据\r\n */\r\n const updateEditingData = (data: Record<string, any>) => {\r\n editingData.value = { ...data };\r\n };\r\n\r\n /**\r\n * 获取当前编辑的行数据\r\n */\r\n const getEditingRowData = (rowKey: DataTableRowKey) => {\r\n if (editingRowKey.value === rowKey) {\r\n return editingData.value;\r\n }\r\n return null;\r\n };\r\n\r\n /**\r\n * 检查是否正在编辑指定行\r\n */\r\n const isEditingRow = (rowKey: DataTableRowKey) => {\r\n return editingRowKey.value === rowKey && isModalVisible.value;\r\n };\r\n\r\n return {\r\n isModalVisible,\r\n editingData,\r\n editingRowKey,\r\n editingRowIndex,\r\n\r\n startEdit,\r\n saveEdit,\r\n cancelEdit,\r\n updateEditingData,\r\n resetEditingState,\r\n\r\n isEditingRow,\r\n getEditingRowData,\r\n };\r\n}\r\n","/*\r\n * @Description: 表格展开功能\r\n * @Migration: naive-ui-components 组件库迁移版本\r\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\r\n */\r\n\r\nimport {\r\n h,\r\n ref,\r\n computed,\r\n unref,\r\n watchEffect,\r\n nextTick,\r\n type VNodeChild,\r\n type Ref,\r\n} from 'vue'\r\nimport { type DataTableRowKey, NSpin, NDataTable } from 'naive-ui/es'\r\nimport type {\r\n TableColumn,\r\n UseTableExpandOptions,\r\n UseTableExpandReturn,\r\n ChildSelectionState,\r\n DataRecord,\r\n} from '../types'\r\n\r\n/* ================= 核心状态管理 ================= */\r\nconst useExpandState = <T extends DataRecord, C>(\r\n options: UseTableExpandOptions<T, C>\r\n) => {\r\n const expandedKeys = ref<DataTableRowKey[]>([\r\n ...(options.defaultExpandedKeys || []),\r\n ])\r\n const expandDataMap = ref(new Map<DataTableRowKey, any>()) as Ref<\r\n Map<DataTableRowKey, C[]>\r\n >\r\n const loadingMap = ref(new Map<DataTableRowKey, boolean>())\r\n\r\n const checkedKeys = options.enableSelection\r\n ? ref<DataTableRowKey[]>([...(options.defaultCheckedKeys || [])])\r\n : ref<DataTableRowKey[]>([])\r\n\r\n const childSelections = options.enableChildSelection\r\n ? ref(new Map<DataTableRowKey, DataTableRowKey[]>())\r\n : ref(new Map<DataTableRowKey, DataTableRowKey[]>())\r\n\r\n return {\r\n expandedKeys,\r\n expandDataMap,\r\n loadingMap,\r\n checkedKeys,\r\n childSelections,\r\n } as const\r\n}\r\n\r\n/* ================= 数据工具函数 ================= */\r\nconst useDataUtils = <T extends DataRecord, C>(\r\n options: UseTableExpandOptions<T, C>\r\n) => {\r\n const data = computed(() => unref(options.data))\r\n\r\n const getRowKey = options.rowKey\r\n const getChildRowKey =\r\n options.childRowKey || ((child: C): DataTableRowKey => (child as any).id)\r\n\r\n const findRow = (key: DataTableRowKey): T | undefined =>\r\n data.value.find(row => getRowKey(row) === key)\r\n\r\n const isRowExpandable = options.rowExpandable || ((): boolean => true)\r\n const isRowCheckable = options.rowCheckable || ((): boolean => true)\r\n const isChildRowCheckable = options.childRowCheckable || ((): boolean => true)\r\n\r\n return {\r\n data,\r\n getRowKey,\r\n getChildRowKey,\r\n findRow,\r\n isRowExpandable,\r\n isRowCheckable,\r\n isChildRowCheckable,\r\n } as const\r\n}\r\n\r\n/* ================= 展开逻辑 ================= */\r\nconst useExpandLogic = <T extends DataRecord, C>(\r\n state: ReturnType<typeof useExpandState<T, C>>,\r\n utils: ReturnType<typeof useDataUtils<T, C>>,\r\n options: UseTableExpandOptions<T, C>\r\n) => {\r\n const loadData = async (row: T): Promise<C[]> => {\r\n if (!options.onLoadData) return []\r\n\r\n const key = utils.getRowKey(row)\r\n const existingData = state.expandDataMap.value.get(key)\r\n if (existingData) return existingData\r\n\r\n state.loadingMap.value.set(key, true)\r\n\r\n try {\r\n const data = await options.onLoadData(row)\r\n const result = data || []\r\n state.expandDataMap.value.set(key, result as any)\r\n\r\n if (\r\n options.enableChildSelection &&\r\n !state.childSelections.value.has(key)\r\n ) {\r\n state.childSelections.value.set(key, [])\r\n }\r\n\r\n return result\r\n } catch (error) {\r\n console.error('加载展开数据失败:', error)\r\n return []\r\n } finally {\r\n state.loadingMap.value.set(key, false)\r\n }\r\n }\r\n\r\n const handleRowExpand = async (row: T, expanded: boolean): Promise<void> => {\r\n const key = utils.getRowKey(row)\r\n\r\n if (expanded) {\r\n await loadData(row)\r\n if (!state.expandedKeys.value.includes(key)) {\r\n state.expandedKeys.value = [...state.expandedKeys.value, key]\r\n }\r\n } else {\r\n state.expandedKeys.value = state.expandedKeys.value.filter(k => k !== key)\r\n }\r\n\r\n options.onExpandChange?.(state.expandedKeys.value)\r\n }\r\n\r\n const expandAll = async (): Promise<void> => {\r\n const expandableRows = utils.data.value.filter(utils.isRowExpandable)\r\n await Promise.allSettled(expandableRows.map(loadData))\r\n state.expandedKeys.value = expandableRows.map(utils.getRowKey)\r\n options.onExpandChange?.(state.expandedKeys.value)\r\n }\r\n\r\n const collapseAll = (): void => {\r\n state.expandedKeys.value = []\r\n state.childSelections.value.clear()\r\n options.onExpandChange?.(state.expandedKeys.value)\r\n }\r\n\r\n const handleExpandChange = async (keys: DataTableRowKey[]): Promise<void> => {\r\n const newExpandedKeys = keys.filter(\r\n key => !state.expandedKeys.value.includes(key)\r\n )\r\n const collapsedKeys = state.expandedKeys.value.filter(\r\n key => !keys.includes(key)\r\n )\r\n\r\n await Promise.all(\r\n newExpandedKeys.map(async key => {\r\n const row = utils.findRow(key)\r\n if (row) {\r\n await loadData(row)\r\n }\r\n })\r\n )\r\n\r\n for (const key of collapsedKeys) {\r\n state.childSelections.value.delete(key)\r\n }\r\n\r\n state.expandedKeys.value = keys\r\n options.onExpandChange?.(keys)\r\n }\r\n\r\n return {\r\n loadData,\r\n handleRowExpand,\r\n expandAll,\r\n collapseAll,\r\n handleExpandChange,\r\n } as const\r\n}\r\n\r\n/* ================= 选择逻辑 ================= */\r\nconst useSelectionLogic = <T extends DataRecord, C>(\r\n state: ReturnType<typeof useExpandState<T, C>>,\r\n utils: ReturnType<typeof useDataUtils<T, C>>,\r\n options: UseTableExpandOptions<T, C>\r\n) => {\r\n const selectableRows = computed(() =>\r\n utils.data.value.filter(utils.isRowCheckable)\r\n )\r\n\r\n const selectedRowsCount = computed(() => state.checkedKeys.value.length)\r\n\r\n const selectAll = (): void => {\r\n if (!options.enableSelection) return\r\n\r\n const keys = selectableRows.value.map(utils.getRowKey)\r\n const finalKeys = options.maxSelection\r\n ? keys.slice(0, options.maxSelection)\r\n : keys\r\n\r\n state.checkedKeys.value = finalKeys\r\n\r\n const selectedRows = selectableRows.value.filter(row =>\r\n finalKeys.includes(utils.getRowKey(row))\r\n )\r\n\r\n options.onSelectionChange?.(\r\n finalKeys,\r\n selectedRows,\r\n state.childSelections.value\r\n )\r\n }\r\n\r\n const clearSelection = (): void => {\r\n if (!options.enableSelection) return\r\n\r\n state.checkedKeys.value = []\r\n options.onSelectionChange?.([], [], state.childSelections.value)\r\n }\r\n\r\n const handleSelectionChange = (keys: DataTableRowKey[]): void => {\r\n if (!options.enableSelection) return\r\n\r\n state.checkedKeys.value = keys\r\n const selectedRows = utils.data.value.filter(row =>\r\n keys.includes(utils.getRowKey(row))\r\n )\r\n options.onSelectionChange?.(keys, selectedRows, state.childSelections.value)\r\n }\r\n\r\n return {\r\n selectAll,\r\n clearSelection,\r\n handleSelectionChange,\r\n selectedRowsCount,\r\n selectableRows,\r\n } as const\r\n}\r\n\r\n/* ================= 父子联动逻辑 ================= */\r\nconst useParentChildLink = <T extends DataRecord, C>(\r\n state: ReturnType<typeof useExpandState<T, C>>,\r\n options: UseTableExpandOptions<T, C>\r\n) => {\r\n const isLinkEnabled = Boolean(\r\n options.enableParentChildLink &&\r\n options.enableSelection &&\r\n options.enableChildSelection\r\n )\r\n\r\n const handleParentChildLink = (\r\n parentKey: DataTableRowKey,\r\n selectedChildKeys: DataTableRowKey[],\r\n totalChildren: number\r\n ): void => {\r\n if (!isLinkEnabled) return\r\n\r\n const shouldSelectParent =\r\n options.parentChildLinkMode === 'strict'\r\n ? selectedChildKeys.length === totalChildren && totalChildren > 0\r\n : selectedChildKeys.length > 0\r\n\r\n const currentKeys = [...state.checkedKeys.value]\r\n const isParentSelected = currentKeys.includes(parentKey)\r\n\r\n if (shouldSelectParent && !isParentSelected) {\r\n state.checkedKeys.value = [...currentKeys, parentKey]\r\n } else if (!shouldSelectParent && isParentSelected) {\r\n state.checkedKeys.value = currentKeys.filter(k => k !== parentKey)\r\n }\r\n }\r\n\r\n return {\r\n handleParentChildLink,\r\n isLinkEnabled,\r\n } as const\r\n}\r\n\r\n/* ================= 子选择逻辑 ================= */\r\nconst useChildSelectionLogic = <T extends DataRecord, C>(\r\n state: ReturnType<typeof useExpandState<T, C>>,\r\n utils: ReturnType<typeof useDataUtils<T, C>>,\r\n parentChildLink: ReturnType<typeof useParentChildLink<T, C>>,\r\n options: UseTableExpandOptions<T, C>\r\n) => {\r\n const totalChildSelections = computed(() => {\r\n if (!options.enableChildSelection) return 0\r\n return Array.from(state.childSelections.value.values()).reduce(\r\n (total, keys) => total + keys.length,\r\n 0\r\n )\r\n })\r\n\r\n const clearAllSelections = (): void => {\r\n state.checkedKeys.value = []\r\n state.childSelections.value.clear()\r\n options.onSelectionChange?.([], [], state.childSelections.value)\r\n }\r\n\r\n const handleChildSelectionChange = (\r\n parentKey: DataTableRowKey,\r\n childKeys: DataTableRowKey[]\r\n ): void => {\r\n if (!options.enableChildSelection) return\r\n\r\n state.childSelections.value.set(parentKey, childKeys)\r\n\r\n const expandData = state.expandDataMap.value.get(parentKey) || []\r\n const selectedChildren = expandData.filter((child: any) =>\r\n childKeys.includes(utils.getChildRowKey(child as C))\r\n ) as C[]\r\n\r\n options.onChildSelectionChange?.(parentKey, childKeys, selectedChildren)\r\n\r\n if (parentChildLink.isLinkEnabled) {\r\n parentChildLink.handleParentChildLink(\r\n parentKey,\r\n childKeys,\r\n expandData.length\r\n )\r\n }\r\n }\r\n\r\n return {\r\n totalChildSelections,\r\n clearAllSelections,\r\n handleChildSelectionChange,\r\n } as const\r\n}\r\n\r\n/* ================= 渲染辅助函数 ================= */\r\nconst createChildSelectionState = <T extends DataRecord, C>(\r\n parentKey: DataTableRowKey,\r\n state: ReturnType<typeof useExpandState<T, C>>,\r\n utils: ReturnType<typeof useDataUtils<T, C>>,\r\n childLogic: ReturnType<typeof useChildSelectionLogic<T, C>>,\r\n options: UseTableExpandOptions<T, C>\r\n): ChildSelectionState | undefined => {\r\n if (!options.enableChildSelection) return undefined\r\n\r\n const selectedKeys = state.childSelections.value.get(parentKey) || []\r\n const expandData = state.expandDataMap.value.get(parentKey) || []\r\n const parent = utils.findRow(parentKey)\r\n\r\n if (!parent) return undefined\r\n\r\n const checkableChildren = expandData.filter((child: any) =>\r\n utils.isChildRowCheckable(child as C, parent)\r\n )\r\n\r\n const isAllChecked =\r\n checkableChildren.length > 0 &&\r\n checkableChildren.every((child: any) =>\r\n selectedKeys.includes(utils.getChildRowKey(child as C))\r\n )\r\n\r\n return {\r\n selectedKeys,\r\n isAllChecked,\r\n selectAll: () => {\r\n const allKeys = checkableChildren.map((child: any) =>\r\n utils.getChildRowKey(child as C)\r\n )\r\n childLogic.handleChildSelectionChange(parentKey, allKeys)\r\n },\r\n clearAll: () => {\r\n childLogic.handleChildSelectionChange(parentKey, [])\r\n },\r\n }\r\n}\r\n\r\nconst createLoadingView = (): VNodeChild => {\r\n return h('div', { class: 'flex justify-center items-center py-8' }, [\r\n h(NSpin, { size: 'small' }),\r\n h('span', { class: 'ml-2 text-gray-500' }, '加载中...'),\r\n ])\r\n}\r\n\r\nconst createEmptyView = (): VNodeChild => {\r\n return h('div', { class: 'text-center py-8 text-gray-400' }, '暂无数据')\r\n}\r\n\r\nconst createDefaultColumns = (expandData: DataRecord[]): DataRecord[] => {\r\n if (!expandData.length) return []\r\n\r\n const firstItem = expandData[0]\r\n if (!firstItem || typeof firstItem !== 'object') return []\r\n\r\n const dataKeys = Object.keys(firstItem).filter(\r\n key => !['id', 'key'].includes(key)\r\n )\r\n\r\n return [\r\n {\r\n title: '序号',\r\n key: '_index',\r\n width: 60,\r\n render: (_: unknown, index: number) => index + 1,\r\n },\r\n ...dataKeys.map(key => ({\r\n key,\r\n title: key.charAt(0).toUpperCase() + key.slice(1),\r\n width: 120,\r\n ellipsis: { tooltip: true },\r\n })),\r\n ]\r\n}\r\n\r\nconst createDefaultTable = <T extends DataRecord, C>(\r\n key: DataTableRowKey,\r\n expandData: DataRecord[],\r\n childSelection: ChildSelectionState | undefined,\r\n utils: ReturnType<typeof useDataUtils<T, C>>,\r\n childLogic: ReturnType<typeof useChildSelectionLogic<T, C>>,\r\n options: UseTableExpandOptions<T, C>\r\n): VNodeChild => {\r\n const columns: Array<Record<string, unknown>> = []\r\n\r\n if (options.enableChildSelection) {\r\n columns.push({ type: 'selection', multiple: true })\r\n }\r\n\r\n columns.push(...createDefaultColumns(expandData))\r\n\r\n return h('div', { class: 'p-4 bg-gray-50' }, [\r\n h(\r\n 'div',\r\n { class: 'mb-2 text-sm text-gray-600' },\r\n `详细信息 (${expandData.length} 条)`\r\n ),\r\n h(NDataTable, {\r\n data: expandData,\r\n columns: columns as any,\r\n size: 'small',\r\n striped: true,\r\n checkedRowKeys: childSelection?.selectedKeys || [],\r\n rowKey: (row: DataRecord) => utils.getChildRowKey(row as C),\r\n onUpdateCheckedRowKeys: options.enableChildSelection\r\n ? (keys: DataTableRowKey[]) => {\r\n childLogic.handleChildSelectionChange(key, keys)\r\n }\r\n : undefined,\r\n }),\r\n ])\r\n}\r\n\r\n/* ================= 渲染逻辑 ================= */\r\nconst useRenderer = <T extends DataRecord, C>(\r\n state: ReturnType<typeof useExpandState<T, C>>,\r\n utils: ReturnType<typeof useDataUtils<T, C>>,\r\n childLogic: ReturnType<typeof useChildSelectionLogic<T, C>>,\r\n expandLogic: ReturnType<typeof useExpandLogic<T, C>>,\r\n options: UseTableExpandOptions<T, C>\r\n) => {\r\n const renderExpandContent = (row: T): VNodeChild => {\r\n const key = utils.getRowKey(row)\r\n const expandData = state.expandDataMap.value.get(key) || []\r\n const loading = state.loadingMap.value.get(key) || false\r\n\r\n if (\r\n !expandData.length &&\r\n !loading &&\r\n state.expandedKeys.value.includes(key)\r\n ) {\r\n nextTick(() => {\r\n expandLogic.loadData(row)\r\n })\r\n }\r\n\r\n const childSelection = createChildSelectionState(\r\n key,\r\n state,\r\n utils,\r\n childLogic,\r\n options\r\n )\r\n\r\n if (options.renderContent) {\r\n return options.renderContent(\r\n row,\r\n expandData as C[],\r\n loading,\r\n childSelection\r\n )\r\n }\r\n\r\n if (loading) return createLoadingView()\r\n if (!expandData.length) return createEmptyView()\r\n\r\n return createDefaultTable(\r\n key,\r\n expandData as DataRecord[],\r\n childSelection,\r\n utils,\r\n childLogic,\r\n options\r\n )\r\n }\r\n\r\n const getTableColumns = (originalColumns: TableColumn<T>[]): unknown[] => {\r\n return originalColumns.map(column => {\r\n if ((column as any).type === 'selection' && options.enableSelection) {\r\n return {\r\n type: 'selection',\r\n disabled: (row: T) => !utils.isRowCheckable(row),\r\n multiple: !options.maxSelection || options.maxSelection > 1,\r\n }\r\n }\r\n\r\n if (\r\n (column as any).type === 'expand' &&\r\n (options.onLoadData || options.renderContent)\r\n ) {\r\n return {\r\n type: 'expand',\r\n expandable: utils.isRowExpandable,\r\n renderExpand: renderExpandContent,\r\n }\r\n }\r\n\r\n return column\r\n })\r\n }\r\n\r\n return {\r\n getTableColumns,\r\n } as const\r\n}\r\n\r\n/* ================= 主函数 ================= */\r\n/**\r\n *\r\n */\r\nexport function useTableExpand<\r\n T extends DataRecord = Record<string, any>,\r\n C = any,\r\n>(options: UseTableExpandOptions<T, C>): UseTableExpandReturn<T, C> {\r\n const state = useExpandState(options)\r\n const utils = useDataUtils(options)\r\n const expandLogic = useExpandLogic(state, utils, options)\r\n const selectionLogic = useSelectionLogic(state, utils, options)\r\n const parentChildLink = useParentChildLink(state, options)\r\n const childLogic = useChildSelectionLogic(\r\n state,\r\n utils,\r\n parentChildLink,\r\n options\r\n )\r\n const renderer = useRenderer(state, utils, childLogic, expandLogic, options)\r\n\r\n if (options.onSelectionChange && options.enableSelection) {\r\n watchEffect(() => {\r\n if (!options.onSelectionChange) return\r\n\r\n const selectedRows = utils.data.value.filter(row =>\r\n state.checkedKeys.value.includes(utils.getRowKey(row))\r\n )\r\n\r\n const hasSelection = state.checkedKeys.value.length > 0\r\n const hasData = utils.data.value.length > 0\r\n\r\n if (hasSelection || !hasData) {\r\n options.onSelectionChange(\r\n state.checkedKeys.value,\r\n selectedRows,\r\n state.childSelections.value\r\n )\r\n }\r\n })\r\n }\r\n\r\n const expandRow = async (key: DataTableRowKey): Promise<void> => {\r\n if (state.expandedKeys.value.includes(key)) return\r\n\r\n const row = utils.findRow(key)\r\n if (!row) return\r\n\r\n await expandLogic.loadData(row)\r\n state.expandedKeys.value = [...state.expandedKeys.value, key]\r\n options.onExpandChange?.(state.expandedKeys.value, row, true)\r\n }\r\n\r\n const initializeData = async (): Promise<void> => {\r\n const keysToLoad = options.defaultExpandedKeys || []\r\n if (keysToLoad.length === 0) return\r\n\r\n const loadPromises = keysToLoad.map(async key => {\r\n const row = utils.findRow(key)\r\n if (row && !state.expandDataMap.value.has(key)) {\r\n await expandLogic.loadData(row)\r\n }\r\n })\r\n\r\n await Promise.allSettled(loadPromises)\r\n }\r\n\r\n if (options.defaultExpandedKeys?.length) {\r\n nextTick(initializeData)\r\n }\r\n\r\n return {\r\n expandedKeys: state.expandedKeys,\r\n checkedKeys: state.checkedKeys,\r\n childSelections: state.childSelections,\r\n\r\n selectedRowsCount: selectionLogic.selectedRowsCount,\r\n totalChildSelections: childLogic.totalChildSelections,\r\n\r\n expandAll: expandLogic.expandAll,\r\n collapseAll: expandLogic.collapseAll,\r\n expandRow,\r\n handleExpandChange: expandLogic.handleExpandChange,\r\n\r\n selectAll: selectionLogic.selectAll,\r\n clearSelection: selectionLogic.clearSelection,\r\n clearAllSelections: childLogic.clearAllSelections,\r\n handleSelectionChange: selectionLogic.handleSelectionChange,\r\n\r\n getTableColumns: renderer.getTableColumns,\r\n\r\n expandDataMap: state.expandDataMap,\r\n loadingMap: state.loadingMap,\r\n }\r\n}\r\n","/*\r\n * @Description: 打印水印组合工具 - 封装 printJS 和 html2canvas\r\n * @Migration: naive-ui-components 组件库迁移版本(从 @/hooks/usePrintWatermark 迁移)\r\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\r\n */\r\n\r\nimport { ref, nextTick } from 'vue'\r\nimport { useMessage as _useNaiveMessage } from 'naive-ui'\r\nimport html2canvas from 'html2canvas'\r\nimport printJS from 'print-js'\r\n\r\n/* ================= 类型定义 ================= */\r\n\r\n/** 消息提示 API 接口 */\r\ninterface MessageApi {\r\n success: (msg: string) => void\r\n error: (msg: string) => void\r\n warning: (msg: string) => void\r\n}\r\n\r\nexport interface WatermarkConfig {\r\n text: string\r\n textSize?: number\r\n textColor?: string\r\n font?: string\r\n opacity?: number\r\n rotate?: number\r\n position?: 'center' | 'repeat' | 'corner' | 'diagonal'\r\n xGap?: number\r\n yGap?: number\r\n}\r\n\r\nexport interface CaptureConfig {\r\n scale?: number\r\n quality?: number\r\n format?: 'png' | 'jpeg' | 'webp'\r\n backgroundColor?: string\r\n logging?: boolean\r\n allowTaint?: boolean\r\n useCORS?: boolean\r\n}\r\n\r\nexport interface PrintConfig {\r\n orientation?: 'portrait' | 'landscape'\r\n paperSize?: 'A4' | 'A3' | 'Letter' | string\r\n margin?: string\r\n style?: string\r\n header?: string\r\n footer?: string\r\n showModal?: boolean\r\n modalMessage?: string\r\n onLoadingStart?: () => void\r\n onLoadingEnd?: () => void\r\n onPrintDialogClose?: () => void\r\n}\r\n\r\nexport interface PrintWatermarkOptions {\r\n watermark?: WatermarkConfig\r\n capture?: CaptureConfig\r\n print?: PrintConfig\r\n}\r\n\r\n/* ================= 默认配置 ================= */\r\nconst DEFAULT_WATERMARK: Required<WatermarkConfig> = {\r\n text: 'Robot Admin',\r\n textSize: 16,\r\n textColor: 'rgba(128, 128, 128, 0.3)',\r\n font: 'Microsoft JhengHei',\r\n opacity: 0.3,\r\n rotate: -20,\r\n position: 'repeat',\r\n xGap: 200,\r\n yGap: 120,\r\n}\r\n\r\nconst DEFAULT_CAPTURE: Required<Omit<CaptureConfig, 'dpi'>> = {\r\n scale: 1,\r\n quality: 0.92,\r\n format: 'png',\r\n backgroundColor: '#ffffff',\r\n logging: false,\r\n allowTaint: true,\r\n useCORS: true,\r\n}\r\n\r\nconst DEFAULT_PRINT: Required<\r\n Omit<PrintConfig, 'onPrintDialogOpen' | 'onError'>\r\n> = {\r\n orientation: 'portrait',\r\n paperSize: 'A4',\r\n margin: '10mm 10mm 0 10mm',\r\n style:\r\n '@page { margin: 10mm 10mm 0 10mm; } @media print { body { margin: 0; } }',\r\n header: '',\r\n footer: '',\r\n showModal: false,\r\n modalMessage: '正在准备打印...',\r\n onLoadingStart: () => {},\r\n onLoadingEnd: () => {},\r\n onPrintDialogClose: () => {},\r\n}\r\n\r\n/* ================= 水印绘制函数 ================= */\r\n\r\n/**\r\n *\r\n */\r\nfunction drawCenterWatermark(\r\n ctx: CanvasRenderingContext2D,\r\n width: number,\r\n height: number,\r\n config: Required<WatermarkConfig>\r\n) {\r\n ctx.save()\r\n ctx.translate(width / 2, height / 2)\r\n ctx.rotate((config.rotate * Math.PI) / 180)\r\n ctx.fillText(config.text, 0, 0)\r\n ctx.restore()\r\n}\r\n\r\n/**\r\n *\r\n */\r\nfunction drawCornerWatermark(\r\n ctx: CanvasRenderingContext2D,\r\n width: number,\r\n height: number,\r\n config: Required<WatermarkConfig>\r\n) {\r\n const offset = 50\r\n const corners = [\r\n [offset, offset],\r\n [width - offset, offset],\r\n [offset, height - offset],\r\n [width - offset, height - offset],\r\n ]\r\n\r\n corners.forEach(([x, y]) => {\r\n ctx.save()\r\n ctx.translate(x, y)\r\n ctx.rotate((config.rotate * Math.PI) / 180)\r\n ctx.fillText(config.text, 0, 0)\r\n ctx.restore()\r\n })\r\n}\r\n\r\n/**\r\n *\r\n */\r\nfunction drawDiagonalWatermark(\r\n ctx: CanvasRenderingContext2D,\r\n width: number,\r\n height: number,\r\n config: Required<WatermarkConfig>\r\n) {\r\n const diagonal = Math.sqrt(width * width + height * height)\r\n const centerX = width / 2\r\n const centerY = height / 2\r\n\r\n ctx.save()\r\n ctx.translate(centerX, centerY)\r\n ctx.rotate(Math.atan2(height, width))\r\n\r\n const count = Math.floor(diagonal / config.xGap)\r\n const start = -diagonal / 2\r\n\r\n for (let i = 0; i <= count; i++) {\r\n const x = start + i * config.xGap\r\n ctx.fillText(config.text, x, 0)\r\n }\r\n\r\n ctx.restore()\r\n}\r\n\r\n/**\r\n *\r\n */\r\nfunction drawRepeatWatermark(\r\n ctx: CanvasRenderingContext2D,\r\n width: number,\r\n height: number,\r\n config: Required<WatermarkConfig>\r\n) {\r\n const cols = Math.ceil(width / config.xGap) + 1\r\n const rows = Math.ceil(height / config.yGap) + 1\r\n\r\n for (let row = 0; row < rows; row++) {\r\n for (let col = 0; col < cols; col++) {\r\n const x = col * config.xGap + config.xGap / 2\r\n const y = row * config.yGap + config.yGap / 2\r\n\r\n ctx.save()\r\n ctx.translate(x, y)\r\n ctx.rotate((config.rotate * Math.PI) / 180)\r\n ctx.fillText(config.text, 0, 0)\r\n ctx.restore()\r\n }\r\n }\r\n}\r\n\r\n/**\r\n *\r\n */\r\nfunction drawWatermark(\r\n canvas: HTMLCanvasElement,\r\n config: WatermarkConfig\r\n): HTMLCanvasElement {\r\n const ctx = canvas.getContext('2d')\r\n if (!ctx) return canvas\r\n\r\n const finalConfig = { ...DEFAULT_WATERMARK, ...config }\r\n const { width, height } = canvas\r\n\r\n ctx.save()\r\n ctx.globalAlpha = finalConfig.opacity\r\n ctx.font = `${finalConfig.textSize}px ${finalConfig.font}`\r\n ctx.fillStyle = finalConfig.textColor\r\n ctx.textAlign = 'center'\r\n ctx.textBaseline = 'middle'\r\n\r\n const drawFunctions = {\r\n center: drawCenterWatermark,\r\n corner: drawCornerWatermark,\r\n diagonal: drawDiagonalWatermark,\r\n repeat: drawRepeatWatermark,\r\n }\r\n\r\n const drawFunction =\r\n drawFunctions[finalConfig.position] || drawFunctions.repeat\r\n drawFunction(ctx, width, height, finalConfig)\r\n\r\n ctx.restore()\r\n return canvas\r\n}\r\n\r\n/* ================= 截图函数 ================= */\r\n\r\n/**\r\n *\r\n */\r\nasync function captureElement(\r\n element: HTMLElement,\r\n config: CaptureConfig = {}\r\n): Promise<HTMLCanvasElement> {\r\n const finalConfig = { ...DEFAULT_CAPTURE, ...config }\r\n\r\n return await html2canvas(element, {\r\n logging: finalConfig.logging,\r\n scale: finalConfig.scale,\r\n allowTaint: finalConfig.allowTaint,\r\n useCORS: finalConfig.useCORS,\r\n backgroundColor: finalConfig.backgroundColor,\r\n onclone: clonedDoc => {\r\n const clonedElement = clonedDoc.body\r\n clonedElement.style.transform = 'scale(1)'\r\n },\r\n })\r\n}\r\n\r\n/**\r\n *\r\n */\r\nfunction canvasToDataURL(\r\n canvas: HTMLCanvasElement,\r\n format: 'png' | 'jpeg' | 'webp' = 'png',\r\n quality: number = 0.92\r\n): string {\r\n const mimeType = `image/${format}`\r\n return format === 'png'\r\n ? canvas.toDataURL(mimeType)\r\n : canvas.toDataURL(mimeType, quality)\r\n}\r\n\r\n/**\r\n *\r\n */\r\nfunction downloadCanvas(\r\n canvas: HTMLCanvasElement,\r\n filename: string = `screenshot-${new Date().toISOString().slice(0, 10)}.png`,\r\n format: 'png' | 'jpeg' | 'webp' = 'png'\r\n) {\r\n const dataURL = canvasToDataURL(canvas, format)\r\n const link = document.createElement('a')\r\n link.download = filename\r\n link.href = dataURL\r\n document.body.appendChild(link)\r\n link.click()\r\n document.body.removeChild(link)\r\n}\r\n\r\n/* ================= 打印函数 ================= */\r\n\r\nlet focusInterval: ReturnType<typeof setInterval> | null = null\r\n\r\n/**\r\n *\r\n */\r\nfunction startFocusKeep() {\r\n focusInterval = setInterval(() => {\r\n window.dispatchEvent(new Event('focus'))\r\n }, 500)\r\n}\r\n\r\n/**\r\n *\r\n */\r\nfunction stopFocusKeep() {\r\n if (focusInterval) {\r\n clearInterval(focusInterval)\r\n focusInterval = null\r\n }\r\n}\r\n\r\n/**\r\n *\r\n */\r\nfunction printImage(\r\n imageDataURL: string,\r\n config: PrintConfig = {}\r\n): Promise<void> {\r\n const finalConfig = { ...DEFAULT_PRINT, ...config }\r\n\r\n return new Promise<void>((resolve, reject) => {\r\n try {\r\n startFocusKeep()\r\n finalConfig.onLoadingStart?.()\r\n\r\n printJS({\r\n printable: imageDataURL,\r\n type: 'image',\r\n style: finalConfig.style,\r\n showModal: finalConfig.showModal,\r\n modalMessage: finalConfig.modalMessage,\r\n onLoadingEnd: finalConfig.onLoadingEnd,\r\n onPrintDialogClose: () => {\r\n stopFocusKeep()\r\n finalConfig.onPrintDialogClose?.()\r\n resolve()\r\n },\r\n })\r\n } catch (error) {\r\n stopFocusKeep()\r\n reject(error)\r\n }\r\n })\r\n}\r\n\r\n/* ================= 合并打印辅助函数 ================= */\r\n\r\n/**\r\n *\r\n */\r\nfunction calculateMergedCanvasSize(\r\n canvases: HTMLCanvasElement[],\r\n orientation: 'horizontal' | 'vertical',\r\n spacing: number\r\n) {\r\n let totalWidth = 0\r\n let totalHeight = 0\r\n\r\n canvases.forEach(canvas => {\r\n const { width, height } = canvas\r\n\r\n if (orientation === 'horizontal') {\r\n totalWidth += width\r\n totalHeight = Math.max(totalHeight, height)\r\n } else {\r\n totalWidth = Math.max(totalWidth, width)\r\n totalHeight += height\r\n }\r\n })\r\n\r\n const spacingTotal = (canvases.length - 1) * spacing\r\n if (orientation === 'horizontal') {\r\n totalWidth += spacingTotal\r\n } else {\r\n totalHeight += spacingTotal\r\n }\r\n\r\n return { totalWidth, totalHeight }\r\n}\r\n\r\n/**\r\n *\r\n */\r\nfunction createMergedCanvas(\r\n canvases: HTMLCanvasElement[],\r\n options: {\r\n orientation: 'horizontal' | 'vertical'\r\n spacing: number\r\n backgroundColor: string\r\n totalWidth: number\r\n totalHeight: number\r\n }\r\n): HTMLCanvasElement {\r\n const { orientation, spacing, backgroundColor, totalWidth, totalHeight } =\r\n options\r\n\r\n const mergedCanvas = document.createElement('canvas')\r\n mergedCanvas.width = totalWidth\r\n mergedCanvas.height = totalHeight\r\n\r\n const ctx = mergedCanvas.getContext('2d')\r\n if (!ctx) throw new Error('无法创建画布上下文')\r\n\r\n ctx.fillStyle = backgroundColor\r\n ctx.fillRect(0, 0, totalWidth, totalHeight)\r\n\r\n let currentX = 0\r\n let currentY = 0\r\n\r\n canvases.forEach(canvas => {\r\n ctx.drawImage(canvas, currentX, currentY)\r\n\r\n if (orientation === 'horizontal') {\r\n currentX += canvas.width + spacing\r\n } else {\r\n currentY += canvas.height + spacing\r\n }\r\n })\r\n\r\n return mergedCanvas\r\n}\r\n\r\n/**\r\n *\r\n */\r\nfunction validateAndGetMergeOptions(\r\n elements: HTMLElement[],\r\n mergeOptions: {\r\n orientation?: 'horizontal' | 'vertical'\r\n spacing?: number\r\n backgroundColor?: string\r\n },\r\n message: MessageApi\r\n) {\r\n if (!elements.length) {\r\n message.warning('没有要打印的元素')\r\n return null\r\n }\r\n\r\n return {\r\n orientation: mergeOptions.orientation || 'vertical',\r\n spacing: mergeOptions.spacing || 20,\r\n backgroundColor: mergeOptions.backgroundColor || '#ffffff',\r\n }\r\n}\r\n\r\n/**\r\n *\r\n */\r\nasync function captureAndMergeCanvas(\r\n elements: HTMLElement[],\r\n options: PrintWatermarkOptions,\r\n validatedOptions: {\r\n orientation: 'horizontal' | 'vertical'\r\n spacing: number\r\n backgroundColor: string\r\n },\r\n updateProgress: (value: number) => void\r\n): Promise<HTMLCanvasElement> {\r\n const { orientation, spacing, backgroundColor } = validatedOptions\r\n\r\n updateProgress(10)\r\n const canvases = await Promise.all(\r\n elements.map(element => captureElement(element, options.capture))\r\n )\r\n\r\n updateProgress(50)\r\n const { totalWidth, totalHeight } = calculateMergedCanvasSize(\r\n canvases,\r\n orientation,\r\n spacing\r\n )\r\n\r\n updateProgress(60)\r\n return createMergedCanvas(canvases, {\r\n orientation,\r\n spacing,\r\n backgroundColor,\r\n totalWidth,\r\n totalHeight,\r\n })\r\n}\r\n\r\n/**\r\n *\r\n */\r\nasync function addWatermarkAndPrint(\r\n mergedCanvas: HTMLCanvasElement,\r\n options: PrintWatermarkOptions,\r\n elementsCount: number,\r\n updateProgress: (value: number) => void,\r\n message: MessageApi\r\n): Promise<void> {\r\n updateProgress(70)\r\n\r\n if (options.watermark) {\r\n drawWatermark(mergedCanvas, options.watermark)\r\n }\r\n\r\n updateProgress(90)\r\n const imageUrl = canvasToDataURL(\r\n mergedCanvas,\r\n options.capture?.format,\r\n options.capture?.quality\r\n )\r\n await printImage(imageUrl, options.print)\r\n\r\n updateProgress(100)\r\n message.success(`合并打印完成,共合并 ${elementsCount} 个元素`)\r\n}\r\n\r\n/* ================= 主要的 Composable ================= */\r\n\r\n/**\r\n *\r\n */\r\nexport function usePrintWatermark() {\r\n /* NOTE: useMessage() requires naive-ui provider context; caller must ensure NMessageProvider is present */\r\n let message: MessageApi\r\n try {\r\n message = _useNaiveMessage()\r\n } catch {\r\n message = {\r\n success: (msg: string) => console.log('[PrintWatermark]', msg),\r\n error: (msg: string) => console.error('[PrintWatermark]', msg),\r\n warning: (msg: string) => console.warn('[PrintWatermark]', msg),\r\n }\r\n }\r\n const loading = ref(false)\r\n const progress = ref(0)\r\n\r\n const updateProgress = (value: number) => {\r\n progress.value = Math.max(0, Math.min(100, value))\r\n }\r\n\r\n const captureWithWatermark = async (\r\n element: HTMLElement,\r\n options: PrintWatermarkOptions = {}\r\n ): Promise<string> => {\r\n try {\r\n updateProgress(0)\r\n updateProgress(30)\r\n const canvas = await captureElement(element, options.capture)\r\n\r\n updateProgress(60)\r\n if (options.watermark) {\r\n drawWatermark(canvas, options.watermark)\r\n }\r\n\r\n updateProgress(90)\r\n const format = options.capture?.format || 'png'\r\n const quality = options.capture?.quality || 0.92\r\n const dataURL = canvasToDataURL(canvas, format, quality)\r\n\r\n updateProgress(100)\r\n return dataURL\r\n } catch (error) {\r\n console.error('截图失败:', error)\r\n throw new Error(\r\n `截图失败: ${error instanceof Error ? error.message : '未知错误'}`\r\n )\r\n }\r\n }\r\n\r\n const printWithWatermark = async (\r\n element: HTMLElement,\r\n options: PrintWatermarkOptions = {}\r\n ): Promise<void> => {\r\n loading.value = true\r\n updateProgress(0)\r\n\r\n try {\r\n await nextTick()\r\n const imageDataURL = await captureWithWatermark(element, options)\r\n updateProgress(95)\r\n await printImage(imageDataURL, options.print)\r\n updateProgress(100)\r\n message.success('打印完成')\r\n } catch (error) {\r\n console.error('打印失败:', error)\r\n message.error(\r\n `打印失败: ${error instanceof Error ? error.message : '未知错误'}`\r\n )\r\n throw error\r\n } finally {\r\n loading.value = false\r\n updateProgress(0)\r\n }\r\n }\r\n\r\n const downloadScreenshot = async (\r\n element: HTMLElement,\r\n filename?: string,\r\n options: PrintWatermarkOptions = {}\r\n ): Promise<void> => {\r\n loading.value = true\r\n\r\n try {\r\n await nextTick()\r\n const canvas = await captureElement(element, options.capture)\r\n\r\n if (options.watermark) {\r\n drawWatermark(canvas, options.watermark)\r\n }\r\n\r\n const format = options.capture?.format || 'png'\r\n const defaultFilename = `screenshot-${new Date()\r\n .toISOString()\r\n .slice(0, 19)\r\n .replace(/:/g, '-')}.${format}`\r\n\r\n downloadCanvas(canvas, filename || defaultFilename, format)\r\n message.success('下载完成')\r\n } catch (error) {\r\n console.error('下载失败:', error)\r\n message.error('下载失败')\r\n throw error\r\n } finally {\r\n loading.value = false\r\n }\r\n }\r\n\r\n const quickPrint = async (\r\n element: HTMLElement,\r\n watermarkText: string = 'Robot Admin'\r\n ): Promise<void> => {\r\n return printWithWatermark(element, {\r\n watermark: {\r\n text: watermarkText,\r\n position: 'repeat',\r\n textColor: 'rgba(128, 128, 128, 0.3)',\r\n },\r\n })\r\n }\r\n\r\n const batchCapture = async (\r\n elements: HTMLElement[],\r\n options: PrintWatermarkOptions = {}\r\n ): Promise<string[]> => {\r\n const total = elements.length\r\n updateProgress(10)\r\n\r\n const capturePromises = elements.map(async (element, index) => {\r\n const url = await captureWithWatermark(element, options)\r\n updateProgress(10 + ((index + 1) / total) * 70)\r\n return url\r\n })\r\n\r\n return Promise.all(capturePromises)\r\n }\r\n\r\n const serialPrint = async (\r\n imageUrls: string[],\r\n printConfig: PrintConfig = {}\r\n ): Promise<void> => {\r\n await imageUrls.reduce(async (prevPromise, imageUrl, index) => {\r\n await prevPromise\r\n await printImage(imageUrl, printConfig)\r\n\r\n if (index < imageUrls.length - 1) {\r\n await new Promise<void>(resolve => {\r\n setTimeout(() => resolve(), 1000)\r\n })\r\n }\r\n }, Promise.resolve())\r\n }\r\n\r\n const batchPrint = async (\r\n elements: HTMLElement[],\r\n options: PrintWatermarkOptions = {}\r\n ): Promise<void> => {\r\n if (!elements.length) {\r\n message.warning('没有要打印的元素')\r\n return\r\n }\r\n\r\n loading.value = true\r\n\r\n try {\r\n updateProgress(0)\r\n const imageUrls = await batchCapture(elements, options)\r\n updateProgress(80)\r\n await serialPrint(imageUrls, options.print)\r\n updateProgress(100)\r\n message.success(`批量打印完成,共 ${elements.length} 个元素`)\r\n } catch (error) {\r\n console.error('批量打印失败:', error)\r\n message.error('批量打印失败')\r\n throw error\r\n } finally {\r\n loading.value = false\r\n updateProgress(0)\r\n }\r\n }\r\n\r\n const mergePrint = async (\r\n elements: HTMLElement[],\r\n options: PrintWatermarkOptions = {},\r\n mergeOptions: {\r\n orientation?: 'horizontal' | 'vertical'\r\n spacing?: number\r\n backgroundColor?: string\r\n } = {}\r\n ): Promise<void> => {\r\n const validatedOptions = validateAndGetMergeOptions(\r\n elements,\r\n mergeOptions,\r\n message\r\n )\r\n if (!validatedOptions) return\r\n\r\n loading.value = true\r\n\r\n try {\r\n const mergedCanvas = await captureAndMergeCanvas(\r\n elements,\r\n options,\r\n validatedOptions,\r\n updateProgress\r\n )\r\n await addWatermarkAndPrint(\r\n mergedCanvas,\r\n options,\r\n elements.length,\r\n updateProgress,\r\n message\r\n )\r\n } catch (error) {\r\n console.error('合并打印失败:', error)\r\n message.error('合并打印失败')\r\n throw error\r\n } finally {\r\n loading.value = false\r\n updateProgress(0)\r\n }\r\n }\r\n\r\n const createPreset = (name: string, options: PrintWatermarkOptions) => {\r\n return {\r\n use: (element: HTMLElement) => printWithWatermark(element, options),\r\n download: (element: HTMLElement, filename?: string) =>\r\n downloadScreenshot(element, filename, options),\r\n }\r\n }\r\n\r\n return {\r\n loading,\r\n progress,\r\n\r\n captureWithWatermark,\r\n printWithWatermark,\r\n downloadScreenshot,\r\n quickPrint,\r\n batchCapture,\r\n batchPrint,\r\n mergePrint,\r\n\r\n createPreset,\r\n updateProgress,\r\n\r\n utils: {\r\n captureElement,\r\n drawWatermark,\r\n canvasToDataURL,\r\n downloadCanvas,\r\n printImage,\r\n },\r\n }\r\n}\r\n\r\n/* ================= 预设配置 ================= */\r\nexport const printPresets = {\r\n table: {\r\n watermark: {\r\n text: 'Robot Admin - 数据表格',\r\n position: 'repeat' as const,\r\n textColor: 'rgba(100, 100, 100, 0.25)',\r\n textSize: 14,\r\n xGap: 250,\r\n yGap: 120,\r\n },\r\n capture: {\r\n scale: 1.2,\r\n },\r\n print: {\r\n orientation: 'landscape' as const,\r\n style: '@page { margin: 8mm; } body { font-family: Arial, sans-serif; }',\r\n },\r\n },\r\n\r\n form: {\r\n watermark: {\r\n text: 'Robot Admin - 表单数据',\r\n position: 'diagonal' as const,\r\n textColor: 'rgba(150, 150, 150, 0.2)',\r\n textSize: 16,\r\n },\r\n print: {\r\n orientation: 'portrait' as const,\r\n },\r\n },\r\n\r\n report: {\r\n watermark: {\r\n text: 'Robot Admin - 机密报告',\r\n position: 'center' as const,\r\n textColor: 'rgba(200, 50, 50, 0.15)',\r\n textSize: 24,\r\n },\r\n capture: {\r\n quality: 0.95,\r\n format: 'png' as const,\r\n },\r\n },\r\n} as const\r\n\r\n/* ================= 工具函数导出 ================= */\r\nexport {\r\n drawWatermark,\r\n captureElement,\r\n canvasToDataURL,\r\n downloadCanvas,\r\n printImage,\r\n}\r\n","/*\r\n * @Description: 表格动态行操作 Hooks — 增行、插行、删除行、复制行、调整行、单选功能、打印功能\r\n * @Migration: naive-ui-components 组件库迁移版本\r\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\r\n */\r\n\r\nimport {\r\n h,\r\n ref,\r\n computed,\r\n onBeforeUnmount,\r\n type VNode,\r\n type Ref,\r\n type VNodeChild,\r\n} from 'vue'\r\nimport {\r\n type DataTableRowKey,\r\n NButton,\r\n NTooltip,\r\n NModal,\r\n NButtonGroup,\r\n NSpace,\r\n useMessage,\r\n} from 'naive-ui/es'\r\nimport {\r\n usePrintWatermark,\r\n printPresets,\r\n type PrintWatermarkOptions,\r\n} from './usePrintWatermark'\r\nimport type { TableColumn, DataRecord } from '../types'\r\nimport C_Icon from '../../C_Icon/index.vue'\r\n\r\n/* ================= 类型定义 ================= */\r\nexport interface DynamicRowsOptions<T extends DataRecord = DataRecord> {\r\n /* 基础配置 */\r\n rowKey?: string | ((row: T) => DataTableRowKey)\r\n defaultRowData?: () => T\r\n\r\n /* 功能开关 */\r\n enableRadioSelection?: boolean\r\n enableAdd?: boolean\r\n enableInsert?: boolean\r\n enableDelete?: boolean\r\n enableCopy?: boolean\r\n enableMove?: boolean\r\n enablePrint?: boolean\r\n\r\n /* 打印配置 */\r\n printOptions?: PrintWatermarkOptions\r\n printPreset?: 'table' | 'form' | 'report'\r\n printWatermarkText?: string\r\n printTargetSelector?: string\r\n\r\n /* 交互配置 */\r\n confirmDelete?: boolean\r\n deleteConfirmText?: string\r\n\r\n /* 事件回调 */\r\n onRowChange?: (data: T[]) => void\r\n onSelectionChange?: (\r\n selectedKey: DataTableRowKey | null,\r\n selectedRow: T | null\r\n ) => void\r\n onRowAdd?: (newRow: T) => void\r\n onRowDelete?: (deletedRow: T, index: number) => void\r\n onRowCopy?: (originalRow: T, newRow: T) => void\r\n onRowMove?: (row: T, fromIndex: number, toIndex: number) => void\r\n}\r\n\r\nexport interface DynamicRowsReturn<T extends DataRecord = DataRecord> {\r\n /* 状态 */\r\n selectedRowKey: Ref<DataTableRowKey | null>\r\n selectedRowData: Ref<T | null>\r\n selectedRowIndex: Ref<number>\r\n canMoveUp: Ref<boolean>\r\n canMoveDown: Ref<boolean>\r\n deleteConfirmVisible: Ref<boolean>\r\n printLoading: Ref<boolean>\r\n printProgress: Ref<number>\r\n\r\n /* 行操作方法 */\r\n addRow: () => void\r\n insertRow: () => void\r\n deleteRow: () => void\r\n confirmDelete: () => void\r\n copyRow: () => void\r\n moveRowUp: () => void\r\n moveRowDown: () => void\r\n\r\n /* 选择方法 */\r\n selectRow: (key: DataTableRowKey) => void\r\n clearSelection: () => void\r\n\r\n /* 打印方法 */\r\n handlePrint: (elementRef: Ref<HTMLElement | undefined>) => Promise<void>\r\n handleDownload: (\r\n elementRef: Ref<HTMLElement | undefined>,\r\n filename?: string\r\n ) => Promise<void>\r\n handleQuickPrint: (elementRef: Ref<HTMLElement | undefined>) => Promise<void>\r\n\r\n /* 列增强方法 */\r\n enhanceColumns: (columns: TableColumn<T>[]) => TableColumn<T>[]\r\n\r\n /* 工具栏渲染 */\r\n renderToolbar: () => VNodeChild\r\n renderConfirmModal: () => VNodeChild\r\n}\r\n\r\n/* ================= 辅助函数 ================= */\r\n\r\n/** 生成唯一ID */\r\nfunction generateUniqueId(): string {\r\n return `row_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`\r\n}\r\n\r\n/** 获取行键值 */\r\nfunction getRowKey<T extends DataRecord>(\r\n row: T,\r\n rowKey: string | ((row: T) => DataTableRowKey)\r\n): DataTableRowKey {\r\n return typeof rowKey === 'function' ? rowKey(row) : (row as any)[rowKey]\r\n}\r\n\r\n/** 创建新行数据 */\r\nfunction createNewRow<T extends DataRecord>(\r\n defaultRowData: (() => T) | undefined,\r\n rowKey: string | ((row: T) => DataTableRowKey)\r\n): T {\r\n const newRow = defaultRowData?.() || ({} as T)\r\n const keyField = typeof rowKey === 'string' ? rowKey : 'id'\r\n return {\r\n ...newRow,\r\n [keyField]: generateUniqueId(),\r\n } as T\r\n}\r\n\r\n/* ================= 主函数 ================= */\r\n\r\n/**\r\n * 表格动态行操作功能组合\r\n */\r\nexport function useDynamicRows<T extends DataRecord = DataRecord>(\r\n data: Ref<T[]>,\r\n options: DynamicRowsOptions<T> = {}\r\n): DynamicRowsReturn<T> {\r\n const message = useMessage()\r\n\r\n /* 默认配置 */\r\n const finalOptions = {\r\n rowKey: 'id',\r\n enableRadioSelection: true,\r\n enableAdd: true,\r\n enableInsert: true,\r\n enableDelete: true,\r\n enableCopy: true,\r\n enableMove: true,\r\n enablePrint: true,\r\n confirmDelete: true,\r\n deleteConfirmText: '确定要删除选中的行吗?此操作不可撤销。',\r\n printPreset: 'table' as const,\r\n printTargetSelector: '.c-table-wrapper',\r\n defaultRowData: () => ({}) as T,\r\n ...options,\r\n }\r\n\r\n /* 状态 */\r\n const selectedRowKey = ref<DataTableRowKey | null>(null)\r\n const deleteConfirmVisible = ref(false)\r\n\r\n const selectedRowData = computed(() => {\r\n if (selectedRowKey.value === null) return null\r\n return (\r\n data.value.find(\r\n row => getRowKey(row, finalOptions.rowKey) === selectedRowKey.value\r\n ) || null\r\n )\r\n })\r\n\r\n const selectedRowIndex = computed(() => {\r\n if (!selectedRowData.value) return -1\r\n return data.value.findIndex(row => row === selectedRowData.value)\r\n })\r\n\r\n const canMoveUp = computed(() => selectedRowIndex.value > 0)\r\n const canMoveDown = computed(\r\n () =>\r\n selectedRowIndex.value >= 0 &&\r\n selectedRowIndex.value < data.value.length - 1\r\n )\r\n\r\n /* 打印功能 */\r\n const {\r\n loading: printLoading,\r\n progress: printProgress,\r\n printWithWatermark,\r\n downloadScreenshot,\r\n quickPrint,\r\n } = usePrintWatermark()\r\n\r\n /** 获取打印配置选项 */\r\n const getPrintOptions = (): PrintWatermarkOptions => {\r\n if (finalOptions.printOptions) {\r\n return finalOptions.printOptions\r\n }\r\n\r\n const preset = finalOptions.printPreset || 'table'\r\n const baseConfig = printPresets[preset]\r\n\r\n if (finalOptions.printWatermarkText && baseConfig.watermark) {\r\n return {\r\n ...baseConfig,\r\n watermark: {\r\n ...baseConfig.watermark,\r\n text: finalOptions.printWatermarkText,\r\n },\r\n }\r\n }\r\n\r\n return baseConfig\r\n }\r\n\r\n /** 更新表格数据 */\r\n const updateData = (newData: T[]) => {\r\n data.value = newData\r\n finalOptions.onRowChange?.(newData)\r\n }\r\n\r\n /** 添加新行到表格末尾 */\r\n const addRow = () => {\r\n if (!finalOptions.enableAdd) return\r\n\r\n const newRow = createNewRow(\r\n finalOptions.defaultRowData,\r\n finalOptions.rowKey\r\n )\r\n const newData = [...data.value, newRow]\r\n updateData(newData)\r\n finalOptions.onRowAdd?.(newRow)\r\n message.success('添加行成功')\r\n }\r\n\r\n /** 在选中行后插入新行 */\r\n const insertRow = () => {\r\n if (!finalOptions.enableInsert || !selectedRowData.value) {\r\n message.warning('请先选择一行数据')\r\n return\r\n }\r\n\r\n const newRow = createNewRow(\r\n finalOptions.defaultRowData,\r\n finalOptions.rowKey\r\n )\r\n const newData = [...data.value]\r\n newData.splice(selectedRowIndex.value + 1, 0, newRow)\r\n updateData(newData)\r\n finalOptions.onRowAdd?.(newRow)\r\n message.success('插入行成功')\r\n }\r\n\r\n /** 删除选中的行 */\r\n const deleteRow = () => {\r\n if (!finalOptions.enableDelete || !selectedRowData.value) {\r\n message.warning('请先选择要删除的行')\r\n return\r\n }\r\n\r\n if (finalOptions.confirmDelete) {\r\n deleteConfirmVisible.value = true\r\n } else {\r\n confirmDeleteFn()\r\n }\r\n }\r\n\r\n /** 确认删除操作 */\r\n const confirmDeleteFn = () => {\r\n if (!selectedRowData.value) return\r\n\r\n const deletedRow = selectedRowData.value\r\n const deletedIndex = selectedRowIndex.value\r\n\r\n const newData = data.value.filter((_, index) => index !== deletedIndex)\r\n updateData(newData)\r\n\r\n selectedRowKey.value = null\r\n finalOptions.onSelectionChange?.(null, null)\r\n finalOptions.onRowDelete?.(deletedRow, deletedIndex)\r\n\r\n message.success('删除行成功')\r\n deleteConfirmVisible.value = false\r\n }\r\n\r\n /** 复制选中的行 */\r\n const copyRow = () => {\r\n if (!finalOptions.enableCopy || !selectedRowData.value) {\r\n message.warning('请先选择要复制的行')\r\n return\r\n }\r\n\r\n const originalRow = selectedRowData.value\r\n const keyField =\r\n typeof finalOptions.rowKey === 'string' ? finalOptions.rowKey : 'id'\r\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\r\n const { [keyField]: _originalKey, ...rowData } = originalRow as Record<\r\n string,\r\n unknown\r\n >\r\n\r\n const newRow = {\r\n ...rowData,\r\n [keyField]: generateUniqueId(),\r\n } as T\r\n\r\n const newData = [...data.value, newRow]\r\n updateData(newData)\r\n finalOptions.onRowCopy?.(originalRow, newRow)\r\n message.success('复制行成功')\r\n }\r\n\r\n /** 将选中行向上移动 */\r\n const moveRowUp = () => {\r\n if (!finalOptions.enableMove || !canMoveUp.value) return\r\n\r\n const currentIndex = selectedRowIndex.value\r\n const newData = [...data.value]\r\n const movingRow = newData[currentIndex]\r\n\r\n ;[newData[currentIndex], newData[currentIndex - 1]] = [\r\n newData[currentIndex - 1],\r\n newData[currentIndex],\r\n ]\r\n\r\n updateData(newData)\r\n finalOptions.onRowMove?.(movingRow, currentIndex, currentIndex - 1)\r\n message.success('行已上移')\r\n }\r\n\r\n /** 将选中行向下移动 */\r\n const moveRowDown = () => {\r\n if (!finalOptions.enableMove || !canMoveDown.value) return\r\n\r\n const currentIndex = selectedRowIndex.value\r\n const newData = [...data.value]\r\n const movingRow = newData[currentIndex]\r\n\r\n ;[newData[currentIndex], newData[currentIndex + 1]] = [\r\n newData[currentIndex + 1],\r\n newData[currentIndex],\r\n ]\r\n\r\n updateData(newData)\r\n finalOptions.onRowMove?.(movingRow, currentIndex, currentIndex + 1)\r\n message.success('行已下移')\r\n }\r\n\r\n /** 选中指定行 */\r\n const selectRow = (key: DataTableRowKey) => {\r\n const row = data.value.find(\r\n row => getRowKey(row, finalOptions.rowKey) === key\r\n )\r\n if (row) {\r\n selectedRowKey.value = key\r\n finalOptions.onSelectionChange?.(key, row)\r\n }\r\n }\r\n\r\n /** 清空选择状态 */\r\n const clearSelection = () => {\r\n selectedRowKey.value = null\r\n finalOptions.onSelectionChange?.(null, null)\r\n }\r\n\r\n /** 处理打印操作 */\r\n const handlePrint = async (elementRef: Ref<HTMLElement | undefined>) => {\r\n if (!elementRef.value) {\r\n message.error('打印元素未找到')\r\n return\r\n }\r\n\r\n const printOptions = getPrintOptions()\r\n await printWithWatermark(elementRef.value, printOptions)\r\n }\r\n\r\n /** 处理下载操作 */\r\n const handleDownload = async (\r\n elementRef: Ref<HTMLElement | undefined>,\r\n filename?: string\r\n ) => {\r\n if (!elementRef.value) {\r\n message.error('下载元素未找到')\r\n return\r\n }\r\n\r\n const printOptions = getPrintOptions()\r\n await downloadScreenshot(elementRef.value, filename, printOptions)\r\n }\r\n\r\n /** 处理快速打印操作 */\r\n const handleQuickPrint = async (elementRef: Ref<HTMLElement | undefined>) => {\r\n if (!elementRef.value) {\r\n message.error('打印元素未找到')\r\n return\r\n }\r\n\r\n const watermarkText = finalOptions.printWatermarkText || 'Robot Admin'\r\n await quickPrint(elementRef.value, watermarkText)\r\n }\r\n\r\n /** 增强表格列配置,添加单选列 */\r\n const enhanceColumns = (columns: TableColumn<T>[]): TableColumn<T>[] => {\r\n const enhancedColumns = [...columns]\r\n\r\n if (finalOptions.enableRadioSelection) {\r\n enhancedColumns.unshift({\r\n key: '_radio_selection',\r\n title: '选择',\r\n width: 80,\r\n align: 'center',\r\n editable: false,\r\n render: (rowData: T) => {\r\n const rowKeyVal = getRowKey(rowData, finalOptions.rowKey)\r\n return h('div', { class: 'flex justify-center' }, [\r\n h('input', {\r\n type: 'radio',\r\n name: 'table-radio-selection',\r\n checked: selectedRowKey.value === rowKeyVal,\r\n class: 'cursor-pointer accent-blue-500 scale-110',\r\n onChange: (e: Event) => {\r\n if ((e.target as HTMLInputElement).checked) {\r\n selectRow(rowKeyVal)\r\n }\r\n },\r\n }),\r\n ])\r\n },\r\n } as TableColumn<T>)\r\n }\r\n\r\n return enhancedColumns\r\n }\r\n\r\n /** 渲染工具栏 */\r\n const renderToolbar = (): VNodeChild => {\r\n const buttons: VNode[] = []\r\n\r\n if (finalOptions.enablePrint) {\r\n buttons.push(\r\n h(\r\n NButton,\r\n {\r\n loading: printLoading.value,\r\n type: 'primary',\r\n ghost: true,\r\n onClick: async () => {\r\n try {\r\n const tableElement = document.querySelector(\r\n finalOptions.printTargetSelector\r\n )\r\n if (tableElement) {\r\n await handlePrint(ref(tableElement as HTMLElement))\r\n } else {\r\n console.warn('未找到表格容器元素')\r\n }\r\n } catch (error) {\r\n console.error('打印失败:', error)\r\n }\r\n },\r\n },\r\n {\r\n icon: () => h(C_Icon, { name: 'mdi:printer', title: '打印' }),\r\n default: () => '打印',\r\n }\r\n )\r\n )\r\n }\r\n\r\n const rowButtons: VNode[] = []\r\n\r\n if (finalOptions.enableAdd) {\r\n rowButtons.push(\r\n h(\r\n NButton,\r\n {\r\n onClick: addRow,\r\n type: 'primary',\r\n },\r\n {\r\n icon: () => h(C_Icon, { name: 'mdi:plus', title: '增行' }),\r\n default: () => '增行',\r\n }\r\n )\r\n )\r\n }\r\n\r\n if (finalOptions.enableInsert) {\r\n rowButtons.push(\r\n h(\r\n NTooltip,\r\n {\r\n disabled: !!selectedRowData.value,\r\n },\r\n {\r\n trigger: () =>\r\n h(\r\n NButton,\r\n {\r\n onClick: insertRow,\r\n disabled: !selectedRowData.value,\r\n type: 'primary',\r\n ghost: true,\r\n },\r\n {\r\n icon: () =>\r\n h(C_Icon, {\r\n name: 'mdi:table-row-plus-after',\r\n title: '插行',\r\n }),\r\n default: () => '插行',\r\n }\r\n ),\r\n default: () => '请先选择一行数据',\r\n }\r\n )\r\n )\r\n }\r\n\r\n if (finalOptions.enableDelete) {\r\n rowButtons.push(\r\n h(\r\n NTooltip,\r\n {\r\n disabled: !!selectedRowData.value,\r\n },\r\n {\r\n trigger: () =>\r\n h(\r\n NButton,\r\n {\r\n onClick: deleteRow,\r\n disabled: !selectedRowData.value,\r\n type: 'error',\r\n ghost: true,\r\n },\r\n {\r\n icon: () =>\r\n h(C_Icon, { name: 'mdi:delete', title: '删除行' }),\r\n default: () => '删除行',\r\n }\r\n ),\r\n default: () => '请先选择要删除的行',\r\n }\r\n )\r\n )\r\n }\r\n\r\n if (finalOptions.enableCopy) {\r\n rowButtons.push(\r\n h(\r\n NTooltip,\r\n {\r\n disabled: !!selectedRowData.value,\r\n },\r\n {\r\n trigger: () =>\r\n h(\r\n NButton,\r\n {\r\n onClick: copyRow,\r\n disabled: !selectedRowData.value,\r\n type: 'info',\r\n ghost: true,\r\n },\r\n {\r\n icon: () =>\r\n h(C_Icon, { name: 'mdi:content-copy', title: '复制行' }),\r\n default: () => '复制行',\r\n }\r\n ),\r\n default: () => '请先选择要复制的行',\r\n }\r\n )\r\n )\r\n }\r\n\r\n if (finalOptions.enableMove) {\r\n rowButtons.push(\r\n h(\r\n NTooltip,\r\n {\r\n disabled: canMoveUp.value,\r\n },\r\n {\r\n trigger: () =>\r\n h(\r\n NButton,\r\n {\r\n onClick: moveRowUp,\r\n disabled: !canMoveUp.value,\r\n type: 'warning',\r\n ghost: true,\r\n },\r\n {\r\n icon: () =>\r\n h(C_Icon, { name: 'mdi:arrow-up', title: '上移' }),\r\n default: () => '上移',\r\n }\r\n ),\r\n default: () =>\r\n !selectedRowData.value ? '请先选择数据' : '已经是第一行',\r\n }\r\n ),\r\n h(\r\n NTooltip,\r\n {\r\n disabled: canMoveDown.value,\r\n },\r\n {\r\n trigger: () =>\r\n h(\r\n NButton,\r\n {\r\n onClick: moveRowDown,\r\n disabled: !canMoveDown.value,\r\n type: 'warning',\r\n ghost: true,\r\n },\r\n {\r\n icon: () =>\r\n h(C_Icon, { name: 'mdi:arrow-down', title: '下移' }),\r\n default: () => '下移',\r\n }\r\n ),\r\n default: () =>\r\n !selectedRowData.value ? '请先选择数据' : '已经是最后一行',\r\n }\r\n )\r\n )\r\n }\r\n\r\n if (rowButtons.length > 0) {\r\n buttons.push(h(NButtonGroup, {}, () => rowButtons))\r\n }\r\n\r\n return h('div', { class: 'dynamic-rows-toolbar mb-4 flex justify-end' }, [\r\n h(NSpace, {}, () => buttons),\r\n ])\r\n }\r\n\r\n /** 渲染删除确认模态框 */\r\n const renderConfirmModal = (): VNodeChild => {\r\n return h(NModal, {\r\n show: deleteConfirmVisible.value,\r\n 'onUpdate:show': (show: boolean) => {\r\n deleteConfirmVisible.value = show\r\n },\r\n preset: 'dialog',\r\n title: '确认删除',\r\n content: finalOptions.deleteConfirmText,\r\n positiveText: '确认删除',\r\n negativeText: '取消',\r\n onPositiveClick: confirmDeleteFn,\r\n })\r\n }\r\n\r\n /* 组件卸载时清理 */\r\n onBeforeUnmount(() => {\r\n selectedRowKey.value = null\r\n deleteConfirmVisible.value = false\r\n })\r\n\r\n return {\r\n /* 状态 */\r\n selectedRowKey,\r\n selectedRowData,\r\n selectedRowIndex,\r\n canMoveUp,\r\n canMoveDown,\r\n deleteConfirmVisible,\r\n printLoading,\r\n printProgress,\r\n\r\n /* 行操作方法 */\r\n addRow,\r\n insertRow,\r\n deleteRow,\r\n confirmDelete: confirmDeleteFn,\r\n copyRow,\r\n moveRowUp,\r\n moveRowDown,\r\n\r\n /* 选择方法 */\r\n selectRow,\r\n clearSelection,\r\n\r\n /* 打印方法 */\r\n handlePrint,\r\n handleDownload,\r\n handleQuickPrint,\r\n\r\n /* 列增强方法 */\r\n enhanceColumns,\r\n\r\n /* 工具栏渲染 */\r\n renderToolbar,\r\n renderConfirmModal,\r\n }\r\n}\r\n","/*\r\n * @Description: 表格统一状态管理器\r\n * @Migration: naive-ui-components 组件库迁移版本\r\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\r\n */\r\n\r\nimport { ref, computed, nextTick } from 'vue'\r\nimport type { DataTableRowKey } from 'naive-ui/es'\r\nimport type { DataRecord, ParentChildLinkMode } from '../types'\r\nimport { useRowEdit } from './useRowEdit'\r\nimport { useCellEdit } from './useCellEdit'\r\nimport { useModalEdit } from './useModalEdit'\r\nimport { useTableExpand } from './useTableExpand'\r\nimport { useDynamicRows, type DynamicRowsOptions } from './useDynamicRow'\r\n\r\n/**\r\n * 表格管理器配置接口\r\n */\r\ninterface TableManagerConfig {\r\n /* 基础配置 */\r\n editable: boolean\r\n editMode: string\r\n showRowActions: boolean\r\n modalTitle: string\r\n modalWidth: number\r\n\r\n /* 展开配置 */\r\n expandable: boolean\r\n defaultExpandedKeys?: DataTableRowKey[]\r\n onLoadExpandData?: (row: DataRecord) => Promise<any[]> | any[]\r\n renderExpandContent?: unknown\r\n rowExpandable?: (row: DataRecord) => boolean\r\n\r\n /* 选择配置 */\r\n enableSelection: boolean\r\n defaultCheckedKeys?: DataTableRowKey[]\r\n rowCheckable?: (row: DataRecord) => boolean\r\n maxSelection?: number\r\n enableChildSelection: boolean\r\n childRowCheckable?: (childRow: DataRecord, parentRow: DataRecord) => boolean\r\n enableParentChildLink: boolean\r\n parentChildLinkMode: ParentChildLinkMode\r\n\r\n /* 动态行配置 */\r\n dynamicRows?: DynamicRowsOptions<DataRecord>\r\n}\r\n\r\n/**\r\n * 表格管理器参数接口\r\n */\r\ninterface TableManagerParams {\r\n config: TableManagerConfig\r\n data: () => DataRecord[]\r\n rowKey: (row: DataRecord) => DataTableRowKey\r\n emit: (...args: any[]) => void\r\n}\r\n\r\n/**\r\n * 事件处理器接口\r\n */\r\ninterface EventHandlers {\r\n onSave: (\r\n rowData: DataRecord,\r\n rowIndex: number,\r\n columnKey?: string\r\n ) => Promise<void>\r\n onCancel: (rowData: DataRecord, rowIndex: number) => void\r\n onExpandChange: (\r\n keys: DataTableRowKey[],\r\n row?: DataRecord,\r\n expanded?: boolean\r\n ) => void\r\n onSelectionChange: (\r\n checkedKeys: DataTableRowKey[],\r\n checkedRows: DataRecord[],\r\n childSelections: Map<DataTableRowKey, DataTableRowKey[]>\r\n ) => void\r\n onChildSelectionChange: (\r\n parentKey: DataTableRowKey,\r\n childKeys: DataTableRowKey[],\r\n childRows: DataRecord[]\r\n ) => void\r\n onRowChange: (data: DataRecord[]) => void\r\n onRowSelectionChange: (\r\n selectedKey: DataTableRowKey | null,\r\n selectedRow: DataRecord | null\r\n ) => void\r\n onRowAdd: (newRow: DataRecord) => void\r\n onRowDelete: (deletedRow: DataRecord, index: number) => void\r\n onRowCopy: (originalRow: DataRecord, newRow: DataRecord) => void\r\n onRowMove: (row: DataRecord, fromIndex: number, toIndex: number) => void\r\n}\r\n\r\n/**\r\n * 表格统一状态管理器\r\n */\r\nexport function useTableManager(params: TableManagerParams) {\r\n const { config, data, rowKey, emit } = params\r\n\r\n /* ================= 事件处理器工厂 ================= */\r\n\r\n /** 创建事件处理器集合 */\r\n const createEventHandlers = (): EventHandlers => ({\r\n /* 通用事件 */\r\n onSave: async (\r\n rowData: DataRecord,\r\n rowIndex: number,\r\n columnKey?: string\r\n ) => {\r\n if (!rowData || rowIndex < 0 || rowIndex >= data().length) return\r\n\r\n const newData = [...data()]\r\n newData[rowIndex] = { ...newData[rowIndex], ...rowData }\r\n\r\n emit('update:data', newData)\r\n await nextTick()\r\n emit('save', newData[rowIndex], rowIndex, columnKey)\r\n },\r\n\r\n onCancel: (rowData: DataRecord, rowIndex: number) => {\r\n emit('cancel', rowData, rowIndex)\r\n },\r\n\r\n /* 展开选择事件 */\r\n onExpandChange: (\r\n keys: DataTableRowKey[],\r\n row?: DataRecord,\r\n expanded?: boolean\r\n ) => {\r\n emit('expand-change', keys, row, expanded)\r\n },\r\n\r\n onSelectionChange: (\r\n checkedKeys: DataTableRowKey[],\r\n checkedRows: DataRecord[],\r\n childSelections: Map<DataTableRowKey, DataTableRowKey[]>\r\n ) => {\r\n emit('selection-change', checkedKeys, checkedRows, childSelections)\r\n },\r\n\r\n onChildSelectionChange: (\r\n parentKey: DataTableRowKey,\r\n childKeys: DataTableRowKey[],\r\n childRows: DataRecord[]\r\n ) => {\r\n emit('child-selection-change', parentKey, childKeys, childRows)\r\n },\r\n\r\n /* 动态行事件 */\r\n onRowChange: (data: DataRecord[]) => {\r\n emit('update:data', data)\r\n config.dynamicRows?.onRowChange?.(data)\r\n },\r\n\r\n onRowSelectionChange: (\r\n selectedKey: DataTableRowKey | null,\r\n selectedRow: DataRecord | null\r\n ) => {\r\n emit('row-selection-change', selectedKey, selectedRow)\r\n config.dynamicRows?.onSelectionChange?.(selectedKey, selectedRow)\r\n },\r\n\r\n onRowAdd: (newRow: DataRecord) => {\r\n emit('row-add', newRow)\r\n config.dynamicRows?.onRowAdd?.(newRow)\r\n },\r\n\r\n onRowDelete: (deletedRow: DataRecord, index: number) => {\r\n emit('row-delete', deletedRow, index)\r\n config.dynamicRows?.onRowDelete?.(deletedRow, index)\r\n },\r\n\r\n onRowCopy: (originalRow: DataRecord, newRow: DataRecord) => {\r\n emit('row-copy', originalRow, newRow)\r\n config.dynamicRows?.onRowCopy?.(originalRow, newRow)\r\n },\r\n\r\n onRowMove: (row: DataRecord, fromIndex: number, toIndex: number) => {\r\n emit('row-move', row, fromIndex, toIndex)\r\n config.dynamicRows?.onRowMove?.(row, fromIndex, toIndex)\r\n },\r\n })\r\n\r\n const eventHandlers = createEventHandlers()\r\n\r\n /* ================= 功能状态初始化 ================= */\r\n\r\n /** 初始化编辑功能状态 */\r\n const initEditStates = () => {\r\n const rowEdit = useRowEdit({\r\n data,\r\n rowKey,\r\n onSave: eventHandlers.onSave,\r\n onCancel: eventHandlers.onCancel,\r\n })\r\n\r\n const cellEdit = useCellEdit({\r\n data,\r\n rowKey,\r\n onSave: eventHandlers.onSave,\r\n })\r\n\r\n const modalEdit = useModalEdit({\r\n data,\r\n rowKey,\r\n onSave: eventHandlers.onSave,\r\n onCancel: eventHandlers.onCancel,\r\n })\r\n\r\n return { rowEdit, cellEdit, modalEdit }\r\n }\r\n\r\n /** 初始化展开功能状态 */\r\n const initExpandState = () => {\r\n const needsExpand =\r\n config.expandable || config.enableSelection || config.enableChildSelection\r\n\r\n if (!needsExpand) return null\r\n\r\n return useTableExpand({\r\n data: computed(() => data()),\r\n rowKey,\r\n childRowKey: (child: DataRecord) => child.id as DataTableRowKey,\r\n\r\n defaultExpandedKeys: config.defaultExpandedKeys,\r\n onLoadData: config.onLoadExpandData,\r\n renderContent: config.renderExpandContent as any,\r\n rowExpandable: config.rowExpandable,\r\n enableSelection: config.enableSelection,\r\n defaultCheckedKeys: config.defaultCheckedKeys,\r\n rowCheckable: config.rowCheckable,\r\n maxSelection: config.maxSelection,\r\n enableChildSelection: config.enableChildSelection,\r\n childRowCheckable: config.childRowCheckable,\r\n enableParentChildLink: config.enableParentChildLink,\r\n parentChildLinkMode: config.parentChildLinkMode,\r\n\r\n onExpandChange: eventHandlers.onExpandChange,\r\n onSelectionChange: eventHandlers.onSelectionChange as any,\r\n onChildSelectionChange: eventHandlers.onChildSelectionChange,\r\n })\r\n }\r\n\r\n /** 初始化动态行功能状态 */\r\n const initDynamicRowsState = () => {\r\n if (!config.dynamicRows) return null\r\n\r\n const dynamicOptions: DynamicRowsOptions<DataRecord> = {\r\n ...config.dynamicRows,\r\n onRowChange: eventHandlers.onRowChange,\r\n onSelectionChange: eventHandlers.onRowSelectionChange,\r\n onRowAdd: eventHandlers.onRowAdd,\r\n onRowDelete: eventHandlers.onRowDelete,\r\n onRowCopy: eventHandlers.onRowCopy,\r\n onRowMove: eventHandlers.onRowMove,\r\n }\r\n\r\n return useDynamicRows(\r\n computed(() => data()),\r\n dynamicOptions\r\n )\r\n }\r\n\r\n /* 初始化所有功能状态 */\r\n const editStates = initEditStates()\r\n const expandState = initExpandState()\r\n const dynamicRowsState = initDynamicRowsState()\r\n\r\n /* ================= 统一的状态管理器 ================= */\r\n\r\n /** 创建统一的状态管理器 */\r\n const createStateManager = () => {\r\n return {\r\n /* 编辑控制 */\r\n edit: {\r\n /** 开始编辑 */\r\n start(rowKey: DataTableRowKey, columnKey?: string) {\r\n const mode = config.editMode\r\n if (mode === 'modal') editStates.modalEdit.startEdit(rowKey)\r\n else if (mode === 'cell' && columnKey)\r\n editStates.cellEdit.startEditCell(rowKey, columnKey)\r\n else if (mode === 'row' || mode === 'both')\r\n editStates.rowEdit.startEditRow(rowKey)\r\n },\r\n\r\n /** 取消编辑 */\r\n cancel() {\r\n if (editStates.modalEdit.isModalVisible.value)\r\n editStates.modalEdit.cancelEdit()\r\n else if (editStates.cellEdit.editingCell.value.rowKey)\r\n editStates.cellEdit.cancelEditCell()\r\n else if (editStates.rowEdit.editingRowKey.value)\r\n editStates.rowEdit.cancelEditRow()\r\n },\r\n\r\n /** 保存编辑 */\r\n async save() {\r\n if (editStates.modalEdit.isModalVisible.value)\r\n await editStates.modalEdit.saveEdit()\r\n else if (editStates.cellEdit.editingCell.value.rowKey)\r\n await editStates.cellEdit.saveEditCell()\r\n else if (editStates.rowEdit.editingRowKey.value)\r\n await editStates.rowEdit.saveEditRow()\r\n },\r\n\r\n /** 是否正在编辑 */\r\n isEditing(rowKey: DataTableRowKey, columnKey?: string) {\r\n if (config.editMode === 'modal')\r\n return editStates.modalEdit.isEditingRow(rowKey)\r\n if (columnKey)\r\n return editStates.cellEdit.isEditingCell(rowKey, columnKey)\r\n return editStates.rowEdit.isEditingRow(rowKey)\r\n },\r\n\r\n /** 获取当前编辑数据 */\r\n getEditingData() {\r\n if (editStates.modalEdit.isModalVisible.value)\r\n return editStates.modalEdit.editingData\r\n if (editStates.rowEdit.editingRowKey.value) {\r\n return editStates.rowEdit.getEditingRowData(\r\n editStates.rowEdit.editingRowKey.value!\r\n )\r\n }\r\n return null\r\n },\r\n },\r\n\r\n /* 展开控制 */\r\n expand: {\r\n /** 展开行 */\r\n async row(rowKey: DataTableRowKey) {\r\n if (expandState) {\r\n const currentKeys = [...expandState.expandedKeys.value]\r\n if (!currentKeys.includes(rowKey)) {\r\n currentKeys.push(rowKey)\r\n expandState.handleExpandChange(currentKeys)\r\n }\r\n }\r\n },\r\n\r\n /** 折叠行 */\r\n collapse(rowKey: DataTableRowKey) {\r\n if (expandState) {\r\n const currentKeys = expandState.expandedKeys.value.filter(\r\n key => key !== rowKey\r\n )\r\n expandState.handleExpandChange(currentKeys)\r\n }\r\n },\r\n\r\n /** 展开折叠切换 */\r\n async toggle(rowKey: DataTableRowKey) {\r\n if (expandState?.expandedKeys.value.includes(rowKey)) {\r\n this.collapse(rowKey)\r\n } else {\r\n await this.row(rowKey)\r\n }\r\n },\r\n\r\n /** 展开所有行 */\r\n async all() {\r\n await expandState?.expandAll()\r\n },\r\n\r\n /** 折叠所有行 */\r\n collapseAll() {\r\n expandState?.collapseAll()\r\n },\r\n\r\n /** 检查行是否已展开 */\r\n isExpanded(rowKey: DataTableRowKey) {\r\n return expandState?.expandedKeys.value.includes(rowKey) ?? false\r\n },\r\n },\r\n\r\n /* 选择控制 */\r\n selection: {\r\n /** 选择指定行 */\r\n select(rowKey: DataTableRowKey) {\r\n if (\r\n expandState?.checkedKeys.value &&\r\n !expandState.checkedKeys.value.includes(rowKey)\r\n ) {\r\n const newKeys = [...expandState.checkedKeys.value, rowKey]\r\n expandState.handleSelectionChange(newKeys)\r\n }\r\n },\r\n\r\n /** 取消选择指定行 */\r\n unselect(rowKey: DataTableRowKey) {\r\n if (expandState?.checkedKeys.value) {\r\n const newKeys = expandState.checkedKeys.value.filter(\r\n key => key !== rowKey\r\n )\r\n expandState.handleSelectionChange(newKeys)\r\n }\r\n },\r\n\r\n /** 选择所有行 */\r\n all() {\r\n expandState?.selectAll()\r\n },\r\n\r\n /** 清除所有行选择 */\r\n clear() {\r\n expandState?.clearSelection()\r\n },\r\n\r\n /** 检查行是否已选择 */\r\n isSelected(rowKey: DataTableRowKey) {\r\n return expandState?.checkedKeys.value.includes(rowKey) ?? false\r\n },\r\n\r\n /** 获取所有已选择的行数据 */\r\n getSelected() {\r\n if (!expandState?.checkedKeys.value) return []\r\n return data().filter(row =>\r\n expandState!.checkedKeys.value.includes(rowKey(row))\r\n )\r\n },\r\n },\r\n\r\n /* 子选择控制 */\r\n childSelection: {\r\n /** 选择指定父行下的子行 */\r\n select(parentKey: DataTableRowKey, childKey: DataTableRowKey) {\r\n if (expandState?.childSelections.value) {\r\n const current =\r\n expandState.childSelections.value.get(parentKey) || []\r\n if (!current.includes(childKey)) {\r\n const newSelection = [...current, childKey]\r\n expandState.childSelections.value.set(parentKey, newSelection)\r\n emit('child-selection-change', parentKey, newSelection, [])\r\n }\r\n }\r\n },\r\n\r\n /** 取消选择指定父行下的子行 */\r\n unselect(parentKey: DataTableRowKey, childKey: DataTableRowKey) {\r\n if (expandState?.childSelections.value) {\r\n const current =\r\n expandState.childSelections.value.get(parentKey) || []\r\n const newSelection = current.filter(k => k !== childKey)\r\n expandState.childSelections.value.set(parentKey, newSelection)\r\n emit('child-selection-change', parentKey, newSelection, [])\r\n }\r\n },\r\n\r\n /** 选择指定父行下的所有子行 */\r\n selectAll(parentKey: DataTableRowKey) {\r\n if (\r\n expandState?.childSelections.value &&\r\n expandState.expandDataMap?.value\r\n ) {\r\n const expandData =\r\n expandState.expandDataMap.value.get(parentKey) || []\r\n const allChildKeys = expandData.map(\r\n (child: DataRecord) => child.id as DataTableRowKey\r\n )\r\n expandState.childSelections.value.set(parentKey, allChildKeys)\r\n emit('child-selection-change', parentKey, allChildKeys, expandData)\r\n }\r\n },\r\n\r\n /** 清除指定父行下的所有子行选择 */\r\n clear(parentKey: DataTableRowKey) {\r\n if (expandState?.childSelections.value) {\r\n expandState.childSelections.value.set(parentKey, [])\r\n emit('child-selection-change', parentKey, [], [])\r\n }\r\n },\r\n\r\n /** 获取指定父行下所有已选择的子行数据 */\r\n getSelected(parentKey: DataTableRowKey) {\r\n if (\r\n !expandState?.childSelections.value ||\r\n !expandState.expandDataMap?.value\r\n ) {\r\n return []\r\n }\r\n const selectedKeys =\r\n expandState.childSelections.value.get(parentKey) || []\r\n const expandData =\r\n expandState.expandDataMap.value.get(parentKey) || []\r\n return expandData.filter((child: DataRecord) =>\r\n selectedKeys.includes(child.id as DataTableRowKey)\r\n )\r\n },\r\n },\r\n\r\n /* 动态行控制 */\r\n dynamicRows: {\r\n /** 添加行 */\r\n add() {\r\n dynamicRowsState?.addRow()\r\n },\r\n /** 插入行 */\r\n insert() {\r\n dynamicRowsState?.insertRow()\r\n },\r\n\r\n /** 删除行 */\r\n delete() {\r\n dynamicRowsState?.deleteRow()\r\n },\r\n /** 复制行 */\r\n copy() {\r\n dynamicRowsState?.copyRow()\r\n },\r\n /** 上移动态行 */\r\n moveUp() {\r\n dynamicRowsState?.moveRowUp()\r\n },\r\n /** 下移动态行 */\r\n moveDown() {\r\n dynamicRowsState?.moveRowDown()\r\n },\r\n /** 清空动态行数据 */\r\n clearSelection() {\r\n dynamicRowsState?.clearSelection()\r\n },\r\n /** 获取当前选中的动态行数据 */\r\n getSelected() {\r\n return dynamicRowsState?.selectedRowData.value || null\r\n },\r\n\r\n /** 打印表格 */\r\n async print(elementRef?: HTMLElement) {\r\n if (dynamicRowsState && elementRef) {\r\n await dynamicRowsState.handlePrint(ref(elementRef))\r\n }\r\n },\r\n\r\n /** 导出表格数据 */\r\n async download(elementRef?: HTMLElement, filename?: string) {\r\n if (dynamicRowsState && elementRef) {\r\n await dynamicRowsState.handleDownload(ref(elementRef), filename)\r\n }\r\n },\r\n },\r\n\r\n /** 清除所有选择 */\r\n clearAllSelections() {\r\n expandState?.clearAllSelections()\r\n },\r\n }\r\n }\r\n\r\n const stateManager = createStateManager()\r\n\r\n /* ================= 计算属性 ================= */\r\n\r\n const expandedKeys = computed(() => expandState?.expandedKeys.value ?? [])\r\n const checkedKeys = computed(() => expandState?.checkedKeys.value ?? [])\r\n\r\n /* ================= 返回管理器实例 ================= */\r\n\r\n return {\r\n /* 编辑状态 */\r\n editStates,\r\n\r\n /* 功能状态 */\r\n expandState,\r\n dynamicRowsState,\r\n\r\n /* 状态管理器 */\r\n stateManager,\r\n\r\n /* 计算属性 */\r\n expandedKeys,\r\n checkedKeys,\r\n\r\n /* 事件处理器 */\r\n eventHandlers,\r\n }\r\n}\r\n","/*\r\n * @Description: 表格分页逻辑 Hook\r\n * @Migration: naive-ui-components 组件库迁移版本\r\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\r\n */\r\n\r\nimport { ref, computed, watch, watchEffect, unref } from \"vue\";\r\nimport type { Ref, ComputedRef } from \"vue\";\r\nimport type { PaginationConfig, DataRecord } from \"../types\";\r\n\r\n/* ================= 类型定义 ================= */\r\n\r\n/** 分页信息对象(naive-ui pagination prefix/suffix 回调参数) */\r\ninterface PaginationRenderInfo {\r\n itemCount: number;\r\n startIndex: number;\r\n endIndex: number;\r\n}\r\n\r\nexport interface UsePaginationOptions<T extends DataRecord = DataRecord> {\r\n /** 数据源 - 支持函数、Ref 或 ComputedRef */\r\n data: (() => T[]) | Ref<T[]> | ComputedRef<T[]>;\r\n /** 分页配置 */\r\n config: Ref<PaginationConfig | null> | ComputedRef<PaginationConfig | null>;\r\n /** 事件触发器 */\r\n emit?: (event: \"pagination-change\", page: number, pageSize: number) => void;\r\n}\r\n\r\nexport interface UsePaginationReturn<T extends DataRecord = DataRecord> {\r\n currentPage: Ref<number>;\r\n currentPageSize: Ref<number>;\r\n paginatedData: ComputedRef<T[]>;\r\n paginationConfig: ComputedRef<any>;\r\n handlePageChange: (page: number) => void;\r\n handlePageSizeChange: (pageSize: number) => void;\r\n resetToFirstPage: () => void;\r\n getTotalPages: () => number;\r\n}\r\n\r\n/* ================= 默认配置 ================= */\r\nconst DEFAULT_PAGINATION_CONFIG = {\r\n enabled: true,\r\n page: 1,\r\n pageSize: 10,\r\n showSizePicker: true,\r\n showQuickJumper: true,\r\n pageSizes: [10, 20, 50, 100],\r\n simple: false,\r\n size: \"medium\" as const,\r\n};\r\n\r\n/* ================= Hook 实现 ================= */\r\nexport function usePagination<T extends DataRecord = DataRecord>(\r\n options: UsePaginationOptions<T>,\r\n): UsePaginationReturn<T> {\r\n const { data, config, emit } = options;\r\n\r\n /* ================= 响应式状态 ================= */\r\n const currentPage = ref(1);\r\n const currentPageSize = ref(10);\r\n\r\n /* ================= 数据源统一处理 ================= */\r\n const dataSource = computed(() => {\r\n if (typeof data === \"function\") {\r\n return data() || [];\r\n }\r\n return unref(data) || [];\r\n });\r\n\r\n /* ================= 计算属性 ================= */\r\n\r\n /** 分页后的数据 */\r\n const paginatedData = computed(() => {\r\n if (!config.value?.enabled) {\r\n return dataSource.value;\r\n }\r\n\r\n const start = (currentPage.value - 1) * currentPageSize.value;\r\n const end = start + currentPageSize.value;\r\n return dataSource.value.slice(start, end);\r\n });\r\n\r\n /** 分页配置对象 */\r\n const paginationConfig = computed(() => {\r\n if (!config.value?.enabled) return null;\r\n\r\n return {\r\n page: currentPage.value,\r\n pageSize: currentPageSize.value,\r\n itemCount: dataSource.value.length,\r\n showSizePicker:\r\n config.value.showSizePicker ?? DEFAULT_PAGINATION_CONFIG.showSizePicker,\r\n showQuickJumper:\r\n config.value.showQuickJumper ??\r\n DEFAULT_PAGINATION_CONFIG.showQuickJumper,\r\n pageSizes: config.value.pageSizes ?? DEFAULT_PAGINATION_CONFIG.pageSizes,\r\n simple: config.value.simple ?? DEFAULT_PAGINATION_CONFIG.simple,\r\n size: config.value.size ?? DEFAULT_PAGINATION_CONFIG.size,\r\n prefix: (info: PaginationRenderInfo) => `共 ${info.itemCount} 条`,\r\n suffix: (info: PaginationRenderInfo) =>\r\n `第 ${info.startIndex + 1}-${info.endIndex} 条`,\r\n \"onUpdate:page\": handlePageChange,\r\n \"onUpdate:pageSize\": handlePageSizeChange,\r\n };\r\n });\r\n\r\n /* ================= 事件处理 ================= */\r\n\r\n /** 页码变化处理 */\r\n const handlePageChange = (page: number) => {\r\n currentPage.value = page;\r\n emit?.(\"pagination-change\", page, currentPageSize.value);\r\n };\r\n\r\n /** 页面大小变化处理 */\r\n const handlePageSizeChange = (pageSize: number) => {\r\n currentPageSize.value = pageSize;\r\n currentPage.value = 1;\r\n emit?.(\"pagination-change\", 1, pageSize);\r\n };\r\n\r\n /** 重置到第一页 */\r\n const resetToFirstPage = () => {\r\n if (currentPage.value !== 1) {\r\n handlePageChange(1);\r\n }\r\n };\r\n\r\n /** 获取总页数 */\r\n const getTotalPages = () => {\r\n if (!config.value?.enabled || currentPageSize.value === 0) return 1;\r\n return Math.ceil(dataSource.value.length / currentPageSize.value);\r\n };\r\n\r\n /* ================= 响应式逻辑 ================= */\r\n\r\n /** 初始化分页配置 */\r\n watchEffect(() => {\r\n if (config.value?.enabled) {\r\n currentPage.value = config.value.page ?? DEFAULT_PAGINATION_CONFIG.page;\r\n currentPageSize.value =\r\n config.value.pageSize ?? DEFAULT_PAGINATION_CONFIG.pageSize;\r\n }\r\n });\r\n\r\n /** 监听数据变化,确保分页状态正确 */\r\n watch(\r\n () => dataSource.value.length,\r\n (newLength) => {\r\n if (config.value?.enabled && currentPage.value > 1) {\r\n const maxPage = Math.ceil(newLength / currentPageSize.value);\r\n if (currentPage.value > maxPage) {\r\n currentPage.value = Math.max(1, maxPage);\r\n }\r\n }\r\n },\r\n );\r\n\r\n return {\r\n currentPage,\r\n currentPageSize,\r\n paginatedData,\r\n paginationConfig,\r\n handlePageChange,\r\n handlePageSizeChange,\r\n resetToFirstPage,\r\n getTotalPages,\r\n };\r\n}\r\n","/*\r\n * @Description: 表格操作按钮Hook\r\n * @Migration: naive-ui-components 组件库迁移版本\r\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\r\n */\r\n\r\nimport { h, type VNodeChild } from 'vue'\r\nimport { NButton, NSpace, NDropdown, useMessage, useDialog } from 'naive-ui'\r\nimport type { DataTableRowKey } from 'naive-ui/es'\r\nimport type {\r\n DataRecord,\r\n ApiFunction,\r\n UseTableActionsOptions,\r\n UseTableActionsReturn,\r\n} from '../types'\r\nimport C_Icon from '../../C_Icon/index.vue'\r\n\r\n/**\r\n * 表格操作Hook\r\n */\r\nexport function useTableActions<T extends DataRecord = DataRecord>(\r\n options: UseTableActionsOptions<T>\r\n): UseTableActionsReturn<T> {\r\n const { actions, config, tableManager, rowKey, emit, onViewDetail } = options\r\n const message = useMessage()\r\n const dialog = useDialog()\r\n\r\n /* 检查操作是否启用 */\r\n const isActionEnabled = (key: 'edit' | 'delete' | 'detail') =>\r\n actions.value?.[key] !== false\r\n\r\n /* 类型守卫:检查是否为有效API函数 */\r\n const isValidApiFunction = <TData extends DataRecord>(\r\n action: false | ApiFunction<TData> | undefined\r\n ): action is ApiFunction<TData> => {\r\n return action !== false && typeof action === 'function'\r\n }\r\n\r\n /* 自动提取API响应数据 */\r\n const extractApiResponseData = <TData>(response: unknown): TData => {\r\n if (response && typeof response === 'object' && 'data' in response) {\r\n return (response as Record<string, unknown>).data as TData\r\n }\r\n return response as TData\r\n }\r\n\r\n /* 创建按钮的通用方法 */\r\n const createButton = (\r\n icon: string,\r\n title: string,\r\n type = 'primary',\r\n onClick: () => void\r\n ) =>\r\n h(\r\n NButton,\r\n { size: 'small', type: type as any, quaternary: true, onClick },\r\n () => [h(C_Icon, { name: icon, size: 14, title })]\r\n )\r\n\r\n /* 处理编辑操作 */\r\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\r\n const handleEdit = (row: T, _index?: number) => {\r\n const rowKeyValue = rowKey(row)\r\n const editAction = actions.value?.edit\r\n\r\n if (config.value.editMode === 'modal') {\r\n tableManager.editStates.modalEdit.startEdit(\r\n rowKeyValue,\r\n { ...row },\r\n editAction\r\n )\r\n } else {\r\n tableManager.editStates.rowEdit.startEditRow(rowKeyValue, editAction)\r\n }\r\n }\r\n\r\n /* 执行删除API */\r\n const executeDelete = async (\r\n deleteAction: ApiFunction<T>,\r\n row: T,\r\n index: number\r\n ) => {\r\n try {\r\n await deleteAction(row, index)\r\n message.success('删除成功')\r\n emit('row-delete', row, index)\r\n } catch (error) {\r\n console.error('删除失败:', error)\r\n message.error('删除失败')\r\n throw error\r\n }\r\n }\r\n\r\n /* 处理删除操作 */\r\n const handleDelete = (row: T, index: number) => {\r\n const deleteAction = actions.value?.delete\r\n\r\n if (!isValidApiFunction(deleteAction)) {\r\n return\r\n }\r\n\r\n dialog.warning({\r\n title: '确认删除',\r\n content: '确定要删除这条记录吗?',\r\n positiveText: '确定',\r\n negativeText: '取消',\r\n onPositiveClick: () => executeDelete(deleteAction, row, index),\r\n })\r\n }\r\n\r\n /* 处理详情操作 */\r\n const handleDetail = async (row: T, index: number) => {\r\n const detailAction = actions.value?.detail\r\n\r\n if (!isValidApiFunction(detailAction)) {\r\n onViewDetail?.(row)\r\n return\r\n }\r\n\r\n try {\r\n const apiResponse = await detailAction(row, index)\r\n const detailData = extractApiResponseData<T>(apiResponse)\r\n onViewDetail?.(detailData || row)\r\n } catch (error) {\r\n console.error('获取详情失败:', error)\r\n message.error('获取详情失败')\r\n onViewDetail?.(row)\r\n }\r\n }\r\n\r\n /* 渲染行编辑按钮 */\r\n const renderRowEditButtons = (rowKeyValue: DataTableRowKey) => {\r\n const isEditing = tableManager.editStates.rowEdit.isEditingRow(rowKeyValue)\r\n\r\n if (isEditing) {\r\n return [\r\n createButton('mdi:check', '保存', 'primary', () =>\r\n tableManager.editStates.rowEdit.saveEditRow()\r\n ),\r\n createButton('mdi:close', '取消', 'default', () =>\r\n tableManager.editStates.rowEdit.cancelEditRow()\r\n ),\r\n ]\r\n }\r\n\r\n return [\r\n createButton('mdi:pencil', '编辑', 'warning', () =>\r\n tableManager.editStates.rowEdit.startEditRow(rowKeyValue)\r\n ),\r\n ]\r\n }\r\n\r\n /* 渲染基础操作按钮 */\r\n const renderBasicActions = (row: T, index: number) => {\r\n const buttons: VNodeChild[] = []\r\n\r\n if (isActionEnabled('detail')) {\r\n buttons.push(\r\n createButton('mdi:eye', '详情', 'info', () => handleDetail(row, index))\r\n )\r\n }\r\n\r\n if (\r\n config.value.editMode === 'modal' &&\r\n (isActionEnabled('edit') || config.value.editable)\r\n ) {\r\n buttons.push(\r\n createButton('mdi:pencil', '编辑', 'warning', () =>\r\n handleEdit(row, index)\r\n )\r\n )\r\n }\r\n\r\n if (isActionEnabled('delete')) {\r\n buttons.push(\r\n createButton('mdi:delete', '删除', 'error', () =>\r\n handleDelete(row, index)\r\n )\r\n )\r\n }\r\n\r\n return buttons\r\n }\r\n\r\n /* 渲染自定义操作下拉菜单 */\r\n const renderCustomDropdown = (row: T, index: number) => {\r\n const customActions = actions.value?.custom\r\n if (!customActions?.length) return null\r\n\r\n const visibleActions = customActions.filter(action =>\r\n action.show ? action.show(row, index) : true\r\n )\r\n\r\n if (!visibleActions.length) return null\r\n\r\n const options = visibleActions.map(action => {\r\n const label =\r\n typeof action.label === 'function'\r\n ? action.label(row, index)\r\n : action.label\r\n const icon =\r\n typeof action.icon === 'function'\r\n ? action.icon(row, index)\r\n : action.icon\r\n\r\n return {\r\n label,\r\n key: action.key,\r\n icon: () => h(C_Icon, { name: icon, size: 14 }),\r\n disabled: action.disabled?.(row, index) || false,\r\n }\r\n })\r\n\r\n return h(\r\n NDropdown,\r\n {\r\n options,\r\n onSelect: (key: string) => {\r\n const action = visibleActions.find(a => a.key === key)\r\n if (action) {\r\n try {\r\n action.onClick(row, index)\r\n } catch (error) {\r\n console.error(`操作\"${action.label}\"执行失败:`, error)\r\n message.error(`${action.label}失败`)\r\n }\r\n }\r\n },\r\n },\r\n () => createButton('mdi:dots-horizontal', '更多操作', 'default', () => {})\r\n )\r\n }\r\n\r\n /* 主渲染方法 */\r\n const renderActions = (row: T, index: number): VNodeChild => {\r\n if (actions.value?.render) {\r\n return actions.value.render(row, index)\r\n }\r\n\r\n const rowKeyValue = rowKey(row)\r\n const isRowEditMode = ['row', 'both'].includes(config.value.editMode)\r\n\r\n if (\r\n isRowEditMode &&\r\n tableManager.editStates.rowEdit.isEditingRow(rowKeyValue)\r\n ) {\r\n return h(NSpace, { size: 2, wrap: false }, () =>\r\n renderRowEditButtons(rowKeyValue)\r\n )\r\n }\r\n\r\n const allButtons = [\r\n ...(isRowEditMode ? renderRowEditButtons(rowKeyValue) : []),\r\n ...renderBasicActions(row, index),\r\n ]\r\n\r\n const dropdown = renderCustomDropdown(row, index)\r\n if (dropdown) allButtons.push(dropdown)\r\n\r\n return h(NSpace, { size: 2, wrap: false }, () => allButtons)\r\n }\r\n\r\n return { renderActions, isActionEnabled }\r\n}\r\n","/*\r\n * @Description: 表格配置中心 — 编辑组件映射、表单生成、显示值处理\r\n * @Migration: naive-ui-components 组件库迁移版本\r\n */\r\n\r\nimport { h } from \"vue\";\r\nimport { NInput, NInputNumber, NSwitch, NDatePicker, NSelect } from \"naive-ui\";\r\nimport type { TableColumn, EditType } from \"./types\";\r\n\r\n/* ================= 编辑组件映射 ================= */\r\n\r\nexport const EDIT_COMPONENTS: Record<EditType, any> = {\r\n number: NInputNumber,\r\n switch: NSwitch,\r\n input: NInput,\r\n email: NInput,\r\n mobile: NInput,\r\n date: (props: any) =>\r\n h(NDatePicker, { ...props, type: \"date\", format: \"yyyy-MM-dd\" }),\r\n select: (props: any) =>\r\n h(NSelect, { ...props, options: props.options || [] }),\r\n textarea: (props: any) => h(NInput, { ...props, type: \"textarea\", rows: 3 }),\r\n};\r\n\r\n/* ================= 表单选项生成 ================= */\r\n\r\ninterface FormOption {\r\n prop: string;\r\n label: string;\r\n type: string;\r\n placeholder: string;\r\n rules: any[];\r\n attrs: any;\r\n layout: { span: number };\r\n show: boolean;\r\n}\r\n\r\ntype ComponentType = string;\r\n\r\nconst EDIT_TO_FORM_TYPE: Record<string, ComponentType> = {\r\n number: \"inputNumber\",\r\n date: \"datePicker\",\r\n textarea: \"textarea\",\r\n select: \"select\",\r\n switch: \"switch\",\r\n};\r\n\r\n/**\r\n * 根据可编辑列配置自动生成表单选项(用于编辑弹窗)\r\n */\r\nexport const generateFormOptions = (columns: TableColumn[]): FormOption[] =>\r\n columns.map((column) => {\r\n const rules: any[] = (column as any).required\r\n ? [\r\n {\r\n required: true,\r\n message: `请输入${(column as any).title}`,\r\n trigger: [\"blur\", \"input\"],\r\n validator: async (_: any, value: any) => {\r\n if (!value && value !== 0 && value !== false)\r\n throw new Error(`请输入${(column as any).title}`);\r\n },\r\n },\r\n ]\r\n : [];\r\n\r\n return {\r\n prop: (column as any).key,\r\n label: (column as any).title || (column as any).key,\r\n type: EDIT_TO_FORM_TYPE[(column as any).editType || \"input\"] || \"input\",\r\n placeholder: `请输入${(column as any).title}`,\r\n rules,\r\n attrs: (column as any).editProps || {},\r\n layout: { span: 1 },\r\n show: true,\r\n };\r\n });\r\n","/*\r\n * @Description: 表格列处理引擎 — 列映射、单元格渲染、列设置状态管理\r\n * @Migration: naive-ui-components 组件库迁移版本\r\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\r\n */\r\n\r\nimport {\r\n h,\r\n ref,\r\n watch,\r\n computed,\r\n type Ref,\r\n type VNodeChild,\r\n type ComputedRef,\r\n} from 'vue'\r\nimport type { DataTableRowKey, DataTableColumn } from 'naive-ui/es'\r\nimport type { TableColumn, DataRecord } from '../types'\r\nimport type { ResolvedConfig } from './useTableConfig'\r\nimport { EDIT_COMPONENTS } from '../data'\r\nimport C_Icon from '../../C_Icon/index.vue'\r\n\r\n/* ================= 内部辅助类型 ================= */\r\n\r\n/**\r\n * 内部列类型,展平 TableColumn 联合类型以便直接访问 key/title/visible 等属性\r\n */\r\ntype ColumnWithKey = TableColumn & {\r\n key?: string\r\n title?: string\r\n visible?: boolean\r\n width?: number | string\r\n align?: string\r\n titleAlign?: string\r\n}\r\n\r\n/** useTableColumns 中对 tableManager 的最小依赖接口 */\r\ninterface TableManagerRef {\r\n editStates: {\r\n cellEdit: {\r\n isEditingCell: (rowKey: DataTableRowKey, columnKey: string) => boolean\r\n getEditingCellValue: (\r\n rowKey: DataTableRowKey,\r\n columnKey: string\r\n ) => unknown\r\n updateEditingCellValue: (\r\n rowKey: DataTableRowKey,\r\n columnKey: string,\r\n value: unknown\r\n ) => void\r\n saveEditCell: () => Promise<unknown>\r\n cancelEditCell: () => void\r\n startEditCell: (rowKey: DataTableRowKey, columnKey: string) => void\r\n }\r\n rowEdit: {\r\n isEditingRow: (rowKey: DataTableRowKey) => boolean\r\n getEditingRowData: (rowKey: DataTableRowKey) => DataRecord | undefined\r\n updateEditingRowData: (\r\n rowKey: DataTableRowKey,\r\n field: string,\r\n value: unknown\r\n ) => void\r\n }\r\n }\r\n dynamicRowsState?: {\r\n enhanceColumns: (columns: TableColumn[]) => TableColumn[]\r\n } | null\r\n expandState?: {\r\n getTableColumns: (originalColumns: TableColumn[]) => DataTableColumn[]\r\n } | null\r\n}\r\n\r\n/* ================= 渲染工具函数 ================= */\r\n\r\n/**\r\n * 渲染编辑组件(行编辑/单元格编辑通用)\r\n */\r\nexport function renderEditComponent(\r\n column: TableColumn,\r\n value: unknown,\r\n onUpdate: (val: unknown) => void\r\n): VNodeChild {\r\n if (column.editRender) return column.editRender(value, {} as DataRecord, 0)\r\n\r\n const col = column as ColumnWithKey\r\n const componentProps = {\r\n value,\r\n 'onUpdate:value': onUpdate,\r\n placeholder: `请输入${col.title ?? ''}`,\r\n style: { width: '100%' },\r\n ...column.editProps,\r\n }\r\n\r\n const Component =\r\n EDIT_COMPONENTS[column.editType || 'input'] || EDIT_COMPONENTS.input\r\n return h(Component, componentProps)\r\n}\r\n\r\n/**\r\n * 渲染只读单元格\r\n */\r\nexport function renderDisplayCell(\r\n column: TableColumn,\r\n rowData: DataRecord,\r\n rowIndex: number,\r\n value: unknown\r\n): VNodeChild {\r\n return column.render\r\n ? (column.render(rowData, rowIndex) ?? String(value ?? ''))\r\n : String(value ?? '')\r\n}\r\n\r\n/**\r\n * 渲染编辑中的单元格(带保存/取消按钮)\r\n */\r\nexport function renderEditingCell(\r\n column: TableColumn,\r\n value: unknown,\r\n onUpdate: (val: unknown) => void,\r\n onSave: () => void,\r\n onCancel: () => void\r\n): VNodeChild {\r\n const actionBtn = (\r\n name: string,\r\n title: string,\r\n onClick: (e: Event) => void\r\n ) =>\r\n h(\r\n 'button',\r\n {\r\n class: `cell-action-btn cell-action-${name}`,\r\n title,\r\n type: 'button',\r\n onClick: (e: Event) => {\r\n e.stopPropagation()\r\n e.preventDefault()\r\n onClick(e)\r\n },\r\n },\r\n [\r\n h(C_Icon, {\r\n name: `mdi:${name === 'save' ? 'check' : 'close'}`,\r\n title: name === 'save' ? '保存' : '取消',\r\n size: 12,\r\n }),\r\n ]\r\n )\r\n\r\n return h('div', { class: 'cell-editing-container' }, [\r\n h('div', { class: 'cell-editing-input' }, [\r\n renderEditComponent(column, value, onUpdate),\r\n ]),\r\n h('div', { class: 'cell-editing-actions' }, [\r\n actionBtn('save', '保存', onSave),\r\n actionBtn('cancel', '取消', onCancel),\r\n ]),\r\n ])\r\n}\r\n\r\n/**\r\n * 渲染可编辑单元格(显示值 + 编辑图标)\r\n */\r\nexport function renderEditableCell(\r\n column: TableColumn,\r\n rowData: DataRecord,\r\n rowIndex: number,\r\n value: unknown,\r\n onStartEdit: () => void\r\n): VNodeChild {\r\n const displayValue = column.render\r\n ? (column.render(rowData, rowIndex) ?? String(value ?? ''))\r\n : String(value ?? '')\r\n\r\n return h('div', { class: 'cell-edit-wrapper' }, [\r\n h('span', { class: 'cell-value' }, displayValue),\r\n h(C_Icon, {\r\n name: 'mdi:square-edit-outline',\r\n title: '编辑',\r\n class: 'cell-edit-icon',\r\n size: 14,\r\n clickable: true,\r\n onClick: (e: Event) => {\r\n e.stopPropagation()\r\n onStartEdit()\r\n },\r\n }),\r\n ])\r\n}\r\n\r\n/* ================= 组合式函数入参 ================= */\r\n\r\nexport interface UseTableColumnsOptions {\r\n /** 原始列配置 */\r\n rawColumns: ComputedRef<TableColumn[]>\r\n /** 统一配置 */\r\n config: ComputedRef<ResolvedConfig>\r\n /** 默认列宽 */\r\n columnWidth: number\r\n /** 用户设定的 scrollX */\r\n scrollX?: number | string\r\n /** 行键获取函数 */\r\n rowKey: (row: DataRecord) => DataTableRowKey\r\n /** 表格管理器 */\r\n tableManager: TableManagerRef\r\n /** 操作列渲染函数 */\r\n actionsRenderer: (row: DataRecord, index: number) => VNodeChild\r\n /** 编辑模式检查器 */\r\n editModeChecker: ComputedRef<{\r\n isNonEditable: (column: TableColumn) => boolean\r\n isRowEditMode: () => boolean\r\n isCellEditMode: () => boolean\r\n }>\r\n}\r\n\r\nexport interface UseTableColumnsReturn {\r\n reactiveColumns: Ref<TableColumn[]>\r\n showSettingsPanel: Ref<boolean>\r\n computedColumns: ComputedRef<DataTableColumn[]>\r\n computedScrollX: ComputedRef<number | string | undefined>\r\n handleColumnChange: (columns: TableColumn[]) => void\r\n}\r\n\r\n/* ================= 主函数 ================= */\r\n\r\n/**\r\n * 表格列处理引擎\r\n */\r\nexport function useTableColumns(\r\n options: UseTableColumnsOptions\r\n): UseTableColumnsReturn {\r\n const {\r\n rawColumns,\r\n config,\r\n columnWidth,\r\n rowKey,\r\n tableManager,\r\n actionsRenderer,\r\n editModeChecker,\r\n } = options\r\n\r\n /* ================= 响应式列状态 ================= */\r\n\r\n const reactiveColumns = ref<TableColumn[]>([])\r\n const showSettingsPanel = ref(false)\r\n\r\n /** 同步外部列配置到响应式状态 */\r\n const syncColumns = (newColumns: TableColumn[]) => {\r\n const existingActions = reactiveColumns.value.find(\r\n col => (col as ColumnWithKey).key === '_actions'\r\n )\r\n reactiveColumns.value = [\r\n ...newColumns,\r\n existingActions ||\r\n ({\r\n key: '_actions',\r\n title: '操作',\r\n width: 180,\r\n editable: false,\r\n visible: true,\r\n fixed: 'right',\r\n } as TableColumn),\r\n ]\r\n }\r\n\r\n watch(\r\n rawColumns,\r\n cols => {\r\n if (cols?.length) syncColumns(cols)\r\n },\r\n { deep: true, immediate: true }\r\n )\r\n\r\n /* ================= 单元格渲染 ================= */\r\n\r\n /** 单元格编辑模式渲染 */\r\n const renderCellEdit = (\r\n column: TableColumn,\r\n rowData: DataRecord,\r\n rowIndex: number,\r\n rowKeyVal: DataTableRowKey\r\n ): VNodeChild => {\r\n const col = column as ColumnWithKey\r\n const colKey = col.key ?? ''\r\n const value = rowData[colKey]\r\n const {\r\n isEditingCell,\r\n getEditingCellValue,\r\n updateEditingCellValue,\r\n saveEditCell,\r\n cancelEditCell,\r\n startEditCell,\r\n } = tableManager.editStates.cellEdit\r\n\r\n if (isEditingCell(rowKeyVal, colKey)) {\r\n return renderEditingCell(\r\n column,\r\n getEditingCellValue(rowKeyVal, colKey) ?? value,\r\n (val: unknown) => updateEditingCellValue(rowKeyVal, colKey, val),\r\n () => saveEditCell(),\r\n () => cancelEditCell()\r\n )\r\n }\r\n\r\n return renderEditableCell(column, rowData, rowIndex, value, () =>\r\n startEditCell(rowKeyVal, colKey)\r\n )\r\n }\r\n\r\n /** 统一单元格渲染入口 */\r\n const renderCell = (\r\n column: TableColumn,\r\n rowData: DataRecord,\r\n rowIndex: number\r\n ): VNodeChild => {\r\n const col = column as ColumnWithKey\r\n const colKey = col.key ?? ''\r\n const value = rowData[colKey]\r\n const key = rowKey(rowData)\r\n const checker = editModeChecker.value\r\n\r\n if (checker.isNonEditable(column)) {\r\n return renderDisplayCell(column, rowData, rowIndex, value)\r\n }\r\n\r\n if (\r\n checker.isRowEditMode() &&\r\n tableManager.editStates.rowEdit.isEditingRow(key)\r\n ) {\r\n return renderEditComponent(\r\n column,\r\n tableManager.editStates.rowEdit.getEditingRowData(key)?.[colKey] ??\r\n value,\r\n (val: unknown) =>\r\n tableManager.editStates.rowEdit.updateEditingRowData(key, colKey, val)\r\n )\r\n }\r\n\r\n if (checker.isCellEditMode()) {\r\n return renderCellEdit(column, rowData, rowIndex, key)\r\n }\r\n\r\n return renderDisplayCell(column, rowData, rowIndex, value)\r\n }\r\n\r\n /* ================= 列映射 ================= */\r\n\r\n const getColWidth = (col: TableColumn): number => {\r\n const w = (col as ColumnWithKey).width || columnWidth || 180\r\n return typeof w === 'number' ? w : 180\r\n }\r\n\r\n /** 序号列 */\r\n const mapIndexColumn = (column: TableColumn): DataTableColumn => {\r\n const col = column as ColumnWithKey\r\n return {\r\n key: '_index',\r\n title: col.title || '序号',\r\n width:\r\n typeof (col.width || 50) === 'number'\r\n ? (col.width as number) || 50\r\n : 50,\r\n titleAlign: 'center' as const,\r\n align: 'center' as const,\r\n render: (_: DataRecord, index: number) => index + 1,\r\n fixed: column.fixed,\r\n }\r\n }\r\n\r\n /** 普通数据列 */\r\n const mapRegularColumn = (column: TableColumn): DataTableColumn => {\r\n const col = column as ColumnWithKey\r\n const base: Record<string, unknown> = {\r\n ...col,\r\n width: getColWidth(column),\r\n titleAlign: col.titleAlign || ('center' as const),\r\n align: col.align || ('center' as const),\r\n render:\r\n column.render ||\r\n ((rowData: DataRecord, rowIndex: number) =>\r\n renderCell(column, rowData, rowIndex)),\r\n }\r\n\r\n if (column.fixed) base.fixed = column.fixed\r\n\r\n if (column.resizable && typeof base.width === 'number') {\r\n base.resizable = true\r\n base.minWidth = column.minWidth || 80\r\n base.maxWidth = column.maxWidth || 500\r\n }\r\n\r\n return base as unknown as DataTableColumn\r\n }\r\n\r\n /* ================= 计算列 ================= */\r\n\r\n const computedColumns = computed((): DataTableColumn[] => {\r\n let cols: DataTableColumn[] = reactiveColumns.value\r\n .filter(c => {\r\n const col = c as ColumnWithKey\r\n return col.visible !== false && col.key !== '_actions'\r\n })\r\n .map(c => {\r\n const col = c as ColumnWithKey\r\n return (col as any).type === 'index'\r\n ? mapIndexColumn(c)\r\n : mapRegularColumn(c)\r\n }) as DataTableColumn[]\r\n\r\n if (tableManager.dynamicRowsState) {\r\n cols = tableManager.dynamicRowsState.enhanceColumns(\r\n cols as unknown as TableColumn[]\r\n ) as unknown as DataTableColumn[]\r\n }\r\n\r\n if (\r\n tableManager.expandState &&\r\n (config.value.expandable || config.value.enableSelection)\r\n ) {\r\n cols = tableManager.expandState.getTableColumns(\r\n cols as unknown as TableColumn[]\r\n ) as unknown as DataTableColumn[]\r\n }\r\n\r\n const actionsMeta = reactiveColumns.value.find(\r\n c => (c as ColumnWithKey).key === '_actions'\r\n )\r\n cols.push({\r\n key: '_actions',\r\n title: '操作',\r\n align: 'center' as const,\r\n titleAlign: 'center' as const,\r\n render: actionsRenderer,\r\n fixed: (actionsMeta as ColumnWithKey)?.fixed,\r\n })\r\n\r\n return cols\r\n })\r\n\r\n /* ================= 横向滚动 ================= */\r\n\r\n const computedScrollX = computed(() => {\r\n if (options.scrollX !== undefined) return options.scrollX\r\n\r\n const hasFixed = reactiveColumns.value.some(c => {\r\n const col = c as ColumnWithKey\r\n return col.fixed && col.visible !== false\r\n })\r\n if (!hasFixed) return undefined\r\n\r\n const total = reactiveColumns.value\r\n .filter(c => (c as ColumnWithKey).visible !== false)\r\n .reduce((sum: number, c) => sum + getColWidth(c), 0)\r\n\r\n return total + 200\r\n })\r\n\r\n /* ================= 列设置变更 ================= */\r\n\r\n const handleColumnChange = (columns: TableColumn[]) => {\r\n reactiveColumns.value = columns.map(col => {\r\n const c = col as ColumnWithKey\r\n return {\r\n ...col,\r\n visible: c.visible !== false,\r\n fixed: col.fixed,\r\n width: c.width || columnWidth,\r\n align: c.align || 'center',\r\n titleAlign: c.titleAlign || 'center',\r\n } as unknown as TableColumn\r\n })\r\n }\r\n\r\n return {\r\n reactiveColumns,\r\n showSettingsPanel,\r\n computedColumns,\r\n computedScrollX,\r\n handleColumnChange,\r\n }\r\n}\r\n","/*\r\n * @Description: ColumnSettings 配置与工具函数\r\n * @Migration: naive-ui-components 组件库迁移版本\r\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\r\n */\r\n\r\nimport type { TableColumn } from '../../types'\r\n\r\n/** 固定列下拉选项 */\r\nexport const getFixedOptions = (column: TableColumn) => [\r\n {\r\n label: column.fixed ? '✓ 取消固定' : '不固定',\r\n key: 'none',\r\n disabled: !column.fixed,\r\n },\r\n {\r\n label: column.fixed === 'left' ? '✓ 固定左侧' : '固定左侧',\r\n key: 'left',\r\n disabled: false,\r\n },\r\n {\r\n label: column.fixed === 'right' ? '✓ 固定右侧' : '固定右侧',\r\n key: 'right',\r\n disabled: false,\r\n },\r\n]\r\n\r\n/** 不可操作的特殊列 key */\r\nexport const SPECIAL_COLUMN_KEYS = ['_actions'] as const\r\n\r\n/** 不可拖拽的列 type */\r\nexport const SPECIAL_COLUMN_TYPES = ['selection', 'expand'] as const\r\n\r\n/** 判断是否为特殊列(不可调宽、不可拖拽) */\r\nexport const isSpecialColumn = (column: TableColumn): boolean =>\r\n SPECIAL_COLUMN_KEYS.includes(\r\n column.key as (typeof SPECIAL_COLUMN_KEYS)[number]\r\n ) ||\r\n SPECIAL_COLUMN_TYPES.includes(\r\n (column as any).type as (typeof SPECIAL_COLUMN_TYPES)[number]\r\n )\r\n","/* unplugin-vue-components disabled */<!--\r\n * @Description: 列设置面板 — 搜索、拖拽排序、可见性、固定列、列宽调整\r\n * @Migration: naive-ui-components 组件库迁移版本\r\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\r\n -->\r\n\r\n<template>\r\n <div class=\"column-management-tab\">\r\n <!-- 搜索框和列宽调整开关 -->\r\n <div class=\"search-row\">\r\n <NInput\r\n v-model:value=\"searchText\"\r\n placeholder=\"搜索列名...\"\r\n clearable\r\n class=\"search-input\"\r\n >\r\n <template #prefix>\r\n <C_Icon name=\"mdi:magnify\" />\r\n </template>\r\n </NInput>\r\n\r\n <div class=\"resizable-control\">\r\n <C_Icon\r\n name=\"mdi:arrow-split-vertical\"\r\n size=\"14\"\r\n :title=\"\r\n enableResizable ? '开启后可拖拽列边界调整宽度' : '允许调整列宽'\r\n \"\r\n />\r\n <NText :style=\"{ fontSize: '12px', marginLeft: '4px' }\">\r\n 列宽调整\r\n </NText>\r\n <NSwitch\r\n v-model:value=\"enableResizable\"\r\n size=\"small\"\r\n style=\"margin-left: 6px\"\r\n @update:value=\"handleResizableChange\"\r\n />\r\n </div>\r\n </div>\r\n\r\n <!-- 顶部操作栏:统计信息和快捷操作按钮 -->\r\n <div class=\"top-actions-bar\">\r\n <div class=\"stats-info\">\r\n <NText\r\n depth=\"3\"\r\n :style=\"{ fontSize: '13px' }\"\r\n >\r\n 已选 {{ visibleCount }} / 总共 {{ totalCount }} 列\r\n </NText>\r\n </div>\r\n <div class=\"quick-actions\">\r\n <NSpace :size=\"6\">\r\n <NButton\r\n size=\"tiny\"\r\n @click=\"selectAll\"\r\n >\r\n 全选\r\n </NButton>\r\n <NButton\r\n size=\"tiny\"\r\n @click=\"selectNone\"\r\n >\r\n 全不选\r\n </NButton>\r\n <NButton\r\n size=\"tiny\"\r\n @click=\"resetColumns\"\r\n >\r\n 重置\r\n </NButton>\r\n </NSpace>\r\n </div>\r\n </div>\r\n\r\n <!-- 列列表 -->\r\n <div\r\n ref=\"listRef\"\r\n class=\"column-list\"\r\n >\r\n <div\r\n v-for=\"(column, index) in filteredColumns\"\r\n :key=\"column.key\"\r\n class=\"column-item\"\r\n :class=\"{\r\n disabled: column.key === '_actions',\r\n 'fixed-left': column.fixed === 'left',\r\n 'fixed-right': column.fixed === 'right',\r\n }\"\r\n :draggable=\"column.key !== '_actions'\"\r\n @dragstart=\"handleDragStart(index, $event)\"\r\n @dragover=\"handleDragOver(index, $event)\"\r\n @dragend=\"handleDragEnd\"\r\n @drop=\"handleDrop(index)\"\r\n >\r\n <div class=\"column-info\">\r\n <div class=\"column-controls\">\r\n <C_Icon\r\n name=\"mdi:drag\"\r\n size=\"16\"\r\n class=\"drag-handle\"\r\n :class=\"{ disabled: column.key === '_actions' }\"\r\n />\r\n <NCheckbox\r\n :checked=\"column.visible !== false\"\r\n :disabled=\"column.key === '_actions'\"\r\n @update:checked=\"\r\n (value: boolean) => toggleColumnVisibility(index, value)\r\n \"\r\n />\r\n </div>\r\n <div class=\"column-details\">\r\n <NSpace\r\n align=\"center\"\r\n :size=\"6\"\r\n >\r\n <NText\r\n strong\r\n :style=\"{ fontSize: '13px' }\"\r\n >\r\n {{ column.title || column.key }}\r\n </NText>\r\n <NTag\r\n v-if=\"column.fixed === 'left'\"\r\n size=\"tiny\"\r\n type=\"info\"\r\n >\r\n 左固定\r\n </NTag>\r\n <NTag\r\n v-if=\"column.fixed === 'right'\"\r\n size=\"tiny\"\r\n type=\"warning\"\r\n >\r\n 右固定\r\n </NTag>\r\n </NSpace>\r\n <NText\r\n depth=\"3\"\r\n :style=\"{ fontSize: '11px' }\"\r\n >\r\n {{ column.key }}\r\n </NText>\r\n </div>\r\n </div>\r\n <div class=\"column-actions\">\r\n <div class=\"drag-controls\">\r\n <C_Icon\r\n name=\"mdi:chevron-up\"\r\n size=\"14\"\r\n title=\"上移\"\r\n :clickable=\"index !== 0\"\r\n :class=\"{ disabled: index === 0 }\"\r\n @click=\"index !== 0 && moveColumn(index, index - 1)\"\r\n />\r\n <C_Icon\r\n name=\"mdi:chevron-down\"\r\n size=\"14\"\r\n title=\"下移\"\r\n :clickable=\"index !== filteredColumns.length - 1\"\r\n :class=\"{ disabled: index === filteredColumns.length - 1 }\"\r\n @click=\"\r\n index !== filteredColumns.length - 1 &&\r\n moveColumn(index, index + 1)\r\n \"\r\n />\r\n </div>\r\n <NDropdown\r\n :options=\"getFixedOptions(column)\"\r\n @select=\"(value: string) => handleFixedSelect(index, value)\"\r\n >\r\n <C_Icon\r\n name=\"mdi:pin\"\r\n size=\"14\"\r\n title=\"固定列\"\r\n clickable\r\n :style=\"{\r\n color: column.fixed ? 'var(--n-primary-color)' : undefined,\r\n }\"\r\n />\r\n </NDropdown>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\n import { ref, computed, onMounted } from 'vue'\r\n import {\r\n NInput,\r\n NText,\r\n NSwitch,\r\n NSpace,\r\n NButton,\r\n NCheckbox,\r\n NTag,\r\n NDropdown,\r\n } from 'naive-ui/es'\r\n import C_Icon from '../../../C_Icon/index.vue'\r\n import type { TableColumn } from '../../types'\r\n import { getFixedOptions, isSpecialColumn } from './data'\r\n\r\n defineOptions({ name: 'ColumnSettings' })\r\n\r\n /* ================= Props & Emits ================= */\r\n\r\n const props = defineProps<{ columns: TableColumn[] }>()\r\n const emit = defineEmits<{ (e: 'change', columns: TableColumn[]): void }>()\r\n\r\n /* ================= 响应式状态 ================= */\r\n\r\n const localColumns = ref<TableColumn[]>([...props.columns])\r\n const searchText = ref('')\r\n const listRef = ref<HTMLElement>()\r\n const draggedIndex = ref<number | null>(null)\r\n const dragOverIndex = ref<number | null>(null)\r\n const enableResizable = ref(false)\r\n\r\n onMounted(() => {\r\n enableResizable.value = localColumns.value.some(\r\n col => col.resizable === true\r\n )\r\n })\r\n\r\n /* ================= 计算属性 ================= */\r\n\r\n const filteredColumns = computed(() => {\r\n if (!searchText.value) return localColumns.value\r\n const search = searchText.value.toLowerCase()\r\n return localColumns.value.filter(\r\n col =>\r\n col.title?.toLowerCase().includes(search) ||\r\n col.key?.toLowerCase().includes(search)\r\n )\r\n })\r\n\r\n const visibleCount = computed(\r\n () => localColumns.value.filter(col => col.visible !== false).length\r\n )\r\n const totalCount = computed(() => localColumns.value.length)\r\n\r\n /* ================= 批量操作 ================= */\r\n\r\n const setAllColumnsVisible = (visible: boolean) => {\r\n localColumns.value.forEach(col => {\r\n if (col.key !== '_actions') col.visible = visible\r\n })\r\n applyChanges()\r\n }\r\n\r\n const selectAll = () => setAllColumnsVisible(true)\r\n const selectNone = () => setAllColumnsVisible(false)\r\n\r\n /* ================= 列属性更新 ================= */\r\n\r\n const findOriginalIndex = (filteredIndex: number): number => {\r\n const column = filteredColumns.value[filteredIndex]\r\n return localColumns.value.findIndex(col => col.key === column.key)\r\n }\r\n\r\n const updateColumnProperty = (\r\n index: number,\r\n updater: (col: TableColumn) => void\r\n ) => {\r\n const originalIndex = findOriginalIndex(index)\r\n if (originalIndex !== -1) {\r\n updater(localColumns.value[originalIndex])\r\n applyChanges()\r\n }\r\n }\r\n\r\n const toggleColumnVisibility = (index: number, visible: boolean) => {\r\n updateColumnProperty(index, col => {\r\n col.visible = visible\r\n })\r\n }\r\n\r\n const handleFixedSelect = (index: number, value: string) => {\r\n updateColumnProperty(index, col => {\r\n col.fixed = value === 'none' ? undefined : (value as 'left' | 'right')\r\n })\r\n }\r\n\r\n const applyChanges = () => emit('change', [...localColumns.value])\r\n\r\n const resetColumns = () => {\r\n localColumns.value = [...props.columns]\r\n applyChanges()\r\n }\r\n\r\n /* ================= 列宽调整 ================= */\r\n\r\n const handleResizableChange = (value: boolean) => {\r\n localColumns.value.forEach(col => {\r\n if (!isSpecialColumn(col)) {\r\n col.resizable = value\r\n if (value) {\r\n col.minWidth = col.minWidth || 80\r\n col.maxWidth = col.maxWidth || 500\r\n }\r\n }\r\n })\r\n applyChanges()\r\n }\r\n\r\n /* ================= 拖拽排序 ================= */\r\n\r\n const moveColumn = (fromIndex: number, toIndex: number) => {\r\n const originalFromIndex = findOriginalIndex(fromIndex)\r\n const originalToIndex = findOriginalIndex(toIndex)\r\n if (originalFromIndex !== -1 && originalToIndex !== -1) {\r\n const [movedColumn] = localColumns.value.splice(originalFromIndex, 1)\r\n localColumns.value.splice(originalToIndex, 0, movedColumn)\r\n applyChanges()\r\n }\r\n }\r\n\r\n const handleDragStart = (index: number, event: DragEvent) => {\r\n const column = filteredColumns.value[index]\r\n if (column.key === '_actions') {\r\n event.preventDefault()\r\n return\r\n }\r\n draggedIndex.value = index\r\n if (event.dataTransfer) event.dataTransfer.effectAllowed = 'move'\r\n }\r\n\r\n const handleDragOver = (index: number, event: DragEvent) => {\r\n event.preventDefault()\r\n if (\r\n draggedIndex.value === null ||\r\n filteredColumns.value[index].key === '_actions'\r\n )\r\n return\r\n dragOverIndex.value = index\r\n if (event.dataTransfer) event.dataTransfer.dropEffect = 'move'\r\n }\r\n\r\n const handleDrop = (toIndex: number) => {\r\n if (\r\n draggedIndex.value === null ||\r\n filteredColumns.value[toIndex].key === '_actions'\r\n )\r\n return\r\n moveColumn(draggedIndex.value, toIndex)\r\n draggedIndex.value = null\r\n dragOverIndex.value = null\r\n }\r\n\r\n const handleDragEnd = () => {\r\n draggedIndex.value = null\r\n dragOverIndex.value = null\r\n }\r\n</script>\r\n\r\n<style scoped lang=\"scss\">\r\n @use './index.scss';\r\n</style>\r\n","/* unplugin-vue-components disabled */<!--\r\n * @Description: 列设置面板 — 搜索、拖拽排序、可见性、固定列、列宽调整\r\n * @Migration: naive-ui-components 组件库迁移版本\r\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\r\n -->\r\n\r\n<template>\r\n <div class=\"column-management-tab\">\r\n <!-- 搜索框和列宽调整开关 -->\r\n <div class=\"search-row\">\r\n <NInput\r\n v-model:value=\"searchText\"\r\n placeholder=\"搜索列名...\"\r\n clearable\r\n class=\"search-input\"\r\n >\r\n <template #prefix>\r\n <C_Icon name=\"mdi:magnify\" />\r\n </template>\r\n </NInput>\r\n\r\n <div class=\"resizable-control\">\r\n <C_Icon\r\n name=\"mdi:arrow-split-vertical\"\r\n size=\"14\"\r\n :title=\"\r\n enableResizable ? '开启后可拖拽列边界调整宽度' : '允许调整列宽'\r\n \"\r\n />\r\n <NText :style=\"{ fontSize: '12px', marginLeft: '4px' }\">\r\n 列宽调整\r\n </NText>\r\n <NSwitch\r\n v-model:value=\"enableResizable\"\r\n size=\"small\"\r\n style=\"margin-left: 6px\"\r\n @update:value=\"handleResizableChange\"\r\n />\r\n </div>\r\n </div>\r\n\r\n <!-- 顶部操作栏:统计信息和快捷操作按钮 -->\r\n <div class=\"top-actions-bar\">\r\n <div class=\"stats-info\">\r\n <NText\r\n depth=\"3\"\r\n :style=\"{ fontSize: '13px' }\"\r\n >\r\n 已选 {{ visibleCount }} / 总共 {{ totalCount }} 列\r\n </NText>\r\n </div>\r\n <div class=\"quick-actions\">\r\n <NSpace :size=\"6\">\r\n <NButton\r\n size=\"tiny\"\r\n @click=\"selectAll\"\r\n >\r\n 全选\r\n </NButton>\r\n <NButton\r\n size=\"tiny\"\r\n @click=\"selectNone\"\r\n >\r\n 全不选\r\n </NButton>\r\n <NButton\r\n size=\"tiny\"\r\n @click=\"resetColumns\"\r\n >\r\n 重置\r\n </NButton>\r\n </NSpace>\r\n </div>\r\n </div>\r\n\r\n <!-- 列列表 -->\r\n <div\r\n ref=\"listRef\"\r\n class=\"column-list\"\r\n >\r\n <div\r\n v-for=\"(column, index) in filteredColumns\"\r\n :key=\"column.key\"\r\n class=\"column-item\"\r\n :class=\"{\r\n disabled: column.key === '_actions',\r\n 'fixed-left': column.fixed === 'left',\r\n 'fixed-right': column.fixed === 'right',\r\n }\"\r\n :draggable=\"column.key !== '_actions'\"\r\n @dragstart=\"handleDragStart(index, $event)\"\r\n @dragover=\"handleDragOver(index, $event)\"\r\n @dragend=\"handleDragEnd\"\r\n @drop=\"handleDrop(index)\"\r\n >\r\n <div class=\"column-info\">\r\n <div class=\"column-controls\">\r\n <C_Icon\r\n name=\"mdi:drag\"\r\n size=\"16\"\r\n class=\"drag-handle\"\r\n :class=\"{ disabled: column.key === '_actions' }\"\r\n />\r\n <NCheckbox\r\n :checked=\"column.visible !== false\"\r\n :disabled=\"column.key === '_actions'\"\r\n @update:checked=\"\r\n (value: boolean) => toggleColumnVisibility(index, value)\r\n \"\r\n />\r\n </div>\r\n <div class=\"column-details\">\r\n <NSpace\r\n align=\"center\"\r\n :size=\"6\"\r\n >\r\n <NText\r\n strong\r\n :style=\"{ fontSize: '13px' }\"\r\n >\r\n {{ column.title || column.key }}\r\n </NText>\r\n <NTag\r\n v-if=\"column.fixed === 'left'\"\r\n size=\"tiny\"\r\n type=\"info\"\r\n >\r\n 左固定\r\n </NTag>\r\n <NTag\r\n v-if=\"column.fixed === 'right'\"\r\n size=\"tiny\"\r\n type=\"warning\"\r\n >\r\n 右固定\r\n </NTag>\r\n </NSpace>\r\n <NText\r\n depth=\"3\"\r\n :style=\"{ fontSize: '11px' }\"\r\n >\r\n {{ column.key }}\r\n </NText>\r\n </div>\r\n </div>\r\n <div class=\"column-actions\">\r\n <div class=\"drag-controls\">\r\n <C_Icon\r\n name=\"mdi:chevron-up\"\r\n size=\"14\"\r\n title=\"上移\"\r\n :clickable=\"index !== 0\"\r\n :class=\"{ disabled: index === 0 }\"\r\n @click=\"index !== 0 && moveColumn(index, index - 1)\"\r\n />\r\n <C_Icon\r\n name=\"mdi:chevron-down\"\r\n size=\"14\"\r\n title=\"下移\"\r\n :clickable=\"index !== filteredColumns.length - 1\"\r\n :class=\"{ disabled: index === filteredColumns.length - 1 }\"\r\n @click=\"\r\n index !== filteredColumns.length - 1 &&\r\n moveColumn(index, index + 1)\r\n \"\r\n />\r\n </div>\r\n <NDropdown\r\n :options=\"getFixedOptions(column)\"\r\n @select=\"(value: string) => handleFixedSelect(index, value)\"\r\n >\r\n <C_Icon\r\n name=\"mdi:pin\"\r\n size=\"14\"\r\n title=\"固定列\"\r\n clickable\r\n :style=\"{\r\n color: column.fixed ? 'var(--n-primary-color)' : undefined,\r\n }\"\r\n />\r\n </NDropdown>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\n import { ref, computed, onMounted } from 'vue'\r\n import {\r\n NInput,\r\n NText,\r\n NSwitch,\r\n NSpace,\r\n NButton,\r\n NCheckbox,\r\n NTag,\r\n NDropdown,\r\n } from 'naive-ui/es'\r\n import C_Icon from '../../../C_Icon/index.vue'\r\n import type { TableColumn } from '../../types'\r\n import { getFixedOptions, isSpecialColumn } from './data'\r\n\r\n defineOptions({ name: 'ColumnSettings' })\r\n\r\n /* ================= Props & Emits ================= */\r\n\r\n const props = defineProps<{ columns: TableColumn[] }>()\r\n const emit = defineEmits<{ (e: 'change', columns: TableColumn[]): void }>()\r\n\r\n /* ================= 响应式状态 ================= */\r\n\r\n const localColumns = ref<TableColumn[]>([...props.columns])\r\n const searchText = ref('')\r\n const listRef = ref<HTMLElement>()\r\n const draggedIndex = ref<number | null>(null)\r\n const dragOverIndex = ref<number | null>(null)\r\n const enableResizable = ref(false)\r\n\r\n onMounted(() => {\r\n enableResizable.value = localColumns.value.some(\r\n col => col.resizable === true\r\n )\r\n })\r\n\r\n /* ================= 计算属性 ================= */\r\n\r\n const filteredColumns = computed(() => {\r\n if (!searchText.value) return localColumns.value\r\n const search = searchText.value.toLowerCase()\r\n return localColumns.value.filter(\r\n col =>\r\n col.title?.toLowerCase().includes(search) ||\r\n col.key?.toLowerCase().includes(search)\r\n )\r\n })\r\n\r\n const visibleCount = computed(\r\n () => localColumns.value.filter(col => col.visible !== false).length\r\n )\r\n const totalCount = computed(() => localColumns.value.length)\r\n\r\n /* ================= 批量操作 ================= */\r\n\r\n const setAllColumnsVisible = (visible: boolean) => {\r\n localColumns.value.forEach(col => {\r\n if (col.key !== '_actions') col.visible = visible\r\n })\r\n applyChanges()\r\n }\r\n\r\n const selectAll = () => setAllColumnsVisible(true)\r\n const selectNone = () => setAllColumnsVisible(false)\r\n\r\n /* ================= 列属性更新 ================= */\r\n\r\n const findOriginalIndex = (filteredIndex: number): number => {\r\n const column = filteredColumns.value[filteredIndex]\r\n return localColumns.value.findIndex(col => col.key === column.key)\r\n }\r\n\r\n const updateColumnProperty = (\r\n index: number,\r\n updater: (col: TableColumn) => void\r\n ) => {\r\n const originalIndex = findOriginalIndex(index)\r\n if (originalIndex !== -1) {\r\n updater(localColumns.value[originalIndex])\r\n applyChanges()\r\n }\r\n }\r\n\r\n const toggleColumnVisibility = (index: number, visible: boolean) => {\r\n updateColumnProperty(index, col => {\r\n col.visible = visible\r\n })\r\n }\r\n\r\n const handleFixedSelect = (index: number, value: string) => {\r\n updateColumnProperty(index, col => {\r\n col.fixed = value === 'none' ? undefined : (value as 'left' | 'right')\r\n })\r\n }\r\n\r\n const applyChanges = () => emit('change', [...localColumns.value])\r\n\r\n const resetColumns = () => {\r\n localColumns.value = [...props.columns]\r\n applyChanges()\r\n }\r\n\r\n /* ================= 列宽调整 ================= */\r\n\r\n const handleResizableChange = (value: boolean) => {\r\n localColumns.value.forEach(col => {\r\n if (!isSpecialColumn(col)) {\r\n col.resizable = value\r\n if (value) {\r\n col.minWidth = col.minWidth || 80\r\n col.maxWidth = col.maxWidth || 500\r\n }\r\n }\r\n })\r\n applyChanges()\r\n }\r\n\r\n /* ================= 拖拽排序 ================= */\r\n\r\n const moveColumn = (fromIndex: number, toIndex: number) => {\r\n const originalFromIndex = findOriginalIndex(fromIndex)\r\n const originalToIndex = findOriginalIndex(toIndex)\r\n if (originalFromIndex !== -1 && originalToIndex !== -1) {\r\n const [movedColumn] = localColumns.value.splice(originalFromIndex, 1)\r\n localColumns.value.splice(originalToIndex, 0, movedColumn)\r\n applyChanges()\r\n }\r\n }\r\n\r\n const handleDragStart = (index: number, event: DragEvent) => {\r\n const column = filteredColumns.value[index]\r\n if (column.key === '_actions') {\r\n event.preventDefault()\r\n return\r\n }\r\n draggedIndex.value = index\r\n if (event.dataTransfer) event.dataTransfer.effectAllowed = 'move'\r\n }\r\n\r\n const handleDragOver = (index: number, event: DragEvent) => {\r\n event.preventDefault()\r\n if (\r\n draggedIndex.value === null ||\r\n filteredColumns.value[index].key === '_actions'\r\n )\r\n return\r\n dragOverIndex.value = index\r\n if (event.dataTransfer) event.dataTransfer.dropEffect = 'move'\r\n }\r\n\r\n const handleDrop = (toIndex: number) => {\r\n if (\r\n draggedIndex.value === null ||\r\n filteredColumns.value[toIndex].key === '_actions'\r\n )\r\n return\r\n moveColumn(draggedIndex.value, toIndex)\r\n draggedIndex.value = null\r\n dragOverIndex.value = null\r\n }\r\n\r\n const handleDragEnd = () => {\r\n draggedIndex.value = null\r\n dragOverIndex.value = null\r\n }\r\n</script>\r\n\r\n<style scoped lang=\"scss\">\r\n @use './index.scss';\r\n</style>\r\n","<!--\r\n * @Description: 列设置面板 — 搜索、拖拽排序、可见性、固定列、列宽调整\r\n * @Migration: naive-ui-components 组件库迁移版本\r\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\r\n -->\r\n\r\n<template>\r\n <div class=\"column-management-tab\">\r\n <!-- 搜索框和列宽调整开关 -->\r\n <div class=\"search-row\">\r\n <NInput\r\n v-model:value=\"searchText\"\r\n placeholder=\"搜索列名...\"\r\n clearable\r\n class=\"search-input\"\r\n >\r\n <template #prefix>\r\n <C_Icon name=\"mdi:magnify\" />\r\n </template>\r\n </NInput>\r\n\r\n <div class=\"resizable-control\">\r\n <C_Icon\r\n name=\"mdi:arrow-split-vertical\"\r\n size=\"14\"\r\n :title=\"\r\n enableResizable ? '开启后可拖拽列边界调整宽度' : '允许调整列宽'\r\n \"\r\n />\r\n <NText :style=\"{ fontSize: '12px', marginLeft: '4px' }\">\r\n 列宽调整\r\n </NText>\r\n <NSwitch\r\n v-model:value=\"enableResizable\"\r\n size=\"small\"\r\n style=\"margin-left: 6px\"\r\n @update:value=\"handleResizableChange\"\r\n />\r\n </div>\r\n </div>\r\n\r\n <!-- 顶部操作栏:统计信息和快捷操作按钮 -->\r\n <div class=\"top-actions-bar\">\r\n <div class=\"stats-info\">\r\n <NText\r\n depth=\"3\"\r\n :style=\"{ fontSize: '13px' }\"\r\n >\r\n 已选 {{ visibleCount }} / 总共 {{ totalCount }} 列\r\n </NText>\r\n </div>\r\n <div class=\"quick-actions\">\r\n <NSpace :size=\"6\">\r\n <NButton\r\n size=\"tiny\"\r\n @click=\"selectAll\"\r\n >\r\n 全选\r\n </NButton>\r\n <NButton\r\n size=\"tiny\"\r\n @click=\"selectNone\"\r\n >\r\n 全不选\r\n </NButton>\r\n <NButton\r\n size=\"tiny\"\r\n @click=\"resetColumns\"\r\n >\r\n 重置\r\n </NButton>\r\n </NSpace>\r\n </div>\r\n </div>\r\n\r\n <!-- 列列表 -->\r\n <div\r\n ref=\"listRef\"\r\n class=\"column-list\"\r\n >\r\n <div\r\n v-for=\"(column, index) in filteredColumns\"\r\n :key=\"column.key\"\r\n class=\"column-item\"\r\n :class=\"{\r\n disabled: column.key === '_actions',\r\n 'fixed-left': column.fixed === 'left',\r\n 'fixed-right': column.fixed === 'right',\r\n }\"\r\n :draggable=\"column.key !== '_actions'\"\r\n @dragstart=\"handleDragStart(index, $event)\"\r\n @dragover=\"handleDragOver(index, $event)\"\r\n @dragend=\"handleDragEnd\"\r\n @drop=\"handleDrop(index)\"\r\n >\r\n <div class=\"column-info\">\r\n <div class=\"column-controls\">\r\n <C_Icon\r\n name=\"mdi:drag\"\r\n size=\"16\"\r\n class=\"drag-handle\"\r\n :class=\"{ disabled: column.key === '_actions' }\"\r\n />\r\n <NCheckbox\r\n :checked=\"column.visible !== false\"\r\n :disabled=\"column.key === '_actions'\"\r\n @update:checked=\"\r\n (value: boolean) => toggleColumnVisibility(index, value)\r\n \"\r\n />\r\n </div>\r\n <div class=\"column-details\">\r\n <NSpace\r\n align=\"center\"\r\n :size=\"6\"\r\n >\r\n <NText\r\n strong\r\n :style=\"{ fontSize: '13px' }\"\r\n >\r\n {{ column.title || column.key }}\r\n </NText>\r\n <NTag\r\n v-if=\"column.fixed === 'left'\"\r\n size=\"tiny\"\r\n type=\"info\"\r\n >\r\n 左固定\r\n </NTag>\r\n <NTag\r\n v-if=\"column.fixed === 'right'\"\r\n size=\"tiny\"\r\n type=\"warning\"\r\n >\r\n 右固定\r\n </NTag>\r\n </NSpace>\r\n <NText\r\n depth=\"3\"\r\n :style=\"{ fontSize: '11px' }\"\r\n >\r\n {{ column.key }}\r\n </NText>\r\n </div>\r\n </div>\r\n <div class=\"column-actions\">\r\n <div class=\"drag-controls\">\r\n <C_Icon\r\n name=\"mdi:chevron-up\"\r\n size=\"14\"\r\n title=\"上移\"\r\n :clickable=\"index !== 0\"\r\n :class=\"{ disabled: index === 0 }\"\r\n @click=\"index !== 0 && moveColumn(index, index - 1)\"\r\n />\r\n <C_Icon\r\n name=\"mdi:chevron-down\"\r\n size=\"14\"\r\n title=\"下移\"\r\n :clickable=\"index !== filteredColumns.length - 1\"\r\n :class=\"{ disabled: index === filteredColumns.length - 1 }\"\r\n @click=\"\r\n index !== filteredColumns.length - 1 &&\r\n moveColumn(index, index + 1)\r\n \"\r\n />\r\n </div>\r\n <NDropdown\r\n :options=\"getFixedOptions(column)\"\r\n @select=\"(value: string) => handleFixedSelect(index, value)\"\r\n >\r\n <C_Icon\r\n name=\"mdi:pin\"\r\n size=\"14\"\r\n title=\"固定列\"\r\n clickable\r\n :style=\"{\r\n color: column.fixed ? 'var(--n-primary-color)' : undefined,\r\n }\"\r\n />\r\n </NDropdown>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\n import { ref, computed, onMounted } from 'vue'\r\n import {\r\n NInput,\r\n NText,\r\n NSwitch,\r\n NSpace,\r\n NButton,\r\n NCheckbox,\r\n NTag,\r\n NDropdown,\r\n } from 'naive-ui/es'\r\n import C_Icon from '../../../C_Icon/index.vue'\r\n import type { TableColumn } from '../../types'\r\n import { getFixedOptions, isSpecialColumn } from './data'\r\n\r\n defineOptions({ name: 'ColumnSettings' })\r\n\r\n /* ================= Props & Emits ================= */\r\n\r\n const props = defineProps<{ columns: TableColumn[] }>()\r\n const emit = defineEmits<{ (e: 'change', columns: TableColumn[]): void }>()\r\n\r\n /* ================= 响应式状态 ================= */\r\n\r\n const localColumns = ref<TableColumn[]>([...props.columns])\r\n const searchText = ref('')\r\n const listRef = ref<HTMLElement>()\r\n const draggedIndex = ref<number | null>(null)\r\n const dragOverIndex = ref<number | null>(null)\r\n const enableResizable = ref(false)\r\n\r\n onMounted(() => {\r\n enableResizable.value = localColumns.value.some(\r\n col => col.resizable === true\r\n )\r\n })\r\n\r\n /* ================= 计算属性 ================= */\r\n\r\n const filteredColumns = computed(() => {\r\n if (!searchText.value) return localColumns.value\r\n const search = searchText.value.toLowerCase()\r\n return localColumns.value.filter(\r\n col =>\r\n col.title?.toLowerCase().includes(search) ||\r\n col.key?.toLowerCase().includes(search)\r\n )\r\n })\r\n\r\n const visibleCount = computed(\r\n () => localColumns.value.filter(col => col.visible !== false).length\r\n )\r\n const totalCount = computed(() => localColumns.value.length)\r\n\r\n /* ================= 批量操作 ================= */\r\n\r\n const setAllColumnsVisible = (visible: boolean) => {\r\n localColumns.value.forEach(col => {\r\n if (col.key !== '_actions') col.visible = visible\r\n })\r\n applyChanges()\r\n }\r\n\r\n const selectAll = () => setAllColumnsVisible(true)\r\n const selectNone = () => setAllColumnsVisible(false)\r\n\r\n /* ================= 列属性更新 ================= */\r\n\r\n const findOriginalIndex = (filteredIndex: number): number => {\r\n const column = filteredColumns.value[filteredIndex]\r\n return localColumns.value.findIndex(col => col.key === column.key)\r\n }\r\n\r\n const updateColumnProperty = (\r\n index: number,\r\n updater: (col: TableColumn) => void\r\n ) => {\r\n const originalIndex = findOriginalIndex(index)\r\n if (originalIndex !== -1) {\r\n updater(localColumns.value[originalIndex])\r\n applyChanges()\r\n }\r\n }\r\n\r\n const toggleColumnVisibility = (index: number, visible: boolean) => {\r\n updateColumnProperty(index, col => {\r\n col.visible = visible\r\n })\r\n }\r\n\r\n const handleFixedSelect = (index: number, value: string) => {\r\n updateColumnProperty(index, col => {\r\n col.fixed = value === 'none' ? undefined : (value as 'left' | 'right')\r\n })\r\n }\r\n\r\n const applyChanges = () => emit('change', [...localColumns.value])\r\n\r\n const resetColumns = () => {\r\n localColumns.value = [...props.columns]\r\n applyChanges()\r\n }\r\n\r\n /* ================= 列宽调整 ================= */\r\n\r\n const handleResizableChange = (value: boolean) => {\r\n localColumns.value.forEach(col => {\r\n if (!isSpecialColumn(col)) {\r\n col.resizable = value\r\n if (value) {\r\n col.minWidth = col.minWidth || 80\r\n col.maxWidth = col.maxWidth || 500\r\n }\r\n }\r\n })\r\n applyChanges()\r\n }\r\n\r\n /* ================= 拖拽排序 ================= */\r\n\r\n const moveColumn = (fromIndex: number, toIndex: number) => {\r\n const originalFromIndex = findOriginalIndex(fromIndex)\r\n const originalToIndex = findOriginalIndex(toIndex)\r\n if (originalFromIndex !== -1 && originalToIndex !== -1) {\r\n const [movedColumn] = localColumns.value.splice(originalFromIndex, 1)\r\n localColumns.value.splice(originalToIndex, 0, movedColumn)\r\n applyChanges()\r\n }\r\n }\r\n\r\n const handleDragStart = (index: number, event: DragEvent) => {\r\n const column = filteredColumns.value[index]\r\n if (column.key === '_actions') {\r\n event.preventDefault()\r\n return\r\n }\r\n draggedIndex.value = index\r\n if (event.dataTransfer) event.dataTransfer.effectAllowed = 'move'\r\n }\r\n\r\n const handleDragOver = (index: number, event: DragEvent) => {\r\n event.preventDefault()\r\n if (\r\n draggedIndex.value === null ||\r\n filteredColumns.value[index].key === '_actions'\r\n )\r\n return\r\n dragOverIndex.value = index\r\n if (event.dataTransfer) event.dataTransfer.dropEffect = 'move'\r\n }\r\n\r\n const handleDrop = (toIndex: number) => {\r\n if (\r\n draggedIndex.value === null ||\r\n filteredColumns.value[toIndex].key === '_actions'\r\n )\r\n return\r\n moveColumn(draggedIndex.value, toIndex)\r\n draggedIndex.value = null\r\n dragOverIndex.value = null\r\n }\r\n\r\n const handleDragEnd = () => {\r\n draggedIndex.value = null\r\n dragOverIndex.value = null\r\n }\r\n</script>\r\n\r\n<style scoped lang=\"scss\">\r\n @use './index.scss';\r\n</style>\r\n","/* unplugin-vue-components disabled */<!--\r\n * @Description: C_Table 超级表格组件(薄 UI 壳)\r\n * @Migration: naive-ui-components 组件库迁移版本\r\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\r\n *\r\n * 使用侧 API:\r\n * <C_Table :columns=\"cols\" :data=\"data\" :loading=\"loading\" :config=\"tableConfig\" />\r\n *\r\n * config 收拢了所有功能配置:edit / selection / expand / pagination / dynamicRows / toolbar / display\r\n * 列处理 → composables/useTableColumns.ts\r\n * 配置解析 → composables/useTableConfig.ts\r\n * 编辑/展开/动态行 → composables/useTableManager.ts\r\n -->\r\n\r\n<template>\r\n <div\r\n ref=\"tableWrapperRef\"\r\n class=\"c-table-wrapper\"\r\n >\r\n <!-- 动态行工具栏 -->\r\n <component\r\n v-if=\"tableManager.dynamicRowsState\"\r\n :is=\"tableManager.dynamicRowsState.renderToolbar()\"\r\n />\r\n\r\n <!-- 表格工具栏 -->\r\n <div\r\n v-if=\"resolved.showToolbar\"\r\n class=\"table-toolbar\"\r\n >\r\n <div class=\"toolbar-left\">\r\n <slot name=\"toolbar-left\" />\r\n </div>\r\n <div class=\"toolbar-right\">\r\n <slot name=\"toolbar-right\" />\r\n <C_Icon\r\n v-if=\"resolved.enableColumnSettings\"\r\n name=\"mdi:cog\"\r\n size=\"18\"\r\n title=\"表格设置\"\r\n clickable\r\n class=\"column-settings-btn\"\r\n @click=\"showSettingsPanel = true\"\r\n />\r\n </div>\r\n </div>\r\n\r\n <!-- 表格主体 -->\r\n <NDataTable\r\n ref=\"tableRef\"\r\n v-bind=\"{ ...computedTableProps, ...$attrs }\"\r\n :columns=\"computedColumns\"\r\n :data=\"normalizedData\"\r\n :loading=\"normalizedLoading\"\r\n :row-key=\"rowKey\"\r\n :expanded-row-keys=\"tableManager.expandedKeys.value\"\r\n :checked-row-keys=\"tableManager.checkedKeys.value\"\r\n @update:expanded-row-keys=\"tableManager.expandState?.handleExpandChange\"\r\n @update:checked-row-keys=\"tableManager.expandState?.handleSelectionChange\"\r\n :scroll-x=\"computedScrollX\"\r\n :virtual-scroll=\"resolved.virtualScroll\"\r\n :virtual-scroll-item-size=\"resolved.virtualScroll ? resolved.virtualItemHeight : undefined\"\r\n :max-height=\"resolved.virtualScroll ? resolved.virtualMaxHeight : resolved.maxHeight\"\r\n :summary=\"summaryFn\"\r\n :summary-placement=\"resolved.summaryPosition\"\r\n style=\"width: 100%\"\r\n />\r\n\r\n <!-- 分页 -->\r\n <NPagination\r\n v-if=\"pagination.paginationConfig.value\"\r\n v-bind=\"pagination.paginationConfig.value\"\r\n class=\"pagination-wrapper\"\r\n />\r\n\r\n <!-- 编辑弹窗 -->\r\n <NModal\r\n v-if=\"resolved.editMode === 'modal' || resolved.editMode === 'both'\"\r\n v-model:show=\"modalVisible\"\r\n :title=\"resolved.modalTitle\"\r\n :width=\"resolved.modalWidth\"\r\n preset=\"card\"\r\n :mask-closable=\"false\"\r\n :close-on-esc=\"false\"\r\n class=\"w60%\"\r\n :closable=\"false\"\r\n >\r\n <C_Form\r\n v-if=\"modalVisible && formOptions.length\"\r\n ref=\"editFormRef\"\r\n :key=\"formKey\"\r\n v-model=\"localEditingData\"\r\n :options=\"formOptions\"\r\n :config=\"{\r\n layout: 'grid',\r\n grid: { cols: 2, xGap: 16, yGap: 16 },\r\n showActions: false,\r\n }\"\r\n />\r\n <template #action>\r\n <NSpace justify=\"end\">\r\n <NButton @click=\"handleModalCancel\">取消</NButton>\r\n <NButton\r\n type=\"primary\"\r\n :loading=\"modalSubmitLoading\"\r\n @click=\"handleModalSave\"\r\n >\r\n 保存\r\n </NButton>\r\n </NSpace>\r\n </template>\r\n </NModal>\r\n\r\n <!-- 动态行确认删除弹窗 -->\r\n <component\r\n v-if=\"tableManager.dynamicRowsState\"\r\n :is=\"tableManager.dynamicRowsState.renderConfirmModal()\"\r\n />\r\n\r\n <!-- 列设置抽屉 -->\r\n <NDrawer\r\n v-model:show=\"showSettingsPanel\"\r\n :width=\"420\"\r\n placement=\"right\"\r\n :mask-closable=\"true\"\r\n >\r\n <NDrawerContent\r\n title=\"列设置\"\r\n closable\r\n >\r\n <ColumnSettings\r\n :columns=\"reactiveColumns\"\r\n @change=\"onColumnSettingsChange\"\r\n />\r\n </NDrawerContent>\r\n </NDrawer>\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\n import {\r\n ref,\r\n computed,\r\n watch,\r\n onMounted,\r\n onBeforeUnmount,\r\n type ComponentPublicInstance,\r\n } from 'vue'\r\n import {\r\n type DataTableRowKey,\r\n NDataTable,\r\n NPagination,\r\n NModal,\r\n NSpace,\r\n NButton,\r\n NDrawer,\r\n NDrawerContent,\r\n } from 'naive-ui/es'\r\n import type {\r\n TableColumn,\r\n TableEmits,\r\n DataRecord,\r\n MaybeRefLike,\r\n } from './types'\r\n import {\r\n resolveConfig,\r\n createEditModeChecker,\r\n type TableConfig,\r\n type CrudBinding,\r\n } from './composables/useTableConfig'\r\n import { useTableManager } from './composables/useTableManager'\r\n import { usePagination } from './composables/usePagination'\r\n import { useTableActions } from './composables/useTableActions'\r\n import { useTableColumns } from './composables/useTableColumns'\r\n import { generateFormOptions } from './data'\r\n import ColumnSettings from './components/ColumnSettings/index.vue'\r\n import C_Icon from '../C_Icon/index.vue'\r\n import C_Form from '../C_Form/index.vue'\r\n\r\n defineOptions({ name: 'C_Table', inheritAttrs: false })\r\n\r\n /* ================= Props(极简 API) ================= */\r\n\r\n const props = withDefaults(\r\n defineProps<{\r\n /** 列配置(crud 模式下可省略) */\r\n columns?: TableColumn[]\r\n /** 数据源(crud 模式下可省略) */\r\n data?: MaybeRefLike<DataRecord[]>\r\n /** 加载状态 */\r\n loading?: MaybeRefLike<boolean>\r\n /** 行唯一键 */\r\n rowKey?: (row: DataRecord) => DataTableRowKey\r\n /** 统一功能配置(edit / selection / expand / pagination / dynamicRows / toolbar / display) */\r\n config?: TableConfig\r\n /** CRUD 绑定 — 传入 useTableCrud() 的返回值,自动接管 data/columns/loading/actions/pagination/events */\r\n crud?: CrudBinding\r\n }>(),\r\n {\r\n rowKey: (row: DataRecord) => row.id as DataTableRowKey,\r\n config: () => ({}),\r\n }\r\n )\r\n\r\n const emit = defineEmits<\r\n TableEmits & {\r\n 'row-add': [newRow: DataRecord]\r\n 'row-delete': [deletedRow: DataRecord, index: number]\r\n 'row-copy': [originalRow: DataRecord, newRow: DataRecord]\r\n 'row-move': [row: DataRecord, fromIndex: number, toIndex: number]\r\n 'row-selection-change': [\r\n selectedKey: DataTableRowKey | null,\r\n selectedRow: DataRecord | null,\r\n ]\r\n 'pagination-change': [page: number, pageSize: number]\r\n 'view-detail': [data: DataRecord]\r\n 'column-change': [columns: TableColumn[]]\r\n }\r\n >()\r\n\r\n /* ================= CRUD 桥接 ================= */\r\n\r\n /** 包装 emit:在触发事件的同时自动调用 crud 对应的方法 */\r\n const bridgedEmit: typeof emit = (event: any, ...args: any[]) => {\r\n ;(emit as any)(event, ...args)\r\n\r\n if (!props.crud) return\r\n const handlers: Record<string, ((...a: any[]) => void) | undefined> = {\r\n save: props.crud.save,\r\n cancel: props.crud.handleCancel,\r\n 'pagination-change': props.crud.handlePaginationChange,\r\n 'row-delete': props.crud.handleRowDelete,\r\n 'view-detail': props.crud.detail?.show,\r\n }\r\n handlers[event]?.(...args)\r\n }\r\n\r\n /* ================= 有效值(crud 优先 → props 覆盖) ================= */\r\n\r\n /** 合并 crud 返回的 actions/pagination 到用户 config */\r\n const effectiveConfig = computed<TableConfig>(() => {\r\n if (!props.crud) return props.config || {}\r\n const fromCrud: Partial<TableConfig> = {}\r\n if (props.crud.actions) fromCrud.actions = props.crud.actions.value\r\n if (props.crud.pagination)\r\n fromCrud.pagination = props.crud.pagination.value ?? undefined\r\n return { ...fromCrud, ...props.config }\r\n })\r\n\r\n const effectiveColumns = computed<TableColumn[]>(\r\n () => props.columns ?? props.crud?.columns.value ?? []\r\n )\r\n\r\n /* ================= 配置解析 ================= */\r\n\r\n const resolved = computed(() => resolveConfig(effectiveConfig.value))\r\n const editModeChecker = computed(() => createEditModeChecker(resolved.value))\r\n\r\n /* ================= 数据规范化(兼容跨实例 Ref) ================= */\r\n\r\n const unwrapRef = <T,>(val: MaybeRefLike<T> | undefined): T | undefined =>\r\n val && typeof val === 'object' && 'value' in val ? val.value : (val as T)\r\n\r\n const normalizedData = computed<DataRecord[]>(\r\n () => unwrapRef(props.data) ?? props.crud?.data.value ?? []\r\n )\r\n\r\n const normalizedLoading = computed<boolean>(\r\n () => unwrapRef(props.loading) ?? props.crud?.loading.value ?? false\r\n )\r\n\r\n /* ================= Hooks ================= */\r\n\r\n const pagination = usePagination({\r\n data: normalizedData,\r\n config: computed(() => resolved.value.pagination),\r\n emit: bridgedEmit,\r\n })\r\n\r\n const tableManager = useTableManager({\r\n config: resolved.value,\r\n data: () => normalizedData.value,\r\n rowKey: props.rowKey,\r\n emit: bridgedEmit,\r\n })\r\n\r\n const tableActions = useTableActions({\r\n actions: computed(() => effectiveConfig.value.actions || {}),\r\n config: resolved,\r\n tableManager,\r\n rowKey: props.rowKey,\r\n emit: bridgedEmit,\r\n onViewDetail: (data: DataRecord) => bridgedEmit('view-detail', data),\r\n })\r\n\r\n const columnState = useTableColumns({\r\n rawColumns: effectiveColumns,\r\n config: resolved,\r\n columnWidth: resolved.value.columnWidth,\r\n scrollX: resolved.value.scrollX,\r\n rowKey: props.rowKey,\r\n tableManager,\r\n actionsRenderer: tableActions.renderActions,\r\n editModeChecker,\r\n })\r\n\r\n const {\r\n showSettingsPanel,\r\n reactiveColumns,\r\n computedColumns,\r\n computedScrollX,\r\n } = columnState\r\n\r\n /* ================= 合计行 ================= */\r\n\r\n const summaryFn = computed(() => {\r\n const cfg = resolved.value\r\n if (!cfg.summaryRender) return undefined\r\n return (data: DataRecord[]) => {\r\n const result = cfg.summaryRender!(data)\r\n return computedColumns.value.map((col: any) => {\r\n const key = col.key as string\r\n const def = result[key]\r\n return def ? { value: def.value, colSpan: def.colSpan ?? 1 } : { value: '' }\r\n })\r\n }\r\n })\r\n\r\n /* ================= 列拖拽排序 ================= */\r\n\r\n const tableWrapperRef = ref<HTMLElement>()\r\n let sortableInstance: any = null\r\n\r\n const initColumnDrag = async () => {\r\n if (!resolved.value.enableColumnDrag || !tableWrapperRef.value) return\r\n try {\r\n const { default: Sortable } = await import('sortablejs')\r\n const headerRow = tableWrapperRef.value.querySelector(\r\n '.n-data-table-thead tr'\r\n )\r\n if (!headerRow) return\r\n sortableInstance = new Sortable(headerRow as HTMLElement, {\r\n animation: resolved.value.columnDragAnimationDuration,\r\n handle: `.${resolved.value.columnDragHandleClass}`,\r\n ghostClass: 'column-drag-ghost',\r\n onEnd: (evt: any) => {\r\n if (evt.oldIndex == null || evt.newIndex == null) return\r\n const cols = [...reactiveColumns.value]\r\n const [moved] = cols.splice(evt.oldIndex, 1)\r\n cols.splice(evt.newIndex, 0, moved)\r\n columnState.handleColumnChange(cols)\r\n emit('column-change', cols)\r\n },\r\n })\r\n } catch {\r\n // sortablejs 未安装时静默降级\r\n }\r\n }\r\n\r\n /* ================= 表格属性 ================= */\r\n\r\n const tableRef = ref<ComponentPublicInstance>()\r\n\r\n const computedTableProps = computed(() => ({\r\n striped: resolved.value.striped,\r\n bordered: resolved.value.bordered,\r\n singleLine: resolved.value.singleLine,\r\n size: resolved.value.size,\r\n maxHeight: resolved.value.maxHeight,\r\n }))\r\n\r\n /* ================= 编辑弹窗 ================= */\r\n\r\n const editFormRef = ref<any>()\r\n const modalSubmitLoading = ref(false)\r\n const localEditingData = ref<DataRecord>({})\r\n\r\n const modalVisible = computed({\r\n get: () => tableManager.editStates.modalEdit.isModalVisible.value,\r\n set: (val: boolean) => {\r\n tableManager.editStates.modalEdit.isModalVisible.value = val\r\n },\r\n })\r\n\r\n const editableColumns = computed(() =>\r\n effectiveColumns.value.filter(\r\n (col): col is TableColumn => col.editable !== false\r\n )\r\n )\r\n\r\n const formKey = computed(\r\n () =>\r\n `edit-form-${tableManager.editStates.modalEdit.editingRowKey.value || 'new'}`\r\n )\r\n\r\n const formOptions = computed(() => generateFormOptions(editableColumns.value))\r\n\r\n watch(\r\n () => tableManager.editStates.modalEdit.editingData.value,\r\n newData => {\r\n if (newData && Object.keys(newData).length > 0) {\r\n localEditingData.value = JSON.parse(JSON.stringify(newData))\r\n }\r\n },\r\n { immediate: true, deep: true }\r\n )\r\n\r\n let modalCloseTimer: ReturnType<typeof setTimeout> | null = null\r\n\r\n watch(modalVisible, visible => {\r\n if (!visible) {\r\n modalCloseTimer = setTimeout(() => {\r\n localEditingData.value = {}\r\n modalCloseTimer = null\r\n }, 300)\r\n }\r\n })\r\n\r\n const handleModalSave = async () => {\r\n if (!editFormRef.value) return\r\n modalSubmitLoading.value = true\r\n try {\r\n await editFormRef.value.validate()\r\n await tableManager.editStates.modalEdit.saveEdit(localEditingData.value)\r\n } catch {\r\n /* 表单验证错误由组件内联显示 */\r\n } finally {\r\n modalSubmitLoading.value = false\r\n }\r\n }\r\n\r\n const handleModalCancel = () => {\r\n localEditingData.value = {}\r\n tableManager.editStates.modalEdit.cancelEdit()\r\n }\r\n\r\n /* ================= 列设置 ================= */\r\n\r\n const onColumnSettingsChange = (columns: TableColumn[]) => {\r\n columnState.handleColumnChange(columns)\r\n emit('column-change', columnState.reactiveColumns.value)\r\n }\r\n\r\n /* ================= Expose ================= */\r\n\r\n const { edit, expand, selection, dynamicRows } = tableManager.stateManager\r\n\r\n const exposedApi = {\r\n startEdit: edit.start,\r\n expandAll: expand.all,\r\n collapseAll: expand.collapseAll,\r\n selectAll: selection.all,\r\n clearSelection: selection.clear,\r\n clearAllSelections: tableManager.stateManager.clearAllSelections,\r\n clearRowSelection: dynamicRows?.clearSelection,\r\n resetToFirstPage: pagination.resetToFirstPage,\r\n getSelectedRows: selection.getSelected,\r\n getEditingData: edit.getEditingData,\r\n isEditing: edit.isEditing,\r\n isExpanded: expand.isExpanded,\r\n getManager: () => tableManager.stateManager,\r\n }\r\n\r\n defineExpose(exposedApi)\r\n\r\n /* 自动连接 crud.tableRef */\r\n onMounted(() => {\r\n const crudRef = props.crud?.tableRef\r\n if (crudRef) crudRef.value = exposedApi\r\n // 列拖拽初始化\r\n initColumnDrag()\r\n })\r\n\r\n onBeforeUnmount(() => {\r\n if (modalCloseTimer) clearTimeout(modalCloseTimer)\r\n if (sortableInstance) sortableInstance.destroy()\r\n })\r\n</script>\r\n\r\n<style scoped lang=\"scss\">\r\n @use './index.scss';\r\n</style>\r\n","/* unplugin-vue-components disabled */<!--\r\n * @Description: C_Table 超级表格组件(薄 UI 壳)\r\n * @Migration: naive-ui-components 组件库迁移版本\r\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\r\n *\r\n * 使用侧 API:\r\n * <C_Table :columns=\"cols\" :data=\"data\" :loading=\"loading\" :config=\"tableConfig\" />\r\n *\r\n * config 收拢了所有功能配置:edit / selection / expand / pagination / dynamicRows / toolbar / display\r\n * 列处理 → composables/useTableColumns.ts\r\n * 配置解析 → composables/useTableConfig.ts\r\n * 编辑/展开/动态行 → composables/useTableManager.ts\r\n -->\r\n\r\n<template>\r\n <div\r\n ref=\"tableWrapperRef\"\r\n class=\"c-table-wrapper\"\r\n >\r\n <!-- 动态行工具栏 -->\r\n <component\r\n v-if=\"tableManager.dynamicRowsState\"\r\n :is=\"tableManager.dynamicRowsState.renderToolbar()\"\r\n />\r\n\r\n <!-- 表格工具栏 -->\r\n <div\r\n v-if=\"resolved.showToolbar\"\r\n class=\"table-toolbar\"\r\n >\r\n <div class=\"toolbar-left\">\r\n <slot name=\"toolbar-left\" />\r\n </div>\r\n <div class=\"toolbar-right\">\r\n <slot name=\"toolbar-right\" />\r\n <C_Icon\r\n v-if=\"resolved.enableColumnSettings\"\r\n name=\"mdi:cog\"\r\n size=\"18\"\r\n title=\"表格设置\"\r\n clickable\r\n class=\"column-settings-btn\"\r\n @click=\"showSettingsPanel = true\"\r\n />\r\n </div>\r\n </div>\r\n\r\n <!-- 表格主体 -->\r\n <NDataTable\r\n ref=\"tableRef\"\r\n v-bind=\"{ ...computedTableProps, ...$attrs }\"\r\n :columns=\"computedColumns\"\r\n :data=\"normalizedData\"\r\n :loading=\"normalizedLoading\"\r\n :row-key=\"rowKey\"\r\n :expanded-row-keys=\"tableManager.expandedKeys.value\"\r\n :checked-row-keys=\"tableManager.checkedKeys.value\"\r\n @update:expanded-row-keys=\"tableManager.expandState?.handleExpandChange\"\r\n @update:checked-row-keys=\"tableManager.expandState?.handleSelectionChange\"\r\n :scroll-x=\"computedScrollX\"\r\n :virtual-scroll=\"resolved.virtualScroll\"\r\n :virtual-scroll-item-size=\"resolved.virtualScroll ? resolved.virtualItemHeight : undefined\"\r\n :max-height=\"resolved.virtualScroll ? resolved.virtualMaxHeight : resolved.maxHeight\"\r\n :summary=\"summaryFn\"\r\n :summary-placement=\"resolved.summaryPosition\"\r\n style=\"width: 100%\"\r\n />\r\n\r\n <!-- 分页 -->\r\n <NPagination\r\n v-if=\"pagination.paginationConfig.value\"\r\n v-bind=\"pagination.paginationConfig.value\"\r\n class=\"pagination-wrapper\"\r\n />\r\n\r\n <!-- 编辑弹窗 -->\r\n <NModal\r\n v-if=\"resolved.editMode === 'modal' || resolved.editMode === 'both'\"\r\n v-model:show=\"modalVisible\"\r\n :title=\"resolved.modalTitle\"\r\n :width=\"resolved.modalWidth\"\r\n preset=\"card\"\r\n :mask-closable=\"false\"\r\n :close-on-esc=\"false\"\r\n class=\"w60%\"\r\n :closable=\"false\"\r\n >\r\n <C_Form\r\n v-if=\"modalVisible && formOptions.length\"\r\n ref=\"editFormRef\"\r\n :key=\"formKey\"\r\n v-model=\"localEditingData\"\r\n :options=\"formOptions\"\r\n :config=\"{\r\n layout: 'grid',\r\n grid: { cols: 2, xGap: 16, yGap: 16 },\r\n showActions: false,\r\n }\"\r\n />\r\n <template #action>\r\n <NSpace justify=\"end\">\r\n <NButton @click=\"handleModalCancel\">取消</NButton>\r\n <NButton\r\n type=\"primary\"\r\n :loading=\"modalSubmitLoading\"\r\n @click=\"handleModalSave\"\r\n >\r\n 保存\r\n </NButton>\r\n </NSpace>\r\n </template>\r\n </NModal>\r\n\r\n <!-- 动态行确认删除弹窗 -->\r\n <component\r\n v-if=\"tableManager.dynamicRowsState\"\r\n :is=\"tableManager.dynamicRowsState.renderConfirmModal()\"\r\n />\r\n\r\n <!-- 列设置抽屉 -->\r\n <NDrawer\r\n v-model:show=\"showSettingsPanel\"\r\n :width=\"420\"\r\n placement=\"right\"\r\n :mask-closable=\"true\"\r\n >\r\n <NDrawerContent\r\n title=\"列设置\"\r\n closable\r\n >\r\n <ColumnSettings\r\n :columns=\"reactiveColumns\"\r\n @change=\"onColumnSettingsChange\"\r\n />\r\n </NDrawerContent>\r\n </NDrawer>\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\n import {\r\n ref,\r\n computed,\r\n watch,\r\n onMounted,\r\n onBeforeUnmount,\r\n type ComponentPublicInstance,\r\n } from 'vue'\r\n import {\r\n type DataTableRowKey,\r\n NDataTable,\r\n NPagination,\r\n NModal,\r\n NSpace,\r\n NButton,\r\n NDrawer,\r\n NDrawerContent,\r\n } from 'naive-ui/es'\r\n import type {\r\n TableColumn,\r\n TableEmits,\r\n DataRecord,\r\n MaybeRefLike,\r\n } from './types'\r\n import {\r\n resolveConfig,\r\n createEditModeChecker,\r\n type TableConfig,\r\n type CrudBinding,\r\n } from './composables/useTableConfig'\r\n import { useTableManager } from './composables/useTableManager'\r\n import { usePagination } from './composables/usePagination'\r\n import { useTableActions } from './composables/useTableActions'\r\n import { useTableColumns } from './composables/useTableColumns'\r\n import { generateFormOptions } from './data'\r\n import ColumnSettings from './components/ColumnSettings/index.vue'\r\n import C_Icon from '../C_Icon/index.vue'\r\n import C_Form from '../C_Form/index.vue'\r\n\r\n defineOptions({ name: 'C_Table', inheritAttrs: false })\r\n\r\n /* ================= Props(极简 API) ================= */\r\n\r\n const props = withDefaults(\r\n defineProps<{\r\n /** 列配置(crud 模式下可省略) */\r\n columns?: TableColumn[]\r\n /** 数据源(crud 模式下可省略) */\r\n data?: MaybeRefLike<DataRecord[]>\r\n /** 加载状态 */\r\n loading?: MaybeRefLike<boolean>\r\n /** 行唯一键 */\r\n rowKey?: (row: DataRecord) => DataTableRowKey\r\n /** 统一功能配置(edit / selection / expand / pagination / dynamicRows / toolbar / display) */\r\n config?: TableConfig\r\n /** CRUD 绑定 — 传入 useTableCrud() 的返回值,自动接管 data/columns/loading/actions/pagination/events */\r\n crud?: CrudBinding\r\n }>(),\r\n {\r\n rowKey: (row: DataRecord) => row.id as DataTableRowKey,\r\n config: () => ({}),\r\n }\r\n )\r\n\r\n const emit = defineEmits<\r\n TableEmits & {\r\n 'row-add': [newRow: DataRecord]\r\n 'row-delete': [deletedRow: DataRecord, index: number]\r\n 'row-copy': [originalRow: DataRecord, newRow: DataRecord]\r\n 'row-move': [row: DataRecord, fromIndex: number, toIndex: number]\r\n 'row-selection-change': [\r\n selectedKey: DataTableRowKey | null,\r\n selectedRow: DataRecord | null,\r\n ]\r\n 'pagination-change': [page: number, pageSize: number]\r\n 'view-detail': [data: DataRecord]\r\n 'column-change': [columns: TableColumn[]]\r\n }\r\n >()\r\n\r\n /* ================= CRUD 桥接 ================= */\r\n\r\n /** 包装 emit:在触发事件的同时自动调用 crud 对应的方法 */\r\n const bridgedEmit: typeof emit = (event: any, ...args: any[]) => {\r\n ;(emit as any)(event, ...args)\r\n\r\n if (!props.crud) return\r\n const handlers: Record<string, ((...a: any[]) => void) | undefined> = {\r\n save: props.crud.save,\r\n cancel: props.crud.handleCancel,\r\n 'pagination-change': props.crud.handlePaginationChange,\r\n 'row-delete': props.crud.handleRowDelete,\r\n 'view-detail': props.crud.detail?.show,\r\n }\r\n handlers[event]?.(...args)\r\n }\r\n\r\n /* ================= 有效值(crud 优先 → props 覆盖) ================= */\r\n\r\n /** 合并 crud 返回的 actions/pagination 到用户 config */\r\n const effectiveConfig = computed<TableConfig>(() => {\r\n if (!props.crud) return props.config || {}\r\n const fromCrud: Partial<TableConfig> = {}\r\n if (props.crud.actions) fromCrud.actions = props.crud.actions.value\r\n if (props.crud.pagination)\r\n fromCrud.pagination = props.crud.pagination.value ?? undefined\r\n return { ...fromCrud, ...props.config }\r\n })\r\n\r\n const effectiveColumns = computed<TableColumn[]>(\r\n () => props.columns ?? props.crud?.columns.value ?? []\r\n )\r\n\r\n /* ================= 配置解析 ================= */\r\n\r\n const resolved = computed(() => resolveConfig(effectiveConfig.value))\r\n const editModeChecker = computed(() => createEditModeChecker(resolved.value))\r\n\r\n /* ================= 数据规范化(兼容跨实例 Ref) ================= */\r\n\r\n const unwrapRef = <T,>(val: MaybeRefLike<T> | undefined): T | undefined =>\r\n val && typeof val === 'object' && 'value' in val ? val.value : (val as T)\r\n\r\n const normalizedData = computed<DataRecord[]>(\r\n () => unwrapRef(props.data) ?? props.crud?.data.value ?? []\r\n )\r\n\r\n const normalizedLoading = computed<boolean>(\r\n () => unwrapRef(props.loading) ?? props.crud?.loading.value ?? false\r\n )\r\n\r\n /* ================= Hooks ================= */\r\n\r\n const pagination = usePagination({\r\n data: normalizedData,\r\n config: computed(() => resolved.value.pagination),\r\n emit: bridgedEmit,\r\n })\r\n\r\n const tableManager = useTableManager({\r\n config: resolved.value,\r\n data: () => normalizedData.value,\r\n rowKey: props.rowKey,\r\n emit: bridgedEmit,\r\n })\r\n\r\n const tableActions = useTableActions({\r\n actions: computed(() => effectiveConfig.value.actions || {}),\r\n config: resolved,\r\n tableManager,\r\n rowKey: props.rowKey,\r\n emit: bridgedEmit,\r\n onViewDetail: (data: DataRecord) => bridgedEmit('view-detail', data),\r\n })\r\n\r\n const columnState = useTableColumns({\r\n rawColumns: effectiveColumns,\r\n config: resolved,\r\n columnWidth: resolved.value.columnWidth,\r\n scrollX: resolved.value.scrollX,\r\n rowKey: props.rowKey,\r\n tableManager,\r\n actionsRenderer: tableActions.renderActions,\r\n editModeChecker,\r\n })\r\n\r\n const {\r\n showSettingsPanel,\r\n reactiveColumns,\r\n computedColumns,\r\n computedScrollX,\r\n } = columnState\r\n\r\n /* ================= 合计行 ================= */\r\n\r\n const summaryFn = computed(() => {\r\n const cfg = resolved.value\r\n if (!cfg.summaryRender) return undefined\r\n return (data: DataRecord[]) => {\r\n const result = cfg.summaryRender!(data)\r\n return computedColumns.value.map((col: any) => {\r\n const key = col.key as string\r\n const def = result[key]\r\n return def ? { value: def.value, colSpan: def.colSpan ?? 1 } : { value: '' }\r\n })\r\n }\r\n })\r\n\r\n /* ================= 列拖拽排序 ================= */\r\n\r\n const tableWrapperRef = ref<HTMLElement>()\r\n let sortableInstance: any = null\r\n\r\n const initColumnDrag = async () => {\r\n if (!resolved.value.enableColumnDrag || !tableWrapperRef.value) return\r\n try {\r\n const { default: Sortable } = await import('sortablejs')\r\n const headerRow = tableWrapperRef.value.querySelector(\r\n '.n-data-table-thead tr'\r\n )\r\n if (!headerRow) return\r\n sortableInstance = new Sortable(headerRow as HTMLElement, {\r\n animation: resolved.value.columnDragAnimationDuration,\r\n handle: `.${resolved.value.columnDragHandleClass}`,\r\n ghostClass: 'column-drag-ghost',\r\n onEnd: (evt: any) => {\r\n if (evt.oldIndex == null || evt.newIndex == null) return\r\n const cols = [...reactiveColumns.value]\r\n const [moved] = cols.splice(evt.oldIndex, 1)\r\n cols.splice(evt.newIndex, 0, moved)\r\n columnState.handleColumnChange(cols)\r\n emit('column-change', cols)\r\n },\r\n })\r\n } catch {\r\n // sortablejs 未安装时静默降级\r\n }\r\n }\r\n\r\n /* ================= 表格属性 ================= */\r\n\r\n const tableRef = ref<ComponentPublicInstance>()\r\n\r\n const computedTableProps = computed(() => ({\r\n striped: resolved.value.striped,\r\n bordered: resolved.value.bordered,\r\n singleLine: resolved.value.singleLine,\r\n size: resolved.value.size,\r\n maxHeight: resolved.value.maxHeight,\r\n }))\r\n\r\n /* ================= 编辑弹窗 ================= */\r\n\r\n const editFormRef = ref<any>()\r\n const modalSubmitLoading = ref(false)\r\n const localEditingData = ref<DataRecord>({})\r\n\r\n const modalVisible = computed({\r\n get: () => tableManager.editStates.modalEdit.isModalVisible.value,\r\n set: (val: boolean) => {\r\n tableManager.editStates.modalEdit.isModalVisible.value = val\r\n },\r\n })\r\n\r\n const editableColumns = computed(() =>\r\n effectiveColumns.value.filter(\r\n (col): col is TableColumn => col.editable !== false\r\n )\r\n )\r\n\r\n const formKey = computed(\r\n () =>\r\n `edit-form-${tableManager.editStates.modalEdit.editingRowKey.value || 'new'}`\r\n )\r\n\r\n const formOptions = computed(() => generateFormOptions(editableColumns.value))\r\n\r\n watch(\r\n () => tableManager.editStates.modalEdit.editingData.value,\r\n newData => {\r\n if (newData && Object.keys(newData).length > 0) {\r\n localEditingData.value = JSON.parse(JSON.stringify(newData))\r\n }\r\n },\r\n { immediate: true, deep: true }\r\n )\r\n\r\n let modalCloseTimer: ReturnType<typeof setTimeout> | null = null\r\n\r\n watch(modalVisible, visible => {\r\n if (!visible) {\r\n modalCloseTimer = setTimeout(() => {\r\n localEditingData.value = {}\r\n modalCloseTimer = null\r\n }, 300)\r\n }\r\n })\r\n\r\n const handleModalSave = async () => {\r\n if (!editFormRef.value) return\r\n modalSubmitLoading.value = true\r\n try {\r\n await editFormRef.value.validate()\r\n await tableManager.editStates.modalEdit.saveEdit(localEditingData.value)\r\n } catch {\r\n /* 表单验证错误由组件内联显示 */\r\n } finally {\r\n modalSubmitLoading.value = false\r\n }\r\n }\r\n\r\n const handleModalCancel = () => {\r\n localEditingData.value = {}\r\n tableManager.editStates.modalEdit.cancelEdit()\r\n }\r\n\r\n /* ================= 列设置 ================= */\r\n\r\n const onColumnSettingsChange = (columns: TableColumn[]) => {\r\n columnState.handleColumnChange(columns)\r\n emit('column-change', columnState.reactiveColumns.value)\r\n }\r\n\r\n /* ================= Expose ================= */\r\n\r\n const { edit, expand, selection, dynamicRows } = tableManager.stateManager\r\n\r\n const exposedApi = {\r\n startEdit: edit.start,\r\n expandAll: expand.all,\r\n collapseAll: expand.collapseAll,\r\n selectAll: selection.all,\r\n clearSelection: selection.clear,\r\n clearAllSelections: tableManager.stateManager.clearAllSelections,\r\n clearRowSelection: dynamicRows?.clearSelection,\r\n resetToFirstPage: pagination.resetToFirstPage,\r\n getSelectedRows: selection.getSelected,\r\n getEditingData: edit.getEditingData,\r\n isEditing: edit.isEditing,\r\n isExpanded: expand.isExpanded,\r\n getManager: () => tableManager.stateManager,\r\n }\r\n\r\n defineExpose(exposedApi)\r\n\r\n /* 自动连接 crud.tableRef */\r\n onMounted(() => {\r\n const crudRef = props.crud?.tableRef\r\n if (crudRef) crudRef.value = exposedApi\r\n // 列拖拽初始化\r\n initColumnDrag()\r\n })\r\n\r\n onBeforeUnmount(() => {\r\n if (modalCloseTimer) clearTimeout(modalCloseTimer)\r\n if (sortableInstance) sortableInstance.destroy()\r\n })\r\n</script>\r\n\r\n<style scoped lang=\"scss\">\r\n @use './index.scss';\r\n</style>\r\n","<!--\r\n * @Description: C_Table 超级表格组件(薄 UI 壳)\r\n * @Migration: naive-ui-components 组件库迁移版本\r\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\r\n *\r\n * 使用侧 API:\r\n * <C_Table :columns=\"cols\" :data=\"data\" :loading=\"loading\" :config=\"tableConfig\" />\r\n *\r\n * config 收拢了所有功能配置:edit / selection / expand / pagination / dynamicRows / toolbar / display\r\n * 列处理 → composables/useTableColumns.ts\r\n * 配置解析 → composables/useTableConfig.ts\r\n * 编辑/展开/动态行 → composables/useTableManager.ts\r\n -->\r\n\r\n<template>\r\n <div\r\n ref=\"tableWrapperRef\"\r\n class=\"c-table-wrapper\"\r\n >\r\n <!-- 动态行工具栏 -->\r\n <component\r\n v-if=\"tableManager.dynamicRowsState\"\r\n :is=\"tableManager.dynamicRowsState.renderToolbar()\"\r\n />\r\n\r\n <!-- 表格工具栏 -->\r\n <div\r\n v-if=\"resolved.showToolbar\"\r\n class=\"table-toolbar\"\r\n >\r\n <div class=\"toolbar-left\">\r\n <slot name=\"toolbar-left\" />\r\n </div>\r\n <div class=\"toolbar-right\">\r\n <slot name=\"toolbar-right\" />\r\n <C_Icon\r\n v-if=\"resolved.enableColumnSettings\"\r\n name=\"mdi:cog\"\r\n size=\"18\"\r\n title=\"表格设置\"\r\n clickable\r\n class=\"column-settings-btn\"\r\n @click=\"showSettingsPanel = true\"\r\n />\r\n </div>\r\n </div>\r\n\r\n <!-- 表格主体 -->\r\n <NDataTable\r\n ref=\"tableRef\"\r\n v-bind=\"{ ...computedTableProps, ...$attrs }\"\r\n :columns=\"computedColumns\"\r\n :data=\"normalizedData\"\r\n :loading=\"normalizedLoading\"\r\n :row-key=\"rowKey\"\r\n :expanded-row-keys=\"tableManager.expandedKeys.value\"\r\n :checked-row-keys=\"tableManager.checkedKeys.value\"\r\n @update:expanded-row-keys=\"tableManager.expandState?.handleExpandChange\"\r\n @update:checked-row-keys=\"tableManager.expandState?.handleSelectionChange\"\r\n :scroll-x=\"computedScrollX\"\r\n :virtual-scroll=\"resolved.virtualScroll\"\r\n :virtual-scroll-item-size=\"resolved.virtualScroll ? resolved.virtualItemHeight : undefined\"\r\n :max-height=\"resolved.virtualScroll ? resolved.virtualMaxHeight : resolved.maxHeight\"\r\n :summary=\"summaryFn\"\r\n :summary-placement=\"resolved.summaryPosition\"\r\n style=\"width: 100%\"\r\n />\r\n\r\n <!-- 分页 -->\r\n <NPagination\r\n v-if=\"pagination.paginationConfig.value\"\r\n v-bind=\"pagination.paginationConfig.value\"\r\n class=\"pagination-wrapper\"\r\n />\r\n\r\n <!-- 编辑弹窗 -->\r\n <NModal\r\n v-if=\"resolved.editMode === 'modal' || resolved.editMode === 'both'\"\r\n v-model:show=\"modalVisible\"\r\n :title=\"resolved.modalTitle\"\r\n :width=\"resolved.modalWidth\"\r\n preset=\"card\"\r\n :mask-closable=\"false\"\r\n :close-on-esc=\"false\"\r\n class=\"w60%\"\r\n :closable=\"false\"\r\n >\r\n <C_Form\r\n v-if=\"modalVisible && formOptions.length\"\r\n ref=\"editFormRef\"\r\n :key=\"formKey\"\r\n v-model=\"localEditingData\"\r\n :options=\"formOptions\"\r\n :config=\"{\r\n layout: 'grid',\r\n grid: { cols: 2, xGap: 16, yGap: 16 },\r\n showActions: false,\r\n }\"\r\n />\r\n <template #action>\r\n <NSpace justify=\"end\">\r\n <NButton @click=\"handleModalCancel\">取消</NButton>\r\n <NButton\r\n type=\"primary\"\r\n :loading=\"modalSubmitLoading\"\r\n @click=\"handleModalSave\"\r\n >\r\n 保存\r\n </NButton>\r\n </NSpace>\r\n </template>\r\n </NModal>\r\n\r\n <!-- 动态行确认删除弹窗 -->\r\n <component\r\n v-if=\"tableManager.dynamicRowsState\"\r\n :is=\"tableManager.dynamicRowsState.renderConfirmModal()\"\r\n />\r\n\r\n <!-- 列设置抽屉 -->\r\n <NDrawer\r\n v-model:show=\"showSettingsPanel\"\r\n :width=\"420\"\r\n placement=\"right\"\r\n :mask-closable=\"true\"\r\n >\r\n <NDrawerContent\r\n title=\"列设置\"\r\n closable\r\n >\r\n <ColumnSettings\r\n :columns=\"reactiveColumns\"\r\n @change=\"onColumnSettingsChange\"\r\n />\r\n </NDrawerContent>\r\n </NDrawer>\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\n import {\r\n ref,\r\n computed,\r\n watch,\r\n onMounted,\r\n onBeforeUnmount,\r\n type ComponentPublicInstance,\r\n } from 'vue'\r\n import {\r\n type DataTableRowKey,\r\n NDataTable,\r\n NPagination,\r\n NModal,\r\n NSpace,\r\n NButton,\r\n NDrawer,\r\n NDrawerContent,\r\n } from 'naive-ui/es'\r\n import type {\r\n TableColumn,\r\n TableEmits,\r\n DataRecord,\r\n MaybeRefLike,\r\n } from './types'\r\n import {\r\n resolveConfig,\r\n createEditModeChecker,\r\n type TableConfig,\r\n type CrudBinding,\r\n } from './composables/useTableConfig'\r\n import { useTableManager } from './composables/useTableManager'\r\n import { usePagination } from './composables/usePagination'\r\n import { useTableActions } from './composables/useTableActions'\r\n import { useTableColumns } from './composables/useTableColumns'\r\n import { generateFormOptions } from './data'\r\n import ColumnSettings from './components/ColumnSettings/index.vue'\r\n import C_Icon from '../C_Icon/index.vue'\r\n import C_Form from '../C_Form/index.vue'\r\n\r\n defineOptions({ name: 'C_Table', inheritAttrs: false })\r\n\r\n /* ================= Props(极简 API) ================= */\r\n\r\n const props = withDefaults(\r\n defineProps<{\r\n /** 列配置(crud 模式下可省略) */\r\n columns?: TableColumn[]\r\n /** 数据源(crud 模式下可省略) */\r\n data?: MaybeRefLike<DataRecord[]>\r\n /** 加载状态 */\r\n loading?: MaybeRefLike<boolean>\r\n /** 行唯一键 */\r\n rowKey?: (row: DataRecord) => DataTableRowKey\r\n /** 统一功能配置(edit / selection / expand / pagination / dynamicRows / toolbar / display) */\r\n config?: TableConfig\r\n /** CRUD 绑定 — 传入 useTableCrud() 的返回值,自动接管 data/columns/loading/actions/pagination/events */\r\n crud?: CrudBinding\r\n }>(),\r\n {\r\n rowKey: (row: DataRecord) => row.id as DataTableRowKey,\r\n config: () => ({}),\r\n }\r\n )\r\n\r\n const emit = defineEmits<\r\n TableEmits & {\r\n 'row-add': [newRow: DataRecord]\r\n 'row-delete': [deletedRow: DataRecord, index: number]\r\n 'row-copy': [originalRow: DataRecord, newRow: DataRecord]\r\n 'row-move': [row: DataRecord, fromIndex: number, toIndex: number]\r\n 'row-selection-change': [\r\n selectedKey: DataTableRowKey | null,\r\n selectedRow: DataRecord | null,\r\n ]\r\n 'pagination-change': [page: number, pageSize: number]\r\n 'view-detail': [data: DataRecord]\r\n 'column-change': [columns: TableColumn[]]\r\n }\r\n >()\r\n\r\n /* ================= CRUD 桥接 ================= */\r\n\r\n /** 包装 emit:在触发事件的同时自动调用 crud 对应的方法 */\r\n const bridgedEmit: typeof emit = (event: any, ...args: any[]) => {\r\n ;(emit as any)(event, ...args)\r\n\r\n if (!props.crud) return\r\n const handlers: Record<string, ((...a: any[]) => void) | undefined> = {\r\n save: props.crud.save,\r\n cancel: props.crud.handleCancel,\r\n 'pagination-change': props.crud.handlePaginationChange,\r\n 'row-delete': props.crud.handleRowDelete,\r\n 'view-detail': props.crud.detail?.show,\r\n }\r\n handlers[event]?.(...args)\r\n }\r\n\r\n /* ================= 有效值(crud 优先 → props 覆盖) ================= */\r\n\r\n /** 合并 crud 返回的 actions/pagination 到用户 config */\r\n const effectiveConfig = computed<TableConfig>(() => {\r\n if (!props.crud) return props.config || {}\r\n const fromCrud: Partial<TableConfig> = {}\r\n if (props.crud.actions) fromCrud.actions = props.crud.actions.value\r\n if (props.crud.pagination)\r\n fromCrud.pagination = props.crud.pagination.value ?? undefined\r\n return { ...fromCrud, ...props.config }\r\n })\r\n\r\n const effectiveColumns = computed<TableColumn[]>(\r\n () => props.columns ?? props.crud?.columns.value ?? []\r\n )\r\n\r\n /* ================= 配置解析 ================= */\r\n\r\n const resolved = computed(() => resolveConfig(effectiveConfig.value))\r\n const editModeChecker = computed(() => createEditModeChecker(resolved.value))\r\n\r\n /* ================= 数据规范化(兼容跨实例 Ref) ================= */\r\n\r\n const unwrapRef = <T,>(val: MaybeRefLike<T> | undefined): T | undefined =>\r\n val && typeof val === 'object' && 'value' in val ? val.value : (val as T)\r\n\r\n const normalizedData = computed<DataRecord[]>(\r\n () => unwrapRef(props.data) ?? props.crud?.data.value ?? []\r\n )\r\n\r\n const normalizedLoading = computed<boolean>(\r\n () => unwrapRef(props.loading) ?? props.crud?.loading.value ?? false\r\n )\r\n\r\n /* ================= Hooks ================= */\r\n\r\n const pagination = usePagination({\r\n data: normalizedData,\r\n config: computed(() => resolved.value.pagination),\r\n emit: bridgedEmit,\r\n })\r\n\r\n const tableManager = useTableManager({\r\n config: resolved.value,\r\n data: () => normalizedData.value,\r\n rowKey: props.rowKey,\r\n emit: bridgedEmit,\r\n })\r\n\r\n const tableActions = useTableActions({\r\n actions: computed(() => effectiveConfig.value.actions || {}),\r\n config: resolved,\r\n tableManager,\r\n rowKey: props.rowKey,\r\n emit: bridgedEmit,\r\n onViewDetail: (data: DataRecord) => bridgedEmit('view-detail', data),\r\n })\r\n\r\n const columnState = useTableColumns({\r\n rawColumns: effectiveColumns,\r\n config: resolved,\r\n columnWidth: resolved.value.columnWidth,\r\n scrollX: resolved.value.scrollX,\r\n rowKey: props.rowKey,\r\n tableManager,\r\n actionsRenderer: tableActions.renderActions,\r\n editModeChecker,\r\n })\r\n\r\n const {\r\n showSettingsPanel,\r\n reactiveColumns,\r\n computedColumns,\r\n computedScrollX,\r\n } = columnState\r\n\r\n /* ================= 合计行 ================= */\r\n\r\n const summaryFn = computed(() => {\r\n const cfg = resolved.value\r\n if (!cfg.summaryRender) return undefined\r\n return (data: DataRecord[]) => {\r\n const result = cfg.summaryRender!(data)\r\n return computedColumns.value.map((col: any) => {\r\n const key = col.key as string\r\n const def = result[key]\r\n return def ? { value: def.value, colSpan: def.colSpan ?? 1 } : { value: '' }\r\n })\r\n }\r\n })\r\n\r\n /* ================= 列拖拽排序 ================= */\r\n\r\n const tableWrapperRef = ref<HTMLElement>()\r\n let sortableInstance: any = null\r\n\r\n const initColumnDrag = async () => {\r\n if (!resolved.value.enableColumnDrag || !tableWrapperRef.value) return\r\n try {\r\n const { default: Sortable } = await import('sortablejs')\r\n const headerRow = tableWrapperRef.value.querySelector(\r\n '.n-data-table-thead tr'\r\n )\r\n if (!headerRow) return\r\n sortableInstance = new Sortable(headerRow as HTMLElement, {\r\n animation: resolved.value.columnDragAnimationDuration,\r\n handle: `.${resolved.value.columnDragHandleClass}`,\r\n ghostClass: 'column-drag-ghost',\r\n onEnd: (evt: any) => {\r\n if (evt.oldIndex == null || evt.newIndex == null) return\r\n const cols = [...reactiveColumns.value]\r\n const [moved] = cols.splice(evt.oldIndex, 1)\r\n cols.splice(evt.newIndex, 0, moved)\r\n columnState.handleColumnChange(cols)\r\n emit('column-change', cols)\r\n },\r\n })\r\n } catch {\r\n // sortablejs 未安装时静默降级\r\n }\r\n }\r\n\r\n /* ================= 表格属性 ================= */\r\n\r\n const tableRef = ref<ComponentPublicInstance>()\r\n\r\n const computedTableProps = computed(() => ({\r\n striped: resolved.value.striped,\r\n bordered: resolved.value.bordered,\r\n singleLine: resolved.value.singleLine,\r\n size: resolved.value.size,\r\n maxHeight: resolved.value.maxHeight,\r\n }))\r\n\r\n /* ================= 编辑弹窗 ================= */\r\n\r\n const editFormRef = ref<any>()\r\n const modalSubmitLoading = ref(false)\r\n const localEditingData = ref<DataRecord>({})\r\n\r\n const modalVisible = computed({\r\n get: () => tableManager.editStates.modalEdit.isModalVisible.value,\r\n set: (val: boolean) => {\r\n tableManager.editStates.modalEdit.isModalVisible.value = val\r\n },\r\n })\r\n\r\n const editableColumns = computed(() =>\r\n effectiveColumns.value.filter(\r\n (col): col is TableColumn => col.editable !== false\r\n )\r\n )\r\n\r\n const formKey = computed(\r\n () =>\r\n `edit-form-${tableManager.editStates.modalEdit.editingRowKey.value || 'new'}`\r\n )\r\n\r\n const formOptions = computed(() => generateFormOptions(editableColumns.value))\r\n\r\n watch(\r\n () => tableManager.editStates.modalEdit.editingData.value,\r\n newData => {\r\n if (newData && Object.keys(newData).length > 0) {\r\n localEditingData.value = JSON.parse(JSON.stringify(newData))\r\n }\r\n },\r\n { immediate: true, deep: true }\r\n )\r\n\r\n let modalCloseTimer: ReturnType<typeof setTimeout> | null = null\r\n\r\n watch(modalVisible, visible => {\r\n if (!visible) {\r\n modalCloseTimer = setTimeout(() => {\r\n localEditingData.value = {}\r\n modalCloseTimer = null\r\n }, 300)\r\n }\r\n })\r\n\r\n const handleModalSave = async () => {\r\n if (!editFormRef.value) return\r\n modalSubmitLoading.value = true\r\n try {\r\n await editFormRef.value.validate()\r\n await tableManager.editStates.modalEdit.saveEdit(localEditingData.value)\r\n } catch {\r\n /* 表单验证错误由组件内联显示 */\r\n } finally {\r\n modalSubmitLoading.value = false\r\n }\r\n }\r\n\r\n const handleModalCancel = () => {\r\n localEditingData.value = {}\r\n tableManager.editStates.modalEdit.cancelEdit()\r\n }\r\n\r\n /* ================= 列设置 ================= */\r\n\r\n const onColumnSettingsChange = (columns: TableColumn[]) => {\r\n columnState.handleColumnChange(columns)\r\n emit('column-change', columnState.reactiveColumns.value)\r\n }\r\n\r\n /* ================= Expose ================= */\r\n\r\n const { edit, expand, selection, dynamicRows } = tableManager.stateManager\r\n\r\n const exposedApi = {\r\n startEdit: edit.start,\r\n expandAll: expand.all,\r\n collapseAll: expand.collapseAll,\r\n selectAll: selection.all,\r\n clearSelection: selection.clear,\r\n clearAllSelections: tableManager.stateManager.clearAllSelections,\r\n clearRowSelection: dynamicRows?.clearSelection,\r\n resetToFirstPage: pagination.resetToFirstPage,\r\n getSelectedRows: selection.getSelected,\r\n getEditingData: edit.getEditingData,\r\n isEditing: edit.isEditing,\r\n isExpanded: expand.isExpanded,\r\n getManager: () => tableManager.stateManager,\r\n }\r\n\r\n defineExpose(exposedApi)\r\n\r\n /* 自动连接 crud.tableRef */\r\n onMounted(() => {\r\n const crudRef = props.crud?.tableRef\r\n if (crudRef) crudRef.value = exposedApi\r\n // 列拖拽初始化\r\n initColumnDrag()\r\n })\r\n\r\n onBeforeUnmount(() => {\r\n if (modalCloseTimer) clearTimeout(modalCloseTimer)\r\n if (sortableInstance) sortableInstance.destroy()\r\n })\r\n</script>\r\n\r\n<style scoped lang=\"scss\">\r\n @use './index.scss';\r\n</style>\r\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"C_Table2.js","names":["NSpin","_useNaiveMessage","useMessage","NButton","C_Icon","NTooltip","NButtonGroup","NSpace","NModal","C_Icon","C_Icon","$attrs","rowKey"],"sources":["../src/components/C_Table/composables/useTableConfig.ts","../src/components/C_Table/composables/useRowEdit.ts","../src/components/C_Table/composables/useCellEdit.ts","../src/components/C_Table/composables/useModalEdit.ts","../src/components/C_Table/composables/useTableExpand.ts","../src/components/C_Table/composables/usePrintWatermark.ts","../src/components/C_Table/composables/useDynamicRow.ts","../src/components/C_Table/composables/useTableManager.ts","../src/components/C_Table/composables/usePagination.ts","../src/components/C_Table/composables/useTableActions.ts","../src/components/C_Table/composables/useTableGlobalConfig.ts","../src/components/C_Table/data.ts","../src/components/C_Table/composables/useTableColumns.ts","../src/components/C_Table/composables/useRowDrag.ts","../src/components/C_Table/composables/useCrossPageSelection.ts","../src/components/C_Table/composables/useTableExport.ts","../src/components/C_Table/components/ColumnSettings/data.ts","../src/components/C_Table/components/ColumnSettings/index.vue","../src/components/C_Table/components/ColumnSettings/index.vue","../src/components/C_Table/components/ColumnSettings/index.vue","../src/components/C_Table/index.vue","../src/components/C_Table/index.vue","../src/components/C_Table/index.vue"],"sourcesContent":["/*\n * @Description: 表格配置构建 — 从 config 单一入口合并为内部统一配置对象\n * @Migration: naive-ui-components 组件库迁移版本\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\n */\n\nimport type { VNodeChild, Ref, ComputedRef } from 'vue'\nimport type { DataTableRowKey } from 'naive-ui/es'\nimport type {\n DataRecord,\n PaginationConfig,\n ParentChildLinkMode,\n SimpleTableActions,\n TableColumn,\n} from '../types'\nimport type { DynamicRowsOptions } from './useDynamicRow'\nimport type { RowDragConfig } from './useRowDrag'\nimport type { CrossPageSelectionConfig } from './useCrossPageSelection'\nimport type { ExportConfig } from './useTableExport'\nimport type { ColumnFormatter, FormatterConfig } from './useTableGlobalConfig'\n\n/* ================= CRUD 绑定类型 ================= */\n\n/**\n * 可绑定到 C_Table 的 CRUD 对象接口(桥接类型)\n *\n * C_Table 内部已将 data/columns 规范化为 DataRecord/TableColumn,\n * 此接口对 columns/actions 使用 any[] 避免 TableColumn<any> 结构展开异常,\n * 同时允许 useTableCrud<T>() 任意子类型直接传入而无需类型断言。\n */\nexport interface CrudBinding {\n data: Ref<any[]>\n loading: Ref<boolean>\n\n columns: ComputedRef<any[]>\n\n actions?: ComputedRef<any>\n\n pagination?: ComputedRef<any>\n\n tableRef?: Ref<any>\n\n save?: (...args: any[]) => any\n handleCancel?: () => void\n handlePaginationChange?: (page: number, pageSize: number) => void\n\n handleRowDelete?: (...args: any[]) => any\n\n detail?: { show: (...args: any[]) => void }\n}\n\n/* ================= 使用侧配置类型(对外 API) ================= */\n\n/** C_Table :config prop 的类型 — 所有功能配置收拢在此 */\nexport interface TableConfig<T extends DataRecord = DataRecord> {\n /** 编辑配置 */\n edit?: EditConfig | boolean\n /** 操作按钮 */\n actions?: SimpleTableActions<T>\n /** 分页配置 */\n pagination?: PaginationConfig | boolean\n /** 展开行配置 */\n expand?: ExpandConfig<T> | boolean\n /** 选择配置 */\n selection?: SelectionConfig<T> | boolean\n /** 动态行配置 */\n dynamicRows?: DynamicRowsOptions<T> | boolean\n /** 工具栏 */\n toolbar?: ToolbarConfig\n /** 表格显示属性 */\n display?: DisplayConfig\n /** 虚拟滚动配置(大数据量) */\n virtualScroll?: VirtualScrollConfig | boolean\n /** 合计行配置(财务场景) */\n summary?: SummaryConfig<T>\n /** 列拖拽排序 */\n columnDrag?: ColumnDragConfig | boolean\n /** 树形表格 */\n tree?: TreeConfig | boolean\n /** 行拖拽排序 */\n rowDrag?: RowDragConfig | boolean\n /** 跨页多选 */\n crossPageSelection?: CrossPageSelectionConfig | boolean\n /** 导出配置 */\n export?: ExportConfig\n /** 列配置持久化 key(传入即启用 localStorage) */\n persistKey?: string\n /** 全局格式化配置 */\n formatterConfig?: FormatterConfig\n /** 错误状态配置 */\n error?: ErrorConfig\n /** 批量操作配置 */\n batchActions?: BatchActionsConfig\n}\n\nexport interface EditConfig {\n enabled?: boolean\n mode?: 'row' | 'cell' | 'modal' | 'both' | 'none'\n showRowActions?: boolean\n modalTitle?: string\n modalWidth?: number\n}\n\nexport interface ExpandConfig<T extends DataRecord = DataRecord> {\n enabled?: boolean\n defaultExpandedKeys?: DataTableRowKey[]\n onLoadData?: (row: T) => Promise<unknown[]> | unknown[]\n renderContent?: (\n row: T,\n expandData: unknown[],\n loading: boolean,\n childSelection?: unknown\n ) => VNodeChild\n rowExpandable?: (row: T) => boolean\n}\n\nexport interface SelectionConfig<T extends DataRecord = DataRecord> {\n enabled?: boolean\n defaultCheckedKeys?: import('naive-ui/es').DataTableRowKey[]\n rowCheckable?: (row: T) => boolean\n maxSelection?: number\n childSelection?: {\n enabled?: boolean\n childRowCheckable?: (childRow: unknown, parentRow: T) => boolean\n }\n parentChildLink?: {\n enabled?: boolean\n mode?: ParentChildLinkMode\n }\n}\n\nexport interface ToolbarConfig {\n /** 是否显示工具栏 */\n show?: boolean\n /** 是否启用列设置 */\n columnSettings?: boolean\n}\n\nexport interface DisplayConfig {\n striped?: boolean\n bordered?: boolean\n singleLine?: boolean\n size?: 'small' | 'medium' | 'large'\n maxHeight?: number | string\n scrollX?: number | string\n columnWidth?: number\n}\n\n/** 虚拟滚动配置 */\nexport interface VirtualScrollConfig {\n enabled?: boolean\n /** 每行高度(px) */\n itemHeight?: number\n /** 最大高度(px),用于滚动容器 */\n maxHeight?: number\n}\n\n/** 合计行配置 */\nexport interface SummaryConfig<T extends DataRecord = DataRecord> {\n /** 合计行位置 */\n position?: 'top' | 'bottom'\n /** 自定义合计函数,每列返回 { value, colSpan? } */\n render?: (data: T[]) => Record<string, { value: string | number; colSpan?: number }>\n}\n\n/** 列拖拽排序配置 */\nexport interface ColumnDragConfig {\n enabled?: boolean\n /** 拖拽手柄类名(默认拖拽头部区域) */\n handleClass?: string\n /** 拖拽动画时长(ms) */\n animationDuration?: number\n}\n\n/** 树形表格配置 */\nexport interface TreeConfig {\n enabled?: boolean\n /** 子节点字段名 */\n childrenKey?: string\n /** 缩进宽度(px) */\n indent?: number\n /** 默认展开所有 */\n defaultExpandAll?: boolean\n}\n\n/** 错误状态配置 */\nexport interface ErrorConfig {\n /** 是否显示错误状态 */\n show?: boolean\n /** 错误描述文案 */\n message?: string\n /** 重试回调 */\n onRetry?: () => void | Promise<void>\n}\n\n/** 批量操作配置 */\nexport interface BatchActionsConfig {\n enabled?: boolean\n /** 自定义批量操作按钮 */\n actions?: Array<{\n key: string\n label: string\n icon?: string\n type?: string\n onClick: (selectedKeys: DataTableRowKey[], selectedRows: DataRecord[]) => void | Promise<void>\n }>\n}\n\n/* ================= 内部解析后的扁平配置 ================= */\n\nexport interface ResolvedConfig {\n editable: boolean\n editMode: string\n showRowActions: boolean\n modalTitle: string\n modalWidth: number\n expandable: boolean\n defaultExpandedKeys: DataTableRowKey[] | undefined\n onLoadExpandData:\n | ((row: DataRecord) => Promise<unknown[]> | unknown[])\n | undefined\n renderExpandContent:\n | ((\n row: DataRecord,\n expandData: unknown[],\n loading: boolean,\n childSelection?: unknown\n ) => VNodeChild)\n | undefined\n rowExpandable: ((row: DataRecord) => boolean) | undefined\n enableSelection: boolean\n defaultCheckedKeys: DataTableRowKey[] | undefined\n rowCheckable: ((row: DataRecord) => boolean) | undefined\n maxSelection: number | undefined\n enableChildSelection: boolean\n childRowCheckable:\n | ((childRow: unknown, parentRow: DataRecord) => boolean)\n | undefined\n enableParentChildLink: boolean\n parentChildLinkMode: ParentChildLinkMode\n pagination: PaginationConfig | null\n dynamicRows: DynamicRowsOptions<DataRecord> | undefined\n striped: boolean\n bordered: boolean\n singleLine: boolean\n size: 'small' | 'medium' | 'large'\n maxHeight: number | string | undefined\n scrollX: number | string | undefined\n columnWidth: number\n showToolbar: boolean\n enableColumnSettings: boolean\n /** 虚拟滚动 */\n virtualScroll: boolean\n virtualItemHeight: number\n virtualMaxHeight: number\n /** 合计行 */\n summaryPosition: 'top' | 'bottom' | undefined\n summaryRender:\n | ((\n data: DataRecord[]\n ) => Record<string, { value: string | number; colSpan?: number }>)\n | undefined\n /** 列拖拽 */\n enableColumnDrag: boolean\n columnDragHandleClass: string\n columnDragAnimationDuration: number\n /** 树形表格 */\n treeEnabled: boolean\n treeChildrenKey: string\n treeIndent: number\n treeDefaultExpandAll: boolean\n /** 行拖拽 */\n rowDrag: RowDragConfig | undefined\n /** 跨页多选 */\n crossPageSelection: CrossPageSelectionConfig | undefined\n /** 导出配置 */\n exportConfig: ExportConfig | undefined\n /** 列持久化 key */\n persistKey: string | undefined\n /** 全局格式化配置 */\n formatterConfig: FormatterConfig | undefined\n /** 错误状态 */\n error: ErrorConfig | undefined\n /** 批量操作 */\n batchActions: BatchActionsConfig | undefined\n}\n\n/* ================= 解析逻辑 ================= */\n\nconst EDIT_DISABLED: Pick<\n ResolvedConfig,\n 'editable' | 'editMode' | 'showRowActions' | 'modalTitle' | 'modalWidth'\n> = {\n editable: false,\n editMode: 'none',\n showRowActions: false,\n modalTitle: '编辑数据',\n modalWidth: 600,\n}\n\nconst resolveEdit = (\n edit: EditConfig | boolean | undefined\n): Pick<\n ResolvedConfig,\n 'editable' | 'editMode' | 'showRowActions' | 'modalTitle' | 'modalWidth'\n> => {\n if (edit === false || edit === undefined) return EDIT_DISABLED\n if (edit === true)\n return {\n editable: true,\n editMode: 'modal',\n showRowActions: true,\n modalTitle: '编辑数据',\n modalWidth: 600,\n }\n return {\n editable: edit.enabled !== false && edit.mode !== 'none',\n editMode: edit.mode || 'modal',\n showRowActions: edit.showRowActions !== false && edit.mode !== 'none',\n modalTitle: edit.modalTitle || '编辑数据',\n modalWidth: edit.modalWidth || 600,\n }\n}\n\nconst resolveExpand = (expand: ExpandConfig | boolean | undefined) => {\n if (!expand)\n return {\n expandable: false,\n defaultExpandedKeys: undefined,\n onLoadExpandData: undefined,\n renderExpandContent: undefined,\n rowExpandable: undefined,\n }\n if (expand === true)\n return {\n expandable: true,\n defaultExpandedKeys: undefined,\n onLoadExpandData: undefined,\n renderExpandContent: undefined,\n rowExpandable: undefined,\n }\n return {\n expandable: expand.enabled !== false,\n defaultExpandedKeys: expand.defaultExpandedKeys,\n onLoadExpandData: expand.onLoadData,\n renderExpandContent: expand.renderContent,\n rowExpandable: expand.rowExpandable,\n }\n}\n\nconst resolveSelection = (selection: SelectionConfig | boolean | undefined) => {\n if (!selection)\n return {\n enableSelection: false,\n defaultCheckedKeys: undefined,\n rowCheckable: undefined,\n maxSelection: undefined,\n enableChildSelection: false,\n childRowCheckable: undefined,\n enableParentChildLink: false,\n parentChildLinkMode: 'loose' as ParentChildLinkMode,\n }\n if (selection === true)\n return {\n enableSelection: true,\n defaultCheckedKeys: undefined,\n rowCheckable: undefined,\n maxSelection: undefined,\n enableChildSelection: false,\n childRowCheckable: undefined,\n enableParentChildLink: false,\n parentChildLinkMode: 'loose' as ParentChildLinkMode,\n }\n const child = selection.childSelection || {}\n const link = selection.parentChildLink || {}\n return {\n enableSelection: selection.enabled !== false,\n defaultCheckedKeys: selection.defaultCheckedKeys,\n rowCheckable: selection.rowCheckable,\n maxSelection: selection.maxSelection,\n enableChildSelection: child.enabled || false,\n childRowCheckable: child.childRowCheckable,\n enableParentChildLink: link.enabled || false,\n parentChildLinkMode: (link.mode || 'loose') as ParentChildLinkMode,\n }\n}\n\nconst resolvePagination = (\n pagination: PaginationConfig | boolean | undefined\n): { pagination: PaginationConfig | null } => {\n const defaults: PaginationConfig = {\n enabled: true,\n page: 1,\n pageSize: 10,\n showSizePicker: true,\n showQuickJumper: true,\n pageSizes: [10, 20, 50, 100],\n simple: false,\n size: 'medium',\n }\n\n if (pagination === false) return { pagination: null }\n if (pagination === true || pagination === undefined)\n return { pagination: defaults }\n return { pagination: { ...defaults, ...pagination } }\n}\n\nconst resolveDynamicRows = (\n dynamicRows: DynamicRowsOptions | boolean | undefined\n) => {\n if (!dynamicRows) return { dynamicRows: undefined }\n if (dynamicRows === true) {\n return {\n dynamicRows: {\n enableRadioSelection: true,\n enableAdd: true,\n enableInsert: true,\n enableDelete: true,\n enableCopy: true,\n enableMove: true,\n enablePrint: true,\n },\n }\n }\n return { dynamicRows }\n}\n\nconst DISPLAY_DEFAULTS: Pick<\n ResolvedConfig,\n | 'striped'\n | 'bordered'\n | 'singleLine'\n | 'size'\n | 'maxHeight'\n | 'scrollX'\n | 'columnWidth'\n> = {\n striped: true,\n bordered: true,\n singleLine: true,\n size: 'medium',\n maxHeight: undefined,\n scrollX: undefined,\n columnWidth: 180,\n}\n\n/** 解析 display 配置,合并默认值 */\nfunction resolveDisplay(display: DisplayConfig | undefined) {\n if (!display) return { ...DISPLAY_DEFAULTS }\n return {\n striped: display.striped ?? DISPLAY_DEFAULTS.striped,\n bordered: display.bordered ?? DISPLAY_DEFAULTS.bordered,\n singleLine: display.singleLine ?? DISPLAY_DEFAULTS.singleLine,\n size: display.size ?? DISPLAY_DEFAULTS.size,\n maxHeight: display.maxHeight,\n scrollX: display.scrollX,\n columnWidth: display.columnWidth ?? DISPLAY_DEFAULTS.columnWidth,\n }\n}\n\nconst resolveToolbar = (toolbar: ToolbarConfig | undefined) => ({\n showToolbar: toolbar?.show !== false,\n enableColumnSettings: toolbar?.columnSettings !== false,\n})\n\nconst resolveVirtualScroll = (\n vs: VirtualScrollConfig | boolean | undefined\n) => {\n if (!vs)\n return {\n virtualScroll: false,\n virtualItemHeight: 34,\n virtualMaxHeight: 600,\n }\n if (vs === true)\n return { virtualScroll: true, virtualItemHeight: 34, virtualMaxHeight: 600 }\n return {\n virtualScroll: vs.enabled !== false,\n virtualItemHeight: vs.itemHeight ?? 34,\n virtualMaxHeight: vs.maxHeight ?? 600,\n }\n}\n\nconst resolveSummary = (summary: SummaryConfig | undefined) => {\n if (!summary) return { summaryPosition: undefined, summaryRender: undefined }\n return {\n summaryPosition: summary.position ?? 'bottom',\n summaryRender: summary.render,\n }\n}\n\nconst resolveColumnDrag = (cd: ColumnDragConfig | boolean | undefined) => {\n if (!cd)\n return {\n enableColumnDrag: false,\n columnDragHandleClass: 'drag-handle',\n columnDragAnimationDuration: 150,\n }\n if (cd === true)\n return {\n enableColumnDrag: true,\n columnDragHandleClass: 'drag-handle',\n columnDragAnimationDuration: 150,\n }\n return {\n enableColumnDrag: cd.enabled !== false,\n columnDragHandleClass: cd.handleClass ?? 'drag-handle',\n columnDragAnimationDuration: cd.animationDuration ?? 150,\n }\n}\n\nconst resolveTree = (tree: TreeConfig | boolean | undefined) => {\n if (!tree)\n return {\n treeEnabled: false,\n treeChildrenKey: 'children',\n treeIndent: 16,\n treeDefaultExpandAll: false,\n }\n if (tree === true)\n return {\n treeEnabled: true,\n treeChildrenKey: 'children',\n treeIndent: 16,\n treeDefaultExpandAll: false,\n }\n return {\n treeEnabled: tree.enabled !== false,\n treeChildrenKey: tree.childrenKey ?? 'children',\n treeIndent: tree.indent ?? 16,\n treeDefaultExpandAll: tree.defaultExpandAll ?? false,\n }\n}\n\nconst resolveRowDrag = (\n rd: RowDragConfig | boolean | undefined\n): { rowDrag: RowDragConfig | undefined } => {\n if (!rd) return { rowDrag: undefined }\n if (rd === true) return { rowDrag: { enabled: true } }\n return { rowDrag: rd.enabled !== false ? rd : undefined }\n}\n\nconst resolveCrossPageSelection = (\n cps: CrossPageSelectionConfig | boolean | undefined\n): { crossPageSelection: CrossPageSelectionConfig | undefined } => {\n if (!cps) return { crossPageSelection: undefined }\n if (cps === true) return { crossPageSelection: { enabled: true } }\n return { crossPageSelection: cps.enabled !== false ? cps : undefined }\n}\n\n/* ================= 主配置函数 ================= */\n\n/**\n * 将 config 单一对象解析为内部扁平化配置\n */\nexport function resolveConfig(config: TableConfig = {}): ResolvedConfig {\n return {\n ...resolveEdit(config.edit),\n ...resolveExpand(config.expand),\n ...resolveSelection(config.selection),\n ...resolvePagination(config.pagination),\n ...resolveDynamicRows(config.dynamicRows),\n ...resolveDisplay(config.display),\n ...resolveToolbar(config.toolbar),\n ...resolveVirtualScroll(config.virtualScroll),\n ...resolveSummary(config.summary),\n ...resolveColumnDrag(config.columnDrag),\n ...resolveTree(config.tree),\n ...resolveRowDrag(config.rowDrag),\n ...resolveCrossPageSelection(config.crossPageSelection),\n exportConfig: config.export,\n persistKey: config.persistKey,\n formatterConfig: config.formatterConfig,\n error: config.error,\n batchActions: config.batchActions,\n }\n}\n\n/* ================= 编辑模式检查器 ================= */\n\nexport const createEditModeChecker = (config: ResolvedConfig) => ({\n isNonEditable: (column: TableColumn) =>\n !config.editable || column.editable === false || config.editMode === 'none',\n isRowEditMode: () => ['row', 'both'].includes(config.editMode),\n isCellEditMode: () => ['cell', 'both'].includes(config.editMode),\n})\n","/*\n * @Description: 可编辑行组合函数,提供表格整行的编辑功能\n * @Migration: naive-ui-components 组件库迁移版本\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\n */\n\nimport { ref } from \"vue\";\nimport type { DataTableRowKey } from \"naive-ui/es\";\nimport type { DataRecord, TableColumn } from '../types'\n\n/** 行级校验错误 */\nexport interface RowValidationError {\n field: string\n message: string\n}\n\n/**\n * 行编辑配置选项\n */\nexport interface RowEditOptions {\n data: () => DataRecord[]\n rowKey: (row: DataRecord) => DataTableRowKey\n onSave?: (rowData: DataRecord, rowIndex: number) => void | Promise<void>\n onCancel?: (rowData: DataRecord, rowIndex: number) => void\n /** 列配置(用于提取校验规则) */\n columns?: () => TableColumn[]\n}\n\n/**\n * 可编辑行组合函数,提供表格整行的编辑功能\n */\nexport function useRowEdit(options: RowEditOptions) {\n const editingRowKey = ref<DataTableRowKey | null>(null)\n const editingData = ref<Record<string, DataRecord>>({})\n /** 校验错误信息 */\n const validationErrors = ref<RowValidationError[]>([])\n\n /**\n * 检查指定行是否正在编辑状态\n */\n const isEditingRow = (rowKey: DataTableRowKey) => {\n return editingRowKey.value === rowKey\n }\n\n /**\n * 根据rowKey实时查找最新的行数据\n */\n const findRowData = (rowKey: DataTableRowKey) => {\n const currentData = options.data()\n if (!currentData || !Array.isArray(currentData)) {\n return null\n }\n return currentData.find(row => options.rowKey(row) === rowKey)\n }\n\n /**\n * 开始编辑指定行,将原始数据复制到编辑缓存中\n */\n const startEditRow = (rowKey: DataTableRowKey) => {\n const rowData = findRowData(rowKey)\n if (!rowData) return\n\n editingRowKey.value = rowKey\n editingData.value[rowKey as string] = { ...rowData }\n }\n\n /**\n * 取消当前行编辑,调用取消回调并清理编辑状态\n */\n const cancelEditRow = async () => {\n if (!editingRowKey.value) return\n\n const currentData = options.data()\n if (!currentData || !Array.isArray(currentData)) return\n\n const rowIndex = currentData.findIndex(\n row => options.rowKey(row) === editingRowKey.value\n )\n\n if (rowIndex > -1) {\n await options.onCancel?.(currentData[rowIndex], rowIndex)\n }\n\n editingRowKey.value = null\n editingData.value = {}\n }\n\n /**\n * 校验当前编辑行数据\n */\n const validateRow = async (): Promise<boolean> => {\n validationErrors.value = []\n if (!editingRowKey.value) return true\n\n const columns = options.columns?.() || []\n const rowData = editingData.value[editingRowKey.value as string]\n if (!rowData) return true\n\n const errors: RowValidationError[] = []\n\n for (const col of columns) {\n if (col.editable === false || !col.editProps?.rules) continue\n const key = (col as any).key as string\n if (!key) continue\n\n const value = rowData[key]\n for (const rule of col.editProps.rules) {\n try {\n const ruleMsg =\n typeof rule.message === 'function' ? rule.message() : rule.message\n if (rule.required && (value == null || value === '')) {\n errors.push({\n field: key,\n message: ruleMsg || `${(col as any).title || key}不能为空`,\n })\n break\n }\n if (rule.validator) {\n await rule.validator(rule as any, value, () => {}, {}, {})\n }\n } catch (e: any) {\n errors.push({\n field: key,\n message: e?.message || `${(col as any).title || key}校验失败`,\n })\n break\n }\n }\n }\n\n validationErrors.value = errors\n return errors.length === 0\n }\n\n /**\n * 保存当前行编辑,调用保存回调并清理编辑状态\n */\n const saveEditRow = async () => {\n if (!editingRowKey.value) return\n\n // 执行校验\n const isValid = await validateRow()\n if (!isValid) return\n\n const rowKey = editingRowKey.value\n const currentData = options.data()\n if (!currentData || !Array.isArray(currentData)) return\n\n const rowIndex = currentData.findIndex(\n row => options.rowKey(row) === rowKey\n )\n\n if (rowIndex === -1) return\n\n const updatedData = editingData.value[rowKey as string]\n if (!updatedData) return\n\n await options.onSave?.(updatedData, rowIndex)\n\n editingRowKey.value = null\n editingData.value = {}\n\n return { updatedData, rowIndex }\n }\n\n /**\n * 获取指定行的编辑中数据\n */\n const getEditingRowData = (rowKey: DataTableRowKey) => {\n return editingData.value[rowKey as string]\n }\n\n /**\n * 更新指定行编辑中的字段值\n */\n const updateEditingRowData = (\n rowKey: DataTableRowKey,\n field: string,\n value: unknown\n ) => {\n if (!editingData.value[rowKey as string]) return\n editingData.value[rowKey as string][field] = value\n }\n\n return {\n editingRowKey,\n isEditingRow,\n startEditRow,\n cancelEditRow,\n saveEditRow,\n validateRow,\n validationErrors,\n getEditingRowData,\n updateEditingRowData,\n findRowData,\n }\n}\n","/*\n * @Description: 可编辑单元格组合函数\n * @Migration: naive-ui-components 组件库迁移版本\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\n */\n\nimport { ref } from \"vue\";\nimport type { DataTableRowKey } from \"naive-ui/es\";\nimport type { DataRecord, TableColumn } from '../types'\n\n/**\n * 单元格编辑配置选项\n */\nexport interface CellEditOptions {\n data: () => DataRecord[]\n rowKey: (row: DataRecord) => DataTableRowKey\n onSave?: (\n rowData: DataRecord,\n rowIndex: number,\n columnKey: string\n ) => void | Promise<void>\n /** 列配置(用于提取校验规则) */\n columns?: () => TableColumn[]\n}\n\n/**\n * 可编辑单元格组合函数,提供表格单元格的编辑功能\n */\nexport function useCellEdit(options: CellEditOptions) {\n const editingCell = ref<{\n rowKey: DataTableRowKey | null\n columnKey: string | null\n }>({\n rowKey: null,\n columnKey: null,\n })\n const editingData = ref<Record<string, unknown>>({})\n /** 当前单元格校验错误 */\n const cellValidationError = ref<string | null>(null)\n\n /**\n * 检查指定单元格是否正在编辑状态\n */\n const isEditingCell = (rowKey: DataTableRowKey, columnKey: string) => {\n return (\n editingCell.value.rowKey === rowKey &&\n editingCell.value.columnKey === columnKey\n )\n }\n\n /**\n * 根据rowKey实时查找最新的行数据\n */\n const findRowData = (rowKey: DataTableRowKey) => {\n const currentData = options.data()\n if (!currentData || !Array.isArray(currentData)) {\n return null\n }\n return currentData.find(row => options.rowKey(row) === rowKey)\n }\n\n /**\n * 开始编辑指定单元格,将原值存储到编辑缓存中\n */\n const startEditCell = (rowKey: DataTableRowKey, columnKey: string) => {\n const rowData = findRowData(rowKey)\n if (!rowData) return\n\n editingCell.value = { rowKey, columnKey }\n editingData.value[`${rowKey}-${columnKey}`] = rowData[columnKey]\n }\n\n /**\n * 校验当前编辑单元格\n */\n const validateCell = async (): Promise<boolean> => {\n cellValidationError.value = null\n const { rowKey, columnKey } = editingCell.value\n if (!rowKey || !columnKey) return true\n\n const columns = options.columns?.() || []\n const col = columns.find(c => (c as any).key === columnKey)\n if (!col || !col.editProps?.rules) return true\n\n const cellKey = `${rowKey}-${columnKey}`\n const value = editingData.value[cellKey]\n\n for (const rule of col.editProps.rules) {\n try {\n const ruleMsg =\n typeof rule.message === 'function' ? rule.message() : rule.message\n if (rule.required && (value == null || value === '')) {\n cellValidationError.value =\n ruleMsg || `${(col as any).title || columnKey}不能为空`\n return false\n }\n if (rule.validator) {\n await rule.validator(rule as any, value, () => {}, {}, {})\n }\n } catch (e: any) {\n cellValidationError.value =\n e?.message || `${(col as any).title || columnKey}校验失败`\n return false\n }\n }\n return true\n }\n\n /**\n * 保存当前编辑的单元格,调用保存回调并清理编辑状态\n */\n const saveEditCell = async () => {\n const { rowKey, columnKey } = editingCell.value\n if (!rowKey || !columnKey) return\n\n // 执行校验\n const isValid = await validateCell()\n if (!isValid) return\n\n const currentData = options.data()\n if (!currentData || !Array.isArray(currentData)) return\n\n const rowIndex = currentData.findIndex(\n row => options.rowKey(row) === rowKey\n )\n if (rowIndex === -1) return\n\n const cellKey = `${rowKey}-${columnKey}`\n const newValue = editingData.value[cellKey]\n\n const updatedData = {\n ...currentData[rowIndex],\n [columnKey]: newValue,\n }\n\n await options.onSave?.(updatedData, rowIndex, columnKey)\n\n editingCell.value = { rowKey: null, columnKey: null }\n delete editingData.value[cellKey]\n\n return { updatedData, rowIndex, columnKey }\n }\n\n /**\n * 取消当前单元格编辑,清理编辑状态和缓存数据\n */\n const cancelEditCell = () => {\n const { rowKey, columnKey } = editingCell.value\n if (rowKey && columnKey) {\n delete editingData.value[`${rowKey}-${columnKey}`]\n }\n editingCell.value = { rowKey: null, columnKey: null }\n }\n\n /**\n * 获取指定单元格的编辑中数值\n */\n const getEditingCellValue = (rowKey: DataTableRowKey, columnKey: string) => {\n return editingData.value[`${rowKey}-${columnKey}`]\n }\n\n /**\n * 更新指定单元格的编辑中数值\n */\n const updateEditingCellValue = (\n rowKey: DataTableRowKey,\n columnKey: string,\n value: unknown\n ) => {\n editingData.value[`${rowKey}-${columnKey}`] = value\n }\n\n return {\n editingCell,\n isEditingCell,\n startEditCell,\n saveEditCell,\n cancelEditCell,\n validateCell,\n cellValidationError,\n getEditingCellValue,\n updateEditingCellValue,\n findRowData,\n }\n}\n","/*\r\n * @Description: 模态框编辑组合函数\r\n * @Migration: naive-ui-components 组件库迁移版本\r\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\r\n */\r\n\r\nimport { ref, computed } from \"vue\";\r\nimport type { DataTableRowKey } from \"naive-ui/es\";\r\n\r\n/**\r\n * 模态框编辑配置选项\r\n */\r\nexport interface ModalEditOptions<T = Record<string, any>> {\r\n data: () => T[];\r\n rowKey: (row: T) => DataTableRowKey;\r\n onSave?: (\r\n editingData: Record<string, any>,\r\n rowIndex: number,\r\n ) => void | Promise<void>;\r\n onCancel?: (originalData: T, rowIndex: number) => void | Promise<void>;\r\n}\r\n\r\n/**\r\n * 模态框编辑组合函数,提供弹窗形式的数据编辑功能\r\n */\r\nexport function useModalEdit<T = Record<string, any>>(\r\n options: ModalEditOptions<T>,\r\n) {\r\n const isModalVisible = ref(false);\r\n const editingRowKey = ref<DataTableRowKey | null>(null);\r\n const editingData = ref<Record<string, any>>({});\r\n\r\n /**\r\n * 计算属性:获取当前编辑行的索引\r\n */\r\n const editingRowIndex = computed(() => {\r\n if (!editingRowKey.value) return -1;\r\n const currentData = options.data();\r\n if (!currentData || !Array.isArray(currentData)) return -1;\r\n\r\n return currentData.findIndex(\r\n (row) => options.rowKey(row) === editingRowKey.value,\r\n );\r\n });\r\n\r\n /**\r\n * 重置编辑状态\r\n */\r\n const resetEditingState = () => {\r\n editingRowKey.value = null;\r\n editingData.value = {};\r\n };\r\n\r\n /**\r\n * 开始编辑指定行\r\n */\r\n const startEdit = (rowKey: DataTableRowKey) => {\r\n console.log(\"🚀 开始编辑:\", rowKey);\r\n\r\n const currentData = options.data();\r\n if (!currentData || !Array.isArray(currentData)) {\r\n console.warn(\"数据源为空或不是数组\");\r\n return;\r\n }\r\n\r\n const rowIndex = currentData.findIndex(\r\n (row) => options.rowKey(row) === rowKey,\r\n );\r\n\r\n if (rowIndex === -1) {\r\n console.warn(\"未找到对应的行数据:\", rowKey);\r\n return;\r\n }\r\n\r\n const rowData = currentData[rowIndex];\r\n console.log(\"找到行数据:\", rowData);\r\n\r\n editingRowKey.value = rowKey;\r\n editingData.value = JSON.parse(JSON.stringify(rowData));\r\n\r\n console.log(\"编辑数据已准备:\", editingData.value);\r\n\r\n isModalVisible.value = true;\r\n };\r\n\r\n /**\r\n * 保存编辑\r\n */\r\n const saveEdit = async (formData?: Record<string, any>) => {\r\n const dataToSave = formData || editingData.value;\r\n const currentIndex = editingRowIndex.value;\r\n\r\n console.log(\"💾 保存编辑:\", {\r\n rowKey: editingRowKey.value,\r\n index: currentIndex,\r\n data: dataToSave,\r\n });\r\n\r\n if (currentIndex === -1) {\r\n console.warn(\"无法找到编辑行的索引\");\r\n return;\r\n }\r\n\r\n try {\r\n await options.onSave?.(dataToSave, currentIndex);\r\n\r\n isModalVisible.value = false;\r\n resetEditingState();\r\n\r\n console.log(\"✅ 保存成功\");\r\n } catch (error) {\r\n console.error(\"❌ 保存失败:\", error);\r\n throw error;\r\n }\r\n };\r\n\r\n /**\r\n * 取消编辑\r\n */\r\n const cancelEdit = async () => {\r\n console.log(\"🚫 取消编辑\");\r\n\r\n try {\r\n const currentIndex = editingRowIndex.value;\r\n if (editingRowKey.value && currentIndex > -1) {\r\n const currentData = options.data();\r\n if (currentData && currentData[currentIndex]) {\r\n await options.onCancel?.(currentData[currentIndex], currentIndex);\r\n }\r\n }\r\n } finally {\r\n isModalVisible.value = false;\r\n resetEditingState();\r\n }\r\n };\r\n\r\n /**\r\n * 更新编辑数据\r\n */\r\n const updateEditingData = (data: Record<string, any>) => {\r\n editingData.value = { ...data };\r\n };\r\n\r\n /**\r\n * 获取当前编辑的行数据\r\n */\r\n const getEditingRowData = (rowKey: DataTableRowKey) => {\r\n if (editingRowKey.value === rowKey) {\r\n return editingData.value;\r\n }\r\n return null;\r\n };\r\n\r\n /**\r\n * 检查是否正在编辑指定行\r\n */\r\n const isEditingRow = (rowKey: DataTableRowKey) => {\r\n return editingRowKey.value === rowKey && isModalVisible.value;\r\n };\r\n\r\n return {\r\n isModalVisible,\r\n editingData,\r\n editingRowKey,\r\n editingRowIndex,\r\n\r\n startEdit,\r\n saveEdit,\r\n cancelEdit,\r\n updateEditingData,\r\n resetEditingState,\r\n\r\n isEditingRow,\r\n getEditingRowData,\r\n };\r\n}\r\n","/*\r\n * @Description: 表格展开功能\r\n * @Migration: naive-ui-components 组件库迁移版本\r\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\r\n */\r\n\r\nimport {\r\n h,\r\n ref,\r\n computed,\r\n unref,\r\n watchEffect,\r\n nextTick,\r\n type VNodeChild,\r\n type Ref,\r\n} from 'vue'\r\nimport { type DataTableRowKey, NSpin, NDataTable } from 'naive-ui/es'\r\nimport type {\r\n TableColumn,\r\n UseTableExpandOptions,\r\n UseTableExpandReturn,\r\n ChildSelectionState,\r\n DataRecord,\r\n} from '../types'\r\n\r\n/* ================= 核心状态管理 ================= */\r\nconst useExpandState = <T extends DataRecord, C>(\r\n options: UseTableExpandOptions<T, C>\r\n) => {\r\n const expandedKeys = ref<DataTableRowKey[]>([\r\n ...(options.defaultExpandedKeys || []),\r\n ])\r\n const expandDataMap = ref(new Map<DataTableRowKey, any>()) as Ref<\r\n Map<DataTableRowKey, C[]>\r\n >\r\n const loadingMap = ref(new Map<DataTableRowKey, boolean>())\r\n\r\n const checkedKeys = options.enableSelection\r\n ? ref<DataTableRowKey[]>([...(options.defaultCheckedKeys || [])])\r\n : ref<DataTableRowKey[]>([])\r\n\r\n const childSelections = options.enableChildSelection\r\n ? ref(new Map<DataTableRowKey, DataTableRowKey[]>())\r\n : ref(new Map<DataTableRowKey, DataTableRowKey[]>())\r\n\r\n return {\r\n expandedKeys,\r\n expandDataMap,\r\n loadingMap,\r\n checkedKeys,\r\n childSelections,\r\n } as const\r\n}\r\n\r\n/* ================= 数据工具函数 ================= */\r\nconst useDataUtils = <T extends DataRecord, C>(\r\n options: UseTableExpandOptions<T, C>\r\n) => {\r\n const data = computed(() => unref(options.data))\r\n\r\n const getRowKey = options.rowKey\r\n const getChildRowKey =\r\n options.childRowKey || ((child: C): DataTableRowKey => (child as any).id)\r\n\r\n const findRow = (key: DataTableRowKey): T | undefined =>\r\n data.value.find(row => getRowKey(row) === key)\r\n\r\n const isRowExpandable = options.rowExpandable || ((): boolean => true)\r\n const isRowCheckable = options.rowCheckable || ((): boolean => true)\r\n const isChildRowCheckable = options.childRowCheckable || ((): boolean => true)\r\n\r\n return {\r\n data,\r\n getRowKey,\r\n getChildRowKey,\r\n findRow,\r\n isRowExpandable,\r\n isRowCheckable,\r\n isChildRowCheckable,\r\n } as const\r\n}\r\n\r\n/* ================= 展开逻辑 ================= */\r\nconst useExpandLogic = <T extends DataRecord, C>(\r\n state: ReturnType<typeof useExpandState<T, C>>,\r\n utils: ReturnType<typeof useDataUtils<T, C>>,\r\n options: UseTableExpandOptions<T, C>\r\n) => {\r\n const loadData = async (row: T): Promise<C[]> => {\r\n if (!options.onLoadData) return []\r\n\r\n const key = utils.getRowKey(row)\r\n const existingData = state.expandDataMap.value.get(key)\r\n if (existingData) return existingData\r\n\r\n state.loadingMap.value.set(key, true)\r\n\r\n try {\r\n const data = await options.onLoadData(row)\r\n const result = data || []\r\n state.expandDataMap.value.set(key, result as any)\r\n\r\n if (\r\n options.enableChildSelection &&\r\n !state.childSelections.value.has(key)\r\n ) {\r\n state.childSelections.value.set(key, [])\r\n }\r\n\r\n return result\r\n } catch (error) {\r\n console.error('加载展开数据失败:', error)\r\n return []\r\n } finally {\r\n state.loadingMap.value.set(key, false)\r\n }\r\n }\r\n\r\n const handleRowExpand = async (row: T, expanded: boolean): Promise<void> => {\r\n const key = utils.getRowKey(row)\r\n\r\n if (expanded) {\r\n await loadData(row)\r\n if (!state.expandedKeys.value.includes(key)) {\r\n state.expandedKeys.value = [...state.expandedKeys.value, key]\r\n }\r\n } else {\r\n state.expandedKeys.value = state.expandedKeys.value.filter(k => k !== key)\r\n }\r\n\r\n options.onExpandChange?.(state.expandedKeys.value)\r\n }\r\n\r\n const expandAll = async (): Promise<void> => {\r\n const expandableRows = utils.data.value.filter(utils.isRowExpandable)\r\n await Promise.allSettled(expandableRows.map(loadData))\r\n state.expandedKeys.value = expandableRows.map(utils.getRowKey)\r\n options.onExpandChange?.(state.expandedKeys.value)\r\n }\r\n\r\n const collapseAll = (): void => {\r\n state.expandedKeys.value = []\r\n state.childSelections.value.clear()\r\n options.onExpandChange?.(state.expandedKeys.value)\r\n }\r\n\r\n const handleExpandChange = async (keys: DataTableRowKey[]): Promise<void> => {\r\n const newExpandedKeys = keys.filter(\r\n key => !state.expandedKeys.value.includes(key)\r\n )\r\n const collapsedKeys = state.expandedKeys.value.filter(\r\n key => !keys.includes(key)\r\n )\r\n\r\n await Promise.all(\r\n newExpandedKeys.map(async key => {\r\n const row = utils.findRow(key)\r\n if (row) {\r\n await loadData(row)\r\n }\r\n })\r\n )\r\n\r\n for (const key of collapsedKeys) {\r\n state.childSelections.value.delete(key)\r\n }\r\n\r\n state.expandedKeys.value = keys\r\n options.onExpandChange?.(keys)\r\n }\r\n\r\n return {\r\n loadData,\r\n handleRowExpand,\r\n expandAll,\r\n collapseAll,\r\n handleExpandChange,\r\n } as const\r\n}\r\n\r\n/* ================= 选择逻辑 ================= */\r\nconst useSelectionLogic = <T extends DataRecord, C>(\r\n state: ReturnType<typeof useExpandState<T, C>>,\r\n utils: ReturnType<typeof useDataUtils<T, C>>,\r\n options: UseTableExpandOptions<T, C>\r\n) => {\r\n const selectableRows = computed(() =>\r\n utils.data.value.filter(utils.isRowCheckable)\r\n )\r\n\r\n const selectedRowsCount = computed(() => state.checkedKeys.value.length)\r\n\r\n const selectAll = (): void => {\r\n if (!options.enableSelection) return\r\n\r\n const keys = selectableRows.value.map(utils.getRowKey)\r\n const finalKeys = options.maxSelection\r\n ? keys.slice(0, options.maxSelection)\r\n : keys\r\n\r\n state.checkedKeys.value = finalKeys\r\n\r\n const selectedRows = selectableRows.value.filter(row =>\r\n finalKeys.includes(utils.getRowKey(row))\r\n )\r\n\r\n options.onSelectionChange?.(\r\n finalKeys,\r\n selectedRows,\r\n state.childSelections.value\r\n )\r\n }\r\n\r\n const clearSelection = (): void => {\r\n if (!options.enableSelection) return\r\n\r\n state.checkedKeys.value = []\r\n options.onSelectionChange?.([], [], state.childSelections.value)\r\n }\r\n\r\n const handleSelectionChange = (keys: DataTableRowKey[]): void => {\r\n if (!options.enableSelection) return\r\n\r\n state.checkedKeys.value = keys\r\n const selectedRows = utils.data.value.filter(row =>\r\n keys.includes(utils.getRowKey(row))\r\n )\r\n options.onSelectionChange?.(keys, selectedRows, state.childSelections.value)\r\n }\r\n\r\n return {\r\n selectAll,\r\n clearSelection,\r\n handleSelectionChange,\r\n selectedRowsCount,\r\n selectableRows,\r\n } as const\r\n}\r\n\r\n/* ================= 父子联动逻辑 ================= */\r\nconst useParentChildLink = <T extends DataRecord, C>(\r\n state: ReturnType<typeof useExpandState<T, C>>,\r\n options: UseTableExpandOptions<T, C>\r\n) => {\r\n const isLinkEnabled = Boolean(\r\n options.enableParentChildLink &&\r\n options.enableSelection &&\r\n options.enableChildSelection\r\n )\r\n\r\n const handleParentChildLink = (\r\n parentKey: DataTableRowKey,\r\n selectedChildKeys: DataTableRowKey[],\r\n totalChildren: number\r\n ): void => {\r\n if (!isLinkEnabled) return\r\n\r\n const shouldSelectParent =\r\n options.parentChildLinkMode === 'strict'\r\n ? selectedChildKeys.length === totalChildren && totalChildren > 0\r\n : selectedChildKeys.length > 0\r\n\r\n const currentKeys = [...state.checkedKeys.value]\r\n const isParentSelected = currentKeys.includes(parentKey)\r\n\r\n if (shouldSelectParent && !isParentSelected) {\r\n state.checkedKeys.value = [...currentKeys, parentKey]\r\n } else if (!shouldSelectParent && isParentSelected) {\r\n state.checkedKeys.value = currentKeys.filter(k => k !== parentKey)\r\n }\r\n }\r\n\r\n return {\r\n handleParentChildLink,\r\n isLinkEnabled,\r\n } as const\r\n}\r\n\r\n/* ================= 子选择逻辑 ================= */\r\nconst useChildSelectionLogic = <T extends DataRecord, C>(\r\n state: ReturnType<typeof useExpandState<T, C>>,\r\n utils: ReturnType<typeof useDataUtils<T, C>>,\r\n parentChildLink: ReturnType<typeof useParentChildLink<T, C>>,\r\n options: UseTableExpandOptions<T, C>\r\n) => {\r\n const totalChildSelections = computed(() => {\r\n if (!options.enableChildSelection) return 0\r\n return Array.from(state.childSelections.value.values()).reduce(\r\n (total, keys) => total + keys.length,\r\n 0\r\n )\r\n })\r\n\r\n const clearAllSelections = (): void => {\r\n state.checkedKeys.value = []\r\n state.childSelections.value.clear()\r\n options.onSelectionChange?.([], [], state.childSelections.value)\r\n }\r\n\r\n const handleChildSelectionChange = (\r\n parentKey: DataTableRowKey,\r\n childKeys: DataTableRowKey[]\r\n ): void => {\r\n if (!options.enableChildSelection) return\r\n\r\n state.childSelections.value.set(parentKey, childKeys)\r\n\r\n const expandData = state.expandDataMap.value.get(parentKey) || []\r\n const selectedChildren = expandData.filter((child: any) =>\r\n childKeys.includes(utils.getChildRowKey(child as C))\r\n ) as C[]\r\n\r\n options.onChildSelectionChange?.(parentKey, childKeys, selectedChildren)\r\n\r\n if (parentChildLink.isLinkEnabled) {\r\n parentChildLink.handleParentChildLink(\r\n parentKey,\r\n childKeys,\r\n expandData.length\r\n )\r\n }\r\n }\r\n\r\n return {\r\n totalChildSelections,\r\n clearAllSelections,\r\n handleChildSelectionChange,\r\n } as const\r\n}\r\n\r\n/* ================= 渲染辅助函数 ================= */\r\nconst createChildSelectionState = <T extends DataRecord, C>(\r\n parentKey: DataTableRowKey,\r\n state: ReturnType<typeof useExpandState<T, C>>,\r\n utils: ReturnType<typeof useDataUtils<T, C>>,\r\n childLogic: ReturnType<typeof useChildSelectionLogic<T, C>>,\r\n options: UseTableExpandOptions<T, C>\r\n): ChildSelectionState | undefined => {\r\n if (!options.enableChildSelection) return undefined\r\n\r\n const selectedKeys = state.childSelections.value.get(parentKey) || []\r\n const expandData = state.expandDataMap.value.get(parentKey) || []\r\n const parent = utils.findRow(parentKey)\r\n\r\n if (!parent) return undefined\r\n\r\n const checkableChildren = expandData.filter((child: any) =>\r\n utils.isChildRowCheckable(child as C, parent)\r\n )\r\n\r\n const isAllChecked =\r\n checkableChildren.length > 0 &&\r\n checkableChildren.every((child: any) =>\r\n selectedKeys.includes(utils.getChildRowKey(child as C))\r\n )\r\n\r\n return {\r\n selectedKeys,\r\n isAllChecked,\r\n selectAll: () => {\r\n const allKeys = checkableChildren.map((child: any) =>\r\n utils.getChildRowKey(child as C)\r\n )\r\n childLogic.handleChildSelectionChange(parentKey, allKeys)\r\n },\r\n clearAll: () => {\r\n childLogic.handleChildSelectionChange(parentKey, [])\r\n },\r\n }\r\n}\r\n\r\nconst createLoadingView = (): VNodeChild => {\r\n return h('div', { class: 'flex justify-center items-center py-8' }, [\r\n h(NSpin, { size: 'small' }),\r\n h('span', { class: 'ml-2 text-gray-500' }, '加载中...'),\r\n ])\r\n}\r\n\r\nconst createEmptyView = (): VNodeChild => {\r\n return h('div', { class: 'text-center py-8 text-gray-400' }, '暂无数据')\r\n}\r\n\r\nconst createDefaultColumns = (expandData: DataRecord[]): DataRecord[] => {\r\n if (!expandData.length) return []\r\n\r\n const firstItem = expandData[0]\r\n if (!firstItem || typeof firstItem !== 'object') return []\r\n\r\n const dataKeys = Object.keys(firstItem).filter(\r\n key => !['id', 'key'].includes(key)\r\n )\r\n\r\n return [\r\n {\r\n title: '序号',\r\n key: '_index',\r\n width: 60,\r\n render: (_: unknown, index: number) => index + 1,\r\n },\r\n ...dataKeys.map(key => ({\r\n key,\r\n title: key.charAt(0).toUpperCase() + key.slice(1),\r\n width: 120,\r\n ellipsis: { tooltip: true },\r\n })),\r\n ]\r\n}\r\n\r\nconst createDefaultTable = <T extends DataRecord, C>(\r\n key: DataTableRowKey,\r\n expandData: DataRecord[],\r\n childSelection: ChildSelectionState | undefined,\r\n utils: ReturnType<typeof useDataUtils<T, C>>,\r\n childLogic: ReturnType<typeof useChildSelectionLogic<T, C>>,\r\n options: UseTableExpandOptions<T, C>\r\n): VNodeChild => {\r\n const columns: Array<Record<string, unknown>> = []\r\n\r\n if (options.enableChildSelection) {\r\n columns.push({ type: 'selection', multiple: true })\r\n }\r\n\r\n columns.push(...createDefaultColumns(expandData))\r\n\r\n return h('div', { class: 'p-4 bg-gray-50' }, [\r\n h(\r\n 'div',\r\n { class: 'mb-2 text-sm text-gray-600' },\r\n `详细信息 (${expandData.length} 条)`\r\n ),\r\n h(NDataTable, {\r\n data: expandData,\r\n columns: columns as any,\r\n size: 'small',\r\n striped: true,\r\n checkedRowKeys: childSelection?.selectedKeys || [],\r\n rowKey: (row: DataRecord) => utils.getChildRowKey(row as C),\r\n onUpdateCheckedRowKeys: options.enableChildSelection\r\n ? (keys: DataTableRowKey[]) => {\r\n childLogic.handleChildSelectionChange(key, keys)\r\n }\r\n : undefined,\r\n }),\r\n ])\r\n}\r\n\r\n/* ================= 渲染逻辑 ================= */\r\nconst useRenderer = <T extends DataRecord, C>(\r\n state: ReturnType<typeof useExpandState<T, C>>,\r\n utils: ReturnType<typeof useDataUtils<T, C>>,\r\n childLogic: ReturnType<typeof useChildSelectionLogic<T, C>>,\r\n expandLogic: ReturnType<typeof useExpandLogic<T, C>>,\r\n options: UseTableExpandOptions<T, C>\r\n) => {\r\n const renderExpandContent = (row: T): VNodeChild => {\r\n const key = utils.getRowKey(row)\r\n const expandData = state.expandDataMap.value.get(key) || []\r\n const loading = state.loadingMap.value.get(key) || false\r\n\r\n if (\r\n !expandData.length &&\r\n !loading &&\r\n state.expandedKeys.value.includes(key)\r\n ) {\r\n nextTick(() => {\r\n expandLogic.loadData(row)\r\n })\r\n }\r\n\r\n const childSelection = createChildSelectionState(\r\n key,\r\n state,\r\n utils,\r\n childLogic,\r\n options\r\n )\r\n\r\n if (options.renderContent) {\r\n return options.renderContent(\r\n row,\r\n expandData as C[],\r\n loading,\r\n childSelection\r\n )\r\n }\r\n\r\n if (loading) return createLoadingView()\r\n if (!expandData.length) return createEmptyView()\r\n\r\n return createDefaultTable(\r\n key,\r\n expandData as DataRecord[],\r\n childSelection,\r\n utils,\r\n childLogic,\r\n options\r\n )\r\n }\r\n\r\n const getTableColumns = (originalColumns: TableColumn<T>[]): unknown[] => {\r\n return originalColumns.map(column => {\r\n if ((column as any).type === 'selection' && options.enableSelection) {\r\n return {\r\n type: 'selection',\r\n disabled: (row: T) => !utils.isRowCheckable(row),\r\n multiple: !options.maxSelection || options.maxSelection > 1,\r\n }\r\n }\r\n\r\n if (\r\n (column as any).type === 'expand' &&\r\n (options.onLoadData || options.renderContent)\r\n ) {\r\n return {\r\n type: 'expand',\r\n expandable: utils.isRowExpandable,\r\n renderExpand: renderExpandContent,\r\n }\r\n }\r\n\r\n return column\r\n })\r\n }\r\n\r\n return {\r\n getTableColumns,\r\n } as const\r\n}\r\n\r\n/* ================= 主函数 ================= */\r\n/**\r\n *\r\n */\r\nexport function useTableExpand<\r\n T extends DataRecord = Record<string, any>,\r\n C = any,\r\n>(options: UseTableExpandOptions<T, C>): UseTableExpandReturn<T, C> {\r\n const state = useExpandState(options)\r\n const utils = useDataUtils(options)\r\n const expandLogic = useExpandLogic(state, utils, options)\r\n const selectionLogic = useSelectionLogic(state, utils, options)\r\n const parentChildLink = useParentChildLink(state, options)\r\n const childLogic = useChildSelectionLogic(\r\n state,\r\n utils,\r\n parentChildLink,\r\n options\r\n )\r\n const renderer = useRenderer(state, utils, childLogic, expandLogic, options)\r\n\r\n if (options.onSelectionChange && options.enableSelection) {\r\n watchEffect(() => {\r\n if (!options.onSelectionChange) return\r\n\r\n const selectedRows = utils.data.value.filter(row =>\r\n state.checkedKeys.value.includes(utils.getRowKey(row))\r\n )\r\n\r\n const hasSelection = state.checkedKeys.value.length > 0\r\n const hasData = utils.data.value.length > 0\r\n\r\n if (hasSelection || !hasData) {\r\n options.onSelectionChange(\r\n state.checkedKeys.value,\r\n selectedRows,\r\n state.childSelections.value\r\n )\r\n }\r\n })\r\n }\r\n\r\n const expandRow = async (key: DataTableRowKey): Promise<void> => {\r\n if (state.expandedKeys.value.includes(key)) return\r\n\r\n const row = utils.findRow(key)\r\n if (!row) return\r\n\r\n await expandLogic.loadData(row)\r\n state.expandedKeys.value = [...state.expandedKeys.value, key]\r\n options.onExpandChange?.(state.expandedKeys.value, row, true)\r\n }\r\n\r\n const initializeData = async (): Promise<void> => {\r\n const keysToLoad = options.defaultExpandedKeys || []\r\n if (keysToLoad.length === 0) return\r\n\r\n const loadPromises = keysToLoad.map(async key => {\r\n const row = utils.findRow(key)\r\n if (row && !state.expandDataMap.value.has(key)) {\r\n await expandLogic.loadData(row)\r\n }\r\n })\r\n\r\n await Promise.allSettled(loadPromises)\r\n }\r\n\r\n if (options.defaultExpandedKeys?.length) {\r\n nextTick(initializeData)\r\n }\r\n\r\n return {\r\n expandedKeys: state.expandedKeys,\r\n checkedKeys: state.checkedKeys,\r\n childSelections: state.childSelections,\r\n\r\n selectedRowsCount: selectionLogic.selectedRowsCount,\r\n totalChildSelections: childLogic.totalChildSelections,\r\n\r\n expandAll: expandLogic.expandAll,\r\n collapseAll: expandLogic.collapseAll,\r\n expandRow,\r\n handleExpandChange: expandLogic.handleExpandChange,\r\n\r\n selectAll: selectionLogic.selectAll,\r\n clearSelection: selectionLogic.clearSelection,\r\n clearAllSelections: childLogic.clearAllSelections,\r\n handleSelectionChange: selectionLogic.handleSelectionChange,\r\n\r\n getTableColumns: renderer.getTableColumns,\r\n\r\n expandDataMap: state.expandDataMap,\r\n loadingMap: state.loadingMap,\r\n }\r\n}\r\n","/*\r\n * @Description: 打印水印组合工具 - 封装 printJS 和 html2canvas\r\n * @Migration: naive-ui-components 组件库迁移版本(从 @/hooks/usePrintWatermark 迁移)\r\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\r\n */\r\n\r\nimport { ref, nextTick } from 'vue'\r\nimport { useMessage as _useNaiveMessage } from 'naive-ui'\r\nimport html2canvas from 'html2canvas'\r\nimport printJS from 'print-js'\r\n\r\n/* ================= 类型定义 ================= */\r\n\r\n/** 消息提示 API 接口 */\r\ninterface MessageApi {\r\n success: (msg: string) => void\r\n error: (msg: string) => void\r\n warning: (msg: string) => void\r\n}\r\n\r\nexport interface WatermarkConfig {\r\n text: string\r\n textSize?: number\r\n textColor?: string\r\n font?: string\r\n opacity?: number\r\n rotate?: number\r\n position?: 'center' | 'repeat' | 'corner' | 'diagonal'\r\n xGap?: number\r\n yGap?: number\r\n}\r\n\r\nexport interface CaptureConfig {\r\n scale?: number\r\n quality?: number\r\n format?: 'png' | 'jpeg' | 'webp'\r\n backgroundColor?: string\r\n logging?: boolean\r\n allowTaint?: boolean\r\n useCORS?: boolean\r\n}\r\n\r\nexport interface PrintConfig {\r\n orientation?: 'portrait' | 'landscape'\r\n paperSize?: 'A4' | 'A3' | 'Letter' | string\r\n margin?: string\r\n style?: string\r\n header?: string\r\n footer?: string\r\n showModal?: boolean\r\n modalMessage?: string\r\n onLoadingStart?: () => void\r\n onLoadingEnd?: () => void\r\n onPrintDialogClose?: () => void\r\n}\r\n\r\nexport interface PrintWatermarkOptions {\r\n watermark?: WatermarkConfig\r\n capture?: CaptureConfig\r\n print?: PrintConfig\r\n}\r\n\r\n/* ================= 默认配置 ================= */\r\nconst DEFAULT_WATERMARK: Required<WatermarkConfig> = {\r\n text: 'Robot Admin',\r\n textSize: 16,\r\n textColor: 'rgba(128, 128, 128, 0.3)',\r\n font: 'Microsoft JhengHei',\r\n opacity: 0.3,\r\n rotate: -20,\r\n position: 'repeat',\r\n xGap: 200,\r\n yGap: 120,\r\n}\r\n\r\nconst DEFAULT_CAPTURE: Required<Omit<CaptureConfig, 'dpi'>> = {\r\n scale: 1,\r\n quality: 0.92,\r\n format: 'png',\r\n backgroundColor: '#ffffff',\r\n logging: false,\r\n allowTaint: true,\r\n useCORS: true,\r\n}\r\n\r\nconst DEFAULT_PRINT: Required<\r\n Omit<PrintConfig, 'onPrintDialogOpen' | 'onError'>\r\n> = {\r\n orientation: 'portrait',\r\n paperSize: 'A4',\r\n margin: '10mm 10mm 0 10mm',\r\n style:\r\n '@page { margin: 10mm 10mm 0 10mm; } @media print { body { margin: 0; } }',\r\n header: '',\r\n footer: '',\r\n showModal: false,\r\n modalMessage: '正在准备打印...',\r\n onLoadingStart: () => {},\r\n onLoadingEnd: () => {},\r\n onPrintDialogClose: () => {},\r\n}\r\n\r\n/* ================= 水印绘制函数 ================= */\r\n\r\n/**\r\n *\r\n */\r\nfunction drawCenterWatermark(\r\n ctx: CanvasRenderingContext2D,\r\n width: number,\r\n height: number,\r\n config: Required<WatermarkConfig>\r\n) {\r\n ctx.save()\r\n ctx.translate(width / 2, height / 2)\r\n ctx.rotate((config.rotate * Math.PI) / 180)\r\n ctx.fillText(config.text, 0, 0)\r\n ctx.restore()\r\n}\r\n\r\n/**\r\n *\r\n */\r\nfunction drawCornerWatermark(\r\n ctx: CanvasRenderingContext2D,\r\n width: number,\r\n height: number,\r\n config: Required<WatermarkConfig>\r\n) {\r\n const offset = 50\r\n const corners = [\r\n [offset, offset],\r\n [width - offset, offset],\r\n [offset, height - offset],\r\n [width - offset, height - offset],\r\n ]\r\n\r\n corners.forEach(([x, y]) => {\r\n ctx.save()\r\n ctx.translate(x, y)\r\n ctx.rotate((config.rotate * Math.PI) / 180)\r\n ctx.fillText(config.text, 0, 0)\r\n ctx.restore()\r\n })\r\n}\r\n\r\n/**\r\n *\r\n */\r\nfunction drawDiagonalWatermark(\r\n ctx: CanvasRenderingContext2D,\r\n width: number,\r\n height: number,\r\n config: Required<WatermarkConfig>\r\n) {\r\n const diagonal = Math.sqrt(width * width + height * height)\r\n const centerX = width / 2\r\n const centerY = height / 2\r\n\r\n ctx.save()\r\n ctx.translate(centerX, centerY)\r\n ctx.rotate(Math.atan2(height, width))\r\n\r\n const count = Math.floor(diagonal / config.xGap)\r\n const start = -diagonal / 2\r\n\r\n for (let i = 0; i <= count; i++) {\r\n const x = start + i * config.xGap\r\n ctx.fillText(config.text, x, 0)\r\n }\r\n\r\n ctx.restore()\r\n}\r\n\r\n/**\r\n *\r\n */\r\nfunction drawRepeatWatermark(\r\n ctx: CanvasRenderingContext2D,\r\n width: number,\r\n height: number,\r\n config: Required<WatermarkConfig>\r\n) {\r\n const cols = Math.ceil(width / config.xGap) + 1\r\n const rows = Math.ceil(height / config.yGap) + 1\r\n\r\n for (let row = 0; row < rows; row++) {\r\n for (let col = 0; col < cols; col++) {\r\n const x = col * config.xGap + config.xGap / 2\r\n const y = row * config.yGap + config.yGap / 2\r\n\r\n ctx.save()\r\n ctx.translate(x, y)\r\n ctx.rotate((config.rotate * Math.PI) / 180)\r\n ctx.fillText(config.text, 0, 0)\r\n ctx.restore()\r\n }\r\n }\r\n}\r\n\r\n/**\r\n *\r\n */\r\nfunction drawWatermark(\r\n canvas: HTMLCanvasElement,\r\n config: WatermarkConfig\r\n): HTMLCanvasElement {\r\n const ctx = canvas.getContext('2d')\r\n if (!ctx) return canvas\r\n\r\n const finalConfig = { ...DEFAULT_WATERMARK, ...config }\r\n const { width, height } = canvas\r\n\r\n ctx.save()\r\n ctx.globalAlpha = finalConfig.opacity\r\n ctx.font = `${finalConfig.textSize}px ${finalConfig.font}`\r\n ctx.fillStyle = finalConfig.textColor\r\n ctx.textAlign = 'center'\r\n ctx.textBaseline = 'middle'\r\n\r\n const drawFunctions = {\r\n center: drawCenterWatermark,\r\n corner: drawCornerWatermark,\r\n diagonal: drawDiagonalWatermark,\r\n repeat: drawRepeatWatermark,\r\n }\r\n\r\n const drawFunction =\r\n drawFunctions[finalConfig.position] || drawFunctions.repeat\r\n drawFunction(ctx, width, height, finalConfig)\r\n\r\n ctx.restore()\r\n return canvas\r\n}\r\n\r\n/* ================= 截图函数 ================= */\r\n\r\n/**\r\n *\r\n */\r\nasync function captureElement(\r\n element: HTMLElement,\r\n config: CaptureConfig = {}\r\n): Promise<HTMLCanvasElement> {\r\n const finalConfig = { ...DEFAULT_CAPTURE, ...config }\r\n\r\n return await html2canvas(element, {\r\n logging: finalConfig.logging,\r\n scale: finalConfig.scale,\r\n allowTaint: finalConfig.allowTaint,\r\n useCORS: finalConfig.useCORS,\r\n backgroundColor: finalConfig.backgroundColor,\r\n onclone: clonedDoc => {\r\n const clonedElement = clonedDoc.body\r\n clonedElement.style.transform = 'scale(1)'\r\n },\r\n })\r\n}\r\n\r\n/**\r\n *\r\n */\r\nfunction canvasToDataURL(\r\n canvas: HTMLCanvasElement,\r\n format: 'png' | 'jpeg' | 'webp' = 'png',\r\n quality: number = 0.92\r\n): string {\r\n const mimeType = `image/${format}`\r\n return format === 'png'\r\n ? canvas.toDataURL(mimeType)\r\n : canvas.toDataURL(mimeType, quality)\r\n}\r\n\r\n/**\r\n *\r\n */\r\nfunction downloadCanvas(\r\n canvas: HTMLCanvasElement,\r\n filename: string = `screenshot-${new Date().toISOString().slice(0, 10)}.png`,\r\n format: 'png' | 'jpeg' | 'webp' = 'png'\r\n) {\r\n const dataURL = canvasToDataURL(canvas, format)\r\n const link = document.createElement('a')\r\n link.download = filename\r\n link.href = dataURL\r\n document.body.appendChild(link)\r\n link.click()\r\n document.body.removeChild(link)\r\n}\r\n\r\n/* ================= 打印函数 ================= */\r\n\r\nlet focusInterval: ReturnType<typeof setInterval> | null = null\r\n\r\n/**\r\n *\r\n */\r\nfunction startFocusKeep() {\r\n focusInterval = setInterval(() => {\r\n window.dispatchEvent(new Event('focus'))\r\n }, 500)\r\n}\r\n\r\n/**\r\n *\r\n */\r\nfunction stopFocusKeep() {\r\n if (focusInterval) {\r\n clearInterval(focusInterval)\r\n focusInterval = null\r\n }\r\n}\r\n\r\n/**\r\n *\r\n */\r\nfunction printImage(\r\n imageDataURL: string,\r\n config: PrintConfig = {}\r\n): Promise<void> {\r\n const finalConfig = { ...DEFAULT_PRINT, ...config }\r\n\r\n return new Promise<void>((resolve, reject) => {\r\n try {\r\n startFocusKeep()\r\n finalConfig.onLoadingStart?.()\r\n\r\n printJS({\r\n printable: imageDataURL,\r\n type: 'image',\r\n style: finalConfig.style,\r\n showModal: finalConfig.showModal,\r\n modalMessage: finalConfig.modalMessage,\r\n onLoadingEnd: finalConfig.onLoadingEnd,\r\n onPrintDialogClose: () => {\r\n stopFocusKeep()\r\n finalConfig.onPrintDialogClose?.()\r\n resolve()\r\n },\r\n })\r\n } catch (error) {\r\n stopFocusKeep()\r\n reject(error)\r\n }\r\n })\r\n}\r\n\r\n/* ================= 合并打印辅助函数 ================= */\r\n\r\n/**\r\n *\r\n */\r\nfunction calculateMergedCanvasSize(\r\n canvases: HTMLCanvasElement[],\r\n orientation: 'horizontal' | 'vertical',\r\n spacing: number\r\n) {\r\n let totalWidth = 0\r\n let totalHeight = 0\r\n\r\n canvases.forEach(canvas => {\r\n const { width, height } = canvas\r\n\r\n if (orientation === 'horizontal') {\r\n totalWidth += width\r\n totalHeight = Math.max(totalHeight, height)\r\n } else {\r\n totalWidth = Math.max(totalWidth, width)\r\n totalHeight += height\r\n }\r\n })\r\n\r\n const spacingTotal = (canvases.length - 1) * spacing\r\n if (orientation === 'horizontal') {\r\n totalWidth += spacingTotal\r\n } else {\r\n totalHeight += spacingTotal\r\n }\r\n\r\n return { totalWidth, totalHeight }\r\n}\r\n\r\n/**\r\n *\r\n */\r\nfunction createMergedCanvas(\r\n canvases: HTMLCanvasElement[],\r\n options: {\r\n orientation: 'horizontal' | 'vertical'\r\n spacing: number\r\n backgroundColor: string\r\n totalWidth: number\r\n totalHeight: number\r\n }\r\n): HTMLCanvasElement {\r\n const { orientation, spacing, backgroundColor, totalWidth, totalHeight } =\r\n options\r\n\r\n const mergedCanvas = document.createElement('canvas')\r\n mergedCanvas.width = totalWidth\r\n mergedCanvas.height = totalHeight\r\n\r\n const ctx = mergedCanvas.getContext('2d')\r\n if (!ctx) throw new Error('无法创建画布上下文')\r\n\r\n ctx.fillStyle = backgroundColor\r\n ctx.fillRect(0, 0, totalWidth, totalHeight)\r\n\r\n let currentX = 0\r\n let currentY = 0\r\n\r\n canvases.forEach(canvas => {\r\n ctx.drawImage(canvas, currentX, currentY)\r\n\r\n if (orientation === 'horizontal') {\r\n currentX += canvas.width + spacing\r\n } else {\r\n currentY += canvas.height + spacing\r\n }\r\n })\r\n\r\n return mergedCanvas\r\n}\r\n\r\n/**\r\n *\r\n */\r\nfunction validateAndGetMergeOptions(\r\n elements: HTMLElement[],\r\n mergeOptions: {\r\n orientation?: 'horizontal' | 'vertical'\r\n spacing?: number\r\n backgroundColor?: string\r\n },\r\n message: MessageApi\r\n) {\r\n if (!elements.length) {\r\n message.warning('没有要打印的元素')\r\n return null\r\n }\r\n\r\n return {\r\n orientation: mergeOptions.orientation || 'vertical',\r\n spacing: mergeOptions.spacing || 20,\r\n backgroundColor: mergeOptions.backgroundColor || '#ffffff',\r\n }\r\n}\r\n\r\n/**\r\n *\r\n */\r\nasync function captureAndMergeCanvas(\r\n elements: HTMLElement[],\r\n options: PrintWatermarkOptions,\r\n validatedOptions: {\r\n orientation: 'horizontal' | 'vertical'\r\n spacing: number\r\n backgroundColor: string\r\n },\r\n updateProgress: (value: number) => void\r\n): Promise<HTMLCanvasElement> {\r\n const { orientation, spacing, backgroundColor } = validatedOptions\r\n\r\n updateProgress(10)\r\n const canvases = await Promise.all(\r\n elements.map(element => captureElement(element, options.capture))\r\n )\r\n\r\n updateProgress(50)\r\n const { totalWidth, totalHeight } = calculateMergedCanvasSize(\r\n canvases,\r\n orientation,\r\n spacing\r\n )\r\n\r\n updateProgress(60)\r\n return createMergedCanvas(canvases, {\r\n orientation,\r\n spacing,\r\n backgroundColor,\r\n totalWidth,\r\n totalHeight,\r\n })\r\n}\r\n\r\n/**\r\n *\r\n */\r\nasync function addWatermarkAndPrint(\r\n mergedCanvas: HTMLCanvasElement,\r\n options: PrintWatermarkOptions,\r\n elementsCount: number,\r\n updateProgress: (value: number) => void,\r\n message: MessageApi\r\n): Promise<void> {\r\n updateProgress(70)\r\n\r\n if (options.watermark) {\r\n drawWatermark(mergedCanvas, options.watermark)\r\n }\r\n\r\n updateProgress(90)\r\n const imageUrl = canvasToDataURL(\r\n mergedCanvas,\r\n options.capture?.format,\r\n options.capture?.quality\r\n )\r\n await printImage(imageUrl, options.print)\r\n\r\n updateProgress(100)\r\n message.success(`合并打印完成,共合并 ${elementsCount} 个元素`)\r\n}\r\n\r\n/* ================= 主要的 Composable ================= */\r\n\r\n/**\r\n *\r\n */\r\nexport function usePrintWatermark() {\r\n /* NOTE: useMessage() requires naive-ui provider context; caller must ensure NMessageProvider is present */\r\n let message: MessageApi\r\n try {\r\n message = _useNaiveMessage()\r\n } catch {\r\n message = {\r\n success: (msg: string) => console.log('[PrintWatermark]', msg),\r\n error: (msg: string) => console.error('[PrintWatermark]', msg),\r\n warning: (msg: string) => console.warn('[PrintWatermark]', msg),\r\n }\r\n }\r\n const loading = ref(false)\r\n const progress = ref(0)\r\n\r\n const updateProgress = (value: number) => {\r\n progress.value = Math.max(0, Math.min(100, value))\r\n }\r\n\r\n const captureWithWatermark = async (\r\n element: HTMLElement,\r\n options: PrintWatermarkOptions = {}\r\n ): Promise<string> => {\r\n try {\r\n updateProgress(0)\r\n updateProgress(30)\r\n const canvas = await captureElement(element, options.capture)\r\n\r\n updateProgress(60)\r\n if (options.watermark) {\r\n drawWatermark(canvas, options.watermark)\r\n }\r\n\r\n updateProgress(90)\r\n const format = options.capture?.format || 'png'\r\n const quality = options.capture?.quality || 0.92\r\n const dataURL = canvasToDataURL(canvas, format, quality)\r\n\r\n updateProgress(100)\r\n return dataURL\r\n } catch (error) {\r\n console.error('截图失败:', error)\r\n throw new Error(\r\n `截图失败: ${error instanceof Error ? error.message : '未知错误'}`\r\n )\r\n }\r\n }\r\n\r\n const printWithWatermark = async (\r\n element: HTMLElement,\r\n options: PrintWatermarkOptions = {}\r\n ): Promise<void> => {\r\n loading.value = true\r\n updateProgress(0)\r\n\r\n try {\r\n await nextTick()\r\n const imageDataURL = await captureWithWatermark(element, options)\r\n updateProgress(95)\r\n await printImage(imageDataURL, options.print)\r\n updateProgress(100)\r\n message.success('打印完成')\r\n } catch (error) {\r\n console.error('打印失败:', error)\r\n message.error(\r\n `打印失败: ${error instanceof Error ? error.message : '未知错误'}`\r\n )\r\n throw error\r\n } finally {\r\n loading.value = false\r\n updateProgress(0)\r\n }\r\n }\r\n\r\n const downloadScreenshot = async (\r\n element: HTMLElement,\r\n filename?: string,\r\n options: PrintWatermarkOptions = {}\r\n ): Promise<void> => {\r\n loading.value = true\r\n\r\n try {\r\n await nextTick()\r\n const canvas = await captureElement(element, options.capture)\r\n\r\n if (options.watermark) {\r\n drawWatermark(canvas, options.watermark)\r\n }\r\n\r\n const format = options.capture?.format || 'png'\r\n const defaultFilename = `screenshot-${new Date()\r\n .toISOString()\r\n .slice(0, 19)\r\n .replace(/:/g, '-')}.${format}`\r\n\r\n downloadCanvas(canvas, filename || defaultFilename, format)\r\n message.success('下载完成')\r\n } catch (error) {\r\n console.error('下载失败:', error)\r\n message.error('下载失败')\r\n throw error\r\n } finally {\r\n loading.value = false\r\n }\r\n }\r\n\r\n const quickPrint = async (\r\n element: HTMLElement,\r\n watermarkText: string = 'Robot Admin'\r\n ): Promise<void> => {\r\n return printWithWatermark(element, {\r\n watermark: {\r\n text: watermarkText,\r\n position: 'repeat',\r\n textColor: 'rgba(128, 128, 128, 0.3)',\r\n },\r\n })\r\n }\r\n\r\n const batchCapture = async (\r\n elements: HTMLElement[],\r\n options: PrintWatermarkOptions = {}\r\n ): Promise<string[]> => {\r\n const total = elements.length\r\n updateProgress(10)\r\n\r\n const capturePromises = elements.map(async (element, index) => {\r\n const url = await captureWithWatermark(element, options)\r\n updateProgress(10 + ((index + 1) / total) * 70)\r\n return url\r\n })\r\n\r\n return Promise.all(capturePromises)\r\n }\r\n\r\n const serialPrint = async (\r\n imageUrls: string[],\r\n printConfig: PrintConfig = {}\r\n ): Promise<void> => {\r\n await imageUrls.reduce(async (prevPromise, imageUrl, index) => {\r\n await prevPromise\r\n await printImage(imageUrl, printConfig)\r\n\r\n if (index < imageUrls.length - 1) {\r\n await new Promise<void>(resolve => {\r\n setTimeout(() => resolve(), 1000)\r\n })\r\n }\r\n }, Promise.resolve())\r\n }\r\n\r\n const batchPrint = async (\r\n elements: HTMLElement[],\r\n options: PrintWatermarkOptions = {}\r\n ): Promise<void> => {\r\n if (!elements.length) {\r\n message.warning('没有要打印的元素')\r\n return\r\n }\r\n\r\n loading.value = true\r\n\r\n try {\r\n updateProgress(0)\r\n const imageUrls = await batchCapture(elements, options)\r\n updateProgress(80)\r\n await serialPrint(imageUrls, options.print)\r\n updateProgress(100)\r\n message.success(`批量打印完成,共 ${elements.length} 个元素`)\r\n } catch (error) {\r\n console.error('批量打印失败:', error)\r\n message.error('批量打印失败')\r\n throw error\r\n } finally {\r\n loading.value = false\r\n updateProgress(0)\r\n }\r\n }\r\n\r\n const mergePrint = async (\r\n elements: HTMLElement[],\r\n options: PrintWatermarkOptions = {},\r\n mergeOptions: {\r\n orientation?: 'horizontal' | 'vertical'\r\n spacing?: number\r\n backgroundColor?: string\r\n } = {}\r\n ): Promise<void> => {\r\n const validatedOptions = validateAndGetMergeOptions(\r\n elements,\r\n mergeOptions,\r\n message\r\n )\r\n if (!validatedOptions) return\r\n\r\n loading.value = true\r\n\r\n try {\r\n const mergedCanvas = await captureAndMergeCanvas(\r\n elements,\r\n options,\r\n validatedOptions,\r\n updateProgress\r\n )\r\n await addWatermarkAndPrint(\r\n mergedCanvas,\r\n options,\r\n elements.length,\r\n updateProgress,\r\n message\r\n )\r\n } catch (error) {\r\n console.error('合并打印失败:', error)\r\n message.error('合并打印失败')\r\n throw error\r\n } finally {\r\n loading.value = false\r\n updateProgress(0)\r\n }\r\n }\r\n\r\n const createPreset = (name: string, options: PrintWatermarkOptions) => {\r\n return {\r\n use: (element: HTMLElement) => printWithWatermark(element, options),\r\n download: (element: HTMLElement, filename?: string) =>\r\n downloadScreenshot(element, filename, options),\r\n }\r\n }\r\n\r\n return {\r\n loading,\r\n progress,\r\n\r\n captureWithWatermark,\r\n printWithWatermark,\r\n downloadScreenshot,\r\n quickPrint,\r\n batchCapture,\r\n batchPrint,\r\n mergePrint,\r\n\r\n createPreset,\r\n updateProgress,\r\n\r\n utils: {\r\n captureElement,\r\n drawWatermark,\r\n canvasToDataURL,\r\n downloadCanvas,\r\n printImage,\r\n },\r\n }\r\n}\r\n\r\n/* ================= 预设配置 ================= */\r\nexport const printPresets = {\r\n table: {\r\n watermark: {\r\n text: 'Robot Admin - 数据表格',\r\n position: 'repeat' as const,\r\n textColor: 'rgba(100, 100, 100, 0.25)',\r\n textSize: 14,\r\n xGap: 250,\r\n yGap: 120,\r\n },\r\n capture: {\r\n scale: 1.2,\r\n },\r\n print: {\r\n orientation: 'landscape' as const,\r\n style: '@page { margin: 8mm; } body { font-family: Arial, sans-serif; }',\r\n },\r\n },\r\n\r\n form: {\r\n watermark: {\r\n text: 'Robot Admin - 表单数据',\r\n position: 'diagonal' as const,\r\n textColor: 'rgba(150, 150, 150, 0.2)',\r\n textSize: 16,\r\n },\r\n print: {\r\n orientation: 'portrait' as const,\r\n },\r\n },\r\n\r\n report: {\r\n watermark: {\r\n text: 'Robot Admin - 机密报告',\r\n position: 'center' as const,\r\n textColor: 'rgba(200, 50, 50, 0.15)',\r\n textSize: 24,\r\n },\r\n capture: {\r\n quality: 0.95,\r\n format: 'png' as const,\r\n },\r\n },\r\n} as const\r\n\r\n/* ================= 工具函数导出 ================= */\r\nexport {\r\n drawWatermark,\r\n captureElement,\r\n canvasToDataURL,\r\n downloadCanvas,\r\n printImage,\r\n}\r\n","/*\r\n * @Description: 表格动态行操作 Hooks — 增行、插行、删除行、复制行、调整行、单选功能、打印功能\r\n * @Migration: naive-ui-components 组件库迁移版本\r\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\r\n */\r\n\r\nimport {\r\n h,\r\n ref,\r\n computed,\r\n onBeforeUnmount,\r\n type VNode,\r\n type Ref,\r\n type VNodeChild,\r\n} from 'vue'\r\nimport {\r\n type DataTableRowKey,\r\n NButton,\r\n NTooltip,\r\n NModal,\r\n NButtonGroup,\r\n NSpace,\r\n useMessage,\r\n} from 'naive-ui/es'\r\nimport {\r\n usePrintWatermark,\r\n printPresets,\r\n type PrintWatermarkOptions,\r\n} from './usePrintWatermark'\r\nimport type { TableColumn, DataRecord } from '../types'\r\nimport C_Icon from '../../C_Icon/index.vue'\r\n\r\n/* ================= 类型定义 ================= */\r\nexport interface DynamicRowsOptions<T extends DataRecord = DataRecord> {\r\n /* 基础配置 */\r\n rowKey?: string | ((row: T) => DataTableRowKey)\r\n defaultRowData?: () => T\r\n\r\n /* 功能开关 */\r\n enableRadioSelection?: boolean\r\n enableAdd?: boolean\r\n enableInsert?: boolean\r\n enableDelete?: boolean\r\n enableCopy?: boolean\r\n enableMove?: boolean\r\n enablePrint?: boolean\r\n\r\n /* 打印配置 */\r\n printOptions?: PrintWatermarkOptions\r\n printPreset?: 'table' | 'form' | 'report'\r\n printWatermarkText?: string\r\n printTargetSelector?: string\r\n\r\n /* 交互配置 */\r\n confirmDelete?: boolean\r\n deleteConfirmText?: string\r\n\r\n /* 事件回调 */\r\n onRowChange?: (data: T[]) => void\r\n onSelectionChange?: (\r\n selectedKey: DataTableRowKey | null,\r\n selectedRow: T | null\r\n ) => void\r\n onRowAdd?: (newRow: T) => void\r\n onRowDelete?: (deletedRow: T, index: number) => void\r\n onRowCopy?: (originalRow: T, newRow: T) => void\r\n onRowMove?: (row: T, fromIndex: number, toIndex: number) => void\r\n}\r\n\r\nexport interface DynamicRowsReturn<T extends DataRecord = DataRecord> {\r\n /* 状态 */\r\n selectedRowKey: Ref<DataTableRowKey | null>\r\n selectedRowData: Ref<T | null>\r\n selectedRowIndex: Ref<number>\r\n canMoveUp: Ref<boolean>\r\n canMoveDown: Ref<boolean>\r\n deleteConfirmVisible: Ref<boolean>\r\n printLoading: Ref<boolean>\r\n printProgress: Ref<number>\r\n\r\n /* 行操作方法 */\r\n addRow: () => void\r\n insertRow: () => void\r\n deleteRow: () => void\r\n confirmDelete: () => void\r\n copyRow: () => void\r\n moveRowUp: () => void\r\n moveRowDown: () => void\r\n\r\n /* 选择方法 */\r\n selectRow: (key: DataTableRowKey) => void\r\n clearSelection: () => void\r\n\r\n /* 打印方法 */\r\n handlePrint: (elementRef: Ref<HTMLElement | undefined>) => Promise<void>\r\n handleDownload: (\r\n elementRef: Ref<HTMLElement | undefined>,\r\n filename?: string\r\n ) => Promise<void>\r\n handleQuickPrint: (elementRef: Ref<HTMLElement | undefined>) => Promise<void>\r\n\r\n /* 列增强方法 */\r\n enhanceColumns: (columns: TableColumn<T>[]) => TableColumn<T>[]\r\n\r\n /* 工具栏渲染 */\r\n renderToolbar: () => VNodeChild\r\n renderConfirmModal: () => VNodeChild\r\n}\r\n\r\n/* ================= 辅助函数 ================= */\r\n\r\n/** 生成唯一ID */\r\nfunction generateUniqueId(): string {\r\n return `row_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`\r\n}\r\n\r\n/** 获取行键值 */\r\nfunction getRowKey<T extends DataRecord>(\r\n row: T,\r\n rowKey: string | ((row: T) => DataTableRowKey)\r\n): DataTableRowKey {\r\n return typeof rowKey === 'function' ? rowKey(row) : (row as any)[rowKey]\r\n}\r\n\r\n/** 创建新行数据 */\r\nfunction createNewRow<T extends DataRecord>(\r\n defaultRowData: (() => T) | undefined,\r\n rowKey: string | ((row: T) => DataTableRowKey)\r\n): T {\r\n const newRow = defaultRowData?.() || ({} as T)\r\n const keyField = typeof rowKey === 'string' ? rowKey : 'id'\r\n return {\r\n ...newRow,\r\n [keyField]: generateUniqueId(),\r\n } as T\r\n}\r\n\r\n/* ================= 主函数 ================= */\r\n\r\n/**\r\n * 表格动态行操作功能组合\r\n */\r\nexport function useDynamicRows<T extends DataRecord = DataRecord>(\r\n data: Ref<T[]>,\r\n options: DynamicRowsOptions<T> = {}\r\n): DynamicRowsReturn<T> {\r\n const message = useMessage()\r\n\r\n /* 默认配置 */\r\n const finalOptions = {\r\n rowKey: 'id',\r\n enableRadioSelection: true,\r\n enableAdd: true,\r\n enableInsert: true,\r\n enableDelete: true,\r\n enableCopy: true,\r\n enableMove: true,\r\n enablePrint: true,\r\n confirmDelete: true,\r\n deleteConfirmText: '确定要删除选中的行吗?此操作不可撤销。',\r\n printPreset: 'table' as const,\r\n printTargetSelector: '.c-table-wrapper',\r\n defaultRowData: () => ({}) as T,\r\n ...options,\r\n }\r\n\r\n /* 状态 */\r\n const selectedRowKey = ref<DataTableRowKey | null>(null)\r\n const deleteConfirmVisible = ref(false)\r\n\r\n const selectedRowData = computed(() => {\r\n if (selectedRowKey.value === null) return null\r\n return (\r\n data.value.find(\r\n row => getRowKey(row, finalOptions.rowKey) === selectedRowKey.value\r\n ) || null\r\n )\r\n })\r\n\r\n const selectedRowIndex = computed(() => {\r\n if (!selectedRowData.value) return -1\r\n return data.value.findIndex(row => row === selectedRowData.value)\r\n })\r\n\r\n const canMoveUp = computed(() => selectedRowIndex.value > 0)\r\n const canMoveDown = computed(\r\n () =>\r\n selectedRowIndex.value >= 0 &&\r\n selectedRowIndex.value < data.value.length - 1\r\n )\r\n\r\n /* 打印功能 */\r\n const {\r\n loading: printLoading,\r\n progress: printProgress,\r\n printWithWatermark,\r\n downloadScreenshot,\r\n quickPrint,\r\n } = usePrintWatermark()\r\n\r\n /** 获取打印配置选项 */\r\n const getPrintOptions = (): PrintWatermarkOptions => {\r\n if (finalOptions.printOptions) {\r\n return finalOptions.printOptions\r\n }\r\n\r\n const preset = finalOptions.printPreset || 'table'\r\n const baseConfig = printPresets[preset]\r\n\r\n if (finalOptions.printWatermarkText && baseConfig.watermark) {\r\n return {\r\n ...baseConfig,\r\n watermark: {\r\n ...baseConfig.watermark,\r\n text: finalOptions.printWatermarkText,\r\n },\r\n }\r\n }\r\n\r\n return baseConfig\r\n }\r\n\r\n /** 更新表格数据 */\r\n const updateData = (newData: T[]) => {\r\n data.value = newData\r\n finalOptions.onRowChange?.(newData)\r\n }\r\n\r\n /** 添加新行到表格末尾 */\r\n const addRow = () => {\r\n if (!finalOptions.enableAdd) return\r\n\r\n const newRow = createNewRow(\r\n finalOptions.defaultRowData,\r\n finalOptions.rowKey\r\n )\r\n const newData = [...data.value, newRow]\r\n updateData(newData)\r\n finalOptions.onRowAdd?.(newRow)\r\n message.success('添加行成功')\r\n }\r\n\r\n /** 在选中行后插入新行 */\r\n const insertRow = () => {\r\n if (!finalOptions.enableInsert || !selectedRowData.value) {\r\n message.warning('请先选择一行数据')\r\n return\r\n }\r\n\r\n const newRow = createNewRow(\r\n finalOptions.defaultRowData,\r\n finalOptions.rowKey\r\n )\r\n const newData = [...data.value]\r\n newData.splice(selectedRowIndex.value + 1, 0, newRow)\r\n updateData(newData)\r\n finalOptions.onRowAdd?.(newRow)\r\n message.success('插入行成功')\r\n }\r\n\r\n /** 删除选中的行 */\r\n const deleteRow = () => {\r\n if (!finalOptions.enableDelete || !selectedRowData.value) {\r\n message.warning('请先选择要删除的行')\r\n return\r\n }\r\n\r\n if (finalOptions.confirmDelete) {\r\n deleteConfirmVisible.value = true\r\n } else {\r\n confirmDeleteFn()\r\n }\r\n }\r\n\r\n /** 确认删除操作 */\r\n const confirmDeleteFn = () => {\r\n if (!selectedRowData.value) return\r\n\r\n const deletedRow = selectedRowData.value\r\n const deletedIndex = selectedRowIndex.value\r\n\r\n const newData = data.value.filter((_, index) => index !== deletedIndex)\r\n updateData(newData)\r\n\r\n selectedRowKey.value = null\r\n finalOptions.onSelectionChange?.(null, null)\r\n finalOptions.onRowDelete?.(deletedRow, deletedIndex)\r\n\r\n message.success('删除行成功')\r\n deleteConfirmVisible.value = false\r\n }\r\n\r\n /** 复制选中的行 */\r\n const copyRow = () => {\r\n if (!finalOptions.enableCopy || !selectedRowData.value) {\r\n message.warning('请先选择要复制的行')\r\n return\r\n }\r\n\r\n const originalRow = selectedRowData.value\r\n const keyField =\r\n typeof finalOptions.rowKey === 'string' ? finalOptions.rowKey : 'id'\r\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\r\n const { [keyField]: _originalKey, ...rowData } = originalRow as Record<\r\n string,\r\n unknown\r\n >\r\n\r\n const newRow = {\r\n ...rowData,\r\n [keyField]: generateUniqueId(),\r\n } as T\r\n\r\n const newData = [...data.value, newRow]\r\n updateData(newData)\r\n finalOptions.onRowCopy?.(originalRow, newRow)\r\n message.success('复制行成功')\r\n }\r\n\r\n /** 将选中行向上移动 */\r\n const moveRowUp = () => {\r\n if (!finalOptions.enableMove || !canMoveUp.value) return\r\n\r\n const currentIndex = selectedRowIndex.value\r\n const newData = [...data.value]\r\n const movingRow = newData[currentIndex]\r\n\r\n ;[newData[currentIndex], newData[currentIndex - 1]] = [\r\n newData[currentIndex - 1],\r\n newData[currentIndex],\r\n ]\r\n\r\n updateData(newData)\r\n finalOptions.onRowMove?.(movingRow, currentIndex, currentIndex - 1)\r\n message.success('行已上移')\r\n }\r\n\r\n /** 将选中行向下移动 */\r\n const moveRowDown = () => {\r\n if (!finalOptions.enableMove || !canMoveDown.value) return\r\n\r\n const currentIndex = selectedRowIndex.value\r\n const newData = [...data.value]\r\n const movingRow = newData[currentIndex]\r\n\r\n ;[newData[currentIndex], newData[currentIndex + 1]] = [\r\n newData[currentIndex + 1],\r\n newData[currentIndex],\r\n ]\r\n\r\n updateData(newData)\r\n finalOptions.onRowMove?.(movingRow, currentIndex, currentIndex + 1)\r\n message.success('行已下移')\r\n }\r\n\r\n /** 选中指定行 */\r\n const selectRow = (key: DataTableRowKey) => {\r\n const row = data.value.find(\r\n row => getRowKey(row, finalOptions.rowKey) === key\r\n )\r\n if (row) {\r\n selectedRowKey.value = key\r\n finalOptions.onSelectionChange?.(key, row)\r\n }\r\n }\r\n\r\n /** 清空选择状态 */\r\n const clearSelection = () => {\r\n selectedRowKey.value = null\r\n finalOptions.onSelectionChange?.(null, null)\r\n }\r\n\r\n /** 处理打印操作 */\r\n const handlePrint = async (elementRef: Ref<HTMLElement | undefined>) => {\r\n if (!elementRef.value) {\r\n message.error('打印元素未找到')\r\n return\r\n }\r\n\r\n const printOptions = getPrintOptions()\r\n await printWithWatermark(elementRef.value, printOptions)\r\n }\r\n\r\n /** 处理下载操作 */\r\n const handleDownload = async (\r\n elementRef: Ref<HTMLElement | undefined>,\r\n filename?: string\r\n ) => {\r\n if (!elementRef.value) {\r\n message.error('下载元素未找到')\r\n return\r\n }\r\n\r\n const printOptions = getPrintOptions()\r\n await downloadScreenshot(elementRef.value, filename, printOptions)\r\n }\r\n\r\n /** 处理快速打印操作 */\r\n const handleQuickPrint = async (elementRef: Ref<HTMLElement | undefined>) => {\r\n if (!elementRef.value) {\r\n message.error('打印元素未找到')\r\n return\r\n }\r\n\r\n const watermarkText = finalOptions.printWatermarkText || 'Robot Admin'\r\n await quickPrint(elementRef.value, watermarkText)\r\n }\r\n\r\n /** 增强表格列配置,添加单选列 */\r\n const enhanceColumns = (columns: TableColumn<T>[]): TableColumn<T>[] => {\r\n const enhancedColumns = [...columns]\r\n\r\n if (finalOptions.enableRadioSelection) {\r\n enhancedColumns.unshift({\r\n key: '_radio_selection',\r\n title: '选择',\r\n width: 80,\r\n align: 'center',\r\n editable: false,\r\n render: (rowData: T) => {\r\n const rowKeyVal = getRowKey(rowData, finalOptions.rowKey)\r\n return h('div', { class: 'flex justify-center' }, [\r\n h('input', {\r\n type: 'radio',\r\n name: 'table-radio-selection',\r\n checked: selectedRowKey.value === rowKeyVal,\r\n class: 'cursor-pointer accent-blue-500 scale-110',\r\n onChange: (e: Event) => {\r\n if ((e.target as HTMLInputElement).checked) {\r\n selectRow(rowKeyVal)\r\n }\r\n },\r\n }),\r\n ])\r\n },\r\n } as TableColumn<T>)\r\n }\r\n\r\n return enhancedColumns\r\n }\r\n\r\n /** 渲染工具栏 */\r\n const renderToolbar = (): VNodeChild => {\r\n const buttons: VNode[] = []\r\n\r\n if (finalOptions.enablePrint) {\r\n buttons.push(\r\n h(\r\n NButton,\r\n {\r\n loading: printLoading.value,\r\n type: 'primary',\r\n ghost: true,\r\n onClick: async () => {\r\n try {\r\n const tableElement = document.querySelector(\r\n finalOptions.printTargetSelector\r\n )\r\n if (tableElement) {\r\n await handlePrint(ref(tableElement as HTMLElement))\r\n } else {\r\n console.warn('未找到表格容器元素')\r\n }\r\n } catch (error) {\r\n console.error('打印失败:', error)\r\n }\r\n },\r\n },\r\n {\r\n icon: () => h(C_Icon, { name: 'mdi:printer', title: '打印' }),\r\n default: () => '打印',\r\n }\r\n )\r\n )\r\n }\r\n\r\n const rowButtons: VNode[] = []\r\n\r\n if (finalOptions.enableAdd) {\r\n rowButtons.push(\r\n h(\r\n NButton,\r\n {\r\n onClick: addRow,\r\n type: 'primary',\r\n },\r\n {\r\n icon: () => h(C_Icon, { name: 'mdi:plus', title: '增行' }),\r\n default: () => '增行',\r\n }\r\n )\r\n )\r\n }\r\n\r\n if (finalOptions.enableInsert) {\r\n rowButtons.push(\r\n h(\r\n NTooltip,\r\n {\r\n disabled: !!selectedRowData.value,\r\n },\r\n {\r\n trigger: () =>\r\n h(\r\n NButton,\r\n {\r\n onClick: insertRow,\r\n disabled: !selectedRowData.value,\r\n type: 'primary',\r\n ghost: true,\r\n },\r\n {\r\n icon: () =>\r\n h(C_Icon, {\r\n name: 'mdi:table-row-plus-after',\r\n title: '插行',\r\n }),\r\n default: () => '插行',\r\n }\r\n ),\r\n default: () => '请先选择一行数据',\r\n }\r\n )\r\n )\r\n }\r\n\r\n if (finalOptions.enableDelete) {\r\n rowButtons.push(\r\n h(\r\n NTooltip,\r\n {\r\n disabled: !!selectedRowData.value,\r\n },\r\n {\r\n trigger: () =>\r\n h(\r\n NButton,\r\n {\r\n onClick: deleteRow,\r\n disabled: !selectedRowData.value,\r\n type: 'error',\r\n ghost: true,\r\n },\r\n {\r\n icon: () =>\r\n h(C_Icon, { name: 'mdi:delete', title: '删除行' }),\r\n default: () => '删除行',\r\n }\r\n ),\r\n default: () => '请先选择要删除的行',\r\n }\r\n )\r\n )\r\n }\r\n\r\n if (finalOptions.enableCopy) {\r\n rowButtons.push(\r\n h(\r\n NTooltip,\r\n {\r\n disabled: !!selectedRowData.value,\r\n },\r\n {\r\n trigger: () =>\r\n h(\r\n NButton,\r\n {\r\n onClick: copyRow,\r\n disabled: !selectedRowData.value,\r\n type: 'info',\r\n ghost: true,\r\n },\r\n {\r\n icon: () =>\r\n h(C_Icon, { name: 'mdi:content-copy', title: '复制行' }),\r\n default: () => '复制行',\r\n }\r\n ),\r\n default: () => '请先选择要复制的行',\r\n }\r\n )\r\n )\r\n }\r\n\r\n if (finalOptions.enableMove) {\r\n rowButtons.push(\r\n h(\r\n NTooltip,\r\n {\r\n disabled: canMoveUp.value,\r\n },\r\n {\r\n trigger: () =>\r\n h(\r\n NButton,\r\n {\r\n onClick: moveRowUp,\r\n disabled: !canMoveUp.value,\r\n type: 'warning',\r\n ghost: true,\r\n },\r\n {\r\n icon: () =>\r\n h(C_Icon, { name: 'mdi:arrow-up', title: '上移' }),\r\n default: () => '上移',\r\n }\r\n ),\r\n default: () =>\r\n !selectedRowData.value ? '请先选择数据' : '已经是第一行',\r\n }\r\n ),\r\n h(\r\n NTooltip,\r\n {\r\n disabled: canMoveDown.value,\r\n },\r\n {\r\n trigger: () =>\r\n h(\r\n NButton,\r\n {\r\n onClick: moveRowDown,\r\n disabled: !canMoveDown.value,\r\n type: 'warning',\r\n ghost: true,\r\n },\r\n {\r\n icon: () =>\r\n h(C_Icon, { name: 'mdi:arrow-down', title: '下移' }),\r\n default: () => '下移',\r\n }\r\n ),\r\n default: () =>\r\n !selectedRowData.value ? '请先选择数据' : '已经是最后一行',\r\n }\r\n )\r\n )\r\n }\r\n\r\n if (rowButtons.length > 0) {\r\n buttons.push(h(NButtonGroup, {}, () => rowButtons))\r\n }\r\n\r\n return h('div', { class: 'dynamic-rows-toolbar mb-4 flex justify-end' }, [\r\n h(NSpace, {}, () => buttons),\r\n ])\r\n }\r\n\r\n /** 渲染删除确认模态框 */\r\n const renderConfirmModal = (): VNodeChild => {\r\n return h(NModal, {\r\n show: deleteConfirmVisible.value,\r\n 'onUpdate:show': (show: boolean) => {\r\n deleteConfirmVisible.value = show\r\n },\r\n preset: 'dialog',\r\n title: '确认删除',\r\n content: finalOptions.deleteConfirmText,\r\n positiveText: '确认删除',\r\n negativeText: '取消',\r\n onPositiveClick: confirmDeleteFn,\r\n })\r\n }\r\n\r\n /* 组件卸载时清理 */\r\n onBeforeUnmount(() => {\r\n selectedRowKey.value = null\r\n deleteConfirmVisible.value = false\r\n })\r\n\r\n return {\r\n /* 状态 */\r\n selectedRowKey,\r\n selectedRowData,\r\n selectedRowIndex,\r\n canMoveUp,\r\n canMoveDown,\r\n deleteConfirmVisible,\r\n printLoading,\r\n printProgress,\r\n\r\n /* 行操作方法 */\r\n addRow,\r\n insertRow,\r\n deleteRow,\r\n confirmDelete: confirmDeleteFn,\r\n copyRow,\r\n moveRowUp,\r\n moveRowDown,\r\n\r\n /* 选择方法 */\r\n selectRow,\r\n clearSelection,\r\n\r\n /* 打印方法 */\r\n handlePrint,\r\n handleDownload,\r\n handleQuickPrint,\r\n\r\n /* 列增强方法 */\r\n enhanceColumns,\r\n\r\n /* 工具栏渲染 */\r\n renderToolbar,\r\n renderConfirmModal,\r\n }\r\n}\r\n","/*\n * @Description: 表格统一状态管理器\n * @Migration: naive-ui-components 组件库迁移版本\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\n */\n\nimport { ref, computed, nextTick } from 'vue'\nimport type { DataTableRowKey } from 'naive-ui/es'\nimport type { DataRecord, ParentChildLinkMode } from '../types'\nimport { useRowEdit } from './useRowEdit'\nimport { useCellEdit } from './useCellEdit'\nimport { useModalEdit } from './useModalEdit'\nimport { useTableExpand } from './useTableExpand'\nimport { useDynamicRows, type DynamicRowsOptions } from './useDynamicRow'\n\n/**\n * 表格管理器配置接口\n */\ninterface TableManagerConfig {\n /* 基础配置 */\n editable: boolean\n editMode: string\n showRowActions: boolean\n modalTitle: string\n modalWidth: number\n\n /* 展开配置 */\n expandable: boolean\n defaultExpandedKeys?: DataTableRowKey[]\n onLoadExpandData?: (row: DataRecord) => Promise<any[]> | any[]\n renderExpandContent?: unknown\n rowExpandable?: (row: DataRecord) => boolean\n\n /* 选择配置 */\n enableSelection: boolean\n defaultCheckedKeys?: DataTableRowKey[]\n rowCheckable?: (row: DataRecord) => boolean\n maxSelection?: number\n enableChildSelection: boolean\n childRowCheckable?: (childRow: DataRecord, parentRow: DataRecord) => boolean\n enableParentChildLink: boolean\n parentChildLinkMode: ParentChildLinkMode\n\n /* 动态行配置 */\n dynamicRows?: DynamicRowsOptions<DataRecord>\n}\n\n/**\n * 表格管理器参数接口\n */\ninterface TableManagerParams {\n config: TableManagerConfig\n data: () => DataRecord[]\n rowKey: (row: DataRecord) => DataTableRowKey\n emit: (...args: any[]) => void\n /** 列配置(用于编辑校验) */\n columns?: () => import('../types').TableColumn[]\n}\n\n/**\n * 事件处理器接口\n */\ninterface EventHandlers {\n onSave: (\n rowData: DataRecord,\n rowIndex: number,\n columnKey?: string\n ) => Promise<void>\n onCancel: (rowData: DataRecord, rowIndex: number) => void\n onExpandChange: (\n keys: DataTableRowKey[],\n row?: DataRecord,\n expanded?: boolean\n ) => void\n onSelectionChange: (\n checkedKeys: DataTableRowKey[],\n checkedRows: DataRecord[],\n childSelections: Map<DataTableRowKey, DataTableRowKey[]>\n ) => void\n onChildSelectionChange: (\n parentKey: DataTableRowKey,\n childKeys: DataTableRowKey[],\n childRows: DataRecord[]\n ) => void\n onRowChange: (data: DataRecord[]) => void\n onRowSelectionChange: (\n selectedKey: DataTableRowKey | null,\n selectedRow: DataRecord | null\n ) => void\n onRowAdd: (newRow: DataRecord) => void\n onRowDelete: (deletedRow: DataRecord, index: number) => void\n onRowCopy: (originalRow: DataRecord, newRow: DataRecord) => void\n onRowMove: (row: DataRecord, fromIndex: number, toIndex: number) => void\n}\n\n/**\n * 表格统一状态管理器\n */\nexport function useTableManager(params: TableManagerParams) {\n const { config, data, rowKey, emit } = params\n\n /* ================= 事件处理器工厂 ================= */\n\n /** 创建事件处理器集合 */\n const createEventHandlers = (): EventHandlers => ({\n /* 通用事件 */\n onSave: async (\n rowData: DataRecord,\n rowIndex: number,\n columnKey?: string\n ) => {\n if (!rowData || rowIndex < 0 || rowIndex >= data().length) return\n\n const newData = [...data()]\n newData[rowIndex] = { ...newData[rowIndex], ...rowData }\n\n emit('update:data', newData)\n await nextTick()\n emit('save', newData[rowIndex], rowIndex, columnKey)\n },\n\n onCancel: (rowData: DataRecord, rowIndex: number) => {\n emit('cancel', rowData, rowIndex)\n },\n\n /* 展开选择事件 */\n onExpandChange: (\n keys: DataTableRowKey[],\n row?: DataRecord,\n expanded?: boolean\n ) => {\n emit('expand-change', keys, row, expanded)\n },\n\n onSelectionChange: (\n checkedKeys: DataTableRowKey[],\n checkedRows: DataRecord[],\n childSelections: Map<DataTableRowKey, DataTableRowKey[]>\n ) => {\n emit('selection-change', checkedKeys, checkedRows, childSelections)\n },\n\n onChildSelectionChange: (\n parentKey: DataTableRowKey,\n childKeys: DataTableRowKey[],\n childRows: DataRecord[]\n ) => {\n emit('child-selection-change', parentKey, childKeys, childRows)\n },\n\n /* 动态行事件 */\n onRowChange: (data: DataRecord[]) => {\n emit('update:data', data)\n config.dynamicRows?.onRowChange?.(data)\n },\n\n onRowSelectionChange: (\n selectedKey: DataTableRowKey | null,\n selectedRow: DataRecord | null\n ) => {\n emit('row-selection-change', selectedKey, selectedRow)\n config.dynamicRows?.onSelectionChange?.(selectedKey, selectedRow)\n },\n\n onRowAdd: (newRow: DataRecord) => {\n emit('row-add', newRow)\n config.dynamicRows?.onRowAdd?.(newRow)\n },\n\n onRowDelete: (deletedRow: DataRecord, index: number) => {\n emit('row-delete', deletedRow, index)\n config.dynamicRows?.onRowDelete?.(deletedRow, index)\n },\n\n onRowCopy: (originalRow: DataRecord, newRow: DataRecord) => {\n emit('row-copy', originalRow, newRow)\n config.dynamicRows?.onRowCopy?.(originalRow, newRow)\n },\n\n onRowMove: (row: DataRecord, fromIndex: number, toIndex: number) => {\n emit('row-move', row, fromIndex, toIndex)\n config.dynamicRows?.onRowMove?.(row, fromIndex, toIndex)\n },\n })\n\n const eventHandlers = createEventHandlers()\n\n /* ================= 功能状态初始化 ================= */\n\n /** 初始化编辑功能状态 */\n const initEditStates = () => {\n const rowEdit = useRowEdit({\n data,\n rowKey,\n onSave: eventHandlers.onSave,\n onCancel: eventHandlers.onCancel,\n columns: params.columns,\n })\n\n const cellEdit = useCellEdit({\n data,\n rowKey,\n onSave: eventHandlers.onSave,\n columns: params.columns,\n })\n\n const modalEdit = useModalEdit({\n data,\n rowKey,\n onSave: eventHandlers.onSave,\n onCancel: eventHandlers.onCancel,\n })\n\n return { rowEdit, cellEdit, modalEdit }\n }\n\n /** 初始化展开功能状态 */\n const initExpandState = () => {\n const needsExpand =\n config.expandable || config.enableSelection || config.enableChildSelection\n\n if (!needsExpand) return null\n\n return useTableExpand({\n data: computed(() => data()),\n rowKey,\n childRowKey: (child: DataRecord) => child.id as DataTableRowKey,\n\n defaultExpandedKeys: config.defaultExpandedKeys,\n onLoadData: config.onLoadExpandData,\n renderContent: config.renderExpandContent as any,\n rowExpandable: config.rowExpandable,\n enableSelection: config.enableSelection,\n defaultCheckedKeys: config.defaultCheckedKeys,\n rowCheckable: config.rowCheckable,\n maxSelection: config.maxSelection,\n enableChildSelection: config.enableChildSelection,\n childRowCheckable: config.childRowCheckable,\n enableParentChildLink: config.enableParentChildLink,\n parentChildLinkMode: config.parentChildLinkMode,\n\n onExpandChange: eventHandlers.onExpandChange,\n onSelectionChange: eventHandlers.onSelectionChange as any,\n onChildSelectionChange: eventHandlers.onChildSelectionChange,\n })\n }\n\n /** 初始化动态行功能状态 */\n const initDynamicRowsState = () => {\n if (!config.dynamicRows) return null\n\n const dynamicOptions: DynamicRowsOptions<DataRecord> = {\n ...config.dynamicRows,\n onRowChange: eventHandlers.onRowChange,\n onSelectionChange: eventHandlers.onRowSelectionChange,\n onRowAdd: eventHandlers.onRowAdd,\n onRowDelete: eventHandlers.onRowDelete,\n onRowCopy: eventHandlers.onRowCopy,\n onRowMove: eventHandlers.onRowMove,\n }\n\n return useDynamicRows(\n computed(() => data()),\n dynamicOptions\n )\n }\n\n /* 初始化所有功能状态 */\n const editStates = initEditStates()\n const expandState = initExpandState()\n const dynamicRowsState = initDynamicRowsState()\n\n /* ================= 统一的状态管理器 ================= */\n\n /** 创建统一的状态管理器 */\n const createStateManager = () => {\n return {\n /* 编辑控制 */\n edit: {\n /** 开始编辑 */\n start(rowKey: DataTableRowKey, columnKey?: string) {\n const mode = config.editMode\n if (mode === 'modal') editStates.modalEdit.startEdit(rowKey)\n else if (mode === 'cell' && columnKey)\n editStates.cellEdit.startEditCell(rowKey, columnKey)\n else if (mode === 'row' || mode === 'both')\n editStates.rowEdit.startEditRow(rowKey)\n },\n\n /** 取消编辑 */\n cancel() {\n if (editStates.modalEdit.isModalVisible.value)\n editStates.modalEdit.cancelEdit()\n else if (editStates.cellEdit.editingCell.value.rowKey)\n editStates.cellEdit.cancelEditCell()\n else if (editStates.rowEdit.editingRowKey.value)\n editStates.rowEdit.cancelEditRow()\n },\n\n /** 保存编辑 */\n async save() {\n if (editStates.modalEdit.isModalVisible.value)\n await editStates.modalEdit.saveEdit()\n else if (editStates.cellEdit.editingCell.value.rowKey)\n await editStates.cellEdit.saveEditCell()\n else if (editStates.rowEdit.editingRowKey.value)\n await editStates.rowEdit.saveEditRow()\n },\n\n /** 是否正在编辑 */\n isEditing(rowKey: DataTableRowKey, columnKey?: string) {\n if (config.editMode === 'modal')\n return editStates.modalEdit.isEditingRow(rowKey)\n if (columnKey)\n return editStates.cellEdit.isEditingCell(rowKey, columnKey)\n return editStates.rowEdit.isEditingRow(rowKey)\n },\n\n /** 获取当前编辑数据 */\n getEditingData() {\n if (editStates.modalEdit.isModalVisible.value)\n return editStates.modalEdit.editingData\n if (editStates.rowEdit.editingRowKey.value) {\n return editStates.rowEdit.getEditingRowData(\n editStates.rowEdit.editingRowKey.value!\n )\n }\n return null\n },\n },\n\n /* 展开控制 */\n expand: {\n /** 展开行 */\n async row(rowKey: DataTableRowKey) {\n if (expandState) {\n const currentKeys = [...expandState.expandedKeys.value]\n if (!currentKeys.includes(rowKey)) {\n currentKeys.push(rowKey)\n expandState.handleExpandChange(currentKeys)\n }\n }\n },\n\n /** 折叠行 */\n collapse(rowKey: DataTableRowKey) {\n if (expandState) {\n const currentKeys = expandState.expandedKeys.value.filter(\n key => key !== rowKey\n )\n expandState.handleExpandChange(currentKeys)\n }\n },\n\n /** 展开折叠切换 */\n async toggle(rowKey: DataTableRowKey) {\n if (expandState?.expandedKeys.value.includes(rowKey)) {\n this.collapse(rowKey)\n } else {\n await this.row(rowKey)\n }\n },\n\n /** 展开所有行 */\n async all() {\n await expandState?.expandAll()\n },\n\n /** 折叠所有行 */\n collapseAll() {\n expandState?.collapseAll()\n },\n\n /** 检查行是否已展开 */\n isExpanded(rowKey: DataTableRowKey) {\n return expandState?.expandedKeys.value.includes(rowKey) ?? false\n },\n },\n\n /* 选择控制 */\n selection: {\n /** 选择指定行 */\n select(rowKey: DataTableRowKey) {\n if (\n expandState?.checkedKeys.value &&\n !expandState.checkedKeys.value.includes(rowKey)\n ) {\n const newKeys = [...expandState.checkedKeys.value, rowKey]\n expandState.handleSelectionChange(newKeys)\n }\n },\n\n /** 取消选择指定行 */\n unselect(rowKey: DataTableRowKey) {\n if (expandState?.checkedKeys.value) {\n const newKeys = expandState.checkedKeys.value.filter(\n key => key !== rowKey\n )\n expandState.handleSelectionChange(newKeys)\n }\n },\n\n /** 选择所有行 */\n all() {\n expandState?.selectAll()\n },\n\n /** 清除所有行选择 */\n clear() {\n expandState?.clearSelection()\n },\n\n /** 检查行是否已选择 */\n isSelected(rowKey: DataTableRowKey) {\n return expandState?.checkedKeys.value.includes(rowKey) ?? false\n },\n\n /** 获取所有已选择的行数据 */\n getSelected() {\n if (!expandState?.checkedKeys.value) return []\n return data().filter(row =>\n expandState!.checkedKeys.value.includes(rowKey(row))\n )\n },\n },\n\n /* 子选择控制 */\n childSelection: {\n /** 选择指定父行下的子行 */\n select(parentKey: DataTableRowKey, childKey: DataTableRowKey) {\n if (expandState?.childSelections.value) {\n const current =\n expandState.childSelections.value.get(parentKey) || []\n if (!current.includes(childKey)) {\n const newSelection = [...current, childKey]\n expandState.childSelections.value.set(parentKey, newSelection)\n emit('child-selection-change', parentKey, newSelection, [])\n }\n }\n },\n\n /** 取消选择指定父行下的子行 */\n unselect(parentKey: DataTableRowKey, childKey: DataTableRowKey) {\n if (expandState?.childSelections.value) {\n const current =\n expandState.childSelections.value.get(parentKey) || []\n const newSelection = current.filter(k => k !== childKey)\n expandState.childSelections.value.set(parentKey, newSelection)\n emit('child-selection-change', parentKey, newSelection, [])\n }\n },\n\n /** 选择指定父行下的所有子行 */\n selectAll(parentKey: DataTableRowKey) {\n if (\n expandState?.childSelections.value &&\n expandState.expandDataMap?.value\n ) {\n const expandData =\n expandState.expandDataMap.value.get(parentKey) || []\n const allChildKeys = expandData.map(\n (child: DataRecord) => child.id as DataTableRowKey\n )\n expandState.childSelections.value.set(parentKey, allChildKeys)\n emit('child-selection-change', parentKey, allChildKeys, expandData)\n }\n },\n\n /** 清除指定父行下的所有子行选择 */\n clear(parentKey: DataTableRowKey) {\n if (expandState?.childSelections.value) {\n expandState.childSelections.value.set(parentKey, [])\n emit('child-selection-change', parentKey, [], [])\n }\n },\n\n /** 获取指定父行下所有已选择的子行数据 */\n getSelected(parentKey: DataTableRowKey) {\n if (\n !expandState?.childSelections.value ||\n !expandState.expandDataMap?.value\n ) {\n return []\n }\n const selectedKeys =\n expandState.childSelections.value.get(parentKey) || []\n const expandData =\n expandState.expandDataMap.value.get(parentKey) || []\n return expandData.filter((child: DataRecord) =>\n selectedKeys.includes(child.id as DataTableRowKey)\n )\n },\n },\n\n /* 动态行控制 */\n dynamicRows: {\n /** 添加行 */\n add() {\n dynamicRowsState?.addRow()\n },\n /** 插入行 */\n insert() {\n dynamicRowsState?.insertRow()\n },\n\n /** 删除行 */\n delete() {\n dynamicRowsState?.deleteRow()\n },\n /** 复制行 */\n copy() {\n dynamicRowsState?.copyRow()\n },\n /** 上移动态行 */\n moveUp() {\n dynamicRowsState?.moveRowUp()\n },\n /** 下移动态行 */\n moveDown() {\n dynamicRowsState?.moveRowDown()\n },\n /** 清空动态行数据 */\n clearSelection() {\n dynamicRowsState?.clearSelection()\n },\n /** 获取当前选中的动态行数据 */\n getSelected() {\n return dynamicRowsState?.selectedRowData.value || null\n },\n\n /** 打印表格 */\n async print(elementRef?: HTMLElement) {\n if (dynamicRowsState && elementRef) {\n await dynamicRowsState.handlePrint(ref(elementRef))\n }\n },\n\n /** 导出表格数据 */\n async download(elementRef?: HTMLElement, filename?: string) {\n if (dynamicRowsState && elementRef) {\n await dynamicRowsState.handleDownload(ref(elementRef), filename)\n }\n },\n },\n\n /** 清除所有选择 */\n clearAllSelections() {\n expandState?.clearAllSelections()\n },\n }\n }\n\n const stateManager = createStateManager()\n\n /* ================= 计算属性 ================= */\n\n const expandedKeys = computed(() => expandState?.expandedKeys.value ?? [])\n const checkedKeys = computed(() => expandState?.checkedKeys.value ?? [])\n\n /* ================= 返回管理器实例 ================= */\n\n return {\n /* 编辑状态 */\n editStates,\n\n /* 功能状态 */\n expandState,\n dynamicRowsState,\n\n /* 状态管理器 */\n stateManager,\n\n /* 计算属性 */\n expandedKeys,\n checkedKeys,\n\n /* 事件处理器 */\n eventHandlers,\n }\n}\n","/*\r\n * @Description: 表格分页逻辑 Hook\r\n * @Migration: naive-ui-components 组件库迁移版本\r\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\r\n */\r\n\r\nimport { ref, computed, watch, watchEffect, unref } from \"vue\";\r\nimport type { Ref, ComputedRef } from \"vue\";\r\nimport type { PaginationConfig, DataRecord } from \"../types\";\r\n\r\n/* ================= 类型定义 ================= */\r\n\r\n/** 分页信息对象(naive-ui pagination prefix/suffix 回调参数) */\r\ninterface PaginationRenderInfo {\r\n itemCount: number;\r\n startIndex: number;\r\n endIndex: number;\r\n}\r\n\r\nexport interface UsePaginationOptions<T extends DataRecord = DataRecord> {\r\n /** 数据源 - 支持函数、Ref 或 ComputedRef */\r\n data: (() => T[]) | Ref<T[]> | ComputedRef<T[]>;\r\n /** 分页配置 */\r\n config: Ref<PaginationConfig | null> | ComputedRef<PaginationConfig | null>;\r\n /** 事件触发器 */\r\n emit?: (event: \"pagination-change\", page: number, pageSize: number) => void;\r\n}\r\n\r\nexport interface UsePaginationReturn<T extends DataRecord = DataRecord> {\r\n currentPage: Ref<number>;\r\n currentPageSize: Ref<number>;\r\n paginatedData: ComputedRef<T[]>;\r\n paginationConfig: ComputedRef<any>;\r\n handlePageChange: (page: number) => void;\r\n handlePageSizeChange: (pageSize: number) => void;\r\n resetToFirstPage: () => void;\r\n getTotalPages: () => number;\r\n}\r\n\r\n/* ================= 默认配置 ================= */\r\nconst DEFAULT_PAGINATION_CONFIG = {\r\n enabled: true,\r\n page: 1,\r\n pageSize: 10,\r\n showSizePicker: true,\r\n showQuickJumper: true,\r\n pageSizes: [10, 20, 50, 100],\r\n simple: false,\r\n size: \"medium\" as const,\r\n};\r\n\r\n/* ================= Hook 实现 ================= */\r\nexport function usePagination<T extends DataRecord = DataRecord>(\r\n options: UsePaginationOptions<T>,\r\n): UsePaginationReturn<T> {\r\n const { data, config, emit } = options;\r\n\r\n /* ================= 响应式状态 ================= */\r\n const currentPage = ref(1);\r\n const currentPageSize = ref(10);\r\n\r\n /* ================= 数据源统一处理 ================= */\r\n const dataSource = computed(() => {\r\n if (typeof data === \"function\") {\r\n return data() || [];\r\n }\r\n return unref(data) || [];\r\n });\r\n\r\n /* ================= 计算属性 ================= */\r\n\r\n /** 分页后的数据 */\r\n const paginatedData = computed(() => {\r\n if (!config.value?.enabled) {\r\n return dataSource.value;\r\n }\r\n\r\n const start = (currentPage.value - 1) * currentPageSize.value;\r\n const end = start + currentPageSize.value;\r\n return dataSource.value.slice(start, end);\r\n });\r\n\r\n /** 分页配置对象 */\r\n const paginationConfig = computed(() => {\r\n if (!config.value?.enabled) return null;\r\n\r\n return {\r\n page: currentPage.value,\r\n pageSize: currentPageSize.value,\r\n itemCount: dataSource.value.length,\r\n showSizePicker:\r\n config.value.showSizePicker ?? DEFAULT_PAGINATION_CONFIG.showSizePicker,\r\n showQuickJumper:\r\n config.value.showQuickJumper ??\r\n DEFAULT_PAGINATION_CONFIG.showQuickJumper,\r\n pageSizes: config.value.pageSizes ?? DEFAULT_PAGINATION_CONFIG.pageSizes,\r\n simple: config.value.simple ?? DEFAULT_PAGINATION_CONFIG.simple,\r\n size: config.value.size ?? DEFAULT_PAGINATION_CONFIG.size,\r\n prefix: (info: PaginationRenderInfo) => `共 ${info.itemCount} 条`,\r\n suffix: (info: PaginationRenderInfo) =>\r\n `第 ${info.startIndex + 1}-${info.endIndex} 条`,\r\n \"onUpdate:page\": handlePageChange,\r\n \"onUpdate:pageSize\": handlePageSizeChange,\r\n };\r\n });\r\n\r\n /* ================= 事件处理 ================= */\r\n\r\n /** 页码变化处理 */\r\n const handlePageChange = (page: number) => {\r\n currentPage.value = page;\r\n emit?.(\"pagination-change\", page, currentPageSize.value);\r\n };\r\n\r\n /** 页面大小变化处理 */\r\n const handlePageSizeChange = (pageSize: number) => {\r\n currentPageSize.value = pageSize;\r\n currentPage.value = 1;\r\n emit?.(\"pagination-change\", 1, pageSize);\r\n };\r\n\r\n /** 重置到第一页 */\r\n const resetToFirstPage = () => {\r\n if (currentPage.value !== 1) {\r\n handlePageChange(1);\r\n }\r\n };\r\n\r\n /** 获取总页数 */\r\n const getTotalPages = () => {\r\n if (!config.value?.enabled || currentPageSize.value === 0) return 1;\r\n return Math.ceil(dataSource.value.length / currentPageSize.value);\r\n };\r\n\r\n /* ================= 响应式逻辑 ================= */\r\n\r\n /** 初始化分页配置 */\r\n watchEffect(() => {\r\n if (config.value?.enabled) {\r\n currentPage.value = config.value.page ?? DEFAULT_PAGINATION_CONFIG.page;\r\n currentPageSize.value =\r\n config.value.pageSize ?? DEFAULT_PAGINATION_CONFIG.pageSize;\r\n }\r\n });\r\n\r\n /** 监听数据变化,确保分页状态正确 */\r\n watch(\r\n () => dataSource.value.length,\r\n (newLength) => {\r\n if (config.value?.enabled && currentPage.value > 1) {\r\n const maxPage = Math.ceil(newLength / currentPageSize.value);\r\n if (currentPage.value > maxPage) {\r\n currentPage.value = Math.max(1, maxPage);\r\n }\r\n }\r\n },\r\n );\r\n\r\n return {\r\n currentPage,\r\n currentPageSize,\r\n paginatedData,\r\n paginationConfig,\r\n handlePageChange,\r\n handlePageSizeChange,\r\n resetToFirstPage,\r\n getTotalPages,\r\n };\r\n}\r\n","/*\r\n * @Description: 表格操作按钮Hook\r\n * @Migration: naive-ui-components 组件库迁移版本\r\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\r\n */\r\n\r\nimport { h, type VNodeChild } from 'vue'\r\nimport { NButton, NSpace, NDropdown, useMessage, useDialog } from 'naive-ui'\r\nimport type { DataTableRowKey } from 'naive-ui/es'\r\nimport type {\r\n DataRecord,\r\n ApiFunction,\r\n UseTableActionsOptions,\r\n UseTableActionsReturn,\r\n} from '../types'\r\nimport C_Icon from '../../C_Icon/index.vue'\r\n\r\n/**\r\n * 表格操作Hook\r\n */\r\nexport function useTableActions<T extends DataRecord = DataRecord>(\r\n options: UseTableActionsOptions<T>\r\n): UseTableActionsReturn<T> {\r\n const { actions, config, tableManager, rowKey, emit, onViewDetail } = options\r\n const message = useMessage()\r\n const dialog = useDialog()\r\n\r\n /* 检查操作是否启用 */\r\n const isActionEnabled = (key: 'edit' | 'delete' | 'detail') =>\r\n actions.value?.[key] !== false\r\n\r\n /* 类型守卫:检查是否为有效API函数 */\r\n const isValidApiFunction = <TData extends DataRecord>(\r\n action: false | ApiFunction<TData> | undefined\r\n ): action is ApiFunction<TData> => {\r\n return action !== false && typeof action === 'function'\r\n }\r\n\r\n /* 自动提取API响应数据 */\r\n const extractApiResponseData = <TData>(response: unknown): TData => {\r\n if (response && typeof response === 'object' && 'data' in response) {\r\n return (response as Record<string, unknown>).data as TData\r\n }\r\n return response as TData\r\n }\r\n\r\n /* 创建按钮的通用方法 */\r\n const createButton = (\r\n icon: string,\r\n title: string,\r\n type = 'primary',\r\n onClick: () => void\r\n ) =>\r\n h(\r\n NButton,\r\n { size: 'small', type: type as any, quaternary: true, onClick },\r\n () => [h(C_Icon, { name: icon, size: 14, title })]\r\n )\r\n\r\n /* 处理编辑操作 */\r\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\r\n const handleEdit = (row: T, _index?: number) => {\r\n const rowKeyValue = rowKey(row)\r\n const editAction = actions.value?.edit\r\n\r\n if (config.value.editMode === 'modal') {\r\n tableManager.editStates.modalEdit.startEdit(\r\n rowKeyValue,\r\n { ...row },\r\n editAction\r\n )\r\n } else {\r\n tableManager.editStates.rowEdit.startEditRow(rowKeyValue, editAction)\r\n }\r\n }\r\n\r\n /* 执行删除API */\r\n const executeDelete = async (\r\n deleteAction: ApiFunction<T>,\r\n row: T,\r\n index: number\r\n ) => {\r\n try {\r\n await deleteAction(row, index)\r\n message.success('删除成功')\r\n emit('row-delete', row, index)\r\n } catch (error) {\r\n console.error('删除失败:', error)\r\n message.error('删除失败')\r\n throw error\r\n }\r\n }\r\n\r\n /* 处理删除操作 */\r\n const handleDelete = (row: T, index: number) => {\r\n const deleteAction = actions.value?.delete\r\n\r\n if (!isValidApiFunction(deleteAction)) {\r\n return\r\n }\r\n\r\n dialog.warning({\r\n title: '确认删除',\r\n content: '确定要删除这条记录吗?',\r\n positiveText: '确定',\r\n negativeText: '取消',\r\n onPositiveClick: () => executeDelete(deleteAction, row, index),\r\n })\r\n }\r\n\r\n /* 处理详情操作 */\r\n const handleDetail = async (row: T, index: number) => {\r\n const detailAction = actions.value?.detail\r\n\r\n if (!isValidApiFunction(detailAction)) {\r\n onViewDetail?.(row)\r\n return\r\n }\r\n\r\n try {\r\n const apiResponse = await detailAction(row, index)\r\n const detailData = extractApiResponseData<T>(apiResponse)\r\n onViewDetail?.(detailData || row)\r\n } catch (error) {\r\n console.error('获取详情失败:', error)\r\n message.error('获取详情失败')\r\n onViewDetail?.(row)\r\n }\r\n }\r\n\r\n /* 渲染行编辑按钮 */\r\n const renderRowEditButtons = (rowKeyValue: DataTableRowKey) => {\r\n const isEditing = tableManager.editStates.rowEdit.isEditingRow(rowKeyValue)\r\n\r\n if (isEditing) {\r\n return [\r\n createButton('mdi:check', '保存', 'primary', () =>\r\n tableManager.editStates.rowEdit.saveEditRow()\r\n ),\r\n createButton('mdi:close', '取消', 'default', () =>\r\n tableManager.editStates.rowEdit.cancelEditRow()\r\n ),\r\n ]\r\n }\r\n\r\n return [\r\n createButton('mdi:pencil', '编辑', 'warning', () =>\r\n tableManager.editStates.rowEdit.startEditRow(rowKeyValue)\r\n ),\r\n ]\r\n }\r\n\r\n /* 渲染基础操作按钮 */\r\n const renderBasicActions = (row: T, index: number) => {\r\n const buttons: VNodeChild[] = []\r\n\r\n if (isActionEnabled('detail')) {\r\n buttons.push(\r\n createButton('mdi:eye', '详情', 'info', () => handleDetail(row, index))\r\n )\r\n }\r\n\r\n if (\r\n config.value.editMode === 'modal' &&\r\n (isActionEnabled('edit') || config.value.editable)\r\n ) {\r\n buttons.push(\r\n createButton('mdi:pencil', '编辑', 'warning', () =>\r\n handleEdit(row, index)\r\n )\r\n )\r\n }\r\n\r\n if (isActionEnabled('delete')) {\r\n buttons.push(\r\n createButton('mdi:delete', '删除', 'error', () =>\r\n handleDelete(row, index)\r\n )\r\n )\r\n }\r\n\r\n return buttons\r\n }\r\n\r\n /* 渲染自定义操作下拉菜单 */\r\n const renderCustomDropdown = (row: T, index: number) => {\r\n const customActions = actions.value?.custom\r\n if (!customActions?.length) return null\r\n\r\n const visibleActions = customActions.filter(action =>\r\n action.show ? action.show(row, index) : true\r\n )\r\n\r\n if (!visibleActions.length) return null\r\n\r\n const options = visibleActions.map(action => {\r\n const label =\r\n typeof action.label === 'function'\r\n ? action.label(row, index)\r\n : action.label\r\n const icon =\r\n typeof action.icon === 'function'\r\n ? action.icon(row, index)\r\n : action.icon\r\n\r\n return {\r\n label,\r\n key: action.key,\r\n icon: () => h(C_Icon, { name: icon, size: 14 }),\r\n disabled: action.disabled?.(row, index) || false,\r\n }\r\n })\r\n\r\n return h(\r\n NDropdown,\r\n {\r\n options,\r\n onSelect: (key: string) => {\r\n const action = visibleActions.find(a => a.key === key)\r\n if (action) {\r\n try {\r\n action.onClick(row, index)\r\n } catch (error) {\r\n console.error(`操作\"${action.label}\"执行失败:`, error)\r\n message.error(`${action.label}失败`)\r\n }\r\n }\r\n },\r\n },\r\n () => createButton('mdi:dots-horizontal', '更多操作', 'default', () => {})\r\n )\r\n }\r\n\r\n /* 主渲染方法 */\r\n const renderActions = (row: T, index: number): VNodeChild => {\r\n if (actions.value?.render) {\r\n return actions.value.render(row, index)\r\n }\r\n\r\n const rowKeyValue = rowKey(row)\r\n const isRowEditMode = ['row', 'both'].includes(config.value.editMode)\r\n\r\n if (\r\n isRowEditMode &&\r\n tableManager.editStates.rowEdit.isEditingRow(rowKeyValue)\r\n ) {\r\n return h(NSpace, { size: 2, wrap: false }, () =>\r\n renderRowEditButtons(rowKeyValue)\r\n )\r\n }\r\n\r\n const allButtons = [\r\n ...(isRowEditMode ? renderRowEditButtons(rowKeyValue) : []),\r\n ...renderBasicActions(row, index),\r\n ]\r\n\r\n const dropdown = renderCustomDropdown(row, index)\r\n if (dropdown) allButtons.push(dropdown)\r\n\r\n return h(NSpace, { size: 2, wrap: false }, () => allButtons)\r\n }\r\n\r\n return { renderActions, isActionEnabled }\r\n}\r\n","/*\n * @Description: 表格全局配置 — provide/inject 机制实现全局默认值 + 局部覆盖\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\n */\n\nimport { inject, type InjectionKey } from 'vue'\nimport type { TableConfig, DisplayConfig } from './useTableConfig'\nimport type { DataRecord } from '../types'\n\n/* ================= 内置格式化器 ================= */\n\nexport type FormatterType =\n | 'date'\n | 'datetime'\n | 'currency'\n | 'percent'\n | 'enum'\n | 'number'\n\nexport interface FormatterConfig {\n /** 日期格式 */\n date?: { format?: string }\n /** 日期时间格式 */\n datetime?: { format?: string }\n /** 货币格式 */\n currency?: { prefix?: string; suffix?: string; precision?: number }\n /** 百分比格式 */\n percent?: { precision?: number; suffix?: string }\n /** 数字格式(千分位) */\n number?: { precision?: number }\n}\n\n/** 列级 formatter 配置 */\nexport interface ColumnFormatter {\n type: FormatterType\n /** enum mapping:{ male: '男', female: '女' } */\n mapping?: Record<string, string>\n /** 自定义格式化函数(优先级最高) */\n fn?: (value: unknown, row: DataRecord) => string\n /** 覆盖全局格式配置 */\n config?: Record<string, unknown>\n}\n\n/* ================= 全局配置类型 ================= */\n\nexport interface TableGlobalConfig {\n /** 表格显示默认值 */\n display?: DisplayConfig\n /** 默认分页条数 */\n pageSize?: number\n /** 空状态文案 */\n emptyText?: string\n /** 全局格式化配置 */\n formatter?: FormatterConfig\n /** 列配置持久化前缀 */\n persistPrefix?: string\n /** 默认导出文件名 */\n exportFilename?: string\n}\n\n/* ================= Injection Key ================= */\n\nexport const TABLE_GLOBAL_CONFIG_KEY: InjectionKey<TableGlobalConfig> = Symbol(\n 'C_TABLE_GLOBAL_CONFIG'\n)\n\n/* ================= 格式化引擎 ================= */\n\nconst defaultFormatters: FormatterConfig = {\n date: { format: 'YYYY-MM-DD' },\n datetime: { format: 'YYYY-MM-DD HH:mm:ss' },\n currency: { prefix: '¥', precision: 2 },\n percent: { precision: 2, suffix: '%' },\n number: { precision: 0 },\n}\n\nfunction formatDate(value: unknown, format: string): string {\n if (value == null || value === '') return '-'\n const d = new Date(typeof value === 'number' ? value : String(value))\n if (Number.isNaN(d.getTime())) return String(value)\n\n const pad = (n: number) => String(n).padStart(2, '0')\n return format\n .replace('YYYY', String(d.getFullYear()))\n .replace('MM', pad(d.getMonth() + 1))\n .replace('DD', pad(d.getDate()))\n .replace('HH', pad(d.getHours()))\n .replace('mm', pad(d.getMinutes()))\n .replace('ss', pad(d.getSeconds()))\n}\n\nfunction formatNumber(value: unknown, precision: number): string {\n if (value == null || value === '') return '-'\n const num = Number(value)\n if (Number.isNaN(num)) return String(value)\n return num.toLocaleString('zh-CN', {\n minimumFractionDigits: precision,\n maximumFractionDigits: precision,\n })\n}\n\n/** 根据 ColumnFormatter 配置格式化单元格值 */\nexport function applyFormatter(\n value: unknown,\n row: DataRecord,\n formatter: ColumnFormatter,\n globalConfig?: FormatterConfig\n): string {\n // 优先使用自定义函数\n if (formatter.fn) return formatter.fn(value, row)\n\n const merged = { ...defaultFormatters, ...globalConfig }\n\n switch (formatter.type) {\n case 'date': {\n const cfg = {\n ...merged.date,\n ...(formatter.config as FormatterConfig['date']),\n }\n return formatDate(value, cfg?.format ?? 'YYYY-MM-DD')\n }\n case 'datetime': {\n const cfg = {\n ...merged.datetime,\n ...(formatter.config as FormatterConfig['datetime']),\n }\n return formatDate(value, cfg?.format ?? 'YYYY-MM-DD HH:mm:ss')\n }\n case 'currency': {\n const cfg = {\n ...merged.currency,\n ...(formatter.config as FormatterConfig['currency']),\n }\n const numStr = formatNumber(value, cfg?.precision ?? 2)\n return numStr === '-'\n ? '-'\n : `${cfg?.prefix ?? '¥'}${numStr}${cfg?.suffix ?? ''}`\n }\n case 'percent': {\n const cfg = {\n ...merged.percent,\n ...(formatter.config as FormatterConfig['percent']),\n }\n if (value == null || value === '') return '-'\n const num = Number(value)\n if (Number.isNaN(num)) return String(value)\n return `${(num * 100).toFixed(cfg?.precision ?? 2)}${cfg?.suffix ?? '%'}`\n }\n case 'number': {\n const cfg = {\n ...merged.number,\n ...(formatter.config as FormatterConfig['number']),\n }\n return formatNumber(value, cfg?.precision ?? 0)\n }\n case 'enum': {\n if (!formatter.mapping) return String(value ?? '-')\n return formatter.mapping[String(value)] ?? String(value ?? '-')\n }\n default:\n return String(value ?? '-')\n }\n}\n\n/* ================= Hook ================= */\n\n/** 在组件内获取全局配置(自动合并默认值) */\nexport function useTableGlobalConfig(): TableGlobalConfig {\n return inject(TABLE_GLOBAL_CONFIG_KEY, {})\n}\n\n/** 合并全局配置到 TableConfig(局部 > 全局 > 默认) */\nexport function mergeGlobalConfig(\n local: TableConfig,\n global: TableGlobalConfig\n): TableConfig {\n const merged = { ...local }\n\n // 合并 display\n if (global.display && !local.display) {\n merged.display = global.display\n } else if (global.display && local.display) {\n merged.display = { ...global.display, ...local.display }\n }\n\n // 合并分页默认值\n if (global.pageSize && local.pagination !== false) {\n const currentPagination =\n local.pagination === true || local.pagination === undefined\n ? {}\n : typeof local.pagination === 'object'\n ? local.pagination\n : {}\n if (!currentPagination.pageSize) {\n merged.pagination = { ...currentPagination, pageSize: global.pageSize }\n }\n }\n\n return merged\n}\n","/*\r\n * @Description: 表格配置中心 — 编辑组件映射、表单生成、显示值处理\r\n * @Migration: naive-ui-components 组件库迁移版本\r\n */\r\n\r\nimport { h } from \"vue\";\r\nimport { NInput, NInputNumber, NSwitch, NDatePicker, NSelect } from \"naive-ui\";\r\nimport type { TableColumn, EditType } from \"./types\";\r\n\r\n/* ================= 编辑组件映射 ================= */\r\n\r\nexport const EDIT_COMPONENTS: Record<EditType, any> = {\r\n number: NInputNumber,\r\n switch: NSwitch,\r\n input: NInput,\r\n email: NInput,\r\n mobile: NInput,\r\n date: (props: any) =>\r\n h(NDatePicker, { ...props, type: \"date\", format: \"yyyy-MM-dd\" }),\r\n select: (props: any) =>\r\n h(NSelect, { ...props, options: props.options || [] }),\r\n textarea: (props: any) => h(NInput, { ...props, type: \"textarea\", rows: 3 }),\r\n};\r\n\r\n/* ================= 表单选项生成 ================= */\r\n\r\ninterface FormOption {\r\n prop: string;\r\n label: string;\r\n type: string;\r\n placeholder: string;\r\n rules: any[];\r\n attrs: any;\r\n layout: { span: number };\r\n show: boolean;\r\n}\r\n\r\ntype ComponentType = string;\r\n\r\nconst EDIT_TO_FORM_TYPE: Record<string, ComponentType> = {\r\n number: \"inputNumber\",\r\n date: \"datePicker\",\r\n textarea: \"textarea\",\r\n select: \"select\",\r\n switch: \"switch\",\r\n};\r\n\r\n/**\r\n * 根据可编辑列配置自动生成表单选项(用于编辑弹窗)\r\n */\r\nexport const generateFormOptions = (columns: TableColumn[]): FormOption[] =>\r\n columns.map((column) => {\r\n const rules: any[] = (column as any).required\r\n ? [\r\n {\r\n required: true,\r\n message: `请输入${(column as any).title}`,\r\n trigger: [\"blur\", \"input\"],\r\n validator: async (_: any, value: any) => {\r\n if (!value && value !== 0 && value !== false)\r\n throw new Error(`请输入${(column as any).title}`);\r\n },\r\n },\r\n ]\r\n : [];\r\n\r\n return {\r\n prop: (column as any).key,\r\n label: (column as any).title || (column as any).key,\r\n type: EDIT_TO_FORM_TYPE[(column as any).editType || \"input\"] || \"input\",\r\n placeholder: `请输入${(column as any).title}`,\r\n rules,\r\n attrs: (column as any).editProps || {},\r\n layout: { span: 1 },\r\n show: true,\r\n };\r\n });\r\n","/*\n * @Description: 表格列处理引擎 — 列映射、单元格渲染、列设置状态管理\n * @Migration: naive-ui-components 组件库迁移版本\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\n */\n\nimport {\n h,\n ref,\n watch,\n computed,\n type Ref,\n type VNodeChild,\n type ComputedRef,\n} from 'vue'\nimport type { DataTableRowKey, DataTableColumn } from 'naive-ui/es'\nimport type { TableColumn, DataRecord, ColumnWithKey } from '../types'\nimport type { ResolvedConfig } from './useTableConfig'\nimport { applyFormatter } from './useTableGlobalConfig'\nimport { EDIT_COMPONENTS } from '../data'\nimport C_Icon from '../../C_Icon/index.vue'\n\n/* ================= 内部辅助类型 ================= */\n\n/** useTableColumns 中对 tableManager 的最小依赖接口 */\ninterface TableManagerRef {\n editStates: {\n cellEdit: {\n isEditingCell: (rowKey: DataTableRowKey, columnKey: string) => boolean\n getEditingCellValue: (\n rowKey: DataTableRowKey,\n columnKey: string\n ) => unknown\n updateEditingCellValue: (\n rowKey: DataTableRowKey,\n columnKey: string,\n value: unknown\n ) => void\n saveEditCell: () => Promise<unknown>\n cancelEditCell: () => void\n startEditCell: (rowKey: DataTableRowKey, columnKey: string) => void\n }\n rowEdit: {\n isEditingRow: (rowKey: DataTableRowKey) => boolean\n getEditingRowData: (rowKey: DataTableRowKey) => DataRecord | undefined\n updateEditingRowData: (\n rowKey: DataTableRowKey,\n field: string,\n value: unknown\n ) => void\n }\n }\n dynamicRowsState?: {\n enhanceColumns: (columns: TableColumn[]) => TableColumn[]\n } | null\n expandState?: {\n getTableColumns: (originalColumns: TableColumn[]) => DataTableColumn[]\n } | null\n}\n\n/* ================= 渲染工具函数 ================= */\n\n/**\n * 渲染编辑组件(行编辑/单元格编辑通用)\n */\nexport function renderEditComponent(\n column: TableColumn,\n value: unknown,\n onUpdate: (val: unknown) => void\n): VNodeChild {\n if (column.editRender) return column.editRender(value, {} as DataRecord, 0)\n\n const col = column as ColumnWithKey\n const componentProps = {\n value,\n 'onUpdate:value': onUpdate,\n placeholder: `请输入${col.title ?? ''}`,\n style: { width: '100%' },\n ...column.editProps,\n }\n\n const Component =\n EDIT_COMPONENTS[column.editType || 'input'] || EDIT_COMPONENTS.input\n return h(Component, componentProps)\n}\n\n/**\n * 渲染只读单元格\n */\nexport function renderDisplayCell(\n column: TableColumn,\n rowData: DataRecord,\n rowIndex: number,\n value: unknown,\n formatterConfig?: import('./useTableGlobalConfig').FormatterConfig\n): VNodeChild {\n if (column.render)\n return column.render(rowData, rowIndex) ?? String(value ?? '')\n\n // 列级 formatter\n if ((column as any).formatter) {\n return applyFormatter(\n value,\n rowData,\n (column as any).formatter,\n formatterConfig\n )\n }\n\n return String(value ?? '')\n}\n\n/**\n * 渲染编辑中的单元格(带保存/取消按钮)\n */\nexport function renderEditingCell(\n column: TableColumn,\n value: unknown,\n onUpdate: (val: unknown) => void,\n onSave: () => void,\n onCancel: () => void\n): VNodeChild {\n const actionBtn = (\n name: string,\n title: string,\n onClick: (e: Event) => void\n ) =>\n h(\n 'button',\n {\n class: `cell-action-btn cell-action-${name}`,\n title,\n type: 'button',\n onClick: (e: Event) => {\n e.stopPropagation()\n e.preventDefault()\n onClick(e)\n },\n },\n [\n h(C_Icon, {\n name: `mdi:${name === 'save' ? 'check' : 'close'}`,\n title: name === 'save' ? '保存' : '取消',\n size: 12,\n }),\n ]\n )\n\n return h('div', { class: 'cell-editing-container' }, [\n h('div', { class: 'cell-editing-input' }, [\n renderEditComponent(column, value, onUpdate),\n ]),\n h('div', { class: 'cell-editing-actions' }, [\n actionBtn('save', '保存', onSave),\n actionBtn('cancel', '取消', onCancel),\n ]),\n ])\n}\n\n/**\n * 渲染可编辑单元格(显示值 + 编辑图标)\n */\nexport function renderEditableCell(\n column: TableColumn,\n rowData: DataRecord,\n rowIndex: number,\n value: unknown,\n onStartEdit: () => void,\n formatterConfig?: import('./useTableGlobalConfig').FormatterConfig\n): VNodeChild {\n let displayValue: string | VNodeChild\n if (column.render) {\n displayValue = column.render(rowData, rowIndex) ?? String(value ?? '')\n } else if ((column as any).formatter) {\n displayValue = applyFormatter(\n value,\n rowData,\n (column as any).formatter,\n formatterConfig\n )\n } else {\n displayValue = String(value ?? '')\n }\n\n return h('div', { class: 'cell-edit-wrapper' }, [\n h('span', { class: 'cell-value' }, displayValue),\n h(C_Icon, {\n name: 'mdi:square-edit-outline',\n title: '编辑',\n class: 'cell-edit-icon',\n size: 14,\n clickable: true,\n onClick: (e: Event) => {\n e.stopPropagation()\n onStartEdit()\n },\n }),\n ])\n}\n\n/* ================= 组合式函数入参 ================= */\n\nexport interface UseTableColumnsOptions {\n /** 原始列配置 */\n rawColumns: ComputedRef<TableColumn[]>\n /** 统一配置 */\n config: ComputedRef<ResolvedConfig>\n /** 默认列宽 */\n columnWidth: number\n /** 用户设定的 scrollX */\n scrollX?: number | string\n /** 行键获取函数 */\n rowKey: (row: DataRecord) => DataTableRowKey\n /** 表格管理器 */\n tableManager: TableManagerRef\n /** 操作列渲染函数 */\n actionsRenderer: (row: DataRecord, index: number) => VNodeChild\n /** 编辑模式检查器 */\n editModeChecker: ComputedRef<{\n isNonEditable: (column: TableColumn) => boolean\n isRowEditMode: () => boolean\n isCellEditMode: () => boolean\n }>\n}\n\nexport interface UseTableColumnsReturn {\n reactiveColumns: Ref<TableColumn[]>\n showSettingsPanel: Ref<boolean>\n computedColumns: ComputedRef<DataTableColumn[]>\n computedScrollX: ComputedRef<number | string | undefined>\n handleColumnChange: (columns: TableColumn[]) => void\n}\n\n/* ================= 主函数 ================= */\n\n/**\n * 表格列处理引擎\n */\nexport function useTableColumns(\n options: UseTableColumnsOptions\n): UseTableColumnsReturn {\n const {\n rawColumns,\n config,\n columnWidth,\n rowKey,\n tableManager,\n actionsRenderer,\n editModeChecker,\n } = options\n\n /* ================= 响应式列状态 ================= */\n\n const reactiveColumns = ref<TableColumn[]>([])\n const showSettingsPanel = ref(false)\n\n /** 同步外部列配置到响应式状态 */\n const syncColumns = (newColumns: TableColumn[]) => {\n const existingActions = reactiveColumns.value.find(\n col => (col as ColumnWithKey).key === '_actions'\n )\n reactiveColumns.value = [\n ...newColumns,\n existingActions ||\n ({\n key: '_actions',\n title: '操作',\n width: 180,\n editable: false,\n visible: true,\n fixed: 'right',\n } as TableColumn),\n ]\n }\n\n watch(\n rawColumns,\n cols => {\n if (cols?.length) syncColumns(cols)\n },\n { deep: true, immediate: true }\n )\n\n /* ================= 单元格渲染 ================= */\n\n /** 单元格编辑模式渲染 */\n const renderCellEdit = (\n column: TableColumn,\n rowData: DataRecord,\n rowIndex: number,\n rowKeyVal: DataTableRowKey\n ): VNodeChild => {\n const col = column as ColumnWithKey\n const colKey = col.key ?? ''\n const value = rowData[colKey]\n const {\n isEditingCell,\n getEditingCellValue,\n updateEditingCellValue,\n saveEditCell,\n cancelEditCell,\n startEditCell,\n } = tableManager.editStates.cellEdit\n\n if (isEditingCell(rowKeyVal, colKey)) {\n return renderEditingCell(\n column,\n getEditingCellValue(rowKeyVal, colKey) ?? value,\n (val: unknown) => updateEditingCellValue(rowKeyVal, colKey, val),\n () => saveEditCell(),\n () => cancelEditCell()\n )\n }\n\n return renderEditableCell(\n column,\n rowData,\n rowIndex,\n value,\n () => startEditCell(rowKeyVal, colKey),\n config.value.formatterConfig\n )\n }\n\n /** 统一单元格渲染入口 */\n const renderCell = (\n column: TableColumn,\n rowData: DataRecord,\n rowIndex: number\n ): VNodeChild => {\n const col = column as ColumnWithKey\n const colKey = col.key ?? ''\n const value = rowData[colKey]\n const key = rowKey(rowData)\n const checker = editModeChecker.value\n\n if (checker.isNonEditable(column)) {\n return renderDisplayCell(\n column,\n rowData,\n rowIndex,\n value,\n config.value.formatterConfig\n )\n }\n\n if (\n checker.isRowEditMode() &&\n tableManager.editStates.rowEdit.isEditingRow(key)\n ) {\n return renderEditComponent(\n column,\n tableManager.editStates.rowEdit.getEditingRowData(key)?.[colKey] ??\n value,\n (val: unknown) =>\n tableManager.editStates.rowEdit.updateEditingRowData(key, colKey, val)\n )\n }\n\n if (checker.isCellEditMode()) {\n return renderCellEdit(column, rowData, rowIndex, key)\n }\n\n return renderDisplayCell(\n column,\n rowData,\n rowIndex,\n value,\n config.value.formatterConfig\n )\n }\n\n /* ================= 列映射 ================= */\n\n const getColWidth = (col: TableColumn): number => {\n const w = (col as ColumnWithKey).width || columnWidth || 180\n return typeof w === 'number' ? w : 180\n }\n\n /** 序号列 */\n const mapIndexColumn = (column: TableColumn): DataTableColumn => {\n const col = column as ColumnWithKey\n return {\n key: '_index',\n title: col.title || '序号',\n width:\n typeof (col.width || 50) === 'number'\n ? (col.width as number) || 50\n : 50,\n titleAlign: 'center' as const,\n align: 'center' as const,\n render: (_: DataRecord, index: number) => index + 1,\n fixed: column.fixed,\n }\n }\n\n /** 普通数据列 */\n const mapRegularColumn = (column: TableColumn): DataTableColumn => {\n const col = column as ColumnWithKey\n const base: Record<string, unknown> = {\n ...col,\n width: getColWidth(column),\n titleAlign: col.titleAlign || ('center' as const),\n align: col.align || ('center' as const),\n render:\n column.render ||\n ((rowData: DataRecord, rowIndex: number) =>\n renderCell(column, rowData, rowIndex)),\n }\n\n if (column.fixed) base.fixed = column.fixed\n\n if (column.resizable && typeof base.width === 'number') {\n base.resizable = true\n base.minWidth = column.minWidth || 80\n base.maxWidth = column.maxWidth || 500\n }\n\n return base as unknown as DataTableColumn\n }\n\n /* ================= 计算列 ================= */\n\n const computedColumns = computed((): DataTableColumn[] => {\n let cols: DataTableColumn[] = reactiveColumns.value\n .filter(c => {\n const col = c as ColumnWithKey\n return col.visible !== false && col.key !== '_actions'\n })\n .map(c => {\n const col = c as ColumnWithKey\n return (col as any).type === 'index'\n ? mapIndexColumn(c)\n : mapRegularColumn(c)\n }) as DataTableColumn[]\n\n if (tableManager.dynamicRowsState) {\n cols = tableManager.dynamicRowsState.enhanceColumns(\n cols as unknown as TableColumn[]\n ) as unknown as DataTableColumn[]\n }\n\n if (\n tableManager.expandState &&\n (config.value.expandable || config.value.enableSelection)\n ) {\n cols = tableManager.expandState.getTableColumns(\n cols as unknown as TableColumn[]\n ) as unknown as DataTableColumn[]\n }\n\n const actionsMeta = reactiveColumns.value.find(\n c => (c as ColumnWithKey).key === '_actions'\n )\n cols.push({\n key: '_actions',\n title: '操作',\n align: 'center' as const,\n titleAlign: 'center' as const,\n render: actionsRenderer,\n fixed: (actionsMeta as ColumnWithKey)?.fixed,\n })\n\n return cols\n })\n\n /* ================= 横向滚动 ================= */\n\n const computedScrollX = computed(() => {\n if (options.scrollX !== undefined) return options.scrollX\n\n const hasFixed = reactiveColumns.value.some(c => {\n const col = c as ColumnWithKey\n return col.fixed && col.visible !== false\n })\n if (!hasFixed) return undefined\n\n const total = reactiveColumns.value\n .filter(c => (c as ColumnWithKey).visible !== false)\n .reduce((sum: number, c) => sum + getColWidth(c), 0)\n\n return total + 200\n })\n\n /* ================= 列设置变更 ================= */\n\n const handleColumnChange = (columns: TableColumn[]) => {\n reactiveColumns.value = columns.map(col => {\n const c = col as ColumnWithKey\n return {\n ...col,\n visible: c.visible !== false,\n fixed: col.fixed,\n width: c.width || columnWidth,\n align: c.align || 'center',\n titleAlign: c.titleAlign || 'center',\n } as unknown as TableColumn\n })\n }\n\n return {\n reactiveColumns,\n showSettingsPanel,\n computedColumns,\n computedScrollX,\n handleColumnChange,\n }\n}\n","/*\n * @Description: 行拖拽排序 — 表格行体 sortablejs 集成\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\n */\n\nimport { ref, onBeforeUnmount, type Ref, type ComputedRef } from 'vue'\nimport type { DataTableRowKey } from 'naive-ui/es'\nimport type { DataRecord } from '../types'\n\n/* ================= 类型定义 ================= */\n\nexport interface RowDragConfig {\n enabled?: boolean\n /** 拖拽手柄 CSS 选择器(不传则整行可拖) */\n handleSelector?: string\n /** 动画时长(ms) */\n animationDuration?: number\n /** 拖拽时 ghost 样式类 */\n ghostClass?: string\n /** 拖拽中样式类 */\n chosenClass?: string\n}\n\nexport interface UseRowDragOptions<T extends DataRecord = DataRecord> {\n /** 数据源(只读即可) */\n data: Ref<T[]> | ComputedRef<T[]>\n /** 获取行键 */\n rowKey: (row: T) => DataTableRowKey\n /** 配置 */\n config: RowDragConfig\n /** 数据重排回调(由外部处理数据更新) */\n onReorder?: (newData: T[]) => void\n /** 排序变更回调 */\n onSort?: (row: T, fromIndex: number, toIndex: number) => void\n}\n\nexport interface UseRowDragReturn {\n /** 初始化拖拽(需在 onMounted 中调用) */\n initRowDrag: (wrapperEl: HTMLElement) => void\n /** 销毁拖拽实例 */\n destroyRowDrag: () => void\n /** 是否正在拖拽 */\n isDragging: Ref<boolean>\n}\n\n/* ================= 实现 ================= */\n\n/** 行拖拽排序组合式函数 */\nexport function useRowDrag<T extends DataRecord = DataRecord>(\n options: UseRowDragOptions<T>\n): UseRowDragReturn {\n const { data, config, onReorder, onSort } = options\n const isDragging = ref(false)\n let sortableInstance: any = null\n\n const initRowDrag = async (wrapperEl: HTMLElement) => {\n if (!config.enabled || !wrapperEl) return\n\n try {\n const { default: Sortable } = await import('sortablejs')\n const tbody = wrapperEl.querySelector('.n-data-table-tbody')\n if (!tbody) return\n\n sortableInstance = new Sortable(tbody as HTMLElement, {\n animation: config.animationDuration ?? 150,\n handle: config.handleSelector,\n ghostClass: config.ghostClass ?? 'row-drag-ghost',\n chosenClass: config.chosenClass ?? 'row-drag-chosen',\n onStart: () => {\n isDragging.value = true\n },\n onEnd: (evt: any) => {\n isDragging.value = false\n if (evt.oldIndex == null || evt.newIndex == null) return\n if (evt.oldIndex === evt.newIndex) return\n\n const newData = [...data.value]\n const [moved] = newData.splice(evt.oldIndex, 1)\n newData.splice(evt.newIndex, 0, moved)\n onReorder?.(newData)\n onSort?.(moved, evt.oldIndex, evt.newIndex)\n },\n })\n } catch {\n // sortablejs 未安装时静默降级\n }\n }\n\n const destroyRowDrag = () => {\n if (sortableInstance) {\n sortableInstance.destroy()\n sortableInstance = null\n }\n }\n\n onBeforeUnmount(destroyRowDrag)\n\n return { initRowDrag, destroyRowDrag, isDragging }\n}\n","/*\n * @Description: 跨页多选 — 分页切换时保留选中状态\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\n */\n\nimport { ref, computed, watch, type Ref, type ComputedRef } from 'vue'\nimport type { DataTableRowKey } from 'naive-ui/es'\nimport type { DataRecord } from '../types'\n\n/* ================= 类型定义 ================= */\n\nexport interface CrossPageSelectionConfig {\n enabled?: boolean\n /** 最大选择数 */\n maxSelection?: number\n}\n\nexport interface UseCrossPageSelectionOptions<\n T extends DataRecord = DataRecord,\n> {\n /** 全量数据(非分页后的) */\n allData: Ref<T[]> | ComputedRef<T[]>\n /** 行键函数 */\n rowKey: (row: T) => DataTableRowKey\n /** 配置 */\n config: CrossPageSelectionConfig\n}\n\nexport interface UseCrossPageSelectionReturn<\n T extends DataRecord = DataRecord,\n> {\n /** 所有选中的 key(跨页) */\n selectedKeys: Ref<Set<DataTableRowKey>>\n /** 选中行数量 */\n selectedCount: ComputedRef<number>\n /** 当前页数据的 checkedKeys(给 NDataTable 绑定) */\n getPageCheckedKeys: (pageData: T[]) => DataTableRowKey[]\n /** NDataTable @update:checked-row-keys 处理器 */\n handlePageSelectionChange: (keys: DataTableRowKey[], pageData: T[]) => void\n /** 全选所有页 */\n selectAll: () => void\n /** 清除所有页选中 */\n clearAll: () => void\n /** 获取所有选中行数据 */\n getSelectedRows: () => T[]\n /** 判断 key 是否选中 */\n isSelected: (key: DataTableRowKey) => boolean\n}\n\n/* ================= 实现 ================= */\n\n/** 跨页多选组合式函数 */\nexport function useCrossPageSelection<T extends DataRecord = DataRecord>(\n options: UseCrossPageSelectionOptions<T>\n): UseCrossPageSelectionReturn<T> {\n const { allData, rowKey, config } = options\n const selectedKeys = ref<Set<DataTableRowKey>>(new Set())\n\n const selectedCount = computed(() => selectedKeys.value.size)\n\n const getPageCheckedKeys = (pageData: T[]): DataTableRowKey[] => {\n return pageData\n .map(row => rowKey(row))\n .filter(key => selectedKeys.value.has(key))\n }\n\n const handlePageSelectionChange = (\n keys: DataTableRowKey[],\n pageData: T[]\n ) => {\n const newSet = new Set(selectedKeys.value)\n // 先清除当前页的所有 key\n for (const row of pageData) {\n newSet.delete(rowKey(row))\n }\n // 再添加当前页新选中的\n for (const key of keys) {\n if (config.maxSelection && newSet.size >= config.maxSelection) break\n newSet.add(key)\n }\n selectedKeys.value = newSet\n }\n\n const selectAll = () => {\n const all = allData.value.map(row => rowKey(row))\n selectedKeys.value = new Set(\n config.maxSelection ? all.slice(0, config.maxSelection) : all\n )\n }\n\n const clearAll = () => {\n selectedKeys.value = new Set()\n }\n\n const getSelectedRows = (): T[] => {\n return allData.value.filter(row => selectedKeys.value.has(rowKey(row)))\n }\n\n const isSelected = (key: DataTableRowKey): boolean => {\n return selectedKeys.value.has(key)\n }\n\n // 数据源变化时清除不存在的 key\n watch(allData, newData => {\n const validKeys = new Set(newData.map(row => rowKey(row)))\n const cleaned = new Set(\n [...selectedKeys.value].filter(k => validKeys.has(k))\n )\n if (cleaned.size !== selectedKeys.value.size) {\n selectedKeys.value = cleaned\n }\n })\n\n return {\n selectedKeys,\n selectedCount,\n getPageCheckedKeys,\n handlePageSelectionChange,\n selectAll,\n clearAll,\n getSelectedRows,\n isSelected,\n }\n}\n","/*\n * @Description: 表格导出 Excel/CSV 功能\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\n */\n\nimport type { DataRecord, TableColumn, ColumnWithKey } from '../types'\nimport { applyFormatter, type FormatterConfig } from './useTableGlobalConfig'\n\n/* ================= 类型定义 ================= */\n\nexport interface ExportConfig {\n /** 导出文件名(不含扩展名) */\n filename?: string\n /** 导出格式 */\n format?: 'csv' | 'xlsx'\n /** 是否包含表头 */\n includeHeader?: boolean\n /** 自定义选择导出列(传 key 数组) */\n columnKeys?: string[]\n /** CSV 分隔符 */\n csvSeparator?: string\n /** 全局格式化配置(用于 formatter) */\n formatterConfig?: FormatterConfig\n}\n\n/* ================= 内部辅助 ================= */\n\n/** 获取导出用的列(过滤掉内置列) */\nfunction getExportColumns(\n columns: TableColumn[],\n columnKeys?: string[]\n): ColumnWithKey[] {\n return (columns as ColumnWithKey[]).filter(col => {\n if (!col.key || col.key.startsWith('_')) return false\n if ((col as any).type === 'selection' || (col as any).type === 'expand')\n return false\n if (columnKeys?.length) return columnKeys.includes(col.key as string)\n return col.visible !== false\n })\n}\n\n/** 获取单元格导出值 */\nfunction getCellValue(\n row: DataRecord,\n col: ColumnWithKey,\n formatterConfig?: FormatterConfig\n): string {\n const raw = row[col.key as string]\n\n // 优先用 formatter\n if (col.formatter) {\n return applyFormatter(raw, row, col.formatter, formatterConfig)\n }\n\n if (raw == null) return ''\n if (typeof raw === 'boolean') return raw ? '是' : '否'\n return String(raw)\n}\n\n/* ================= CSV 导出 ================= */\n\n/** 转义 CSV 单元格值 */\nfunction escapeCSV(value: string, separator: string): string {\n if (\n value.includes(separator) ||\n value.includes('\"') ||\n value.includes('\\n')\n ) {\n return `\"${value.replace(/\"/g, '\"\"')}\"`\n }\n return value\n}\n\n/** 导出 CSV 文件 */\nfunction exportCSV(\n data: DataRecord[],\n columns: TableColumn[],\n config: ExportConfig\n): void {\n const sep = config.csvSeparator ?? ','\n const exportCols = getExportColumns(columns, config.columnKeys)\n const lines: string[] = []\n\n if (config.includeHeader !== false) {\n lines.push(exportCols.map(col => escapeCSV(col.title ?? '', sep)).join(sep))\n }\n\n for (const row of data) {\n const cells = exportCols.map(col =>\n escapeCSV(getCellValue(row, col, config.formatterConfig), sep)\n )\n lines.push(cells.join(sep))\n }\n\n // BOM + UTF-8\n const blob = new Blob(['\\uFEFF' + lines.join('\\n')], {\n type: 'text/csv;charset=utf-8;',\n })\n downloadBlob(blob, `${config.filename || 'export'}.csv`)\n}\n\n/* ================= XLSX 导出 ================= */\n\n/** 导出 XLSX 文件(未安装 xlsx 时降级 CSV) */\nasync function exportXLSX(\n data: DataRecord[],\n columns: TableColumn[],\n config: ExportConfig\n): Promise<void> {\n const exportCols = getExportColumns(columns, config.columnKeys)\n\n try {\n const XLSX = await import('xlsx')\n\n const headers = exportCols.map(col => col.title ?? col.key ?? '')\n const rows = data.map(row =>\n exportCols.map(col => getCellValue(row, col, config.formatterConfig))\n )\n\n const wsData = config.includeHeader !== false ? [headers, ...rows] : rows\n const ws = XLSX.utils.aoa_to_sheet(wsData)\n const wb = XLSX.utils.book_new()\n XLSX.utils.book_append_sheet(wb, ws, 'Sheet1')\n\n XLSX.writeFile(wb, `${config.filename || 'export'}.xlsx`)\n } catch {\n // xlsx 未安装时降级到 CSV\n console.warn('[C_Table] xlsx 库未安装,已自动降级为 CSV 导出')\n exportCSV(data, columns, { ...config, format: 'csv' })\n }\n}\n\n/* ================= 下载工具 ================= */\n\n/** 下载 Blob 为文件 */\nfunction downloadBlob(blob: Blob, filename: string): void {\n const url = URL.createObjectURL(blob)\n const a = document.createElement('a')\n a.href = url\n a.download = filename\n a.style.display = 'none'\n document.body.appendChild(a)\n a.click()\n document.body.removeChild(a)\n URL.revokeObjectURL(url)\n}\n\n/* ================= 对外 API ================= */\n\n/**\n * 导出表格数据\n */\nexport async function exportTableData(\n data: DataRecord[],\n columns: TableColumn[],\n config: ExportConfig = {}\n): Promise<void> {\n if (config.format === 'xlsx') {\n await exportXLSX(data, columns, config)\n } else {\n exportCSV(data, columns, config)\n }\n}\n","/*\r\n * @Description: ColumnSettings 配置与工具函数\r\n * @Migration: naive-ui-components 组件库迁移版本\r\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\r\n */\r\n\r\nimport type { TableColumn } from '../../types'\r\n\r\n/** 固定列下拉选项 */\r\nexport const getFixedOptions = (column: TableColumn) => [\r\n {\r\n label: column.fixed ? '✓ 取消固定' : '不固定',\r\n key: 'none',\r\n disabled: !column.fixed,\r\n },\r\n {\r\n label: column.fixed === 'left' ? '✓ 固定左侧' : '固定左侧',\r\n key: 'left',\r\n disabled: false,\r\n },\r\n {\r\n label: column.fixed === 'right' ? '✓ 固定右侧' : '固定右侧',\r\n key: 'right',\r\n disabled: false,\r\n },\r\n]\r\n\r\n/** 不可操作的特殊列 key */\r\nexport const SPECIAL_COLUMN_KEYS = ['_actions'] as const\r\n\r\n/** 不可拖拽的列 type */\r\nexport const SPECIAL_COLUMN_TYPES = ['selection', 'expand'] as const\r\n\r\n/** 判断是否为特殊列(不可调宽、不可拖拽) */\r\nexport const isSpecialColumn = (column: TableColumn): boolean =>\r\n SPECIAL_COLUMN_KEYS.includes(\r\n column.key as (typeof SPECIAL_COLUMN_KEYS)[number]\r\n ) ||\r\n SPECIAL_COLUMN_TYPES.includes(\r\n (column as any).type as (typeof SPECIAL_COLUMN_TYPES)[number]\r\n )\r\n","/* unplugin-vue-components disabled */<!--\n * @Description: 列设置面板 — 搜索、拖拽排序、可见性、固定列、列宽调整\n * @Migration: naive-ui-components 组件库迁移版本\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\n -->\n\n<template>\n <div class=\"column-management-tab\">\n <!-- 搜索框和列宽调整开关 -->\n <div class=\"search-row\">\n <NInput\n v-model:value=\"searchText\"\n placeholder=\"搜索列名...\"\n clearable\n class=\"search-input\"\n >\n <template #prefix>\n <C_Icon name=\"mdi:magnify\" />\n </template>\n </NInput>\n\n <div class=\"resizable-control\">\n <C_Icon\n name=\"mdi:arrow-split-vertical\"\n size=\"14\"\n :title=\"\n enableResizable ? '开启后可拖拽列边界调整宽度' : '允许调整列宽'\n \"\n />\n <NText :style=\"{ fontSize: '12px', marginLeft: '4px' }\">\n 列宽调整\n </NText>\n <NSwitch\n v-model:value=\"enableResizable\"\n size=\"small\"\n style=\"margin-left: 6px\"\n @update:value=\"handleResizableChange\"\n />\n </div>\n </div>\n\n <!-- 顶部操作栏:统计信息和快捷操作按钮 -->\n <div class=\"top-actions-bar\">\n <div class=\"stats-info\">\n <NText\n depth=\"3\"\n :style=\"{ fontSize: '13px' }\"\n >\n 已选 {{ visibleCount }} / 总共 {{ totalCount }} 列\n </NText>\n </div>\n <div class=\"quick-actions\">\n <NSpace :size=\"6\">\n <NButton\n size=\"tiny\"\n @click=\"selectAll\"\n >\n 全选\n </NButton>\n <NButton\n size=\"tiny\"\n @click=\"selectNone\"\n >\n 全不选\n </NButton>\n <NButton\n size=\"tiny\"\n @click=\"resetColumns\"\n >\n 重置\n </NButton>\n </NSpace>\n </div>\n </div>\n\n <!-- 列列表 -->\n <div\n ref=\"listRef\"\n class=\"column-list\"\n >\n <div\n v-for=\"(column, index) in filteredColumns\"\n :key=\"column.key\"\n class=\"column-item\"\n :class=\"{\n disabled: column.key === '_actions',\n 'fixed-left': column.fixed === 'left',\n 'fixed-right': column.fixed === 'right',\n }\"\n :draggable=\"column.key !== '_actions'\"\n @dragstart=\"handleDragStart(index, $event)\"\n @dragover=\"handleDragOver(index, $event)\"\n @dragend=\"handleDragEnd\"\n @drop=\"handleDrop(index)\"\n >\n <div class=\"column-info\">\n <div class=\"column-controls\">\n <C_Icon\n name=\"mdi:drag\"\n size=\"16\"\n class=\"drag-handle\"\n :class=\"{ disabled: column.key === '_actions' }\"\n />\n <NCheckbox\n :checked=\"column.visible !== false\"\n :disabled=\"column.key === '_actions'\"\n @update:checked=\"\n (value: boolean) => toggleColumnVisibility(index, value)\n \"\n />\n </div>\n <div class=\"column-details\">\n <NSpace\n align=\"center\"\n :size=\"6\"\n >\n <NText\n strong\n :style=\"{ fontSize: '13px' }\"\n >\n {{ column.title || column.key }}\n </NText>\n <NTag\n v-if=\"column.fixed === 'left'\"\n size=\"tiny\"\n type=\"info\"\n >\n 左固定\n </NTag>\n <NTag\n v-if=\"column.fixed === 'right'\"\n size=\"tiny\"\n type=\"warning\"\n >\n 右固定\n </NTag>\n </NSpace>\n <NText\n depth=\"3\"\n :style=\"{ fontSize: '11px' }\"\n >\n {{ column.key }}\n </NText>\n </div>\n </div>\n <div class=\"column-actions\">\n <div class=\"drag-controls\">\n <C_Icon\n name=\"mdi:chevron-up\"\n size=\"14\"\n title=\"上移\"\n :clickable=\"index !== 0\"\n :class=\"{ disabled: index === 0 }\"\n @click=\"index !== 0 && moveColumn(index, index - 1)\"\n />\n <C_Icon\n name=\"mdi:chevron-down\"\n size=\"14\"\n title=\"下移\"\n :clickable=\"index !== filteredColumns.length - 1\"\n :class=\"{ disabled: index === filteredColumns.length - 1 }\"\n @click=\"\n index !== filteredColumns.length - 1 &&\n moveColumn(index, index + 1)\n \"\n />\n </div>\n <NDropdown\n :options=\"getFixedOptions(column)\"\n @select=\"(value: string) => handleFixedSelect(index, value)\"\n >\n <C_Icon\n name=\"mdi:pin\"\n size=\"14\"\n title=\"固定列\"\n clickable\n :style=\"{\n color: column.fixed ? 'var(--n-primary-color)' : undefined,\n }\"\n />\n </NDropdown>\n </div>\n </div>\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\n import { ref, computed, onMounted } from 'vue'\n import {\n NInput,\n NText,\n NSwitch,\n NSpace,\n NButton,\n NCheckbox,\n NTag,\n NDropdown,\n } from 'naive-ui/es'\n import C_Icon from '../../../C_Icon/index.vue'\n import type { TableColumn } from '../../types'\n import { getFixedOptions, isSpecialColumn } from './data'\n\n defineOptions({ name: 'ColumnSettings' })\n\n /* ================= Props & Emits ================= */\n\n const props = defineProps<{\n columns: TableColumn[]\n /** 列配置持久化 key(传入即启用 localStorage) */\n persistKey?: string\n }>()\n const emit = defineEmits<{ (e: 'change', columns: TableColumn[]): void }>()\n\n /* ================= 响应式状态 ================= */\n\n /** 从 localStorage 恢复持久化列配置 */\n const loadPersistedColumns = (columns: TableColumn[]): TableColumn[] => {\n if (!props.persistKey) return [...columns]\n try {\n const stored = localStorage.getItem(`c_table_cols_${props.persistKey}`)\n if (!stored) return [...columns]\n const persisted: Array<{ key: string; visible?: boolean; width?: number | string; fixed?: string }> = JSON.parse(stored)\n const keyMap = new Map(persisted.map((p, i) => [p.key, { ...p, order: i }]))\n const merged = columns.map(col => {\n const saved = keyMap.get((col as any).key)\n if (!saved) return { ...col }\n return {\n ...col,\n visible: saved.visible ?? col.visible,\n width: saved.width ?? col.width,\n fixed: (saved.fixed as any) ?? col.fixed,\n }\n })\n // 按持久化顺序排序\n merged.sort((a, b) => {\n const oa = keyMap.get((a as any).key)?.order ?? 999\n const ob = keyMap.get((b as any).key)?.order ?? 999\n return oa - ob\n })\n return merged\n } catch {\n return [...columns]\n }\n }\n\n /** 保存列配置到 localStorage */\n const persistColumns = (columns: TableColumn[]) => {\n if (!props.persistKey) return\n try {\n const data = columns.map(col => ({\n key: (col as any).key,\n visible: col.visible,\n width: col.width,\n fixed: col.fixed,\n }))\n localStorage.setItem(`c_table_cols_${props.persistKey}`, JSON.stringify(data))\n } catch {\n // localStorage 不可用时静默忽略\n }\n }\n\n const localColumns = ref<TableColumn[]>(loadPersistedColumns(props.columns))\n const searchText = ref('')\n const listRef = ref<HTMLElement>()\n const draggedIndex = ref<number | null>(null)\n const dragOverIndex = ref<number | null>(null)\n const enableResizable = ref(false)\n\n onMounted(() => {\n enableResizable.value = localColumns.value.some(\n col => col.resizable === true\n )\n // 如有持久化配置,初始化时通知父组件\n if (props.persistKey) {\n emit('change', [...localColumns.value])\n }\n })\n\n /* ================= 计算属性 ================= */\n\n const filteredColumns = computed(() => {\n if (!searchText.value) return localColumns.value\n const search = searchText.value.toLowerCase()\n return localColumns.value.filter(\n col =>\n col.title?.toLowerCase().includes(search) ||\n col.key?.toLowerCase().includes(search)\n )\n })\n\n const visibleCount = computed(\n () => localColumns.value.filter(col => col.visible !== false).length\n )\n const totalCount = computed(() => localColumns.value.length)\n\n /* ================= 批量操作 ================= */\n\n const setAllColumnsVisible = (visible: boolean) => {\n localColumns.value.forEach(col => {\n if (col.key !== '_actions') col.visible = visible\n })\n applyChanges()\n }\n\n const selectAll = () => setAllColumnsVisible(true)\n const selectNone = () => setAllColumnsVisible(false)\n\n /* ================= 列属性更新 ================= */\n\n const findOriginalIndex = (filteredIndex: number): number => {\n const column = filteredColumns.value[filteredIndex]\n return localColumns.value.findIndex(col => col.key === column.key)\n }\n\n const updateColumnProperty = (\n index: number,\n updater: (col: TableColumn) => void\n ) => {\n const originalIndex = findOriginalIndex(index)\n if (originalIndex !== -1) {\n updater(localColumns.value[originalIndex])\n applyChanges()\n }\n }\n\n const toggleColumnVisibility = (index: number, visible: boolean) => {\n updateColumnProperty(index, col => {\n col.visible = visible\n })\n }\n\n const handleFixedSelect = (index: number, value: string) => {\n updateColumnProperty(index, col => {\n col.fixed = value === 'none' ? undefined : (value as 'left' | 'right')\n })\n }\n\n const applyChanges = () => {\n persistColumns(localColumns.value)\n emit('change', [...localColumns.value])\n }\n\n const resetColumns = () => {\n localColumns.value = [...props.columns]\n applyChanges()\n }\n\n /* ================= 列宽调整 ================= */\n\n const handleResizableChange = (value: boolean) => {\n localColumns.value.forEach(col => {\n if (!isSpecialColumn(col)) {\n col.resizable = value\n if (value) {\n col.minWidth = col.minWidth || 80\n col.maxWidth = col.maxWidth || 500\n }\n }\n })\n applyChanges()\n }\n\n /* ================= 拖拽排序 ================= */\n\n const moveColumn = (fromIndex: number, toIndex: number) => {\n const originalFromIndex = findOriginalIndex(fromIndex)\n const originalToIndex = findOriginalIndex(toIndex)\n if (originalFromIndex !== -1 && originalToIndex !== -1) {\n const [movedColumn] = localColumns.value.splice(originalFromIndex, 1)\n localColumns.value.splice(originalToIndex, 0, movedColumn)\n applyChanges()\n }\n }\n\n const handleDragStart = (index: number, event: DragEvent) => {\n const column = filteredColumns.value[index]\n if (column.key === '_actions') {\n event.preventDefault()\n return\n }\n draggedIndex.value = index\n if (event.dataTransfer) event.dataTransfer.effectAllowed = 'move'\n }\n\n const handleDragOver = (index: number, event: DragEvent) => {\n event.preventDefault()\n if (\n draggedIndex.value === null ||\n filteredColumns.value[index].key === '_actions'\n )\n return\n dragOverIndex.value = index\n if (event.dataTransfer) event.dataTransfer.dropEffect = 'move'\n }\n\n const handleDrop = (toIndex: number) => {\n if (\n draggedIndex.value === null ||\n filteredColumns.value[toIndex].key === '_actions'\n )\n return\n moveColumn(draggedIndex.value, toIndex)\n draggedIndex.value = null\n dragOverIndex.value = null\n }\n\n const handleDragEnd = () => {\n draggedIndex.value = null\n dragOverIndex.value = null\n }\n</script>\n\n<style scoped lang=\"scss\">\n @use './index.scss';\n</style>\n","/* unplugin-vue-components disabled */<!--\n * @Description: 列设置面板 — 搜索、拖拽排序、可见性、固定列、列宽调整\n * @Migration: naive-ui-components 组件库迁移版本\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\n -->\n\n<template>\n <div class=\"column-management-tab\">\n <!-- 搜索框和列宽调整开关 -->\n <div class=\"search-row\">\n <NInput\n v-model:value=\"searchText\"\n placeholder=\"搜索列名...\"\n clearable\n class=\"search-input\"\n >\n <template #prefix>\n <C_Icon name=\"mdi:magnify\" />\n </template>\n </NInput>\n\n <div class=\"resizable-control\">\n <C_Icon\n name=\"mdi:arrow-split-vertical\"\n size=\"14\"\n :title=\"\n enableResizable ? '开启后可拖拽列边界调整宽度' : '允许调整列宽'\n \"\n />\n <NText :style=\"{ fontSize: '12px', marginLeft: '4px' }\">\n 列宽调整\n </NText>\n <NSwitch\n v-model:value=\"enableResizable\"\n size=\"small\"\n style=\"margin-left: 6px\"\n @update:value=\"handleResizableChange\"\n />\n </div>\n </div>\n\n <!-- 顶部操作栏:统计信息和快捷操作按钮 -->\n <div class=\"top-actions-bar\">\n <div class=\"stats-info\">\n <NText\n depth=\"3\"\n :style=\"{ fontSize: '13px' }\"\n >\n 已选 {{ visibleCount }} / 总共 {{ totalCount }} 列\n </NText>\n </div>\n <div class=\"quick-actions\">\n <NSpace :size=\"6\">\n <NButton\n size=\"tiny\"\n @click=\"selectAll\"\n >\n 全选\n </NButton>\n <NButton\n size=\"tiny\"\n @click=\"selectNone\"\n >\n 全不选\n </NButton>\n <NButton\n size=\"tiny\"\n @click=\"resetColumns\"\n >\n 重置\n </NButton>\n </NSpace>\n </div>\n </div>\n\n <!-- 列列表 -->\n <div\n ref=\"listRef\"\n class=\"column-list\"\n >\n <div\n v-for=\"(column, index) in filteredColumns\"\n :key=\"column.key\"\n class=\"column-item\"\n :class=\"{\n disabled: column.key === '_actions',\n 'fixed-left': column.fixed === 'left',\n 'fixed-right': column.fixed === 'right',\n }\"\n :draggable=\"column.key !== '_actions'\"\n @dragstart=\"handleDragStart(index, $event)\"\n @dragover=\"handleDragOver(index, $event)\"\n @dragend=\"handleDragEnd\"\n @drop=\"handleDrop(index)\"\n >\n <div class=\"column-info\">\n <div class=\"column-controls\">\n <C_Icon\n name=\"mdi:drag\"\n size=\"16\"\n class=\"drag-handle\"\n :class=\"{ disabled: column.key === '_actions' }\"\n />\n <NCheckbox\n :checked=\"column.visible !== false\"\n :disabled=\"column.key === '_actions'\"\n @update:checked=\"\n (value: boolean) => toggleColumnVisibility(index, value)\n \"\n />\n </div>\n <div class=\"column-details\">\n <NSpace\n align=\"center\"\n :size=\"6\"\n >\n <NText\n strong\n :style=\"{ fontSize: '13px' }\"\n >\n {{ column.title || column.key }}\n </NText>\n <NTag\n v-if=\"column.fixed === 'left'\"\n size=\"tiny\"\n type=\"info\"\n >\n 左固定\n </NTag>\n <NTag\n v-if=\"column.fixed === 'right'\"\n size=\"tiny\"\n type=\"warning\"\n >\n 右固定\n </NTag>\n </NSpace>\n <NText\n depth=\"3\"\n :style=\"{ fontSize: '11px' }\"\n >\n {{ column.key }}\n </NText>\n </div>\n </div>\n <div class=\"column-actions\">\n <div class=\"drag-controls\">\n <C_Icon\n name=\"mdi:chevron-up\"\n size=\"14\"\n title=\"上移\"\n :clickable=\"index !== 0\"\n :class=\"{ disabled: index === 0 }\"\n @click=\"index !== 0 && moveColumn(index, index - 1)\"\n />\n <C_Icon\n name=\"mdi:chevron-down\"\n size=\"14\"\n title=\"下移\"\n :clickable=\"index !== filteredColumns.length - 1\"\n :class=\"{ disabled: index === filteredColumns.length - 1 }\"\n @click=\"\n index !== filteredColumns.length - 1 &&\n moveColumn(index, index + 1)\n \"\n />\n </div>\n <NDropdown\n :options=\"getFixedOptions(column)\"\n @select=\"(value: string) => handleFixedSelect(index, value)\"\n >\n <C_Icon\n name=\"mdi:pin\"\n size=\"14\"\n title=\"固定列\"\n clickable\n :style=\"{\n color: column.fixed ? 'var(--n-primary-color)' : undefined,\n }\"\n />\n </NDropdown>\n </div>\n </div>\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\n import { ref, computed, onMounted } from 'vue'\n import {\n NInput,\n NText,\n NSwitch,\n NSpace,\n NButton,\n NCheckbox,\n NTag,\n NDropdown,\n } from 'naive-ui/es'\n import C_Icon from '../../../C_Icon/index.vue'\n import type { TableColumn } from '../../types'\n import { getFixedOptions, isSpecialColumn } from './data'\n\n defineOptions({ name: 'ColumnSettings' })\n\n /* ================= Props & Emits ================= */\n\n const props = defineProps<{\n columns: TableColumn[]\n /** 列配置持久化 key(传入即启用 localStorage) */\n persistKey?: string\n }>()\n const emit = defineEmits<{ (e: 'change', columns: TableColumn[]): void }>()\n\n /* ================= 响应式状态 ================= */\n\n /** 从 localStorage 恢复持久化列配置 */\n const loadPersistedColumns = (columns: TableColumn[]): TableColumn[] => {\n if (!props.persistKey) return [...columns]\n try {\n const stored = localStorage.getItem(`c_table_cols_${props.persistKey}`)\n if (!stored) return [...columns]\n const persisted: Array<{ key: string; visible?: boolean; width?: number | string; fixed?: string }> = JSON.parse(stored)\n const keyMap = new Map(persisted.map((p, i) => [p.key, { ...p, order: i }]))\n const merged = columns.map(col => {\n const saved = keyMap.get((col as any).key)\n if (!saved) return { ...col }\n return {\n ...col,\n visible: saved.visible ?? col.visible,\n width: saved.width ?? col.width,\n fixed: (saved.fixed as any) ?? col.fixed,\n }\n })\n // 按持久化顺序排序\n merged.sort((a, b) => {\n const oa = keyMap.get((a as any).key)?.order ?? 999\n const ob = keyMap.get((b as any).key)?.order ?? 999\n return oa - ob\n })\n return merged\n } catch {\n return [...columns]\n }\n }\n\n /** 保存列配置到 localStorage */\n const persistColumns = (columns: TableColumn[]) => {\n if (!props.persistKey) return\n try {\n const data = columns.map(col => ({\n key: (col as any).key,\n visible: col.visible,\n width: col.width,\n fixed: col.fixed,\n }))\n localStorage.setItem(`c_table_cols_${props.persistKey}`, JSON.stringify(data))\n } catch {\n // localStorage 不可用时静默忽略\n }\n }\n\n const localColumns = ref<TableColumn[]>(loadPersistedColumns(props.columns))\n const searchText = ref('')\n const listRef = ref<HTMLElement>()\n const draggedIndex = ref<number | null>(null)\n const dragOverIndex = ref<number | null>(null)\n const enableResizable = ref(false)\n\n onMounted(() => {\n enableResizable.value = localColumns.value.some(\n col => col.resizable === true\n )\n // 如有持久化配置,初始化时通知父组件\n if (props.persistKey) {\n emit('change', [...localColumns.value])\n }\n })\n\n /* ================= 计算属性 ================= */\n\n const filteredColumns = computed(() => {\n if (!searchText.value) return localColumns.value\n const search = searchText.value.toLowerCase()\n return localColumns.value.filter(\n col =>\n col.title?.toLowerCase().includes(search) ||\n col.key?.toLowerCase().includes(search)\n )\n })\n\n const visibleCount = computed(\n () => localColumns.value.filter(col => col.visible !== false).length\n )\n const totalCount = computed(() => localColumns.value.length)\n\n /* ================= 批量操作 ================= */\n\n const setAllColumnsVisible = (visible: boolean) => {\n localColumns.value.forEach(col => {\n if (col.key !== '_actions') col.visible = visible\n })\n applyChanges()\n }\n\n const selectAll = () => setAllColumnsVisible(true)\n const selectNone = () => setAllColumnsVisible(false)\n\n /* ================= 列属性更新 ================= */\n\n const findOriginalIndex = (filteredIndex: number): number => {\n const column = filteredColumns.value[filteredIndex]\n return localColumns.value.findIndex(col => col.key === column.key)\n }\n\n const updateColumnProperty = (\n index: number,\n updater: (col: TableColumn) => void\n ) => {\n const originalIndex = findOriginalIndex(index)\n if (originalIndex !== -1) {\n updater(localColumns.value[originalIndex])\n applyChanges()\n }\n }\n\n const toggleColumnVisibility = (index: number, visible: boolean) => {\n updateColumnProperty(index, col => {\n col.visible = visible\n })\n }\n\n const handleFixedSelect = (index: number, value: string) => {\n updateColumnProperty(index, col => {\n col.fixed = value === 'none' ? undefined : (value as 'left' | 'right')\n })\n }\n\n const applyChanges = () => {\n persistColumns(localColumns.value)\n emit('change', [...localColumns.value])\n }\n\n const resetColumns = () => {\n localColumns.value = [...props.columns]\n applyChanges()\n }\n\n /* ================= 列宽调整 ================= */\n\n const handleResizableChange = (value: boolean) => {\n localColumns.value.forEach(col => {\n if (!isSpecialColumn(col)) {\n col.resizable = value\n if (value) {\n col.minWidth = col.minWidth || 80\n col.maxWidth = col.maxWidth || 500\n }\n }\n })\n applyChanges()\n }\n\n /* ================= 拖拽排序 ================= */\n\n const moveColumn = (fromIndex: number, toIndex: number) => {\n const originalFromIndex = findOriginalIndex(fromIndex)\n const originalToIndex = findOriginalIndex(toIndex)\n if (originalFromIndex !== -1 && originalToIndex !== -1) {\n const [movedColumn] = localColumns.value.splice(originalFromIndex, 1)\n localColumns.value.splice(originalToIndex, 0, movedColumn)\n applyChanges()\n }\n }\n\n const handleDragStart = (index: number, event: DragEvent) => {\n const column = filteredColumns.value[index]\n if (column.key === '_actions') {\n event.preventDefault()\n return\n }\n draggedIndex.value = index\n if (event.dataTransfer) event.dataTransfer.effectAllowed = 'move'\n }\n\n const handleDragOver = (index: number, event: DragEvent) => {\n event.preventDefault()\n if (\n draggedIndex.value === null ||\n filteredColumns.value[index].key === '_actions'\n )\n return\n dragOverIndex.value = index\n if (event.dataTransfer) event.dataTransfer.dropEffect = 'move'\n }\n\n const handleDrop = (toIndex: number) => {\n if (\n draggedIndex.value === null ||\n filteredColumns.value[toIndex].key === '_actions'\n )\n return\n moveColumn(draggedIndex.value, toIndex)\n draggedIndex.value = null\n dragOverIndex.value = null\n }\n\n const handleDragEnd = () => {\n draggedIndex.value = null\n dragOverIndex.value = null\n }\n</script>\n\n<style scoped lang=\"scss\">\n @use './index.scss';\n</style>\n","<!--\n * @Description: 列设置面板 — 搜索、拖拽排序、可见性、固定列、列宽调整\n * @Migration: naive-ui-components 组件库迁移版本\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\n -->\n\n<template>\n <div class=\"column-management-tab\">\n <!-- 搜索框和列宽调整开关 -->\n <div class=\"search-row\">\n <NInput\n v-model:value=\"searchText\"\n placeholder=\"搜索列名...\"\n clearable\n class=\"search-input\"\n >\n <template #prefix>\n <C_Icon name=\"mdi:magnify\" />\n </template>\n </NInput>\n\n <div class=\"resizable-control\">\n <C_Icon\n name=\"mdi:arrow-split-vertical\"\n size=\"14\"\n :title=\"\n enableResizable ? '开启后可拖拽列边界调整宽度' : '允许调整列宽'\n \"\n />\n <NText :style=\"{ fontSize: '12px', marginLeft: '4px' }\">\n 列宽调整\n </NText>\n <NSwitch\n v-model:value=\"enableResizable\"\n size=\"small\"\n style=\"margin-left: 6px\"\n @update:value=\"handleResizableChange\"\n />\n </div>\n </div>\n\n <!-- 顶部操作栏:统计信息和快捷操作按钮 -->\n <div class=\"top-actions-bar\">\n <div class=\"stats-info\">\n <NText\n depth=\"3\"\n :style=\"{ fontSize: '13px' }\"\n >\n 已选 {{ visibleCount }} / 总共 {{ totalCount }} 列\n </NText>\n </div>\n <div class=\"quick-actions\">\n <NSpace :size=\"6\">\n <NButton\n size=\"tiny\"\n @click=\"selectAll\"\n >\n 全选\n </NButton>\n <NButton\n size=\"tiny\"\n @click=\"selectNone\"\n >\n 全不选\n </NButton>\n <NButton\n size=\"tiny\"\n @click=\"resetColumns\"\n >\n 重置\n </NButton>\n </NSpace>\n </div>\n </div>\n\n <!-- 列列表 -->\n <div\n ref=\"listRef\"\n class=\"column-list\"\n >\n <div\n v-for=\"(column, index) in filteredColumns\"\n :key=\"column.key\"\n class=\"column-item\"\n :class=\"{\n disabled: column.key === '_actions',\n 'fixed-left': column.fixed === 'left',\n 'fixed-right': column.fixed === 'right',\n }\"\n :draggable=\"column.key !== '_actions'\"\n @dragstart=\"handleDragStart(index, $event)\"\n @dragover=\"handleDragOver(index, $event)\"\n @dragend=\"handleDragEnd\"\n @drop=\"handleDrop(index)\"\n >\n <div class=\"column-info\">\n <div class=\"column-controls\">\n <C_Icon\n name=\"mdi:drag\"\n size=\"16\"\n class=\"drag-handle\"\n :class=\"{ disabled: column.key === '_actions' }\"\n />\n <NCheckbox\n :checked=\"column.visible !== false\"\n :disabled=\"column.key === '_actions'\"\n @update:checked=\"\n (value: boolean) => toggleColumnVisibility(index, value)\n \"\n />\n </div>\n <div class=\"column-details\">\n <NSpace\n align=\"center\"\n :size=\"6\"\n >\n <NText\n strong\n :style=\"{ fontSize: '13px' }\"\n >\n {{ column.title || column.key }}\n </NText>\n <NTag\n v-if=\"column.fixed === 'left'\"\n size=\"tiny\"\n type=\"info\"\n >\n 左固定\n </NTag>\n <NTag\n v-if=\"column.fixed === 'right'\"\n size=\"tiny\"\n type=\"warning\"\n >\n 右固定\n </NTag>\n </NSpace>\n <NText\n depth=\"3\"\n :style=\"{ fontSize: '11px' }\"\n >\n {{ column.key }}\n </NText>\n </div>\n </div>\n <div class=\"column-actions\">\n <div class=\"drag-controls\">\n <C_Icon\n name=\"mdi:chevron-up\"\n size=\"14\"\n title=\"上移\"\n :clickable=\"index !== 0\"\n :class=\"{ disabled: index === 0 }\"\n @click=\"index !== 0 && moveColumn(index, index - 1)\"\n />\n <C_Icon\n name=\"mdi:chevron-down\"\n size=\"14\"\n title=\"下移\"\n :clickable=\"index !== filteredColumns.length - 1\"\n :class=\"{ disabled: index === filteredColumns.length - 1 }\"\n @click=\"\n index !== filteredColumns.length - 1 &&\n moveColumn(index, index + 1)\n \"\n />\n </div>\n <NDropdown\n :options=\"getFixedOptions(column)\"\n @select=\"(value: string) => handleFixedSelect(index, value)\"\n >\n <C_Icon\n name=\"mdi:pin\"\n size=\"14\"\n title=\"固定列\"\n clickable\n :style=\"{\n color: column.fixed ? 'var(--n-primary-color)' : undefined,\n }\"\n />\n </NDropdown>\n </div>\n </div>\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\n import { ref, computed, onMounted } from 'vue'\n import {\n NInput,\n NText,\n NSwitch,\n NSpace,\n NButton,\n NCheckbox,\n NTag,\n NDropdown,\n } from 'naive-ui/es'\n import C_Icon from '../../../C_Icon/index.vue'\n import type { TableColumn } from '../../types'\n import { getFixedOptions, isSpecialColumn } from './data'\n\n defineOptions({ name: 'ColumnSettings' })\n\n /* ================= Props & Emits ================= */\n\n const props = defineProps<{\n columns: TableColumn[]\n /** 列配置持久化 key(传入即启用 localStorage) */\n persistKey?: string\n }>()\n const emit = defineEmits<{ (e: 'change', columns: TableColumn[]): void }>()\n\n /* ================= 响应式状态 ================= */\n\n /** 从 localStorage 恢复持久化列配置 */\n const loadPersistedColumns = (columns: TableColumn[]): TableColumn[] => {\n if (!props.persistKey) return [...columns]\n try {\n const stored = localStorage.getItem(`c_table_cols_${props.persistKey}`)\n if (!stored) return [...columns]\n const persisted: Array<{ key: string; visible?: boolean; width?: number | string; fixed?: string }> = JSON.parse(stored)\n const keyMap = new Map(persisted.map((p, i) => [p.key, { ...p, order: i }]))\n const merged = columns.map(col => {\n const saved = keyMap.get((col as any).key)\n if (!saved) return { ...col }\n return {\n ...col,\n visible: saved.visible ?? col.visible,\n width: saved.width ?? col.width,\n fixed: (saved.fixed as any) ?? col.fixed,\n }\n })\n // 按持久化顺序排序\n merged.sort((a, b) => {\n const oa = keyMap.get((a as any).key)?.order ?? 999\n const ob = keyMap.get((b as any).key)?.order ?? 999\n return oa - ob\n })\n return merged\n } catch {\n return [...columns]\n }\n }\n\n /** 保存列配置到 localStorage */\n const persistColumns = (columns: TableColumn[]) => {\n if (!props.persistKey) return\n try {\n const data = columns.map(col => ({\n key: (col as any).key,\n visible: col.visible,\n width: col.width,\n fixed: col.fixed,\n }))\n localStorage.setItem(`c_table_cols_${props.persistKey}`, JSON.stringify(data))\n } catch {\n // localStorage 不可用时静默忽略\n }\n }\n\n const localColumns = ref<TableColumn[]>(loadPersistedColumns(props.columns))\n const searchText = ref('')\n const listRef = ref<HTMLElement>()\n const draggedIndex = ref<number | null>(null)\n const dragOverIndex = ref<number | null>(null)\n const enableResizable = ref(false)\n\n onMounted(() => {\n enableResizable.value = localColumns.value.some(\n col => col.resizable === true\n )\n // 如有持久化配置,初始化时通知父组件\n if (props.persistKey) {\n emit('change', [...localColumns.value])\n }\n })\n\n /* ================= 计算属性 ================= */\n\n const filteredColumns = computed(() => {\n if (!searchText.value) return localColumns.value\n const search = searchText.value.toLowerCase()\n return localColumns.value.filter(\n col =>\n col.title?.toLowerCase().includes(search) ||\n col.key?.toLowerCase().includes(search)\n )\n })\n\n const visibleCount = computed(\n () => localColumns.value.filter(col => col.visible !== false).length\n )\n const totalCount = computed(() => localColumns.value.length)\n\n /* ================= 批量操作 ================= */\n\n const setAllColumnsVisible = (visible: boolean) => {\n localColumns.value.forEach(col => {\n if (col.key !== '_actions') col.visible = visible\n })\n applyChanges()\n }\n\n const selectAll = () => setAllColumnsVisible(true)\n const selectNone = () => setAllColumnsVisible(false)\n\n /* ================= 列属性更新 ================= */\n\n const findOriginalIndex = (filteredIndex: number): number => {\n const column = filteredColumns.value[filteredIndex]\n return localColumns.value.findIndex(col => col.key === column.key)\n }\n\n const updateColumnProperty = (\n index: number,\n updater: (col: TableColumn) => void\n ) => {\n const originalIndex = findOriginalIndex(index)\n if (originalIndex !== -1) {\n updater(localColumns.value[originalIndex])\n applyChanges()\n }\n }\n\n const toggleColumnVisibility = (index: number, visible: boolean) => {\n updateColumnProperty(index, col => {\n col.visible = visible\n })\n }\n\n const handleFixedSelect = (index: number, value: string) => {\n updateColumnProperty(index, col => {\n col.fixed = value === 'none' ? undefined : (value as 'left' | 'right')\n })\n }\n\n const applyChanges = () => {\n persistColumns(localColumns.value)\n emit('change', [...localColumns.value])\n }\n\n const resetColumns = () => {\n localColumns.value = [...props.columns]\n applyChanges()\n }\n\n /* ================= 列宽调整 ================= */\n\n const handleResizableChange = (value: boolean) => {\n localColumns.value.forEach(col => {\n if (!isSpecialColumn(col)) {\n col.resizable = value\n if (value) {\n col.minWidth = col.minWidth || 80\n col.maxWidth = col.maxWidth || 500\n }\n }\n })\n applyChanges()\n }\n\n /* ================= 拖拽排序 ================= */\n\n const moveColumn = (fromIndex: number, toIndex: number) => {\n const originalFromIndex = findOriginalIndex(fromIndex)\n const originalToIndex = findOriginalIndex(toIndex)\n if (originalFromIndex !== -1 && originalToIndex !== -1) {\n const [movedColumn] = localColumns.value.splice(originalFromIndex, 1)\n localColumns.value.splice(originalToIndex, 0, movedColumn)\n applyChanges()\n }\n }\n\n const handleDragStart = (index: number, event: DragEvent) => {\n const column = filteredColumns.value[index]\n if (column.key === '_actions') {\n event.preventDefault()\n return\n }\n draggedIndex.value = index\n if (event.dataTransfer) event.dataTransfer.effectAllowed = 'move'\n }\n\n const handleDragOver = (index: number, event: DragEvent) => {\n event.preventDefault()\n if (\n draggedIndex.value === null ||\n filteredColumns.value[index].key === '_actions'\n )\n return\n dragOverIndex.value = index\n if (event.dataTransfer) event.dataTransfer.dropEffect = 'move'\n }\n\n const handleDrop = (toIndex: number) => {\n if (\n draggedIndex.value === null ||\n filteredColumns.value[toIndex].key === '_actions'\n )\n return\n moveColumn(draggedIndex.value, toIndex)\n draggedIndex.value = null\n dragOverIndex.value = null\n }\n\n const handleDragEnd = () => {\n draggedIndex.value = null\n dragOverIndex.value = null\n }\n</script>\n\n<style scoped lang=\"scss\">\n @use './index.scss';\n</style>\n","/* unplugin-vue-components disabled */<!--\n * @Description: C_Table 超级表格组件(薄 UI 壳)\n * @Migration: naive-ui-components 组件库迁移版本\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\n *\n * 使用侧 API:\n * <C_Table :columns=\"cols\" :data=\"data\" :loading=\"loading\" :config=\"tableConfig\" />\n *\n * config 收拢了所有功能配置:edit / selection / expand / pagination / dynamicRows / toolbar / display\n * 列处理 → composables/useTableColumns.ts\n * 配置解析 → composables/useTableConfig.ts\n * 编辑/展开/动态行 → composables/useTableManager.ts\n -->\n\n<template>\n <div\n ref=\"tableWrapperRef\"\n class=\"c-table-wrapper\"\n >\n <!-- 动态行工具栏 -->\n <component\n v-if=\"tableManager.dynamicRowsState\"\n :is=\"tableManager.dynamicRowsState.renderToolbar()\"\n />\n\n <!-- 表格工具栏 -->\n <div\n v-if=\"resolved.showToolbar\"\n class=\"table-toolbar\"\n >\n <div class=\"toolbar-left\">\n <slot name=\"toolbar-left\" />\n </div>\n <div class=\"toolbar-right\">\n <slot name=\"toolbar-right\" />\n <C_Icon\n v-if=\"resolved.exportConfig\"\n name=\"mdi:download\"\n size=\"18\"\n title=\"导出\"\n clickable\n class=\"column-settings-btn\"\n @click=\"handleExport()\"\n />\n <C_Icon\n v-if=\"resolved.enableColumnSettings\"\n name=\"mdi:cog\"\n size=\"18\"\n title=\"表格设置\"\n clickable\n class=\"column-settings-btn\"\n @click=\"showSettingsPanel = true\"\n />\n </div>\n </div>\n\n <!-- 批量操作栏 -->\n <div\n v-if=\"resolved.batchActions?.enabled && batchSelectedCount > 0\"\n class=\"batch-actions-bar\"\n >\n <span class=\"batch-info\">已选择 {{ batchSelectedCount }} 项</span>\n <NSpace :size=\"8\">\n <NButton\n v-for=\"action in (resolved.batchActions.actions || [])\"\n :key=\"action.key\"\n :type=\"(action.type as any) || 'default'\"\n size=\"small\"\n @click=\"handleBatchAction(action)\"\n >\n <template v-if=\"action.icon\" #icon>\n <C_Icon :name=\"action.icon\" size=\"14\" />\n </template>\n {{ action.label }}\n </NButton>\n <NButton size=\"small\" @click=\"clearBatchSelection\">取消选择</NButton>\n </NSpace>\n </div>\n\n <!-- 错误状态 -->\n <div v-if=\"resolved.error?.show\" class=\"table-error-state\">\n <slot name=\"error\" :error=\"resolved.error\">\n <div class=\"error-content\">\n <C_Icon name=\"mdi:alert-circle-outline\" size=\"48\" />\n <p class=\"error-message\">{{ resolved.error.message || '数据加载失败' }}</p>\n <NButton v-if=\"resolved.error.onRetry\" type=\"primary\" size=\"small\" @click=\"resolved.error.onRetry\">\n 重试\n </NButton>\n </div>\n </slot>\n </div>\n\n <!-- 表格主体 -->\n <NDataTable\n v-if=\"!resolved.error?.show\"\n ref=\"tableRef\"\n v-bind=\"{ ...computedTableProps, ...$attrs }\"\n :columns=\"computedColumns\"\n :data=\"normalizedData\"\n :loading=\"normalizedLoading\"\n :row-key=\"rowKey\"\n :expanded-row-keys=\"tableManager.expandedKeys.value\"\n :checked-row-keys=\"crossPageCheckedKeys ?? tableManager.checkedKeys.value\"\n @update:expanded-row-keys=\"tableManager.expandState?.handleExpandChange\"\n @update:checked-row-keys=\"handleCheckedKeysChange\"\n :scroll-x=\"computedScrollX\"\n :virtual-scroll=\"resolved.virtualScroll\"\n :virtual-scroll-item-size=\"resolved.virtualScroll ? resolved.virtualItemHeight : undefined\"\n :max-height=\"resolved.virtualScroll ? resolved.virtualMaxHeight : resolved.maxHeight\"\n :summary=\"summaryFn\"\n :summary-placement=\"resolved.summaryPosition\"\n :children-key=\"resolved.treeEnabled ? resolved.treeChildrenKey : undefined\"\n :indent=\"resolved.treeEnabled ? resolved.treeIndent : undefined\"\n :default-expand-all=\"resolved.treeEnabled ? resolved.treeDefaultExpandAll : undefined\"\n style=\"width: 100%\"\n />\n\n <!-- 分页 -->\n <NPagination\n v-if=\"pagination.paginationConfig.value\"\n v-bind=\"pagination.paginationConfig.value\"\n class=\"pagination-wrapper\"\n />\n\n <!-- 编辑弹窗 -->\n <NModal\n v-if=\"resolved.editMode === 'modal' || resolved.editMode === 'both'\"\n v-model:show=\"modalVisible\"\n :title=\"resolved.modalTitle\"\n :width=\"resolved.modalWidth\"\n preset=\"card\"\n :mask-closable=\"false\"\n :close-on-esc=\"false\"\n class=\"w60%\"\n :closable=\"false\"\n >\n <C_Form\n v-if=\"modalVisible && formOptions.length\"\n ref=\"editFormRef\"\n :key=\"formKey\"\n v-model=\"localEditingData\"\n :options=\"formOptions\"\n :config=\"{\n layout: 'grid',\n grid: { cols: 2, xGap: 16, yGap: 16 },\n showActions: false,\n }\"\n />\n <template #action>\n <NSpace justify=\"end\">\n <NButton @click=\"handleModalCancel\">取消</NButton>\n <NButton\n type=\"primary\"\n :loading=\"modalSubmitLoading\"\n @click=\"handleModalSave\"\n >\n 保存\n </NButton>\n </NSpace>\n </template>\n </NModal>\n\n <!-- 动态行确认删除弹窗 -->\n <component\n v-if=\"tableManager.dynamicRowsState\"\n :is=\"tableManager.dynamicRowsState.renderConfirmModal()\"\n />\n\n <!-- 列设置抽屉 -->\n <NDrawer\n v-model:show=\"showSettingsPanel\"\n :width=\"420\"\n placement=\"right\"\n :mask-closable=\"true\"\n >\n <NDrawerContent\n title=\"列设置\"\n closable\n >\n <ColumnSettings\n :columns=\"reactiveColumns\"\n :persist-key=\"resolved.persistKey\"\n @change=\"onColumnSettingsChange\"\n />\n </NDrawerContent>\n </NDrawer>\n </div>\n</template>\n\n<script setup lang=\"ts\">\n import {\n ref,\n computed,\n watch,\n onMounted,\n onBeforeUnmount,\n type ComponentPublicInstance,\n } from 'vue'\n import {\n type DataTableRowKey,\n NDataTable,\n NPagination,\n NModal,\n NSpace,\n NButton,\n NDrawer,\n NDrawerContent,\n } from 'naive-ui/es'\n import type {\n TableColumn,\n TableEmits,\n DataRecord,\n MaybeRefLike,\n } from './types'\n import {\n resolveConfig,\n createEditModeChecker,\n type TableConfig,\n type CrudBinding,\n } from './composables/useTableConfig'\n import { useTableManager } from './composables/useTableManager'\n import { usePagination } from './composables/usePagination'\n import { useTableActions } from './composables/useTableActions'\n import { useTableColumns } from './composables/useTableColumns'\n import { useTableGlobalConfig, mergeGlobalConfig } from './composables/useTableGlobalConfig'\n import { useRowDrag } from './composables/useRowDrag'\n import { useCrossPageSelection } from './composables/useCrossPageSelection'\n import { exportTableData } from './composables/useTableExport'\n import { generateFormOptions } from './data'\n import ColumnSettings from './components/ColumnSettings/index.vue'\n import C_Icon from '../C_Icon/index.vue'\n import C_Form from '../C_Form/index.vue'\n\n defineOptions({ name: 'C_Table', inheritAttrs: false })\n\n /* ================= Props(极简 API) ================= */\n\n const props = withDefaults(\n defineProps<{\n /** 列配置(crud 模式下可省略) */\n columns?: TableColumn[]\n /** 数据源(crud 模式下可省略) */\n data?: MaybeRefLike<DataRecord[]>\n /** 加载状态 */\n loading?: MaybeRefLike<boolean>\n /** 行唯一键 */\n rowKey?: (row: DataRecord) => DataTableRowKey\n /** 统一功能配置(edit / selection / expand / pagination / dynamicRows / toolbar / display) */\n config?: TableConfig\n /** CRUD 绑定 — 传入 useTableCrud() 的返回值,自动接管 data/columns/loading/actions/pagination/events */\n crud?: CrudBinding\n }>(),\n {\n rowKey: (row: DataRecord) => row.id as DataTableRowKey,\n config: () => ({}),\n }\n )\n\n const emit = defineEmits<\n TableEmits & {\n 'row-add': [newRow: DataRecord]\n 'row-delete': [deletedRow: DataRecord, index: number]\n 'row-copy': [originalRow: DataRecord, newRow: DataRecord]\n 'row-move': [row: DataRecord, fromIndex: number, toIndex: number]\n 'row-selection-change': [\n selectedKey: DataTableRowKey | null,\n selectedRow: DataRecord | null,\n ]\n 'pagination-change': [page: number, pageSize: number]\n 'view-detail': [data: DataRecord]\n 'column-change': [columns: TableColumn[]]\n }\n >()\n\n /* ================= CRUD 桥接 ================= */\n\n /** 包装 emit:在触发事件的同时自动调用 crud 对应的方法 */\n const bridgedEmit: typeof emit = (event: any, ...args: any[]) => {\n ;(emit as any)(event, ...args)\n\n if (!props.crud) return\n const handlers: Record<string, ((...a: any[]) => void) | undefined> = {\n save: props.crud.save,\n cancel: props.crud.handleCancel,\n 'pagination-change': props.crud.handlePaginationChange,\n 'row-delete': props.crud.handleRowDelete,\n 'view-detail': props.crud.detail?.show,\n }\n handlers[event]?.(...args)\n }\n\n /* ================= 有效值(全局 → crud → props 覆盖) ================= */\n\n const globalConfig = useTableGlobalConfig()\n\n /** 合并 crud 返回的 actions/pagination 到用户 config,并叠加全局配置 */\n const effectiveConfig = computed<TableConfig>(() => {\n let cfg: TableConfig = props.config || {}\n if (props.crud) {\n const fromCrud: Partial<TableConfig> = {}\n if (props.crud.actions) fromCrud.actions = props.crud.actions.value\n if (props.crud.pagination)\n fromCrud.pagination = props.crud.pagination.value ?? undefined\n cfg = { ...fromCrud, ...cfg }\n }\n return mergeGlobalConfig(cfg, globalConfig)\n })\n\n const effectiveColumns = computed<TableColumn[]>(\n () => props.columns ?? props.crud?.columns.value ?? []\n )\n\n /* ================= 配置解析 ================= */\n\n const resolved = computed(() => resolveConfig(effectiveConfig.value))\n const editModeChecker = computed(() => createEditModeChecker(resolved.value))\n\n /* ================= 数据规范化(兼容跨实例 Ref) ================= */\n\n const unwrapRef = <T,>(val: MaybeRefLike<T> | undefined): T | undefined =>\n val && typeof val === 'object' && 'value' in val ? val.value : (val as T)\n\n const normalizedData = computed<DataRecord[]>(\n () => unwrapRef(props.data) ?? props.crud?.data.value ?? []\n )\n\n const normalizedLoading = computed<boolean>(\n () => unwrapRef(props.loading) ?? props.crud?.loading.value ?? false\n )\n\n /* ================= Hooks ================= */\n\n const pagination = usePagination({\n data: normalizedData,\n config: computed(() => resolved.value.pagination),\n emit: bridgedEmit,\n })\n\n const tableManager = useTableManager({\n config: resolved.value,\n data: () => normalizedData.value,\n rowKey: props.rowKey,\n emit: bridgedEmit,\n columns: () => effectiveColumns.value,\n })\n\n const tableActions = useTableActions({\n actions: computed(() => effectiveConfig.value.actions || {}),\n config: resolved,\n tableManager,\n rowKey: props.rowKey,\n emit: bridgedEmit,\n onViewDetail: (data: DataRecord) => bridgedEmit('view-detail', data),\n })\n\n const columnState = useTableColumns({\n rawColumns: effectiveColumns,\n config: resolved,\n columnWidth: resolved.value.columnWidth,\n scrollX: resolved.value.scrollX,\n rowKey: props.rowKey,\n tableManager,\n actionsRenderer: tableActions.renderActions,\n editModeChecker,\n })\n\n const {\n showSettingsPanel,\n reactiveColumns,\n computedColumns,\n computedScrollX,\n } = columnState\n\n /* ================= 行拖拽(可选) ================= */\n\n const rowDragState = resolved.value.rowDrag?.enabled\n ? useRowDrag({\n data: normalizedData,\n rowKey: props.rowKey,\n config: resolved.value.rowDrag!,\n onReorder: (newData) => bridgedEmit('update:data' as any, newData),\n onSort: (row, from, to) => emit('row-move', row, from, to),\n })\n : null\n\n /* ================= 跨页多选(可选) ================= */\n\n const crossPageState = resolved.value.crossPageSelection?.enabled\n ? useCrossPageSelection({\n allData: normalizedData,\n rowKey: props.rowKey,\n config: resolved.value.crossPageSelection!,\n })\n : null\n\n /** 批量选中数量(用于批量操作栏) */\n const batchSelectedCount = computed(() =>\n crossPageState\n ? crossPageState.selectedCount.value\n : (tableManager.checkedKeys?.value?.length ?? 0)\n )\n\n /** 处理批量操作按钮点击 */\n const handleBatchAction = async (action: { onClick: (keys: DataTableRowKey[], rows: DataRecord[]) => void | Promise<void> }) => {\n const keys = crossPageState\n ? [...crossPageState.selectedKeys.value]\n : (tableManager.checkedKeys?.value ?? [])\n const rows = crossPageState\n ? crossPageState.getSelectedRows()\n : normalizedData.value.filter(r => keys.includes(props.rowKey(r)))\n await action.onClick(keys, rows)\n }\n\n /** 清除批量选择 */\n const clearBatchSelection = () => {\n if (crossPageState) crossPageState.clearAll()\n else tableManager.stateManager.selection.clear()\n }\n\n /** 跨页多选时的 checked-row-keys */\n const crossPageCheckedKeys = computed(() =>\n crossPageState ? crossPageState.getPageCheckedKeys(normalizedData.value) : null\n )\n\n /** 选中行变更统一处理 */\n const handleCheckedKeysChange = (keys: DataTableRowKey[]) => {\n if (crossPageState) {\n crossPageState.handlePageSelectionChange(keys, normalizedData.value)\n }\n tableManager.expandState?.handleSelectionChange?.(keys)\n }\n\n /** 导出 */\n const handleExport = async () => {\n const cfg = resolved.value.exportConfig ?? {}\n await exportTableData(normalizedData.value, effectiveColumns.value, {\n ...cfg,\n formatterConfig: resolved.value.formatterConfig,\n })\n }\n\n /* ================= 合计行 ================= */\n\n const summaryFn = computed(() => {\n const cfg = resolved.value\n if (!cfg.summaryRender) return undefined\n return (data: DataRecord[]) => {\n const result = cfg.summaryRender!(data)\n return computedColumns.value.map((col: any) => {\n const key = col.key as string\n const def = result[key]\n return def ? { value: def.value, colSpan: def.colSpan ?? 1 } : { value: '' }\n })\n }\n })\n\n /* ================= 列拖拽排序 ================= */\n\n const tableWrapperRef = ref<HTMLElement>()\n let sortableInstance: any = null\n\n const initColumnDrag = async () => {\n if (!resolved.value.enableColumnDrag || !tableWrapperRef.value) return\n try {\n const { default: Sortable } = await import('sortablejs')\n const headerRow = tableWrapperRef.value.querySelector(\n '.n-data-table-thead tr'\n )\n if (!headerRow) return\n sortableInstance = new Sortable(headerRow as HTMLElement, {\n animation: resolved.value.columnDragAnimationDuration,\n handle: `.${resolved.value.columnDragHandleClass}`,\n ghostClass: 'column-drag-ghost',\n onEnd: (evt: any) => {\n if (evt.oldIndex == null || evt.newIndex == null) return\n const cols = [...reactiveColumns.value]\n const [moved] = cols.splice(evt.oldIndex, 1)\n cols.splice(evt.newIndex, 0, moved)\n columnState.handleColumnChange(cols)\n emit('column-change', cols)\n },\n })\n } catch {\n // sortablejs 未安装时静默降级\n }\n }\n\n /* ================= 表格属性 ================= */\n\n const tableRef = ref<ComponentPublicInstance>()\n\n const computedTableProps = computed(() => ({\n striped: resolved.value.striped,\n bordered: resolved.value.bordered,\n singleLine: resolved.value.singleLine,\n size: resolved.value.size,\n maxHeight: resolved.value.maxHeight,\n }))\n\n /* ================= 编辑弹窗 ================= */\n\n const editFormRef = ref<any>()\n const modalSubmitLoading = ref(false)\n const localEditingData = ref<DataRecord>({})\n\n const modalVisible = computed({\n get: () => tableManager.editStates.modalEdit.isModalVisible.value,\n set: (val: boolean) => {\n tableManager.editStates.modalEdit.isModalVisible.value = val\n },\n })\n\n const editableColumns = computed(() =>\n effectiveColumns.value.filter(\n (col): col is TableColumn => col.editable !== false\n )\n )\n\n const formKey = computed(\n () =>\n `edit-form-${tableManager.editStates.modalEdit.editingRowKey.value || 'new'}`\n )\n\n const formOptions = computed(() => generateFormOptions(editableColumns.value))\n\n watch(\n () => tableManager.editStates.modalEdit.editingData.value,\n newData => {\n if (newData && Object.keys(newData).length > 0) {\n localEditingData.value = JSON.parse(JSON.stringify(newData))\n }\n },\n { immediate: true, deep: true }\n )\n\n let modalCloseTimer: ReturnType<typeof setTimeout> | null = null\n\n watch(modalVisible, visible => {\n if (!visible) {\n modalCloseTimer = setTimeout(() => {\n localEditingData.value = {}\n modalCloseTimer = null\n }, 300)\n }\n })\n\n const handleModalSave = async () => {\n if (!editFormRef.value) return\n modalSubmitLoading.value = true\n try {\n await editFormRef.value.validate()\n await tableManager.editStates.modalEdit.saveEdit(localEditingData.value)\n } catch {\n /* 表单验证错误由组件内联显示 */\n } finally {\n modalSubmitLoading.value = false\n }\n }\n\n const handleModalCancel = () => {\n localEditingData.value = {}\n tableManager.editStates.modalEdit.cancelEdit()\n }\n\n /* ================= 列设置 ================= */\n\n const onColumnSettingsChange = (columns: TableColumn[]) => {\n columnState.handleColumnChange(columns)\n emit('column-change', columnState.reactiveColumns.value)\n }\n\n /* ================= Expose ================= */\n\n const { edit, expand, selection, dynamicRows } = tableManager.stateManager\n\n const exposedApi = {\n startEdit: edit.start,\n expandAll: expand.all,\n collapseAll: expand.collapseAll,\n selectAll: selection.all,\n clearSelection: selection.clear,\n clearAllSelections: tableManager.stateManager.clearAllSelections,\n clearRowSelection: dynamicRows?.clearSelection,\n resetToFirstPage: pagination.resetToFirstPage,\n getSelectedRows: selection.getSelected,\n getEditingData: edit.getEditingData,\n isEditing: edit.isEditing,\n isExpanded: expand.isExpanded,\n getManager: () => tableManager.stateManager,\n /** 导出数据 */\n exportData: handleExport,\n /** 跨页选择 */\n crossPageSelection: crossPageState\n ? {\n selectedKeys: crossPageState.selectedKeys,\n selectedCount: crossPageState.selectedCount,\n selectAll: crossPageState.selectAll,\n clearAll: crossPageState.clearAll,\n getSelectedRows: crossPageState.getSelectedRows,\n }\n : undefined,\n }\n\n defineExpose(exposedApi)\n\n /* 自动连接 crud.tableRef */\n onMounted(() => {\n const crudRef = props.crud?.tableRef\n if (crudRef) crudRef.value = exposedApi\n // 列拖拽初始化\n initColumnDrag()\n // 行拖拽初始化\n if (rowDragState && tableWrapperRef.value) {\n rowDragState.initRowDrag(tableWrapperRef.value)\n }\n })\n\n onBeforeUnmount(() => {\n if (modalCloseTimer) clearTimeout(modalCloseTimer)\n if (sortableInstance) sortableInstance.destroy()\n rowDragState?.destroyRowDrag()\n })\n</script>\n\n<style scoped lang=\"scss\">\n @use './index.scss';\n</style>\n","/* unplugin-vue-components disabled */<!--\n * @Description: C_Table 超级表格组件(薄 UI 壳)\n * @Migration: naive-ui-components 组件库迁移版本\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\n *\n * 使用侧 API:\n * <C_Table :columns=\"cols\" :data=\"data\" :loading=\"loading\" :config=\"tableConfig\" />\n *\n * config 收拢了所有功能配置:edit / selection / expand / pagination / dynamicRows / toolbar / display\n * 列处理 → composables/useTableColumns.ts\n * 配置解析 → composables/useTableConfig.ts\n * 编辑/展开/动态行 → composables/useTableManager.ts\n -->\n\n<template>\n <div\n ref=\"tableWrapperRef\"\n class=\"c-table-wrapper\"\n >\n <!-- 动态行工具栏 -->\n <component\n v-if=\"tableManager.dynamicRowsState\"\n :is=\"tableManager.dynamicRowsState.renderToolbar()\"\n />\n\n <!-- 表格工具栏 -->\n <div\n v-if=\"resolved.showToolbar\"\n class=\"table-toolbar\"\n >\n <div class=\"toolbar-left\">\n <slot name=\"toolbar-left\" />\n </div>\n <div class=\"toolbar-right\">\n <slot name=\"toolbar-right\" />\n <C_Icon\n v-if=\"resolved.exportConfig\"\n name=\"mdi:download\"\n size=\"18\"\n title=\"导出\"\n clickable\n class=\"column-settings-btn\"\n @click=\"handleExport()\"\n />\n <C_Icon\n v-if=\"resolved.enableColumnSettings\"\n name=\"mdi:cog\"\n size=\"18\"\n title=\"表格设置\"\n clickable\n class=\"column-settings-btn\"\n @click=\"showSettingsPanel = true\"\n />\n </div>\n </div>\n\n <!-- 批量操作栏 -->\n <div\n v-if=\"resolved.batchActions?.enabled && batchSelectedCount > 0\"\n class=\"batch-actions-bar\"\n >\n <span class=\"batch-info\">已选择 {{ batchSelectedCount }} 项</span>\n <NSpace :size=\"8\">\n <NButton\n v-for=\"action in (resolved.batchActions.actions || [])\"\n :key=\"action.key\"\n :type=\"(action.type as any) || 'default'\"\n size=\"small\"\n @click=\"handleBatchAction(action)\"\n >\n <template v-if=\"action.icon\" #icon>\n <C_Icon :name=\"action.icon\" size=\"14\" />\n </template>\n {{ action.label }}\n </NButton>\n <NButton size=\"small\" @click=\"clearBatchSelection\">取消选择</NButton>\n </NSpace>\n </div>\n\n <!-- 错误状态 -->\n <div v-if=\"resolved.error?.show\" class=\"table-error-state\">\n <slot name=\"error\" :error=\"resolved.error\">\n <div class=\"error-content\">\n <C_Icon name=\"mdi:alert-circle-outline\" size=\"48\" />\n <p class=\"error-message\">{{ resolved.error.message || '数据加载失败' }}</p>\n <NButton v-if=\"resolved.error.onRetry\" type=\"primary\" size=\"small\" @click=\"resolved.error.onRetry\">\n 重试\n </NButton>\n </div>\n </slot>\n </div>\n\n <!-- 表格主体 -->\n <NDataTable\n v-if=\"!resolved.error?.show\"\n ref=\"tableRef\"\n v-bind=\"{ ...computedTableProps, ...$attrs }\"\n :columns=\"computedColumns\"\n :data=\"normalizedData\"\n :loading=\"normalizedLoading\"\n :row-key=\"rowKey\"\n :expanded-row-keys=\"tableManager.expandedKeys.value\"\n :checked-row-keys=\"crossPageCheckedKeys ?? tableManager.checkedKeys.value\"\n @update:expanded-row-keys=\"tableManager.expandState?.handleExpandChange\"\n @update:checked-row-keys=\"handleCheckedKeysChange\"\n :scroll-x=\"computedScrollX\"\n :virtual-scroll=\"resolved.virtualScroll\"\n :virtual-scroll-item-size=\"resolved.virtualScroll ? resolved.virtualItemHeight : undefined\"\n :max-height=\"resolved.virtualScroll ? resolved.virtualMaxHeight : resolved.maxHeight\"\n :summary=\"summaryFn\"\n :summary-placement=\"resolved.summaryPosition\"\n :children-key=\"resolved.treeEnabled ? resolved.treeChildrenKey : undefined\"\n :indent=\"resolved.treeEnabled ? resolved.treeIndent : undefined\"\n :default-expand-all=\"resolved.treeEnabled ? resolved.treeDefaultExpandAll : undefined\"\n style=\"width: 100%\"\n />\n\n <!-- 分页 -->\n <NPagination\n v-if=\"pagination.paginationConfig.value\"\n v-bind=\"pagination.paginationConfig.value\"\n class=\"pagination-wrapper\"\n />\n\n <!-- 编辑弹窗 -->\n <NModal\n v-if=\"resolved.editMode === 'modal' || resolved.editMode === 'both'\"\n v-model:show=\"modalVisible\"\n :title=\"resolved.modalTitle\"\n :width=\"resolved.modalWidth\"\n preset=\"card\"\n :mask-closable=\"false\"\n :close-on-esc=\"false\"\n class=\"w60%\"\n :closable=\"false\"\n >\n <C_Form\n v-if=\"modalVisible && formOptions.length\"\n ref=\"editFormRef\"\n :key=\"formKey\"\n v-model=\"localEditingData\"\n :options=\"formOptions\"\n :config=\"{\n layout: 'grid',\n grid: { cols: 2, xGap: 16, yGap: 16 },\n showActions: false,\n }\"\n />\n <template #action>\n <NSpace justify=\"end\">\n <NButton @click=\"handleModalCancel\">取消</NButton>\n <NButton\n type=\"primary\"\n :loading=\"modalSubmitLoading\"\n @click=\"handleModalSave\"\n >\n 保存\n </NButton>\n </NSpace>\n </template>\n </NModal>\n\n <!-- 动态行确认删除弹窗 -->\n <component\n v-if=\"tableManager.dynamicRowsState\"\n :is=\"tableManager.dynamicRowsState.renderConfirmModal()\"\n />\n\n <!-- 列设置抽屉 -->\n <NDrawer\n v-model:show=\"showSettingsPanel\"\n :width=\"420\"\n placement=\"right\"\n :mask-closable=\"true\"\n >\n <NDrawerContent\n title=\"列设置\"\n closable\n >\n <ColumnSettings\n :columns=\"reactiveColumns\"\n :persist-key=\"resolved.persistKey\"\n @change=\"onColumnSettingsChange\"\n />\n </NDrawerContent>\n </NDrawer>\n </div>\n</template>\n\n<script setup lang=\"ts\">\n import {\n ref,\n computed,\n watch,\n onMounted,\n onBeforeUnmount,\n type ComponentPublicInstance,\n } from 'vue'\n import {\n type DataTableRowKey,\n NDataTable,\n NPagination,\n NModal,\n NSpace,\n NButton,\n NDrawer,\n NDrawerContent,\n } from 'naive-ui/es'\n import type {\n TableColumn,\n TableEmits,\n DataRecord,\n MaybeRefLike,\n } from './types'\n import {\n resolveConfig,\n createEditModeChecker,\n type TableConfig,\n type CrudBinding,\n } from './composables/useTableConfig'\n import { useTableManager } from './composables/useTableManager'\n import { usePagination } from './composables/usePagination'\n import { useTableActions } from './composables/useTableActions'\n import { useTableColumns } from './composables/useTableColumns'\n import { useTableGlobalConfig, mergeGlobalConfig } from './composables/useTableGlobalConfig'\n import { useRowDrag } from './composables/useRowDrag'\n import { useCrossPageSelection } from './composables/useCrossPageSelection'\n import { exportTableData } from './composables/useTableExport'\n import { generateFormOptions } from './data'\n import ColumnSettings from './components/ColumnSettings/index.vue'\n import C_Icon from '../C_Icon/index.vue'\n import C_Form from '../C_Form/index.vue'\n\n defineOptions({ name: 'C_Table', inheritAttrs: false })\n\n /* ================= Props(极简 API) ================= */\n\n const props = withDefaults(\n defineProps<{\n /** 列配置(crud 模式下可省略) */\n columns?: TableColumn[]\n /** 数据源(crud 模式下可省略) */\n data?: MaybeRefLike<DataRecord[]>\n /** 加载状态 */\n loading?: MaybeRefLike<boolean>\n /** 行唯一键 */\n rowKey?: (row: DataRecord) => DataTableRowKey\n /** 统一功能配置(edit / selection / expand / pagination / dynamicRows / toolbar / display) */\n config?: TableConfig\n /** CRUD 绑定 — 传入 useTableCrud() 的返回值,自动接管 data/columns/loading/actions/pagination/events */\n crud?: CrudBinding\n }>(),\n {\n rowKey: (row: DataRecord) => row.id as DataTableRowKey,\n config: () => ({}),\n }\n )\n\n const emit = defineEmits<\n TableEmits & {\n 'row-add': [newRow: DataRecord]\n 'row-delete': [deletedRow: DataRecord, index: number]\n 'row-copy': [originalRow: DataRecord, newRow: DataRecord]\n 'row-move': [row: DataRecord, fromIndex: number, toIndex: number]\n 'row-selection-change': [\n selectedKey: DataTableRowKey | null,\n selectedRow: DataRecord | null,\n ]\n 'pagination-change': [page: number, pageSize: number]\n 'view-detail': [data: DataRecord]\n 'column-change': [columns: TableColumn[]]\n }\n >()\n\n /* ================= CRUD 桥接 ================= */\n\n /** 包装 emit:在触发事件的同时自动调用 crud 对应的方法 */\n const bridgedEmit: typeof emit = (event: any, ...args: any[]) => {\n ;(emit as any)(event, ...args)\n\n if (!props.crud) return\n const handlers: Record<string, ((...a: any[]) => void) | undefined> = {\n save: props.crud.save,\n cancel: props.crud.handleCancel,\n 'pagination-change': props.crud.handlePaginationChange,\n 'row-delete': props.crud.handleRowDelete,\n 'view-detail': props.crud.detail?.show,\n }\n handlers[event]?.(...args)\n }\n\n /* ================= 有效值(全局 → crud → props 覆盖) ================= */\n\n const globalConfig = useTableGlobalConfig()\n\n /** 合并 crud 返回的 actions/pagination 到用户 config,并叠加全局配置 */\n const effectiveConfig = computed<TableConfig>(() => {\n let cfg: TableConfig = props.config || {}\n if (props.crud) {\n const fromCrud: Partial<TableConfig> = {}\n if (props.crud.actions) fromCrud.actions = props.crud.actions.value\n if (props.crud.pagination)\n fromCrud.pagination = props.crud.pagination.value ?? undefined\n cfg = { ...fromCrud, ...cfg }\n }\n return mergeGlobalConfig(cfg, globalConfig)\n })\n\n const effectiveColumns = computed<TableColumn[]>(\n () => props.columns ?? props.crud?.columns.value ?? []\n )\n\n /* ================= 配置解析 ================= */\n\n const resolved = computed(() => resolveConfig(effectiveConfig.value))\n const editModeChecker = computed(() => createEditModeChecker(resolved.value))\n\n /* ================= 数据规范化(兼容跨实例 Ref) ================= */\n\n const unwrapRef = <T,>(val: MaybeRefLike<T> | undefined): T | undefined =>\n val && typeof val === 'object' && 'value' in val ? val.value : (val as T)\n\n const normalizedData = computed<DataRecord[]>(\n () => unwrapRef(props.data) ?? props.crud?.data.value ?? []\n )\n\n const normalizedLoading = computed<boolean>(\n () => unwrapRef(props.loading) ?? props.crud?.loading.value ?? false\n )\n\n /* ================= Hooks ================= */\n\n const pagination = usePagination({\n data: normalizedData,\n config: computed(() => resolved.value.pagination),\n emit: bridgedEmit,\n })\n\n const tableManager = useTableManager({\n config: resolved.value,\n data: () => normalizedData.value,\n rowKey: props.rowKey,\n emit: bridgedEmit,\n columns: () => effectiveColumns.value,\n })\n\n const tableActions = useTableActions({\n actions: computed(() => effectiveConfig.value.actions || {}),\n config: resolved,\n tableManager,\n rowKey: props.rowKey,\n emit: bridgedEmit,\n onViewDetail: (data: DataRecord) => bridgedEmit('view-detail', data),\n })\n\n const columnState = useTableColumns({\n rawColumns: effectiveColumns,\n config: resolved,\n columnWidth: resolved.value.columnWidth,\n scrollX: resolved.value.scrollX,\n rowKey: props.rowKey,\n tableManager,\n actionsRenderer: tableActions.renderActions,\n editModeChecker,\n })\n\n const {\n showSettingsPanel,\n reactiveColumns,\n computedColumns,\n computedScrollX,\n } = columnState\n\n /* ================= 行拖拽(可选) ================= */\n\n const rowDragState = resolved.value.rowDrag?.enabled\n ? useRowDrag({\n data: normalizedData,\n rowKey: props.rowKey,\n config: resolved.value.rowDrag!,\n onReorder: (newData) => bridgedEmit('update:data' as any, newData),\n onSort: (row, from, to) => emit('row-move', row, from, to),\n })\n : null\n\n /* ================= 跨页多选(可选) ================= */\n\n const crossPageState = resolved.value.crossPageSelection?.enabled\n ? useCrossPageSelection({\n allData: normalizedData,\n rowKey: props.rowKey,\n config: resolved.value.crossPageSelection!,\n })\n : null\n\n /** 批量选中数量(用于批量操作栏) */\n const batchSelectedCount = computed(() =>\n crossPageState\n ? crossPageState.selectedCount.value\n : (tableManager.checkedKeys?.value?.length ?? 0)\n )\n\n /** 处理批量操作按钮点击 */\n const handleBatchAction = async (action: { onClick: (keys: DataTableRowKey[], rows: DataRecord[]) => void | Promise<void> }) => {\n const keys = crossPageState\n ? [...crossPageState.selectedKeys.value]\n : (tableManager.checkedKeys?.value ?? [])\n const rows = crossPageState\n ? crossPageState.getSelectedRows()\n : normalizedData.value.filter(r => keys.includes(props.rowKey(r)))\n await action.onClick(keys, rows)\n }\n\n /** 清除批量选择 */\n const clearBatchSelection = () => {\n if (crossPageState) crossPageState.clearAll()\n else tableManager.stateManager.selection.clear()\n }\n\n /** 跨页多选时的 checked-row-keys */\n const crossPageCheckedKeys = computed(() =>\n crossPageState ? crossPageState.getPageCheckedKeys(normalizedData.value) : null\n )\n\n /** 选中行变更统一处理 */\n const handleCheckedKeysChange = (keys: DataTableRowKey[]) => {\n if (crossPageState) {\n crossPageState.handlePageSelectionChange(keys, normalizedData.value)\n }\n tableManager.expandState?.handleSelectionChange?.(keys)\n }\n\n /** 导出 */\n const handleExport = async () => {\n const cfg = resolved.value.exportConfig ?? {}\n await exportTableData(normalizedData.value, effectiveColumns.value, {\n ...cfg,\n formatterConfig: resolved.value.formatterConfig,\n })\n }\n\n /* ================= 合计行 ================= */\n\n const summaryFn = computed(() => {\n const cfg = resolved.value\n if (!cfg.summaryRender) return undefined\n return (data: DataRecord[]) => {\n const result = cfg.summaryRender!(data)\n return computedColumns.value.map((col: any) => {\n const key = col.key as string\n const def = result[key]\n return def ? { value: def.value, colSpan: def.colSpan ?? 1 } : { value: '' }\n })\n }\n })\n\n /* ================= 列拖拽排序 ================= */\n\n const tableWrapperRef = ref<HTMLElement>()\n let sortableInstance: any = null\n\n const initColumnDrag = async () => {\n if (!resolved.value.enableColumnDrag || !tableWrapperRef.value) return\n try {\n const { default: Sortable } = await import('sortablejs')\n const headerRow = tableWrapperRef.value.querySelector(\n '.n-data-table-thead tr'\n )\n if (!headerRow) return\n sortableInstance = new Sortable(headerRow as HTMLElement, {\n animation: resolved.value.columnDragAnimationDuration,\n handle: `.${resolved.value.columnDragHandleClass}`,\n ghostClass: 'column-drag-ghost',\n onEnd: (evt: any) => {\n if (evt.oldIndex == null || evt.newIndex == null) return\n const cols = [...reactiveColumns.value]\n const [moved] = cols.splice(evt.oldIndex, 1)\n cols.splice(evt.newIndex, 0, moved)\n columnState.handleColumnChange(cols)\n emit('column-change', cols)\n },\n })\n } catch {\n // sortablejs 未安装时静默降级\n }\n }\n\n /* ================= 表格属性 ================= */\n\n const tableRef = ref<ComponentPublicInstance>()\n\n const computedTableProps = computed(() => ({\n striped: resolved.value.striped,\n bordered: resolved.value.bordered,\n singleLine: resolved.value.singleLine,\n size: resolved.value.size,\n maxHeight: resolved.value.maxHeight,\n }))\n\n /* ================= 编辑弹窗 ================= */\n\n const editFormRef = ref<any>()\n const modalSubmitLoading = ref(false)\n const localEditingData = ref<DataRecord>({})\n\n const modalVisible = computed({\n get: () => tableManager.editStates.modalEdit.isModalVisible.value,\n set: (val: boolean) => {\n tableManager.editStates.modalEdit.isModalVisible.value = val\n },\n })\n\n const editableColumns = computed(() =>\n effectiveColumns.value.filter(\n (col): col is TableColumn => col.editable !== false\n )\n )\n\n const formKey = computed(\n () =>\n `edit-form-${tableManager.editStates.modalEdit.editingRowKey.value || 'new'}`\n )\n\n const formOptions = computed(() => generateFormOptions(editableColumns.value))\n\n watch(\n () => tableManager.editStates.modalEdit.editingData.value,\n newData => {\n if (newData && Object.keys(newData).length > 0) {\n localEditingData.value = JSON.parse(JSON.stringify(newData))\n }\n },\n { immediate: true, deep: true }\n )\n\n let modalCloseTimer: ReturnType<typeof setTimeout> | null = null\n\n watch(modalVisible, visible => {\n if (!visible) {\n modalCloseTimer = setTimeout(() => {\n localEditingData.value = {}\n modalCloseTimer = null\n }, 300)\n }\n })\n\n const handleModalSave = async () => {\n if (!editFormRef.value) return\n modalSubmitLoading.value = true\n try {\n await editFormRef.value.validate()\n await tableManager.editStates.modalEdit.saveEdit(localEditingData.value)\n } catch {\n /* 表单验证错误由组件内联显示 */\n } finally {\n modalSubmitLoading.value = false\n }\n }\n\n const handleModalCancel = () => {\n localEditingData.value = {}\n tableManager.editStates.modalEdit.cancelEdit()\n }\n\n /* ================= 列设置 ================= */\n\n const onColumnSettingsChange = (columns: TableColumn[]) => {\n columnState.handleColumnChange(columns)\n emit('column-change', columnState.reactiveColumns.value)\n }\n\n /* ================= Expose ================= */\n\n const { edit, expand, selection, dynamicRows } = tableManager.stateManager\n\n const exposedApi = {\n startEdit: edit.start,\n expandAll: expand.all,\n collapseAll: expand.collapseAll,\n selectAll: selection.all,\n clearSelection: selection.clear,\n clearAllSelections: tableManager.stateManager.clearAllSelections,\n clearRowSelection: dynamicRows?.clearSelection,\n resetToFirstPage: pagination.resetToFirstPage,\n getSelectedRows: selection.getSelected,\n getEditingData: edit.getEditingData,\n isEditing: edit.isEditing,\n isExpanded: expand.isExpanded,\n getManager: () => tableManager.stateManager,\n /** 导出数据 */\n exportData: handleExport,\n /** 跨页选择 */\n crossPageSelection: crossPageState\n ? {\n selectedKeys: crossPageState.selectedKeys,\n selectedCount: crossPageState.selectedCount,\n selectAll: crossPageState.selectAll,\n clearAll: crossPageState.clearAll,\n getSelectedRows: crossPageState.getSelectedRows,\n }\n : undefined,\n }\n\n defineExpose(exposedApi)\n\n /* 自动连接 crud.tableRef */\n onMounted(() => {\n const crudRef = props.crud?.tableRef\n if (crudRef) crudRef.value = exposedApi\n // 列拖拽初始化\n initColumnDrag()\n // 行拖拽初始化\n if (rowDragState && tableWrapperRef.value) {\n rowDragState.initRowDrag(tableWrapperRef.value)\n }\n })\n\n onBeforeUnmount(() => {\n if (modalCloseTimer) clearTimeout(modalCloseTimer)\n if (sortableInstance) sortableInstance.destroy()\n rowDragState?.destroyRowDrag()\n })\n</script>\n\n<style scoped lang=\"scss\">\n @use './index.scss';\n</style>\n","<!--\n * @Description: C_Table 超级表格组件(薄 UI 壳)\n * @Migration: naive-ui-components 组件库迁移版本\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\n *\n * 使用侧 API:\n * <C_Table :columns=\"cols\" :data=\"data\" :loading=\"loading\" :config=\"tableConfig\" />\n *\n * config 收拢了所有功能配置:edit / selection / expand / pagination / dynamicRows / toolbar / display\n * 列处理 → composables/useTableColumns.ts\n * 配置解析 → composables/useTableConfig.ts\n * 编辑/展开/动态行 → composables/useTableManager.ts\n -->\n\n<template>\n <div\n ref=\"tableWrapperRef\"\n class=\"c-table-wrapper\"\n >\n <!-- 动态行工具栏 -->\n <component\n v-if=\"tableManager.dynamicRowsState\"\n :is=\"tableManager.dynamicRowsState.renderToolbar()\"\n />\n\n <!-- 表格工具栏 -->\n <div\n v-if=\"resolved.showToolbar\"\n class=\"table-toolbar\"\n >\n <div class=\"toolbar-left\">\n <slot name=\"toolbar-left\" />\n </div>\n <div class=\"toolbar-right\">\n <slot name=\"toolbar-right\" />\n <C_Icon\n v-if=\"resolved.exportConfig\"\n name=\"mdi:download\"\n size=\"18\"\n title=\"导出\"\n clickable\n class=\"column-settings-btn\"\n @click=\"handleExport()\"\n />\n <C_Icon\n v-if=\"resolved.enableColumnSettings\"\n name=\"mdi:cog\"\n size=\"18\"\n title=\"表格设置\"\n clickable\n class=\"column-settings-btn\"\n @click=\"showSettingsPanel = true\"\n />\n </div>\n </div>\n\n <!-- 批量操作栏 -->\n <div\n v-if=\"resolved.batchActions?.enabled && batchSelectedCount > 0\"\n class=\"batch-actions-bar\"\n >\n <span class=\"batch-info\">已选择 {{ batchSelectedCount }} 项</span>\n <NSpace :size=\"8\">\n <NButton\n v-for=\"action in (resolved.batchActions.actions || [])\"\n :key=\"action.key\"\n :type=\"(action.type as any) || 'default'\"\n size=\"small\"\n @click=\"handleBatchAction(action)\"\n >\n <template v-if=\"action.icon\" #icon>\n <C_Icon :name=\"action.icon\" size=\"14\" />\n </template>\n {{ action.label }}\n </NButton>\n <NButton size=\"small\" @click=\"clearBatchSelection\">取消选择</NButton>\n </NSpace>\n </div>\n\n <!-- 错误状态 -->\n <div v-if=\"resolved.error?.show\" class=\"table-error-state\">\n <slot name=\"error\" :error=\"resolved.error\">\n <div class=\"error-content\">\n <C_Icon name=\"mdi:alert-circle-outline\" size=\"48\" />\n <p class=\"error-message\">{{ resolved.error.message || '数据加载失败' }}</p>\n <NButton v-if=\"resolved.error.onRetry\" type=\"primary\" size=\"small\" @click=\"resolved.error.onRetry\">\n 重试\n </NButton>\n </div>\n </slot>\n </div>\n\n <!-- 表格主体 -->\n <NDataTable\n v-if=\"!resolved.error?.show\"\n ref=\"tableRef\"\n v-bind=\"{ ...computedTableProps, ...$attrs }\"\n :columns=\"computedColumns\"\n :data=\"normalizedData\"\n :loading=\"normalizedLoading\"\n :row-key=\"rowKey\"\n :expanded-row-keys=\"tableManager.expandedKeys.value\"\n :checked-row-keys=\"crossPageCheckedKeys ?? tableManager.checkedKeys.value\"\n @update:expanded-row-keys=\"tableManager.expandState?.handleExpandChange\"\n @update:checked-row-keys=\"handleCheckedKeysChange\"\n :scroll-x=\"computedScrollX\"\n :virtual-scroll=\"resolved.virtualScroll\"\n :virtual-scroll-item-size=\"resolved.virtualScroll ? resolved.virtualItemHeight : undefined\"\n :max-height=\"resolved.virtualScroll ? resolved.virtualMaxHeight : resolved.maxHeight\"\n :summary=\"summaryFn\"\n :summary-placement=\"resolved.summaryPosition\"\n :children-key=\"resolved.treeEnabled ? resolved.treeChildrenKey : undefined\"\n :indent=\"resolved.treeEnabled ? resolved.treeIndent : undefined\"\n :default-expand-all=\"resolved.treeEnabled ? resolved.treeDefaultExpandAll : undefined\"\n style=\"width: 100%\"\n />\n\n <!-- 分页 -->\n <NPagination\n v-if=\"pagination.paginationConfig.value\"\n v-bind=\"pagination.paginationConfig.value\"\n class=\"pagination-wrapper\"\n />\n\n <!-- 编辑弹窗 -->\n <NModal\n v-if=\"resolved.editMode === 'modal' || resolved.editMode === 'both'\"\n v-model:show=\"modalVisible\"\n :title=\"resolved.modalTitle\"\n :width=\"resolved.modalWidth\"\n preset=\"card\"\n :mask-closable=\"false\"\n :close-on-esc=\"false\"\n class=\"w60%\"\n :closable=\"false\"\n >\n <C_Form\n v-if=\"modalVisible && formOptions.length\"\n ref=\"editFormRef\"\n :key=\"formKey\"\n v-model=\"localEditingData\"\n :options=\"formOptions\"\n :config=\"{\n layout: 'grid',\n grid: { cols: 2, xGap: 16, yGap: 16 },\n showActions: false,\n }\"\n />\n <template #action>\n <NSpace justify=\"end\">\n <NButton @click=\"handleModalCancel\">取消</NButton>\n <NButton\n type=\"primary\"\n :loading=\"modalSubmitLoading\"\n @click=\"handleModalSave\"\n >\n 保存\n </NButton>\n </NSpace>\n </template>\n </NModal>\n\n <!-- 动态行确认删除弹窗 -->\n <component\n v-if=\"tableManager.dynamicRowsState\"\n :is=\"tableManager.dynamicRowsState.renderConfirmModal()\"\n />\n\n <!-- 列设置抽屉 -->\n <NDrawer\n v-model:show=\"showSettingsPanel\"\n :width=\"420\"\n placement=\"right\"\n :mask-closable=\"true\"\n >\n <NDrawerContent\n title=\"列设置\"\n closable\n >\n <ColumnSettings\n :columns=\"reactiveColumns\"\n :persist-key=\"resolved.persistKey\"\n @change=\"onColumnSettingsChange\"\n />\n </NDrawerContent>\n </NDrawer>\n </div>\n</template>\n\n<script setup lang=\"ts\">\n import {\n ref,\n computed,\n watch,\n onMounted,\n onBeforeUnmount,\n type ComponentPublicInstance,\n } from 'vue'\n import {\n type DataTableRowKey,\n NDataTable,\n NPagination,\n NModal,\n NSpace,\n NButton,\n NDrawer,\n NDrawerContent,\n } from 'naive-ui/es'\n import type {\n TableColumn,\n TableEmits,\n DataRecord,\n MaybeRefLike,\n } from './types'\n import {\n resolveConfig,\n createEditModeChecker,\n type TableConfig,\n type CrudBinding,\n } from './composables/useTableConfig'\n import { useTableManager } from './composables/useTableManager'\n import { usePagination } from './composables/usePagination'\n import { useTableActions } from './composables/useTableActions'\n import { useTableColumns } from './composables/useTableColumns'\n import { useTableGlobalConfig, mergeGlobalConfig } from './composables/useTableGlobalConfig'\n import { useRowDrag } from './composables/useRowDrag'\n import { useCrossPageSelection } from './composables/useCrossPageSelection'\n import { exportTableData } from './composables/useTableExport'\n import { generateFormOptions } from './data'\n import ColumnSettings from './components/ColumnSettings/index.vue'\n import C_Icon from '../C_Icon/index.vue'\n import C_Form from '../C_Form/index.vue'\n\n defineOptions({ name: 'C_Table', inheritAttrs: false })\n\n /* ================= Props(极简 API) ================= */\n\n const props = withDefaults(\n defineProps<{\n /** 列配置(crud 模式下可省略) */\n columns?: TableColumn[]\n /** 数据源(crud 模式下可省略) */\n data?: MaybeRefLike<DataRecord[]>\n /** 加载状态 */\n loading?: MaybeRefLike<boolean>\n /** 行唯一键 */\n rowKey?: (row: DataRecord) => DataTableRowKey\n /** 统一功能配置(edit / selection / expand / pagination / dynamicRows / toolbar / display) */\n config?: TableConfig\n /** CRUD 绑定 — 传入 useTableCrud() 的返回值,自动接管 data/columns/loading/actions/pagination/events */\n crud?: CrudBinding\n }>(),\n {\n rowKey: (row: DataRecord) => row.id as DataTableRowKey,\n config: () => ({}),\n }\n )\n\n const emit = defineEmits<\n TableEmits & {\n 'row-add': [newRow: DataRecord]\n 'row-delete': [deletedRow: DataRecord, index: number]\n 'row-copy': [originalRow: DataRecord, newRow: DataRecord]\n 'row-move': [row: DataRecord, fromIndex: number, toIndex: number]\n 'row-selection-change': [\n selectedKey: DataTableRowKey | null,\n selectedRow: DataRecord | null,\n ]\n 'pagination-change': [page: number, pageSize: number]\n 'view-detail': [data: DataRecord]\n 'column-change': [columns: TableColumn[]]\n }\n >()\n\n /* ================= CRUD 桥接 ================= */\n\n /** 包装 emit:在触发事件的同时自动调用 crud 对应的方法 */\n const bridgedEmit: typeof emit = (event: any, ...args: any[]) => {\n ;(emit as any)(event, ...args)\n\n if (!props.crud) return\n const handlers: Record<string, ((...a: any[]) => void) | undefined> = {\n save: props.crud.save,\n cancel: props.crud.handleCancel,\n 'pagination-change': props.crud.handlePaginationChange,\n 'row-delete': props.crud.handleRowDelete,\n 'view-detail': props.crud.detail?.show,\n }\n handlers[event]?.(...args)\n }\n\n /* ================= 有效值(全局 → crud → props 覆盖) ================= */\n\n const globalConfig = useTableGlobalConfig()\n\n /** 合并 crud 返回的 actions/pagination 到用户 config,并叠加全局配置 */\n const effectiveConfig = computed<TableConfig>(() => {\n let cfg: TableConfig = props.config || {}\n if (props.crud) {\n const fromCrud: Partial<TableConfig> = {}\n if (props.crud.actions) fromCrud.actions = props.crud.actions.value\n if (props.crud.pagination)\n fromCrud.pagination = props.crud.pagination.value ?? undefined\n cfg = { ...fromCrud, ...cfg }\n }\n return mergeGlobalConfig(cfg, globalConfig)\n })\n\n const effectiveColumns = computed<TableColumn[]>(\n () => props.columns ?? props.crud?.columns.value ?? []\n )\n\n /* ================= 配置解析 ================= */\n\n const resolved = computed(() => resolveConfig(effectiveConfig.value))\n const editModeChecker = computed(() => createEditModeChecker(resolved.value))\n\n /* ================= 数据规范化(兼容跨实例 Ref) ================= */\n\n const unwrapRef = <T,>(val: MaybeRefLike<T> | undefined): T | undefined =>\n val && typeof val === 'object' && 'value' in val ? val.value : (val as T)\n\n const normalizedData = computed<DataRecord[]>(\n () => unwrapRef(props.data) ?? props.crud?.data.value ?? []\n )\n\n const normalizedLoading = computed<boolean>(\n () => unwrapRef(props.loading) ?? props.crud?.loading.value ?? false\n )\n\n /* ================= Hooks ================= */\n\n const pagination = usePagination({\n data: normalizedData,\n config: computed(() => resolved.value.pagination),\n emit: bridgedEmit,\n })\n\n const tableManager = useTableManager({\n config: resolved.value,\n data: () => normalizedData.value,\n rowKey: props.rowKey,\n emit: bridgedEmit,\n columns: () => effectiveColumns.value,\n })\n\n const tableActions = useTableActions({\n actions: computed(() => effectiveConfig.value.actions || {}),\n config: resolved,\n tableManager,\n rowKey: props.rowKey,\n emit: bridgedEmit,\n onViewDetail: (data: DataRecord) => bridgedEmit('view-detail', data),\n })\n\n const columnState = useTableColumns({\n rawColumns: effectiveColumns,\n config: resolved,\n columnWidth: resolved.value.columnWidth,\n scrollX: resolved.value.scrollX,\n rowKey: props.rowKey,\n tableManager,\n actionsRenderer: tableActions.renderActions,\n editModeChecker,\n })\n\n const {\n showSettingsPanel,\n reactiveColumns,\n computedColumns,\n computedScrollX,\n } = columnState\n\n /* ================= 行拖拽(可选) ================= */\n\n const rowDragState = resolved.value.rowDrag?.enabled\n ? useRowDrag({\n data: normalizedData,\n rowKey: props.rowKey,\n config: resolved.value.rowDrag!,\n onReorder: (newData) => bridgedEmit('update:data' as any, newData),\n onSort: (row, from, to) => emit('row-move', row, from, to),\n })\n : null\n\n /* ================= 跨页多选(可选) ================= */\n\n const crossPageState = resolved.value.crossPageSelection?.enabled\n ? useCrossPageSelection({\n allData: normalizedData,\n rowKey: props.rowKey,\n config: resolved.value.crossPageSelection!,\n })\n : null\n\n /** 批量选中数量(用于批量操作栏) */\n const batchSelectedCount = computed(() =>\n crossPageState\n ? crossPageState.selectedCount.value\n : (tableManager.checkedKeys?.value?.length ?? 0)\n )\n\n /** 处理批量操作按钮点击 */\n const handleBatchAction = async (action: { onClick: (keys: DataTableRowKey[], rows: DataRecord[]) => void | Promise<void> }) => {\n const keys = crossPageState\n ? [...crossPageState.selectedKeys.value]\n : (tableManager.checkedKeys?.value ?? [])\n const rows = crossPageState\n ? crossPageState.getSelectedRows()\n : normalizedData.value.filter(r => keys.includes(props.rowKey(r)))\n await action.onClick(keys, rows)\n }\n\n /** 清除批量选择 */\n const clearBatchSelection = () => {\n if (crossPageState) crossPageState.clearAll()\n else tableManager.stateManager.selection.clear()\n }\n\n /** 跨页多选时的 checked-row-keys */\n const crossPageCheckedKeys = computed(() =>\n crossPageState ? crossPageState.getPageCheckedKeys(normalizedData.value) : null\n )\n\n /** 选中行变更统一处理 */\n const handleCheckedKeysChange = (keys: DataTableRowKey[]) => {\n if (crossPageState) {\n crossPageState.handlePageSelectionChange(keys, normalizedData.value)\n }\n tableManager.expandState?.handleSelectionChange?.(keys)\n }\n\n /** 导出 */\n const handleExport = async () => {\n const cfg = resolved.value.exportConfig ?? {}\n await exportTableData(normalizedData.value, effectiveColumns.value, {\n ...cfg,\n formatterConfig: resolved.value.formatterConfig,\n })\n }\n\n /* ================= 合计行 ================= */\n\n const summaryFn = computed(() => {\n const cfg = resolved.value\n if (!cfg.summaryRender) return undefined\n return (data: DataRecord[]) => {\n const result = cfg.summaryRender!(data)\n return computedColumns.value.map((col: any) => {\n const key = col.key as string\n const def = result[key]\n return def ? { value: def.value, colSpan: def.colSpan ?? 1 } : { value: '' }\n })\n }\n })\n\n /* ================= 列拖拽排序 ================= */\n\n const tableWrapperRef = ref<HTMLElement>()\n let sortableInstance: any = null\n\n const initColumnDrag = async () => {\n if (!resolved.value.enableColumnDrag || !tableWrapperRef.value) return\n try {\n const { default: Sortable } = await import('sortablejs')\n const headerRow = tableWrapperRef.value.querySelector(\n '.n-data-table-thead tr'\n )\n if (!headerRow) return\n sortableInstance = new Sortable(headerRow as HTMLElement, {\n animation: resolved.value.columnDragAnimationDuration,\n handle: `.${resolved.value.columnDragHandleClass}`,\n ghostClass: 'column-drag-ghost',\n onEnd: (evt: any) => {\n if (evt.oldIndex == null || evt.newIndex == null) return\n const cols = [...reactiveColumns.value]\n const [moved] = cols.splice(evt.oldIndex, 1)\n cols.splice(evt.newIndex, 0, moved)\n columnState.handleColumnChange(cols)\n emit('column-change', cols)\n },\n })\n } catch {\n // sortablejs 未安装时静默降级\n }\n }\n\n /* ================= 表格属性 ================= */\n\n const tableRef = ref<ComponentPublicInstance>()\n\n const computedTableProps = computed(() => ({\n striped: resolved.value.striped,\n bordered: resolved.value.bordered,\n singleLine: resolved.value.singleLine,\n size: resolved.value.size,\n maxHeight: resolved.value.maxHeight,\n }))\n\n /* ================= 编辑弹窗 ================= */\n\n const editFormRef = ref<any>()\n const modalSubmitLoading = ref(false)\n const localEditingData = ref<DataRecord>({})\n\n const modalVisible = computed({\n get: () => tableManager.editStates.modalEdit.isModalVisible.value,\n set: (val: boolean) => {\n tableManager.editStates.modalEdit.isModalVisible.value = val\n },\n })\n\n const editableColumns = computed(() =>\n effectiveColumns.value.filter(\n (col): col is TableColumn => col.editable !== false\n )\n )\n\n const formKey = computed(\n () =>\n `edit-form-${tableManager.editStates.modalEdit.editingRowKey.value || 'new'}`\n )\n\n const formOptions = computed(() => generateFormOptions(editableColumns.value))\n\n watch(\n () => tableManager.editStates.modalEdit.editingData.value,\n newData => {\n if (newData && Object.keys(newData).length > 0) {\n localEditingData.value = JSON.parse(JSON.stringify(newData))\n }\n },\n { immediate: true, deep: true }\n )\n\n let modalCloseTimer: ReturnType<typeof setTimeout> | null = null\n\n watch(modalVisible, visible => {\n if (!visible) {\n modalCloseTimer = setTimeout(() => {\n localEditingData.value = {}\n modalCloseTimer = null\n }, 300)\n }\n })\n\n const handleModalSave = async () => {\n if (!editFormRef.value) return\n modalSubmitLoading.value = true\n try {\n await editFormRef.value.validate()\n await tableManager.editStates.modalEdit.saveEdit(localEditingData.value)\n } catch {\n /* 表单验证错误由组件内联显示 */\n } finally {\n modalSubmitLoading.value = false\n }\n }\n\n const handleModalCancel = () => {\n localEditingData.value = {}\n tableManager.editStates.modalEdit.cancelEdit()\n }\n\n /* ================= 列设置 ================= */\n\n const onColumnSettingsChange = (columns: TableColumn[]) => {\n columnState.handleColumnChange(columns)\n emit('column-change', columnState.reactiveColumns.value)\n }\n\n /* ================= Expose ================= */\n\n const { edit, expand, selection, dynamicRows } = tableManager.stateManager\n\n const exposedApi = {\n startEdit: edit.start,\n expandAll: expand.all,\n collapseAll: expand.collapseAll,\n selectAll: selection.all,\n clearSelection: selection.clear,\n clearAllSelections: tableManager.stateManager.clearAllSelections,\n clearRowSelection: dynamicRows?.clearSelection,\n resetToFirstPage: pagination.resetToFirstPage,\n getSelectedRows: selection.getSelected,\n getEditingData: edit.getEditingData,\n isEditing: edit.isEditing,\n isExpanded: expand.isExpanded,\n getManager: () => tableManager.stateManager,\n /** 导出数据 */\n exportData: handleExport,\n /** 跨页选择 */\n crossPageSelection: crossPageState\n ? {\n selectedKeys: crossPageState.selectedKeys,\n selectedCount: crossPageState.selectedCount,\n selectAll: crossPageState.selectAll,\n clearAll: crossPageState.clearAll,\n getSelectedRows: crossPageState.getSelectedRows,\n }\n : undefined,\n }\n\n defineExpose(exposedApi)\n\n /* 自动连接 crud.tableRef */\n onMounted(() => {\n const crudRef = props.crud?.tableRef\n if (crudRef) crudRef.value = exposedApi\n // 列拖拽初始化\n initColumnDrag()\n // 行拖拽初始化\n if (rowDragState && tableWrapperRef.value) {\n rowDragState.initRowDrag(tableWrapperRef.value)\n }\n })\n\n onBeforeUnmount(() => {\n if (modalCloseTimer) clearTimeout(modalCloseTimer)\n if (sortableInstance) sortableInstance.destroy()\n rowDragState?.destroyRowDrag()\n })\n</script>\n\n<style scoped lang=\"scss\">\n @use './index.scss';\n</style>\n"],"mappings":"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"}
|