From faa47e26cdc5b1f885c95d0dc8c47462a559c463 Mon Sep 17 00:00:00 2001 From: chengkai3 Date: Sat, 20 Jun 2026 11:09:19 +0800 Subject: [PATCH] =?UTF-8?q?[fix]:[FL-153][=E7=B3=BB=E7=BB=9F=E5=8F=82?= =?UTF-8?q?=E6=95=B0=E7=8A=B6=E6=80=81=E4=BA=A4=E4=BA=92=E5=AF=B9=E9=BD=90?= =?UTF-8?q?]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: multica-agent --- memory/2026-06-20.md | 17 ++++++ web/src/app/admin/system-params/page.tsx | 69 ++++++++++++++++++------ 2 files changed, 69 insertions(+), 17 deletions(-) diff --git a/memory/2026-06-20.md b/memory/2026-06-20.md index e0c1901..e8e04b2 100644 --- a/memory/2026-06-20.md +++ b/memory/2026-06-20.md @@ -374,3 +374,20 @@ - 风险与关注点: - 改动仅影响 `/admin/syslog` 前端筛选交互、分页展示和空态文案,不改变 `/api/v1/admin/audit-logs` 字段或权限语义。 - 当前 dev 分支存在 unrelated `elevation-records` TypeScript 错误,会阻断全量 `tsc` 与 `next build`。 + +## Follow-up - 系统参数状态修改交互对齐(FL-153) + +- 背景: + - 复核指出系统参数页状态修改仍在编辑弹窗内完成,而用户管理页状态修改通过行内 Dropdown 独立完成。 + +- 本次处理: + - 系统参数编辑弹窗移除“状态”字段,编辑保存仅更新参数名称、参数值和说明。 + - 表格行与移动卡片的更多菜单补齐“启用/禁用”独立操作,状态切换走单独 PATCH 请求和行级 loading。 + - 新建参数仍默认创建为 `enabled`,保持原创建语义不变。 + +- 验证: + - 修改后:`npm --workspace web exec eslint src/app/admin/system-params/page.tsx --max-warnings=0` 通过。 + - 修改后:`npm --workspace web exec tsc --noEmit` 通过。 + +- 风险与关注点: + - 改动仅影响系统参数页前端交互组织方式,不改变后端接口字段、权限码或参数状态语义。 diff --git a/web/src/app/admin/system-params/page.tsx b/web/src/app/admin/system-params/page.tsx index b639197..7b97ed3 100644 --- a/web/src/app/admin/system-params/page.tsx +++ b/web/src/app/admin/system-params/page.tsx @@ -41,7 +41,6 @@ type FormState = { param_name: string; param_value: string; description: string; - status: "enabled" | "disabled"; }; const EMPTY_FORM: FormState = { @@ -49,14 +48,8 @@ const EMPTY_FORM: FormState = { param_name: "", param_value: "", description: "", - status: "enabled", }; -const PARAM_STATUS_OPTIONS = [ - { label: "已启用", value: "enabled" }, - { label: "已禁用", value: "disabled" }, -] as const satisfies ReadonlyArray<{ label: string; value: FormState["status"] }>; - const AntCard = Card as unknown as ComponentType>; const PARAM_TABLE_MIN_SCROLL_Y = 180; @@ -83,6 +76,7 @@ export default function AdminSystemParamsPage() { const [error, setError] = useState(""); const [success, setSuccess] = useState(""); const [deletingId, setDeletingId] = useState(null); + const [updatingStatusId, setUpdatingStatusId] = useState(null); const [pagination, setPagination] = useState({ current: 1, pageSize: 20 }); const [tableScrollY, setTableScrollY] = useState(PARAM_TABLE_MIN_SCROLL_Y); const tableScrollAnchorRef = useRef(null); @@ -167,7 +161,6 @@ export default function AdminSystemParamsPage() { param_name: item.param_name, param_value: item.param_value, description: item.description ?? "", - status: item.status, }); setEditorOpen(true); }, [formApi]); @@ -264,7 +257,7 @@ export default function AdminSystemParamsPage() { param_name: values.param_name.trim(), param_value: values.param_value, description: values.description, - status: values.status, + status: "enabled", }), }); if (!response.ok) { @@ -280,7 +273,6 @@ export default function AdminSystemParamsPage() { param_name: values.param_name.trim(), param_value: values.param_value, description: values.description, - status: values.status, }), }); if (!response.ok) { @@ -339,6 +331,39 @@ export default function AdminSystemParamsPage() { } }, [deleteMutation]); + const updateStatusMutation = useMutation({ + mutationFn: async ({ item, status }: { item: SystemParamSummary; status: SystemParamSummary["status"] }) => { + const response = await fetchWithAuth(`/api/v1/admin/system-params/${item.id}`, { + method: "PATCH", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ status }), + }); + if (!response.ok) { + throw new Error(await readApiError(response)); + } + return status; + }, + onMutate: ({ item }) => { + setUpdatingStatusId(item.id); + setError(""); + setSuccess(""); + }, + onSuccess: async (status) => { + setSuccess(status === "enabled" ? "系统参数已启用" : "系统参数已禁用"); + await refreshList(); + }, + onError: (candidate) => { + setSuccess(""); + setError(candidate instanceof Error ? candidate.message : "更新系统参数状态失败"); + }, + onSettled: () => setUpdatingStatusId(null), + }); + + const toggleParamStatus = useCallback((item: SystemParamSummary) => { + const nextStatus: SystemParamSummary["status"] = item.status === "enabled" ? "disabled" : "enabled"; + updateStatusMutation.mutate({ item, status: nextStatus }); + }, [updateStatusMutation]); + const handleKeywordChange = (value: string) => { setKeywordInput(value); @@ -444,7 +469,8 @@ export default function AdminSystemParamsPage() { const renderParamCard = (param: SystemParamSummary) => { const deleteLoading = deletingId === param.id; - const rowBusy = deleteLoading; + const statusLoading = updatingStatusId === param.id; + const rowBusy = deleteLoading || statusLoading; const moreMenuItems: MenuProps["items"] = [ { @@ -456,6 +482,12 @@ export default function AdminSystemParamsPage() { setSuccess(`已复制参数键: ${param.param_key}`); }, }, + { + key: "toggle-status", + label: param.status === "enabled" ? "禁用" : "启用", + disabled: rowBusy || !canManage, + onClick: () => toggleParamStatus(param), + }, { key: "delete", label: "删除", @@ -591,7 +623,8 @@ export default function AdminSystemParamsPage() { width: 180, render: (_, record) => { const deleteLoading = deletingId === record.id; - const rowBusy = deleteLoading; + const statusLoading = updatingStatusId === record.id; + const rowBusy = deleteLoading || statusLoading; const moreMenuItems: MenuProps["items"] = [ { @@ -603,6 +636,12 @@ export default function AdminSystemParamsPage() { setSuccess(`已复制参数键: ${record.param_key}`); }, }, + { + key: "toggle-status", + label: record.status === "enabled" ? "禁用" : "启用", + disabled: rowBusy, + onClick: () => toggleParamStatus(record), + }, ]; return ( @@ -633,7 +672,7 @@ export default function AdminSystemParamsPage() { } return baseColumns; - }, [canManage, deletingId, removeParam, startEdit]); + }, [canManage, deletingId, removeParam, startEdit, toggleParamStatus, updatingStatusId]); const updateTableScrollY = useCallback(() => { if (typeof window === "undefined") { @@ -931,10 +970,6 @@ export default function AdminSystemParamsPage() { > - - label="状态" name="status"> -