feat: 去掉角色权限点与菜单权限码配置
This commit is contained in:
@@ -1018,3 +1018,9 @@
|
||||
- 总数以接口返回 `total` 为准。
|
||||
- 筛选条件(关键词/塔型/风险等级)或线路切换时,分页需自动回到第 1 页,避免落在无数据页。
|
||||
- 地图视图保留大页查询(当前 500 条)用于展示线路点位,不与表格分页参数共用同一页码。
|
||||
|
||||
## 角色/菜单配置口径(2026-05-01)
|
||||
|
||||
- 角色管理页面(`/admin/roles`)当前仅提供角色基础信息与“可见菜单”配置,不再提供权限点(`permission_codes`)配置入口。
|
||||
- 菜单管理页面(`/admin/menus`)当前不再提供菜单权限码(`permission_code`)配置入口与列表展示。
|
||||
- 后端权限字段与接口兼容能力保留,作为历史数据与鉴权映射兜底;前端交互层默认不暴露该配置。
|
||||
|
||||
@@ -419,3 +419,34 @@
|
||||
- 风险与影响:
|
||||
- `.img/.tif` 回填依赖 `rasterio`(及底层 GDAL 运行时),部署环境需确保镜像能成功安装该依赖。
|
||||
- 栅格 bbox 直接来自源栅格 CRS;非经纬度坐标系场景会返回告警,便于识别与后续治理。
|
||||
|
||||
## Work Log - 去掉角色权限点与菜单权限码配置(2026-05-01)
|
||||
|
||||
- 背景:
|
||||
- Issue `FL-139` 要求“去掉角色管理的权限点配置,菜单的权限码配置”。
|
||||
|
||||
- 本次改动(最小闭环):
|
||||
- 角色管理页面:`web/src/app/admin/roles/page.tsx`
|
||||
- 移除权限点配置入口与相关请求链路:
|
||||
- 移除 `permissions` 状态与 `/api/v1/admin/permissions` 加载请求。
|
||||
- 新建/编辑角色表单移除 `permission_codes` 字段,仅保留 `code/name/menu_ids`。
|
||||
- 角色更新请求不再提交 `permission_codes`。
|
||||
- 列表展示移除“权限”列,仅展示“角色编码/角色名称/菜单/操作”。
|
||||
- 搜索口径同步调整为“角色编码、名称、菜单”。
|
||||
- 菜单管理页面:`web/src/app/admin/menus/page.tsx`
|
||||
- 移除菜单权限码配置入口:
|
||||
- 菜单表单移除 `permission_code` 字段。
|
||||
- 新建/编辑菜单提交 payload 不再包含 `permission_code`。
|
||||
- 列表展示移除“权限码”列。
|
||||
- 搜索口径移除权限码匹配,关键词仅匹配“编码/名称/路径”。
|
||||
- 后台首页文案:`web/src/app/admin/page.tsx`
|
||||
- 角色管理说明改为“配置角色并分配菜单可见范围”。
|
||||
- 菜单管理说明改为“维护后台导航结构、菜单层级与展示状态”。
|
||||
|
||||
- 验证(遵循任务约束,未执行编译检查):
|
||||
- `git diff` 已确认改动仅涉及上述三个前端文件。
|
||||
- 代码扫描确认上述页面不再包含 `permission_codes` / `permission_code` 配置与展示逻辑。
|
||||
|
||||
- 风险与影响:
|
||||
- 影响范围限定在前端管理页交互层;后端接口仍保持兼容,可继续返回权限相关字段但前端不再暴露配置入口。
|
||||
- 若后续需彻底下线该能力(含后端字段/持久化),需单独评估接口契约与历史数据兼容。
|
||||
|
||||
@@ -48,7 +48,6 @@ type MenuFormValues = {
|
||||
visible: boolean;
|
||||
cacheable: boolean;
|
||||
component?: string;
|
||||
permission_code?: string;
|
||||
};
|
||||
|
||||
const SORT_OPTIONS: Array<{ value: SortKey; label: string }> = [
|
||||
@@ -108,7 +107,6 @@ const DEFAULT_FORM_VALUES: MenuFormValues = {
|
||||
visible: true,
|
||||
cacheable: false,
|
||||
component: "",
|
||||
permission_code: "",
|
||||
};
|
||||
|
||||
function compareMenuIds(a: string, b: string): number {
|
||||
@@ -166,7 +164,7 @@ export default function AdminMenusPage() {
|
||||
if (!query) {
|
||||
return true;
|
||||
}
|
||||
const haystack = [menu.code, menu.name, menu.path ?? "", menu.permission_code ?? ""]
|
||||
const haystack = [menu.code, menu.name, menu.path ?? ""]
|
||||
.join(" ")
|
||||
.toLowerCase();
|
||||
return haystack.includes(query);
|
||||
@@ -250,7 +248,6 @@ export default function AdminMenusPage() {
|
||||
visible: menu.visible,
|
||||
cacheable: menu.cacheable,
|
||||
component: menu.component ?? "",
|
||||
permission_code: menu.permission_code ?? "",
|
||||
});
|
||||
setDialogOpen(true);
|
||||
}, [form]);
|
||||
@@ -273,7 +270,6 @@ export default function AdminMenusPage() {
|
||||
visible: values.visible,
|
||||
cacheable: values.cacheable,
|
||||
component: values.component?.trim() ? values.component.trim() : null,
|
||||
permission_code: values.permission_code?.trim() ? values.permission_code.trim() : null,
|
||||
};
|
||||
|
||||
const response = editingMenuId
|
||||
@@ -362,12 +358,6 @@ export default function AdminMenusPage() {
|
||||
width: 220,
|
||||
render: (value: string | null) => value || "-",
|
||||
},
|
||||
{
|
||||
title: "权限码",
|
||||
dataIndex: "permission_code",
|
||||
width: 180,
|
||||
render: (value: string | null) => value || "-",
|
||||
},
|
||||
{
|
||||
title: "父菜单",
|
||||
dataIndex: "parent_id",
|
||||
@@ -488,7 +478,7 @@ export default function AdminMenusPage() {
|
||||
allowClear
|
||||
value={keyword}
|
||||
onChange={(event) => setKeyword(event.currentTarget.value)}
|
||||
placeholder="按编码/名称/路径/权限筛选"
|
||||
placeholder="按编码/名称/路径筛选"
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
@@ -634,12 +624,6 @@ export default function AdminMenusPage() {
|
||||
<Input placeholder="app/admin/users/page" />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col xs={24} md={12}>
|
||||
<Form.Item label="权限码" name="permission_code">
|
||||
<Input placeholder="menu.read" />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
|
||||
<Col xs={24} md={12}>
|
||||
<Form.Item name="visible" valuePropName="checked">
|
||||
<Checkbox>可见</Checkbox>
|
||||
|
||||
@@ -43,7 +43,7 @@ const CARDS: DashboardCard[] = [
|
||||
{
|
||||
href: "/roles",
|
||||
title: "角色管理",
|
||||
description: "配置角色、绑定权限点、分配菜单可见范围。",
|
||||
description: "配置角色并分配菜单可见范围。",
|
||||
category: "权限",
|
||||
icon: <SafetyCertificateOutlined />,
|
||||
visible: (hasPermission) => hasPermission("role.read") || hasPermission("role.manage"),
|
||||
@@ -51,7 +51,7 @@ const CARDS: DashboardCard[] = [
|
||||
{
|
||||
href: "/menus",
|
||||
title: "菜单管理",
|
||||
description: "维护后台导航、菜单层级和菜单对应权限。",
|
||||
description: "维护后台导航结构、菜单层级与展示状态。",
|
||||
category: "权限",
|
||||
icon: <AppstoreOutlined />,
|
||||
visible: (hasPermission) => hasPermission("menu.read") || hasPermission("menu.manage"),
|
||||
|
||||
@@ -27,25 +27,22 @@ import type { ComponentType } from "react";
|
||||
import { useAuth } from "@/components/auth-provider";
|
||||
import { useTopicSubscription } from "@/hooks/use-topic-subscription";
|
||||
import { readApiError } from "@/lib/api";
|
||||
import type { MenuItem, PermissionItem, RoleItem, RoleListResponse } from "@/types/auth";
|
||||
import type { MenuItem, RoleItem, RoleListResponse } from "@/types/auth";
|
||||
|
||||
const AntCard = Card as unknown as ComponentType<CardProps>;
|
||||
const AntResult = Result as unknown as ComponentType<ResultProps>;
|
||||
|
||||
type PermissionResponse = { items: PermissionItem[] };
|
||||
type MenuListResponse = { items: MenuItem[]; total: number };
|
||||
|
||||
type RoleFormValues = {
|
||||
code: string;
|
||||
name: string;
|
||||
permission_codes: string[];
|
||||
menu_ids: string[];
|
||||
};
|
||||
|
||||
const EMPTY_FORM: RoleFormValues = {
|
||||
code: "",
|
||||
name: "",
|
||||
permission_codes: [],
|
||||
menu_ids: [],
|
||||
};
|
||||
|
||||
@@ -55,7 +52,6 @@ export default function AdminRolesPage() {
|
||||
const [form] = Form.useForm<RoleFormValues>();
|
||||
const [roles, setRoles] = useState<RoleItem[]>([]);
|
||||
const [searchKeyword, setSearchKeyword] = useState("");
|
||||
const [permissions, setPermissions] = useState<PermissionItem[]>([]);
|
||||
const [menus, setMenus] = useState<MenuItem[]>([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [saving, setSaving] = useState(false);
|
||||
@@ -66,15 +62,6 @@ export default function AdminRolesPage() {
|
||||
const canRead = hasPermission("role.read") || hasPermission("role.manage");
|
||||
const canManage = hasPermission("role.manage");
|
||||
|
||||
const permissionOptions = useMemo(
|
||||
() =>
|
||||
permissions.map((permission) => ({
|
||||
value: permission.code,
|
||||
label: `${permission.name || permission.code} (${permission.code})`,
|
||||
})),
|
||||
[permissions],
|
||||
);
|
||||
|
||||
const menuOptions = useMemo(
|
||||
() => menus.map((menu) => ({ value: menu.id, label: `${menu.name} (${menu.code})` })),
|
||||
[menus],
|
||||
@@ -97,7 +84,6 @@ export default function AdminRolesPage() {
|
||||
const haystack = [
|
||||
role.code,
|
||||
role.name,
|
||||
role.permission_codes.join(" "),
|
||||
menuNames,
|
||||
]
|
||||
.join(" ")
|
||||
@@ -115,28 +101,22 @@ export default function AdminRolesPage() {
|
||||
setLoading(true);
|
||||
setError("");
|
||||
try {
|
||||
const [roleRes, permissionRes, menuRes] = await Promise.all([
|
||||
const [roleRes, menuRes] = await Promise.all([
|
||||
fetchWithAuth("/api/v1/admin/roles"),
|
||||
fetchWithAuth("/api/v1/admin/permissions"),
|
||||
fetchWithAuth("/api/v1/admin/menus"),
|
||||
]);
|
||||
|
||||
if (!roleRes.ok) {
|
||||
throw new Error(await readApiError(roleRes));
|
||||
}
|
||||
if (!permissionRes.ok) {
|
||||
throw new Error(await readApiError(permissionRes));
|
||||
}
|
||||
if (!menuRes.ok) {
|
||||
throw new Error(await readApiError(menuRes));
|
||||
}
|
||||
|
||||
const rolePayload = (await roleRes.json()) as RoleListResponse;
|
||||
const permissionPayload = (await permissionRes.json()) as PermissionResponse;
|
||||
const menuPayload = (await menuRes.json()) as MenuListResponse;
|
||||
|
||||
setRoles(rolePayload.items);
|
||||
setPermissions(permissionPayload.items);
|
||||
setMenus(menuPayload.items);
|
||||
} catch (candidate) {
|
||||
setError(candidate instanceof Error ? candidate.message : "角色数据加载失败");
|
||||
@@ -183,7 +163,6 @@ export default function AdminRolesPage() {
|
||||
form.setFieldsValue({
|
||||
code: role.code,
|
||||
name: role.name,
|
||||
permission_codes: role.permission_codes,
|
||||
menu_ids: role.menu_ids,
|
||||
});
|
||||
setDialogOpen(true);
|
||||
@@ -198,7 +177,6 @@ export default function AdminRolesPage() {
|
||||
const payload: RoleFormValues = {
|
||||
code: values.code.trim(),
|
||||
name: values.name.trim(),
|
||||
permission_codes: values.permission_codes ?? [],
|
||||
menu_ids: values.menu_ids ?? [],
|
||||
};
|
||||
|
||||
@@ -208,7 +186,6 @@ export default function AdminRolesPage() {
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
name: payload.name,
|
||||
permission_codes: payload.permission_codes,
|
||||
menu_ids: payload.menu_ids,
|
||||
}),
|
||||
})
|
||||
@@ -282,22 +259,6 @@ export default function AdminRolesPage() {
|
||||
dataIndex: "name",
|
||||
width: 180,
|
||||
},
|
||||
{
|
||||
title: "权限",
|
||||
dataIndex: "permission_codes",
|
||||
render: (value: string[]) => {
|
||||
if (value.length === 0) {
|
||||
return <Typography.Text type="secondary">未绑定权限</Typography.Text>;
|
||||
}
|
||||
return (
|
||||
<Space wrap size={[4, 4]}>
|
||||
{value.map((permissionCode) => (
|
||||
<Tag key={permissionCode}>{permissionCode}</Tag>
|
||||
))}
|
||||
</Space>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "菜单",
|
||||
dataIndex: "menu_ids",
|
||||
@@ -414,7 +375,7 @@ export default function AdminRolesPage() {
|
||||
<Space align="center" wrap>
|
||||
<Input.Search
|
||||
allowClear
|
||||
placeholder="搜索角色编码、名称、权限或菜单"
|
||||
placeholder="搜索角色编码、名称或菜单"
|
||||
style={{ width: 360, maxWidth: "100%" }}
|
||||
value={searchKeyword}
|
||||
onChange={(event) => setSearchKeyword(event.currentTarget.value)}
|
||||
@@ -482,16 +443,6 @@ export default function AdminRolesPage() {
|
||||
<Input placeholder="运营管理员" />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item label="权限点" name="permission_codes">
|
||||
<Select
|
||||
allowClear
|
||||
mode="multiple"
|
||||
optionFilterProp="label"
|
||||
options={permissionOptions}
|
||||
placeholder="请选择权限点"
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item label="可见菜单" name="menu_ids">
|
||||
<Select
|
||||
allowClear
|
||||
|
||||
Reference in New Issue
Block a user