20260810223432

This commit is contained in:
oneao committed 2026-08-10 22:34:33 +08:00
1 parent ed5ab3885d
commit 5adbf59425
364 files changed
+7559 -4358

No files matched your search

@@ -0,0 +1,227 @@
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();
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, className = "fms-action-btn") {
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: cn(
buttonVariants({ variant: "ghost", size: "xs" }),
danger && "text-destructive hover:bg-destructive/10 hover:text-destructive",
className,
),
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: cn(
buttonVariants({ variant: "ghost", size: "xs" }),
"fms-action-btn fms-action-more",
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 };
}
// 纯展示列:保持文本可选中,同时通过表格选区激活行编辑。
if (col.dataIndex) {
return {
...col,
customCell: ({ row }) => {
const value = row[col.dataIndex];
const content = value ?? "";
return h("span", { class: "fms-cell-display", title: String(content) }, content);
},
};
}
return col;
}