import { useEffect, useState } from "react"; import { App as AntApp } from "antd"; import { PlusOutlined, EditOutlined, DeleteOutlined, SaveOutlined, CloseOutlined, AppstoreOutlined, FileExcelOutlined, KeyOutlined, RollbackOutlined, SettingOutlined, } from "@ant-design/icons"; import { Field, PrimInput, PrimSelect, PrimCheckbox, ToolbarBtnDark, } from "@/components/Primitives"; import StaffPicker from "@/components/StaffPicker"; import { createUser, updateUser, type UserDTO } from "@/api/user"; import { USER_TYPES, LANGUAGE_OPTIONS, PERMISSION_GROUPS, SCOPE_ITEMS, } from "@/utils/data"; import { useAppDispatch, useAppSelector } from "@/store"; import { closeTab, setActiveTab } from "@/store/tabsSlice"; import { fmtDateTime } from "@/utils/format"; type Mode = "view" | "edit" | "new"; interface Props { userId?: number; mode: Mode; } interface FormState { sUserNo: string; sUserName: string; iStaffId: number | null; staffName: string; sUserType: string; sLanguage: string; bCanModifyDocs: boolean; } const PERM_TABS = [ { id: "groups", label: "权限组" }, { id: "customer", label: "客户查看权限" }, { id: "supplier", label: "供应商查看权限" }, { id: "staff", label: "人员查看权限" }, { id: "process", label: "工序查看权限" }, { id: "driver", label: "司机查看权限" }, ]; export default function UserDetail({ userId, mode: initialMode }: Props) { const [mode, setMode] = useState(initialMode); const [submitting, setSubmitting] = useState(false); const dispatch = useAppDispatch(); const { message } = AntApp.useApp(); const tabs = useAppSelector((s) => s.tabs.tabs); const activeTabId = useAppSelector((s) => s.tabs.activeTabId); const tab = tabs.find((t) => t.id === activeTabId); const snapshot = tab?.meta?.snapshot as | { sUserNo: string; sUserName: string; iStaffId: number | null; staffName: string | null; sUserType: string; sLanguage: string; bCanModifyDocs?: boolean; tCreateDate?: string; sCreatedBy?: string; } | undefined; const initialForm: FormState = mode === "new" || !snapshot ? { sUserNo: "", sUserName: "", iStaffId: null, staffName: "", sUserType: "普通用户", sLanguage: "zh", bCanModifyDocs: false, } : { sUserNo: snapshot.sUserNo, sUserName: snapshot.sUserName, iStaffId: snapshot.iStaffId, staffName: snapshot.staffName ?? "", sUserType: snapshot.sUserType, sLanguage: snapshot.sLanguage, bCanModifyDocs: !!snapshot.bCanModifyDocs, }; const [form, setForm] = useState(initialForm); const [permTab, setPermTab] = useState("groups"); // Visual-only — not persisted to backend. const [permissions, setPermissions] = useState>(() => Object.fromEntries(PERMISSION_GROUPS.map((g) => [g, false])) ); const [tabPerms, setTabPerms] = useState>({}); useEffect(() => { setForm(initialForm); // eslint-disable-next-line react-hooks/exhaustive-deps }, [snapshot, mode]); const set = (k: K, v: FormState[K]) => setForm((s) => ({ ...s, [k]: v })); const disabled = mode === "view"; const checkedCount = Object.values(permissions).filter(Boolean).length; const startEdit = () => setMode("edit"); const startNew = () => setMode("new"); const cancel = () => { if (mode === "new") { dispatch(closeTab(activeTabId)); dispatch(setActiveTab("userlist")); } else { setForm(initialForm); setMode("view"); } }; const save = async () => { if (!form.sUserNo.trim() || !form.sUserName.trim() || !form.sUserType || !form.sLanguage) { message.error("请填写必填项"); return; } const dto: UserDTO = { sUserNo: form.sUserNo, sUserName: form.sUserName, iStaffId: form.iStaffId, sUserType: form.sUserType, sLanguage: form.sLanguage, bCanModifyDocs: form.bCanModifyDocs, // permissionCategoryIds omitted: prototype permissions are by name; backend wants IDs. }; setSubmitting(true); try { if (mode === "new") { await createUser(dto); message.success("新增成功"); dispatch(closeTab(activeTabId)); dispatch(setActiveTab("userlist")); } else if (userId) { await updateUser(userId, dto); message.success("保存成功"); setMode("view"); } } catch { // interceptor } finally { setSubmitting(false); } }; return (
} onClick={startNew} disabled={mode !== "view"}> 新增 } onClick={startEdit} disabled={mode !== "view" || !userId} > 修改 } disabled={mode !== "view"} danger> 删除 } onClick={save} disabled={mode === "view" || submitting}> 保存 } onClick={cancel} disabled={mode === "view"}> 取消 }>功能 }>作废 }>重置密码 }>取消作废
已选权限: {checkedCount} /{" "} {PERMISSION_GROUPS.length - 1} {mode === "view" ? "只读" : mode === "new" ? "新增中" : "编辑中"}
setForm((s) => ({ ...s, staffName: name, iStaffId: id, // Auto-fill 用户号 / 用户名 to the staff name on selection // (matches prototype behavior — user can still override after). ...(id != null ? { sUserNo: name, sUserName: name } : {}), })) } disabled={disabled} required placeholder="输入员工姓名搜索" /> set("sUserName", v)} disabled={disabled} required /> set("sUserType", v)} disabled={disabled} required allowEmpty={false} options={USER_TYPES} /> set("sUserNo", v)} disabled={disabled} required mono /> set("sLanguage", v)} disabled={disabled} required allowEmpty={false} options={LANGUAGE_OPTIONS} />
set("bCanModifyDocs", v)} disabled={disabled} />
{PERM_TABS.map((t) => { const active = t.id === permTab; return (
setPermTab(t.id)} style={{ padding: "6px 14px", fontSize: 12, cursor: "pointer", color: active ? "var(--accent-strong)" : "var(--text)", background: active ? "var(--accent-soft)" : "transparent", borderTop: active ? "2px solid var(--accent)" : "2px solid transparent", borderRight: "1px solid var(--border)", fontWeight: active ? 500 : 400, }} onMouseEnter={(e) => { if (!active) e.currentTarget.style.background = "var(--bg-row-hover)"; }} onMouseLeave={(e) => { if (!active) e.currentTarget.style.background = "transparent"; }} > {t.label}
); })}
{permTab === "groups" ? ( setPermissions((s) => ({ ...s, [g]: v })) } disabled={disabled} /> ) : ( setTabPerms((s) => ({ ...s, [permTab]: v }))} disabled={disabled} /> )}
); } interface PermissionGridProps { permissions: Record; setPermission: (g: string, v: boolean) => void; disabled?: boolean; } function PermissionGrid({ permissions, setPermission, disabled }: PermissionGridProps) { const [filter, setFilter] = useState(""); const [hovered, setHovered] = useState(null); const allChecked = PERMISSION_GROUPS.every((g) => permissions[g]); return ( {PERMISSION_GROUPS.filter((g) => !filter || g.includes(filter)).map((g, i) => { const isHover = hovered === g; const checked = !!permissions[g]; return ( setHovered(g)} onMouseLeave={() => setHovered(null)} onClick={disabled ? undefined : () => setPermission(g, !checked)} style={{ background: isHover && !disabled ? "var(--bg-row-hover)" : checked ? "var(--accent-soft)" : i % 2 === 0 ? "#fff" : "var(--bg-row-zebra)", cursor: disabled ? "default" : "pointer", height: 24, }} > ); })}
{ PERMISSION_GROUPS.forEach((g) => setPermission(g, v)); }} disabled={disabled} /> 权限分类 setFilter(e.target.value)} placeholder="过滤..." style={{ height: 20, padding: "0 6px", border: "1px solid var(--border-input)", background: "#fff", fontSize: 11, width: 120, fontFamily: "inherit", outline: "none", }} />
setPermission(g, v)} disabled={disabled} /> {g}
); } interface ScopeTabProps { tabKey: string; enabled: boolean; onToggle: (v: boolean) => void; disabled?: boolean; } function ScopeTab({ tabKey, enabled, onToggle, disabled }: ScopeTabProps) { const cfg = SCOPE_ITEMS[tabKey]; const [filter, setFilter] = useState(""); if (!cfg) return null; const filtered = cfg.items.filter((it) => !filter || it.includes(filter)); return (
setFilter(e.target.value)} placeholder={`筛选${cfg.label}...`} style={{ height: 24, padding: "0 8px", border: "1px solid var(--border-input)", background: "var(--bg-input)", fontSize: 12, width: 200, fontFamily: "inherit", outline: "none", }} /> 共 {cfg.items.length} 个{cfg.label}
{filtered.map((it) => ( ))}
); }