import { h, ref } from "vue"; import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, } from "@/components/ui/alert-dialog"; import { DropdownMenu, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; import { buttonVariants } from "@/components/ui/button"; import { cn } from "@/lib/utils"; import { createEditableCell } from "./createEditableCellRenderer"; const stop = (event) => event.stopPropagation(); const actionButtonClass = cn(buttonVariants({ variant: "ghost", size: "xs" }), "fms-action-btn"); const dangerActionButtonClass = cn( actionButtonClass, "text-destructive hover:bg-destructive/10 hover:text-destructive", ); const moreActionButtonClass = cn( buttonVariants({ variant: "ghost", size: "xs" }), "fms-action-btn fms-action-more", ); function resolveActionState(state, row) { return typeof state === "function" ? Boolean(state(row)) : Boolean(state); } function isActionVisible(action, row) { return action.visible === undefined || resolveActionState(action.visible, row); } function actionButton(action, row, triggerAction) { const disabled = resolveActionState(action.disabled, row); const danger = action.danger || action.type === "danger" || action.type === "delete"; const content = []; if (action.icon) content.push(h(action.icon, { "data-icon": "inline-start" })); if (action.label || action.type) content.push(action.label || action.type); return h( "button", { type: "button", class: danger ? dangerActionButtonClass : actionButtonClass, title: action.title || action.label, disabled, onMousedown: stop, onClick: (event) => { event.stopPropagation(); triggerAction(action, row); }, }, content, ); } function createConfirmDialog(state, close, confirm) { const config = typeof confirm === "string" ? { title: confirm } : confirm || {}; const title = config.title || "确认操作"; const description = config.description ?? config.content ?? "确定要执行此操作吗?"; return h( AlertDialog, { open: Boolean(state.value), "onUpdate:open": (open) => { if (!open) close(); }, }, { default: () => h(AlertDialogContent, {}, () => [ h(AlertDialogHeader, {}, () => [ h(AlertDialogTitle, {}, () => title), h(AlertDialogDescription, {}, () => description), ]), h(AlertDialogFooter, {}, () => [ h(AlertDialogCancel, { onClick: close }, () => config.cancelText || "取消"), h( AlertDialogAction, { class: config.okType === "danger" ? buttonVariants({ variant: "destructive" }) : undefined, onClick: () => { const pending = state.value; close(); pending?.action?.onClick?.(pending.row); }, }, () => config.okText || "确定", ), ]), ]), }, ); } function createActionsCell(actions) { if (!actions?.length) return undefined; const inlineActions = actions.filter((action) => action.type !== "more"); const moreAction = actions.find((action) => action.type === "more"); const openRow = ref(null); const confirmState = ref(null); function closeMenu() { openRow.value = null; } function closeConfirm() { confirmState.value = null; } function triggerAction(action, row) { if (resolveActionState(action.disabled, row)) return; if (!action.confirm) { action.onClick?.(row); return; } confirmState.value = { action, row }; } return ({ row }) => { const children = inlineActions .filter((action) => isActionVisible(action, row)) .map((action) => actionButton(action, row, triggerAction)); const visibleMoreItems = (moreAction?.items || []).filter((item) => isActionVisible(item, row)); if (visibleMoreItems.length) { const moreButton = h( "button", { type: "button", class: [ moreActionButtonClass, openRow.value === row && "bg-accent text-accent-foreground", ], title: moreAction.title || "更多操作", "aria-label": moreAction.title || "更多操作", onMousedown: stop, onClick: (event) => { event.stopPropagation(); openRow.value = row; }, }, moreAction.label || "⋯", ); if (openRow.value !== row) { children.push(moreButton); } else { children.push( h( DropdownMenu, { open: true, "onUpdate:open": (open) => { if (!open) closeMenu(); }, }, { default: () => [ h(DropdownMenuTrigger, { asChild: true }, () => moreButton), h(DropdownMenuContent, { align: "end", onMousedown: stop }, () => h(DropdownMenuGroup, {}, () => visibleMoreItems.map((item, index) => h( DropdownMenuItem, { key: String(item.key ?? item.type ?? index), disabled: resolveActionState(item.disabled, row), variant: item.danger || item.type === "danger" || item.type === "delete" ? "destructive" : "default", onSelect: (event) => { event.stopPropagation(); closeMenu(); triggerAction(item, row); }, }, () => [item.icon ? h(item.icon) : null, item.label], ), ), ), ), ], }, ), ); } } if (!children.length) return h("span", { class: "fms-actions-cell" }); const dialog = confirmState.value ? createConfirmDialog(confirmState, closeConfirm, confirmState.value.action.confirm) : null; if (dialog) children.push(dialog); return h("span", { class: "fms-actions-cell" }, children); }; } export function resolveCustomCell(col, editingContext) { if (col.customCell) return col; if (col.editor) return { ...col, customCell: createEditableCell(col, editingContext) }; if (col.actions?.length) { const customCell = createActionsCell(col.actions); if (customCell) return { ...col, customCell }; } // 纯展示列使用 StkTable 原生渲染,避免虚拟滚动时重复创建函数组件。 return col; }