20260810223432
This commit is contained in:
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;
|
||||
}
|
||||
Reference in new issue
Block a user