背景:角色权限只描述模块的一部分能力,侧栏同级均为模块级命名(用户与组织 / 客户 / 行业树);角色页实际覆盖角色 CRUD、权限点、数据范围、特殊权限与 SoD。 改动: - 侧栏、面包屑、页面标题统一为「角色管理」,页面内权限点 / 数据范围 / 特殊权限分区名不变; - 用户详情与超管授予弹窗中的模块引用同步改为「角色管理」; - 开发计划页面清单与 A2 实施报告同步; - 更新导航与面包屑单测、A2 e2e 标题断言。 验证:format:check / lint / typecheck / test 全绿;5173 实测侧栏、面包屑、页标题均为「角色管理」。
This commit is contained in:
1 parent
a3419cf5da
commit
f5a8afe835
11 files changed
+16
-16
No files matched your search
@@ -47,7 +47,7 @@
|
|||||||
| **脚本** | 要素卡 / 卖点库 / 模板 / 生成 / 质量校核 / 审核入库 / 版本 |
|
| **脚本** | 要素卡 / 卖点库 / 模板 / 生成 / 质量校核 / 审核入库 / 版本 |
|
||||||
| **素材** | 图片线(PSD 套版、抠图、规格矩阵)、视频线(逐镜生成、变体)、快筛与预览 |
|
| **素材** | 图片线(PSD 套版、抠图、规格矩阵)、视频线(逐镜生成、变体)、快筛与预览 |
|
||||||
| **资产** | 15 类资产的库、版本、引用、复审与复用度量 |
|
| **资产** | 15 类资产的库、版本、引用、复审与复用度量 |
|
||||||
| **平台管理** | 组织账号、客户、角色权限、AI 配置、数据字典、审计、行业包与表单设计器 |
|
| **平台管理** | 组织账号、客户、角色管理、AI 配置、数据字典、审计、行业包与表单设计器 |
|
||||||
|
|
||||||
> 早期文档中的「四大模块(脚本生成 / 素材生产 / 编辑 / 管理)」已于 PRD V1.6 重构为「脚本 / 素材 / 资产 / 平台管理」三主线 + 一底座。
|
> 早期文档中的「四大模块(脚本生成 / 素材生产 / 编辑 / 管理)」已于 PRD V1.6 重构为「脚本 / 素材 / 资产 / 平台管理」三主线 + 一底座。
|
||||||
|
|
||||||
|
|||||||
@@ -3,12 +3,12 @@ import { describe, expect, it } from 'vitest';
|
|||||||
import { getRoleSectionTrail, getUserSectionTrail } from './breadcrumbs';
|
import { getRoleSectionTrail, getUserSectionTrail } from './breadcrumbs';
|
||||||
|
|
||||||
describe('面包屑子路由补全', () => {
|
describe('面包屑子路由补全', () => {
|
||||||
it('角色新建页:工作台 / 管理中心 / 角色权限 / 新建角色', () => {
|
it('角色新建页:工作台 / 管理中心 / 角色管理 / 新建角色', () => {
|
||||||
const trail = getRoleSectionTrail('/system/roles/new');
|
const trail = getRoleSectionTrail('/system/roles/new');
|
||||||
expect(trail?.map((entry) => entry.label)).toEqual([
|
expect(trail?.map((entry) => entry.label)).toEqual([
|
||||||
'工作台',
|
'工作台',
|
||||||
'管理中心',
|
'管理中心',
|
||||||
'角色权限',
|
'角色管理',
|
||||||
'新建角色',
|
'新建角色',
|
||||||
]);
|
]);
|
||||||
expect(trail?.[2]?.path).toBe('/system/roles');
|
expect(trail?.[2]?.path).toBe('/system/roles');
|
||||||
@@ -19,7 +19,7 @@ describe('面包屑子路由补全', () => {
|
|||||||
expect(trail?.map((entry) => entry.label)).toEqual([
|
expect(trail?.map((entry) => entry.label)).toEqual([
|
||||||
'工作台',
|
'工作台',
|
||||||
'管理中心',
|
'管理中心',
|
||||||
'角色权限',
|
'角色管理',
|
||||||
'角色编辑',
|
'角色编辑',
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ export function getRoleSectionTrail(pathname: string): BreadcrumbEntry[] | null
|
|||||||
return [
|
return [
|
||||||
{ key: 'home', label: '工作台', path: ROUTES.home },
|
{ key: 'home', label: '工作台', path: ROUTES.home },
|
||||||
{ key: 'system', label: '管理中心' },
|
{ key: 'system', label: '管理中心' },
|
||||||
{ key: 'roles', label: '角色权限', path: ROUTES.system.roles },
|
{ key: 'roles', label: '角色管理', path: ROUTES.system.roles },
|
||||||
{ key: 'role-new', label: '新建角色' },
|
{ key: 'role-new', label: '新建角色' },
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
@@ -16,7 +16,7 @@ export function getRoleSectionTrail(pathname: string): BreadcrumbEntry[] | null
|
|||||||
return [
|
return [
|
||||||
{ key: 'home', label: '工作台', path: ROUTES.home },
|
{ key: 'home', label: '工作台', path: ROUTES.home },
|
||||||
{ key: 'system', label: '管理中心' },
|
{ key: 'system', label: '管理中心' },
|
||||||
{ key: 'roles', label: '角色权限', path: ROUTES.system.roles },
|
{ key: 'roles', label: '角色管理', path: ROUTES.system.roles },
|
||||||
{ key: 'role-detail', label: '角色编辑' },
|
{ key: 'role-detail', label: '角色编辑' },
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,12 +33,12 @@ describe('导航配置', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('角色权限菜单使用 role:view,只读角色也能进入角色页', () => {
|
it('角色管理菜单使用 role:view,只读角色也能进入角色页', () => {
|
||||||
const rolesItem = NAV_MODULES.find((module) => module.key === 'system')?.items.find(
|
const rolesItem = NAV_MODULES.find((module) => module.key === 'system')?.items.find(
|
||||||
(item) => item.key === 'roles',
|
(item) => item.key === 'roles',
|
||||||
);
|
);
|
||||||
expect(rolesItem?.permission).toBe('role:view');
|
expect(rolesItem?.permission).toBe('role:view');
|
||||||
expect(findActiveNav(NAV_MODULES, '/system/roles/new')?.item?.label).toBe('角色权限');
|
expect(findActiveNav(NAV_MODULES, '/system/roles/new')?.item?.label).toBe('角色管理');
|
||||||
expect(findActiveNav(NAV_MODULES, '/system/roles/01HZZZZZZZZZZZZZZZZZZZZZZZ')?.module.key).toBe(
|
expect(findActiveNav(NAV_MODULES, '/system/roles/01HZZZZZZZZZZZZZZZZZZZZZZZ')?.module.key).toBe(
|
||||||
'system',
|
'system',
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -256,7 +256,7 @@ export const NAV_MODULES: readonly NavModule[] = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'roles',
|
key: 'roles',
|
||||||
label: '角色权限',
|
label: '角色管理',
|
||||||
path: '/system/roles',
|
path: '/system/roles',
|
||||||
icon: IconShield,
|
icon: IconShield,
|
||||||
permission: Permission.ROLE_VIEW,
|
permission: Permission.ROLE_VIEW,
|
||||||
|
|||||||
@@ -170,7 +170,7 @@ export function RolesPage(): ReactNode {
|
|||||||
<div className="lx-roles">
|
<div className="lx-roles">
|
||||||
<header className="lx-roles__header">
|
<header className="lx-roles__header">
|
||||||
<div className="lx-roles__head-txt">
|
<div className="lx-roles__head-txt">
|
||||||
<h1>角色权限</h1>
|
<h1>角色管理</h1>
|
||||||
<p>配置功能权限、数据范围与特殊权限,变更即时生效并留痕</p>
|
<p>配置功能权限、数据范围与特殊权限,变更即时生效并留痕</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="lx-roles__header-actions">
|
<div className="lx-roles__header-actions">
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ export function UserRolesTab({
|
|||||||
<div className="lx-detail-tab">
|
<div className="lx-detail-tab">
|
||||||
<SectionHead
|
<SectionHead
|
||||||
title="已绑定角色"
|
title="已绑定角色"
|
||||||
hint="角色决定成员可访问的模块与操作;权限点由「角色权限」统一维护。"
|
hint="角色决定成员可访问的模块与操作;权限点由「角色管理」统一维护。"
|
||||||
action={
|
action={
|
||||||
canManage ? (
|
canManage ? (
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ export function SuperAdminGrantDialog({
|
|||||||
consequences={[
|
consequences={[
|
||||||
'超级管理员拥有全部模块权限与全部客户数据范围',
|
'超级管理员拥有全部模块权限与全部客户数据范围',
|
||||||
'授予即时生效并写入审计;建议全平台超级管理员不超过 2 人',
|
'授予即时生效并写入审计;建议全平台超级管理员不超过 2 人',
|
||||||
'如需收回,请在「角色权限」中移除该角色',
|
'如需收回,请在「角色管理」中移除该角色',
|
||||||
]}
|
]}
|
||||||
acknowledgeLabel="我已确认授权范围,并同意授予超级管理员"
|
acknowledgeLabel="我已确认授权范围,并同意授予超级管理员"
|
||||||
confirmText="确认授予"
|
confirmText="确认授予"
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ A2-QA-01 覆盖:6 个角色端点权限三态(200/201/204 vs 403 `40301`)
|
|||||||
pnpm check:dev # 确认 PG 5432 / Redis 6379
|
pnpm check:dev # 确认 PG 5432 / Redis 6379
|
||||||
pnpm db:init # 首次/迁移后:migrate deploy + 幂等种子(不重置数据、不改已有密码)
|
pnpm db:init # 首次/迁移后:migrate deploy + 幂等种子(不重置数据、不改已有密码)
|
||||||
pnpm dev # API :3000 + Web :5173(日常启动不需要任何 DB 命令)
|
pnpm dev # API :3000 + Web :5173(日常启动不需要任何 DB 命令)
|
||||||
# 浏览器打开 http://127.0.0.1:5173/login → 管理中心 / 角色权限
|
# 浏览器打开 http://127.0.0.1:5173/login → 管理中心 / 角色管理
|
||||||
```
|
```
|
||||||
|
|
||||||
种子账号:`admin` 超管、`jiang.lan` 平台管理员等 11 个账号;初始密码 `lingxi2026`,首次登录强制修改(仅 `must_change_password=true` 时;改完即持久为 false)。
|
种子账号:`admin` 超管、`jiang.lan` 平台管理员等 11 个账号;初始密码 `lingxi2026`,首次登录强制修改(仅 `must_change_password=true` 时;改完即持久为 false)。
|
||||||
|
|||||||
@@ -1374,7 +1374,7 @@ infra/:compose、Nginx、监控、备份脚本占位
|
|||||||
| --- | --- | --- | --- |
|
| --- | --- | --- | --- |
|
||||||
| `/login` | 登录 | 公开 | A1 |
|
| `/login` | 登录 | 公开 | A1 |
|
||||||
| `/system/users`、`/system/users/new`、`/system/users/:id` | 用户与部门 | `user:view` / `user:manage` | A1 |
|
| `/system/users`、`/system/users/new`、`/system/users/:id` | 用户与部门 | `user:view` / `user:manage` | A1 |
|
||||||
| `/system/roles`、`/system/roles/:id` | 角色与权限 | `role:view` / `role:manage` | A2 |
|
| `/system/roles`、`/system/roles/:id` | 角色管理 | `role:view` / `role:manage` | A2 |
|
||||||
| `/system/industries` | 行业管理 | `industry_pack:view` / `customer:manage` | A3 |
|
| `/system/industries` | 行业管理 | `industry_pack:view` / `customer:manage` | A3 |
|
||||||
| `/system/customers`、`/system/customers/new`、`/system/customers/:id` | 客户管理 | `customer:view` / `customer:manage` | A3 |
|
| `/system/customers`、`/system/customers/new`、`/system/customers/:id` | 客户管理 | `customer:view` / `customer:manage` | A3 |
|
||||||
| `/system/customer-groups` | 客户组 | `customer_group:manage` | A3 |
|
| `/system/customer-groups` | 客户组 | `customer_group:manage` | A3 |
|
||||||
|
|||||||
@@ -533,7 +533,7 @@ test('角色管理:新建 → 编辑器 → SoD 拦截 → 特殊权限二次
|
|||||||
// ① 管理员登录 → /system/roles
|
// ① 管理员登录 → /system/roles
|
||||||
await loginAsAdmin(page);
|
await loginAsAdmin(page);
|
||||||
await page.goto('/system/roles');
|
await page.goto('/system/roles');
|
||||||
await expect(page.getByRole('heading', { name: '角色权限' })).toBeVisible();
|
await expect(page.getByRole('heading', { name: '角色管理' })).toBeVisible();
|
||||||
await expect(page.getByTestId('app-shell')).toBeVisible();
|
await expect(page.getByTestId('app-shell')).toBeVisible();
|
||||||
await screenshot(page, '01-roles-list');
|
await screenshot(page, '01-roles-list');
|
||||||
|
|
||||||
@@ -699,7 +699,7 @@ test('角色管理:新建 → 编辑器 → SoD 拦截 → 特殊权限二次
|
|||||||
|
|
||||||
// ⑥ 列表状态变化:停用筛选下可见该角色,且行内标记「停用」
|
// ⑥ 列表状态变化:停用筛选下可见该角色,且行内标记「停用」
|
||||||
await page.goto('/system/roles');
|
await page.goto('/system/roles');
|
||||||
await expect(page.getByRole('heading', { name: '角色权限' })).toBeVisible();
|
await expect(page.getByRole('heading', { name: '角色管理' })).toBeVisible();
|
||||||
const disabledSeg = page
|
const disabledSeg = page
|
||||||
.getByRole('group', { name: '角色状态' })
|
.getByRole('group', { name: '角色状态' })
|
||||||
.getByRole('button', { name: /^停用/ });
|
.getByRole('button', { name: /^停用/ });
|
||||||
|
|||||||
Reference in new issue
Block a user