import { useCallback, useMemo, useRef, useState } from 'react'; import { Button, message } from 'antd'; import { SettingOutlined } from '@ant-design/icons'; import type { ColumnsType, ColumnType } from 'antd/es/table'; import type { HqListColumnKey } from '@dukang/shared-types'; import { AdminListColumnsModal } from '../components/AdminListColumnsModal'; import { useListColumnPrefs } from './ListColumnPrefsContext'; import { ACTIONS_COLUMN_KEY, SERIAL_COLUMN_KEY, applyColumnPrefs, columnKey, settingItems, type ListColumnSettingItem, } from './list-column-prefs'; import { beginColumnResize, withResizeTitle } from './column-resize'; type Options = { page?: number; pageSize?: number; }; export function useAdminListColumns( listKey: HqListColumnKey, allColumns: ColumnsType, options: Options = {}, ) { const { page = 1, pageSize = 20 } = options; const { prefs, save } = useListColumnPrefs(); const [open, setOpen] = useState(false); const [saving, setSaving] = useState(false); const [localWidths, setLocalWidths] = useState>({}); const pref = prefs[listKey]; const prefRef = useRef(pref); prefRef.current = pref; const localWidthsRef = useRef(localWidths); localWidthsRef.current = localWidths; const itemsRef = useRef([]); const configured = useMemo( () => applyColumnPrefs(allColumns as ColumnType[], pref), [allColumns, pref], ); const items = useMemo( () => settingItems(allColumns as ColumnType[], pref), [allColumns, pref], ); itemsRef.current = items; const serialCol: ColumnType = useMemo( () => ({ key: SERIAL_COLUMN_KEY, title: '序号', width: 64, fixed: 'left' as const, render: (_: unknown, __: T, index: number) => (page - 1) * pageSize + index + 1, }), [page, pageSize], ); const persistPref = useCallback( async (next: { order: string[]; hidden: string[]; widths?: Record } | null) => { if (!next) { setLocalWidths({}); await save(listKey, null); return; } await save(listKey, next); }, [listKey, save], ); const persist = useCallback( async (next: ListColumnSettingItem[] | null) => { setSaving(true); try { if (!next) { await persistPref(null); } else { const widths = { ...(prefRef.current?.widths ?? {}), ...localWidthsRef.current }; await persistPref({ order: next.map((i) => i.key), hidden: next.filter((i) => !i.visible).map((i) => i.key), ...(Object.keys(widths).length ? { widths } : {}), }); } setOpen(false); message.success(next ? '列设置已保存' : '已恢复默认列'); } catch (e) { message.error(e instanceof Error ? e.message : '保存失败'); } finally { setSaving(false); } }, [persistPref], ); const persistWidth = useCallback( async (key: string, width: number) => { const current = prefRef.current; const prevW = current?.widths?.[key]; if (prevW === width) return; const order = current?.order?.length ? current.order : itemsRef.current.map((i) => i.key); const hidden = current?.hidden ?? []; const widths = { ...(current?.widths ?? {}), ...localWidthsRef.current, [key]: width }; try { await persistPref({ order, hidden, widths }); } catch (e) { message.error(e instanceof Error ? e.message : '列宽保存失败'); } }, [persistPref], ); const columns = useMemo>(() => { const merged: ColumnType[] = [ serialCol, ...configured.map((col) => col.title === '操作' || col.key === ACTIONS_COLUMN_KEY || col.key === 'actions' ? { ...col, key: col.key ?? ACTIONS_COLUMN_KEY } : col, ), ]; const widths = { ...(pref?.widths ?? {}), ...localWidths }; return merged.map((col, i) => { const key = columnKey(col, i); const width = widths[key] ?? (typeof col.width === 'number' ? col.width : undefined); const prevHeader = col.onHeaderCell; return { ...col, key, width, title: withResizeTitle(col.title, (e) => { const th = (e.currentTarget as HTMLElement).closest('th'); const startWidth = width ?? th?.getBoundingClientRect().width ?? 120; beginColumnResize( e, startWidth, (next) => setLocalWidths((prev) => ({ ...prev, [key]: next })), (next) => void persistWidth(key, next), ); }), onHeaderCell: (column) => { const extra = typeof prevHeader === 'function' ? prevHeader(column) : {}; return { ...extra, className: [extra.className, 'admin-th-resizable'].filter(Boolean).join(' '), }; }, }; }); }, [configured, serialCol, pref?.widths, localWidths, persistWidth]); const settingsButton = ( ); const settingsModal = ( setOpen(false)} onReset={() => void persist(null)} onSave={(draft) => void persist(draft)} /> ); return { columns, settingsButton, settingsModal }; }