import React, { useMemo, useState } from 'react'; import { Dropdown } from 'antd'; import type { MenuProps } from 'antd'; import { ArrowRightLeft, Car, CircleDollarSign, Download, Eye, FileText, MoreHorizontal, OctagonX, Pencil, RefreshCw, Settings2, Trash2, Undo2, Upload, Users, type LucideIcon, } from 'lucide-react'; export type OperationActionItem = { key: string; label: string; onClick: () => void; danger?: boolean; disabled?: boolean; hidden?: boolean; icon?: LucideIcon; }; const MORE_ACTION_ICONS: Record = { edit: Pencil, del: Trash2, delete: Trash2, addVehicle: Car, renew: RefreshCw, authorized: FileText, extraFee: CircleDollarSign, toTripartite: Users, terminate: OctagonX, withdraw: Undo2, toFormal: ArrowRightLeft, stampSupplement: Upload, uploadStamped: Upload, manage: Settings2, preview: Eye, download: Download, }; function renderMenuItemLabel(item: OperationActionItem) { const Icon = item.icon ?? MORE_ACTION_ICONS[item.key]; return ( {Icon ? ( ) : ( )} {item.label} ); } export type OperationPrimaryAction = { onClick: () => void; label?: string; hidden?: boolean; disabled?: boolean; }; export type OperationActionsProps = { /** 详情:固定外显主操作 */ view?: OperationPrimaryAction; /** * @deprecated 编辑已收入「更多」下拉,请放入 more 数组 */ edit?: OperationPrimaryAction; /** 其余操作(含编辑)收入「更多」下拉 */ more?: OperationActionItem[]; /** 更多按钮文案,默认「更多」 */ moreLabel?: string; className?: string; annotationId?: string; }; function ViewIcon() { return ; } function MoreIcon() { return ; } const PrimaryActionButton = React.forwardRef< HTMLButtonElement, { icon: 'view' | 'more'; label: string; onClick?: (event: React.MouseEvent) => void; disabled?: boolean; ariaLabel: string; ariaHasPopup?: boolean; className?: string; } >(function PrimaryActionButton( { icon, label, onClick, disabled, ariaLabel, ariaHasPopup, className, }, ref, ) { return ( ); }); function mergeMoreItems( more: OperationActionItem[], edit?: OperationPrimaryAction, ): OperationActionItem[] { const visibleMore = more.filter((item) => !item.hidden); if (!edit || edit.hidden) return visibleMore; const hasEdit = visibleMore.some((item) => item.key === 'edit' || item.label === '编辑'); if (hasEdit) return visibleMore; return [ { key: 'edit', label: edit.label || '编辑', onClick: edit.onClick, disabled: edit.disabled, }, ...visibleMore, ]; } export function OperationActions({ view, edit, more = [], moreLabel = '更多', className, annotationId, }: OperationActionsProps) { const [moreOpen, setMoreOpen] = useState(false); const visibleMore = useMemo( () => mergeMoreItems(more, edit), [more, edit], ); const menuItems: MenuProps['items'] = useMemo(() => { const items: MenuProps['items'] = []; visibleMore.forEach((item, index) => { if (item.danger && index > 0 && !visibleMore[index - 1]?.danger) { items.push({ type: 'divider' }); } items.push({ key: item.key, label: renderMenuItemLabel(item), danger: item.danger, disabled: item.disabled, className: item.danger ? 'vm-op-dropdown-menu-item--danger' : undefined, }); }); return items; }, [visibleMore]); const showView = !!(view && !view.hidden); const showMore = visibleMore.length > 0; if (!showView && !showMore) { return -; } const handleMenuClick: MenuProps['onClick'] = ({ key, domEvent }) => { domEvent.stopPropagation(); const target = visibleMore.find((item) => item.key === key); if (target && !target.disabled) target.onClick(); }; return (
event.stopPropagation()} > {showView ? ( { event.stopPropagation(); if (!view?.disabled) view!.onClick(); }} /> ) : null} {showMore ? ( document.body} overlayClassName="vm-op-dropdown" open={moreOpen} onOpenChange={setMoreOpen} align={{ offset: [0, 4] }} > ) : null}
); } /** * 从扁平操作列表中拆分出 view / more(编辑收入 more)。 */ export function splitOperationActions(items: OperationActionItem[]): { view?: OperationPrimaryAction; more: OperationActionItem[]; } { const visible = items.filter((item) => !item.hidden); const viewItem = visible.find((item) => item.label === '详情' || item.label === '查看' || item.key === 'view'); const more = visible.filter((item) => item !== viewItem); return { view: viewItem ? { onClick: viewItem.onClick, label: viewItem.label === '查看' ? '详情' : viewItem.label, disabled: viewItem.disabled, } : undefined, more, }; }