feat(admin): Add user management and upgrade to module permission system
Features - User Management (Phase 4.1): - Database: Add user_modules table for fine-grained module permissions - Database: Add 4 user permissions (view/create/edit/delete) to role_permissions - Backend: UserService (780 lines) - CRUD with tenant isolation - Backend: UserController + UserRoutes (648 lines) - 13 API endpoints - Backend: Batch import users from Excel - Frontend: UserListPage (412 lines) - list/filter/search/pagination - Frontend: UserFormPage (341 lines) - create/edit with module config - Frontend: UserDetailPage (393 lines) - details/tenant/module management - Frontend: 3 modal components (592 lines) - import/assign/configure - API: GET/POST/PUT/DELETE /api/admin/users/* endpoints Architecture Upgrade - Module Permission System: - Backend: Add getUserModules() method in auth.service - Backend: Login API returns modules array in user object - Frontend: AuthContext adds hasModule() method - Frontend: Navigation filters modules based on user.modules - Frontend: RouteGuard checks requiredModule instead of requiredVersion - Frontend: Remove deprecated version-based permission system - UX: Only show accessible modules in navigation (clean UI) - UX: Smart redirect after login (avoid 403 for regular users) Fixes: - Fix UTF-8 encoding corruption in ~100 docs files - Fix pageSize type conversion in userService (String to Number) - Fix authUser undefined error in TopNavigation - Fix login redirect logic with role-based access check - Update Git commit guidelines v1.2 with UTF-8 safety rules Database Changes: - CREATE TABLE user_modules (user_id, tenant_id, module_code, is_enabled) - ADD UNIQUE CONSTRAINT (user_id, tenant_id, module_code) - INSERT 4 permissions + role assignments - UPDATE PUBLIC tenant with 8 module subscriptions Technical: - Backend: 5 new files (~2400 lines) - Frontend: 10 new files (~2500 lines) - Docs: 1 development record + 2 status updates + 1 guideline update - Total: ~4900 lines of code Status: User management 100% complete, module permission system operational
This commit is contained in:
@@ -1,17 +1,17 @@
|
||||
# ASL模块 - 路由问题修复报告
|
||||
|
||||
**日期**: 2025-11-18
|
||||
**问题**: 点击"设置与å<C5BD>¯åŠ?按钮å<C2AE>Žé¡µé<C2B5>¢æ˜¾ç¤ºç©ºç™?
|
||||
**状�*: �已修�
|
||||
**问题**: 点击"设置与启动"按钮后页面显示空白
|
||||
**状态**: ✅ 已修复
|
||||
|
||||
---
|
||||
|
||||
## 🐛 问题描述
|
||||
|
||||
### 用户反馈
|
||||
1. â<EFBFBD>?点击左侧"设置与å<C5BD>¯åŠ?按钮
|
||||
2. â<EFBFBD>?页é<C2B5>¢æ˜¾ç¤ºç©ºç™½
|
||||
3. â<EFBFBD>?æµ<C3A6>览器控制å<C2B6>°è¦å‘Šï¼š`Warning: [antd: Spin] tip only work in nest or fullscreen pattern.`
|
||||
1. ❌ 点击左侧"设置与启动"按钮
|
||||
2. ❌ 页面显示空白
|
||||
3. ❌ 浏览器控制台警告:`Warning: [antd: Spin] tip only work in nest or fullscreen pattern.`
|
||||
|
||||
---
|
||||
|
||||
@@ -19,29 +19,29 @@
|
||||
|
||||
### 根本原因
|
||||
|
||||
å<EFBFBD>‘现äº?*2个问é¢?*ï¼?
|
||||
发现了**2个问题**:
|
||||
|
||||
#### 问题1: Spin组件的tip属性è¦å‘?âš ï¸<C3AF>
|
||||
#### 问题1: Spin组件的tip属性警告 ⚠️
|
||||
|
||||
**位置**: `frontend-v2/src/framework/layout/MainLayout.tsx:30`
|
||||
|
||||
```typescript
|
||||
// â<EFBFBD>?错误代ç <C3A7>
|
||||
<Spin size="large" tip="åŠ è½½ä¸?.." />
|
||||
// ❌ 错误代码
|
||||
<Spin size="large" tip="加载中..." />
|
||||
```
|
||||
|
||||
**åŽŸå› **: Ant Design çš?`Spin` 组件çš?`tip` 属性å<C2A7>ªèƒ½åœ¨ä»¥ä¸‹æ¨¡å¼<C3A5>使用ï¼?
|
||||
**原因**: Ant Design 的 `Spin` 组件的 `tip` 属性只能在以下模式使用:
|
||||
- `nest` 模式(嵌套在内容中)
|
||||
- `fullscreen` 模式(全屏显示)
|
||||
|
||||
当å‰<EFBFBD>使用的是普通模å¼<EFBFBD>,ä¸<EFBFBD>支æŒ?`tip` 属性ã€?
|
||||
当前使用的是普通模式,不支持 `tip` 属性。
|
||||
|
||||
#### 问题2: 嵌套路由é…<C3A9>置错误 â<>?
|
||||
#### 问题2: 嵌套路由配置错误 ❌
|
||||
|
||||
**位置**: `frontend-v2/src/modules/asl/index.tsx`
|
||||
|
||||
```typescript
|
||||
// â<EFBFBD>?错误代ç <C3A7>
|
||||
// ❌ 错误代码
|
||||
<Routes>
|
||||
{aslRoutes.map((route, index) => (
|
||||
<Route
|
||||
@@ -56,35 +56,35 @@
|
||||
|
||||
**原因**:
|
||||
- `aslRoutes` 是一个复杂的嵌套路由结构
|
||||
- `map` 方法å<EFBFBD>ªèƒ½æ¸²æŸ“ç¬¬ä¸€å±‚è·¯ç”±ï¼Œæ— æ³•å¤„ç<EFBFBD>† `children` 属æ€?
|
||||
- `map` 方法只能渲染第一层路由,无法处理 `children` 属性
|
||||
- 导致 `ASLLayout` 的子路由无法正常渲染
|
||||
- 结果:页é<EFBFBD>¢æ˜¾ç¤ºç©ºç™?
|
||||
- 结果:页面显示空白
|
||||
|
||||
**路由结构**:
|
||||
```
|
||||
ASLLayout (父路�
|
||||
└── screening/title (å<EFBFBD>è·¯ç”?
|
||||
ASLLayout (父路由)
|
||||
└── screening/title (子路由)
|
||||
├── settings
|
||||
├── workbench
|
||||
└── results
|
||||
```
|
||||
|
||||
è¿™ç§<EFBFBD>嵌套结构需è¦<EFBFBD>在 JSX 䏿˜¾å¼<C3A5>声明ã€?
|
||||
这种嵌套结构需要在 JSX 中显式声明。
|
||||
|
||||
---
|
||||
|
||||
## âœ?ä¿®å¤<C3A5>方案
|
||||
## ✅ 修复方案
|
||||
|
||||
### ä¿®å¤<EFBFBD>1: 移除Spinçš„tip属æ€?
|
||||
### 修复1: 移除Spin的tip属性
|
||||
|
||||
**文件**: `frontend-v2/src/framework/layout/MainLayout.tsx`
|
||||
|
||||
```typescript
|
||||
// âœ?ä¿®å¤<C3A5>å<EFBFBD>?
|
||||
// ✅ 修复后
|
||||
<Spin size="large" />
|
||||
```
|
||||
|
||||
**效果**: è¦å‘Šæ¶ˆå¤±ï¼ŒåŠ è½½åŠ¨ç”»æ£å¸¸æ˜¾ç¤?
|
||||
**效果**: 警告消失,加载动画正常显示
|
||||
|
||||
---
|
||||
|
||||
@@ -93,12 +93,12 @@ ASLLayout (父路
|
||||
**文件**: `frontend-v2/src/modules/asl/index.tsx`
|
||||
|
||||
```typescript
|
||||
// âœ?ä¿®å¤<C3A5>å<EFBFBD>?
|
||||
// ✅ 修复后
|
||||
import { Suspense, lazy } from 'react';
|
||||
import { Routes, Route, Navigate } from 'react-router-dom';
|
||||
import { Spin } from 'antd';
|
||||
|
||||
// æ‡’åŠ è½½ç»„ä»?
|
||||
// 懒加载组件
|
||||
const ASLLayout = lazy(() => import('./components/ASLLayout'));
|
||||
const TitleScreeningSettings = lazy(() => import('./pages/TitleScreeningSettings'));
|
||||
const TitleScreeningWorkbench = lazy(() => import('./pages/ScreeningWorkbench'));
|
||||
@@ -114,12 +114,12 @@ const ASLModule = () => {
|
||||
}
|
||||
>
|
||||
<Routes>
|
||||
{/* 父路� ASLLayout 布局 */}
|
||||
{/* 父路由: ASLLayout 布局 */}
|
||||
<Route path="" element={<ASLLayout />}>
|
||||
{/* 默认é‡<EFBFBD>定å<EFBFBD>‘到设置é¡?*/}
|
||||
{/* 默认重定向到设置页 */}
|
||||
<Route index element={<Navigate to="screening/title/settings" replace />} />
|
||||
|
||||
{/* æ ‡é¢˜æ‘˜è¦<EFBFBD>åˆ<EFBFBD>ç›å<EFBFBD>è·¯ç”?*/}
|
||||
{/* 标题摘要初筛子路由 */}
|
||||
<Route path="screening/title">
|
||||
<Route index element={<Navigate to="settings" replace />} />
|
||||
<Route path="settings" element={<TitleScreeningSettings />} />
|
||||
@@ -136,11 +136,11 @@ export default ASLModule;
|
||||
```
|
||||
|
||||
**改进**:
|
||||
- âœ?使用嵌套çš?`<Route>` æ ‡ç¾æ˜¾å¼<C3A5>声明层级关系
|
||||
- �`ASLLayout` 作为父路�
|
||||
- âœ?`screening/title` 作为ä¸é—´å±?
|
||||
- âœ?`settings/workbench/results` 作为å<EFBFBD>¶å<EFBFBD>路由
|
||||
- âœ?两个 `<Navigate>` 实现自动é‡<EFBFBD>定å<EFBFBD>?
|
||||
- ✅ 使用嵌套的 `<Route>` 标签显式声明层级关系
|
||||
- ✅ `ASLLayout` 作为父路由
|
||||
- ✅ `screening/title` 作为中间层
|
||||
- ✅ `settings/workbench/results` 作为叶子路由
|
||||
- ✅ 两个 `<Navigate>` 实现自动重定向
|
||||
|
||||
---
|
||||
|
||||
@@ -149,8 +149,8 @@ export default ASLModule;
|
||||
**删除**: `frontend-v2/src/modules/asl/routes.tsx`
|
||||
|
||||
**原因**:
|
||||
- 路由é…<EFBFBD>置已ç»<EFBFBD>直接åœ?`index.tsx` ä¸å®žçŽ?
|
||||
- `routes.tsx` 文件ä¸<EFBFBD>å†<EFBFBD>被引ç”?
|
||||
- 路由配置已经直接在 `index.tsx` 中实现
|
||||
- `routes.tsx` 文件不再被引用
|
||||
- 避免维护两份路由配置
|
||||
|
||||
---
|
||||
@@ -161,47 +161,47 @@ export default ASLModule;
|
||||
|
||||
```
|
||||
1. 点击"AI智能文献"
|
||||
�进入 /literature
|
||||
→ 进入 /literature
|
||||
|
||||
2. ASLModule 接收路径 ""
|
||||
�渲染 ASLLayout(左侧导�+ Outlet�
|
||||
→ 渲染 ASLLayout(左侧导航 + Outlet)
|
||||
|
||||
3. index route 触发
|
||||
�<Navigate to="screening/title/settings" replace />
|
||||
→ <Navigate to="screening/title/settings" replace />
|
||||
|
||||
4. 路径变为 /literature/screening/title/settings
|
||||
â†?ASLLayout ä¿<EFBFBD>æŒ<EFBFBD>显示
|
||||
�Outlet 渲染 TitleScreeningSettings 组件
|
||||
→ ASLLayout 保持显示
|
||||
→ Outlet 渲染 TitleScreeningSettings 组件
|
||||
|
||||
5. 用户看到完整页é<EFBFBD>¢ï¼?
|
||||
┌─────────────────────────────────────────�
|
||||
â”?左侧导航 â”? 设置与å<C5BD>¯åЍ页é<C2B5>? â”?
|
||||
â”?(ASL) â”? (PICOS + Excelä¸Šä¼ ) â”?
|
||||
└─────────────────────────────────────────�
|
||||
5. 用户看到完整页面:
|
||||
┌─────────────────────────────────────────┐
|
||||
│ 左侧导航 │ 设置与启动页面 │
|
||||
│ (ASL) │ (PICOS + Excel上传) │
|
||||
└─────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 路由匹配测试
|
||||
|
||||
| 路径 | 匹配结果 | 显示组件 |
|
||||
|------|---------|---------|
|
||||
| `/literature` | index route | Navigate �settings |
|
||||
| `/literature/screening/title` | index route | Navigate �settings |
|
||||
| `/literature/screening/title/settings` | �| TitleScreeningSettings |
|
||||
| `/literature/screening/title/workbench` | �| TitleScreeningWorkbench |
|
||||
| `/literature/screening/title/results` | �| TitleScreeningResults |
|
||||
| `/literature` | index route | Navigate → settings |
|
||||
| `/literature/screening/title` | index route | Navigate → settings |
|
||||
| `/literature/screening/title/settings` | ✅ | TitleScreeningSettings |
|
||||
| `/literature/screening/title/workbench` | ✅ | TitleScreeningWorkbench |
|
||||
| `/literature/screening/title/results` | ✅ | TitleScreeningResults |
|
||||
|
||||
---
|
||||
|
||||
## 📊 修复效果
|
||||
|
||||
### ä¿®å¤<EFBFBD>å‰?â<>?
|
||||
### 修复前 ❌
|
||||
- 页面空白
|
||||
- 控制å<EFBFBD>°è¦å‘?
|
||||
- 控制台警告
|
||||
- 路由无法正确渲染
|
||||
|
||||
### ä¿®å¤<EFBFBD>å<EFBFBD>?âœ?
|
||||
### 修复后 ✅
|
||||
- 左侧导航正常显示
|
||||
- "设置与å<EFBFBD>¯åŠ?页é<C2B5>¢å®Œæ•´æ¸²æŸ“
|
||||
- "设置与启动"页面完整渲染
|
||||
- PICOS表单可以正常填写
|
||||
- 无控制台警告
|
||||
|
||||
@@ -223,17 +223,17 @@ export default ASLModule;
|
||||
const Parent = () => (
|
||||
<div>
|
||||
<Sidebar />
|
||||
<Outlet /> {/* å<EFBFBD>路由渲染ä½<EFBFBD>ç½?*/}
|
||||
<Outlet /> {/* 子路由渲染位置 */}
|
||||
</div>
|
||||
);
|
||||
```
|
||||
|
||||
3. **ä¸<EFBFBD>能ç”?`map` 渲染嵌套路由**:
|
||||
3. **不能用 `map` 渲染嵌套路由**:
|
||||
```tsx
|
||||
// â<EFBFBD>?错误
|
||||
// ❌ 错误
|
||||
{routes.map(r => <Route key={r.path} {...r} />)}
|
||||
|
||||
// âœ?æ£ç¡®
|
||||
// ✅ 正确
|
||||
<Route path="parent" element={<Parent />}>
|
||||
<Route path="child" element={<Child />} />
|
||||
</Route>
|
||||
@@ -243,51 +243,51 @@ const Parent = () => (
|
||||
|
||||
1. **`tip` 属性的限制**:
|
||||
```tsx
|
||||
// â<EFBFBD>?普通模å¼<C3A5>ä¸<C3A4>支æŒ<C3A6> tip
|
||||
<Spin size="large" tip="åŠ è½½ä¸?.." />
|
||||
// ❌ 普通模式不支持 tip
|
||||
<Spin size="large" tip="加载中..." />
|
||||
|
||||
// �方案1: 移除 tip
|
||||
// ✅ 方案1: 移除 tip
|
||||
<Spin size="large" />
|
||||
|
||||
// �方案2: 使用 fullscreen
|
||||
<Spin size="large" tip="åŠ è½½ä¸?.." fullscreen />
|
||||
// ✅ 方案2: 使用 fullscreen
|
||||
<Spin size="large" tip="加载中..." fullscreen />
|
||||
|
||||
// �方案3: 自定义文�
|
||||
// ✅ 方案3: 自定义文本
|
||||
<div>
|
||||
<Spin size="large" />
|
||||
<div className="mt-2">åŠ è½½ä¸?..</div>
|
||||
<div className="mt-2">加载中...</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## âœ?验收清å<E280A6>•
|
||||
## ✅ 验收清单
|
||||
|
||||
- [x] 点击"AI智能文献"能进入模å<C2A1>?
|
||||
- [x] 左侧导航æ£å¸¸æ˜¾ç¤º7个è<EFBFBD>œå<EFBFBD>?
|
||||
- [x] 点击"AI智能文献"能进入模块
|
||||
- [x] 左侧导航正常显示7个菜单
|
||||
- [x] "标题摘要初筛"展开3个子菜单
|
||||
- [x] 默认显示"设置与å<C5BD>¯åŠ?页é<C2B5>¢
|
||||
- [x] PICOS表å<EFBFBD>•完整显示ï¼?-8行TextAreaï¼?
|
||||
- [x] æ— æµ<EFBFBD>览器控制å<EFBFBD>°è¦å‘?错误
|
||||
- [x] 点击其他å<EFBFBD>è<EFBFBD>œå<EFBFBD>•å<EFBFBD>¯ä»¥æ£å¸¸è·³è½?
|
||||
- [x] 默认显示"设置与启动"页面
|
||||
- [x] PICOS表单完整显示(6-8行TextArea)
|
||||
- [x] 无浏览器控制台警告/错误
|
||||
- [x] 点击其他子菜单可以正常跳转
|
||||
|
||||
---
|
||||
|
||||
## 🎉 修复完成
|
||||
|
||||
**修复文件**:
|
||||
1. �`MainLayout.tsx` - 移除Spin的tip属�
|
||||
2. âœ?`asl/index.tsx` - é‡<EFBFBD>写嵌套路由
|
||||
3. âœ?åˆ é™¤ `asl/routes.tsx`
|
||||
1. ✅ `MainLayout.tsx` - 移除Spin的tip属性
|
||||
2. ✅ `asl/index.tsx` - 重写嵌套路由
|
||||
3. ✅ 删除 `asl/routes.tsx`
|
||||
|
||||
**修复时间**: 15分钟
|
||||
**问题å¤<EFBFBD>æ<EFBFBD>‚åº?*: â<C3A2>â<C3A2>â?(ä¸ç‰)
|
||||
**ä¿®å¤<EFBFBD>è´¨é‡<EFBFBD>**: â<C3A2>â<C3A2>â<C3A2>â<C3A2>â?(完美)
|
||||
**问题复杂度**: ⭐⭐⭐ (中等)
|
||||
**修复质量**: ⭐⭐⭐⭐⭐ (完美)
|
||||
|
||||
---
|
||||
|
||||
**修复完成时间**: 2025-11-18 22:15
|
||||
**下一æ?*: ç»§ç» Week 2 Day 2 å¼€å<EFBFBD>?
|
||||
**下一步**: 继续 Week 2 Day 2 开发
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user