[fix]:[FL-151][菜单管理页面一致性优化]
Co-authored-by: multica-agent <github@multica.ai>
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
# Work Log - 菜单管理页面一致性优化(FL-151)
|
||||
|
||||
- 背景:
|
||||
- 菜单管理页需要继续对齐用户管理页的消息反馈、筛选表单、空态与表格滚动配置规范。
|
||||
|
||||
- 本次处理:
|
||||
- 移除菜单管理页对 `App.useApp()` 的依赖,创建、编辑、删除结果统一走 `error` / `success` state + `useToastFeedback`。
|
||||
- 桌面筛选表单项由 Tailwind `min-w-*` 改为与用户管理页一致的 `style={{ width: ... }}`。
|
||||
- 表格空态 `Empty` 属性顺序调整为 `image` 在前、`description` 在后。
|
||||
- 移除菜单表格 `scroll.x`,仅保留纵向滚动配置,与用户管理页保持一致。
|
||||
|
||||
- 验证:
|
||||
- 基线:`npm --workspace web exec eslint src/app/admin/menus/page.tsx` 通过。
|
||||
- 基线:`npm --workspace web exec tsc --noEmit` 通过。
|
||||
- 修改后:`npm --workspace web exec eslint src/app/admin/menus/page.tsx` 通过。
|
||||
- 修改后:`npm --workspace web exec tsc --noEmit` 通过。
|
||||
|
||||
- 风险与关注点:
|
||||
- 改动仅影响菜单管理页前端展示与提示机制,不改变菜单接口、字段结构或权限语义。
|
||||
@@ -3,7 +3,6 @@
|
||||
import Link from "next/link";
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import {
|
||||
App,
|
||||
Button,
|
||||
Card,
|
||||
Checkbox,
|
||||
@@ -91,7 +90,6 @@ function normalizeMenuItemPath(menu: MenuItem): MenuItem {
|
||||
|
||||
export default function AdminMenusPage() {
|
||||
const { user, initializing, fetchWithAuth, hasPermission } = useAuth();
|
||||
const { message: messageApi, modal } = App.useApp();
|
||||
const isMobile = useMobileDetection();
|
||||
const [menus, setMenus] = useState<MenuItem[]>([]);
|
||||
const [menuTotal, setMenuTotal] = useState(0);
|
||||
@@ -321,6 +319,7 @@ export default function AdminMenusPage() {
|
||||
try {
|
||||
setSaving(true);
|
||||
setError("");
|
||||
setSuccess("");
|
||||
|
||||
const values = await form.validateFields();
|
||||
const payload = {
|
||||
@@ -352,11 +351,10 @@ export default function AdminMenusPage() {
|
||||
if (!response.ok) {
|
||||
const msg = await readApiError(response);
|
||||
setError(msg);
|
||||
messageApi.error(msg);
|
||||
return;
|
||||
}
|
||||
|
||||
messageApi.success(editingMenuId ? "菜单已更新" : "菜单已创建");
|
||||
setSuccess(editingMenuId ? "菜单已更新" : "菜单已创建");
|
||||
closeDialog();
|
||||
await loadMenus(paginationCurrent, paginationPageSize);
|
||||
} catch (candidate) {
|
||||
@@ -372,11 +370,10 @@ export default function AdminMenusPage() {
|
||||
|
||||
const msg = candidate instanceof Error ? candidate.message : "提交失败,请稍后重试";
|
||||
setError(msg);
|
||||
messageApi.error(msg);
|
||||
} finally {
|
||||
setSaving(false);
|
||||
}
|
||||
}, [closeDialog, editingMenuId, fetchWithAuth, form, loadMenus, messageApi, paginationCurrent, paginationPageSize]);
|
||||
}, [closeDialog, editingMenuId, fetchWithAuth, form, loadMenus, paginationCurrent, paginationPageSize]);
|
||||
|
||||
const removeMenu = useCallback(async (menu: MenuItem) => {
|
||||
setDeletingMenuId(menu.id);
|
||||
@@ -390,31 +387,32 @@ export default function AdminMenusPage() {
|
||||
if (!response.ok) {
|
||||
const msg = await readApiError(response);
|
||||
setError(msg);
|
||||
messageApi.error(msg);
|
||||
return;
|
||||
}
|
||||
|
||||
messageApi.success("菜单已删除");
|
||||
setSuccess("菜单已删除");
|
||||
if (editingMenuId === menu.id) {
|
||||
closeDialog();
|
||||
}
|
||||
setMenuTotal((previous) => Math.max(0, previous - 1));
|
||||
setAllLoadedMenus((previous) => previous.filter((item) => item.id !== menu.id));
|
||||
await loadMenus(paginationCurrent, paginationPageSize);
|
||||
} catch (candidate) {
|
||||
setError(candidate instanceof Error ? candidate.message : "菜单删除失败");
|
||||
} finally {
|
||||
setDeletingMenuId(null);
|
||||
}
|
||||
}, [closeDialog, editingMenuId, fetchWithAuth, loadMenus, messageApi, paginationCurrent, paginationPageSize]);
|
||||
}, [closeDialog, editingMenuId, fetchWithAuth, loadMenus, paginationCurrent, paginationPageSize]);
|
||||
|
||||
const confirmRemoveMenu = useCallback((menu: MenuItem) => {
|
||||
modal.confirm({
|
||||
Modal.confirm({
|
||||
title: `确认删除菜单 ${menu.name}(${menu.code})?`,
|
||||
okText: "删除",
|
||||
cancelText: "取消",
|
||||
okButtonProps: { danger: true },
|
||||
onOk: () => removeMenu(menu),
|
||||
});
|
||||
}, [modal, removeMenu]);
|
||||
}, [removeMenu]);
|
||||
|
||||
const updateMenuStatus = useCallback(async (menu: MenuItem) => {
|
||||
const nextStatus: "enabled" | "disabled" = menu.status === "enabled" ? "disabled" : "enabled";
|
||||
@@ -748,7 +746,7 @@ export default function AdminMenusPage() {
|
||||
</Form>
|
||||
) : (
|
||||
<Form layout="inline" style={{ rowGap: 12 }}>
|
||||
<Form.Item label="关键词" className="min-w-[240px]">
|
||||
<Form.Item label="关键词" style={{ width: 240 }}>
|
||||
<Input
|
||||
allowClear
|
||||
value={keyword}
|
||||
@@ -757,7 +755,7 @@ export default function AdminMenusPage() {
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item label="状态" className="min-w-[170px]">
|
||||
<Form.Item label="状态" style={{ width: 170 }}>
|
||||
<Select<Exclude<FilterStatus, "all">>
|
||||
value={statusFilter === "all" ? undefined : statusFilter}
|
||||
allowClear
|
||||
@@ -784,7 +782,7 @@ export default function AdminMenusPage() {
|
||||
columns={columns}
|
||||
loading={loading}
|
||||
tableLayout="fixed"
|
||||
scroll={{ x: 1200, y: tableScrollY }}
|
||||
scroll={{ y: tableScrollY }}
|
||||
pagination={{
|
||||
current: pagination.current,
|
||||
pageSize: pagination.pageSize,
|
||||
@@ -799,7 +797,12 @@ export default function AdminMenusPage() {
|
||||
},
|
||||
}}
|
||||
locale={{
|
||||
emptyText: <Empty description="未找到符合筛选条件的菜单项。" image={Empty.PRESENTED_IMAGE_SIMPLE} />,
|
||||
emptyText: (
|
||||
<Empty
|
||||
image={Empty.PRESENTED_IMAGE_SIMPLE}
|
||||
description="未找到符合筛选条件的菜单项。"
|
||||
/>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user