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,7 +1,7 @@
|
||||
# AIA V2.1 前端组件设计
|
||||
|
||||
> **版本**:V2.1
|
||||
> **创建日期**�026-01-11
|
||||
> **创建日期**:2026-01-11
|
||||
> **技术栈**:React 19 + TypeScript 5 + Ant Design 6 + Ant Design X 2.1
|
||||
|
||||
---
|
||||
@@ -11,45 +11,45 @@
|
||||
```
|
||||
frontend-v2/src/modules/aia/
|
||||
├── pages/
|
||||
� ├── Dashboard.tsx # 智能体大厅(首页�
|
||||
â”? └── Workspace.tsx # 对è¯<EFBFBD>工作å<EFBFBD>?
|
||||
│ ├── Dashboard.tsx # 智能体大厅(首页)
|
||||
│ └── Workspace.tsx # 对话工作台
|
||||
├── components/
|
||||
� ├── AgentPipeline/
|
||||
â”? â”? ├── index.tsx # 5阶段æµ<EFBFBD>水线容å™?
|
||||
â”? â”? ├── StageColumn.tsx # å<EFBFBD>•阶段列
|
||||
â”? â”? └── AgentCard.tsx # 智能体å<EFBFBD>¡ç‰?
|
||||
� ├── IntentSearch/
|
||||
â”? â”? ├── index.tsx # æ„<EFBFBD>图æ<EFBFBD>œç´¢æ¡?
|
||||
� � └── SuggestionDropdown.tsx # 建议下拉�
|
||||
� ├── ConversationList/
|
||||
â”? â”? ├── index.tsx # 历å<EFBFBD>²ä¼šè¯<EFBFBD>列表
|
||||
â”? â”? └── ConversationItem.tsx # 会è¯<EFBFBD>é¡?
|
||||
� ├── MessageList/
|
||||
â”? â”? ├── index.tsx # 消æ<EFBFBD>¯åˆ—表
|
||||
â”? â”? ├── UserMessage.tsx # 用户消æ<EFBFBD>¯
|
||||
â”? â”? ├── AssistantMessage.tsx # AI回å¤<EFBFBD>
|
||||
â”? â”? └── ThinkingBlock.tsx # 深度æ€<EFBFBD>考折å<EFBFBD> å<EFBFBD>—
|
||||
� ├── Attachment/
|
||||
â”? â”? ├── AttachmentUpload.tsx # 附件上ä¼
|
||||
â”? â”? ├── AttachmentCard.tsx # 附件å<EFBFBD>¡ç‰‡
|
||||
� � └── AttachmentPreview.tsx # 附件预览
|
||||
� ├── SlashCommands/
|
||||
â”? â”? └── index.tsx # å¿«æ<EFBFBD>·æŒ‡ä»¤è<EFBFBD>œå<EFBFBD>•
|
||||
� └── ActionBar/
|
||||
â”? └── index.tsx # 结果æ“<EFBFBD>作æ ?
|
||||
│ ├── AgentPipeline/
|
||||
│ │ ├── index.tsx # 5阶段流水线容器
|
||||
│ │ ├── StageColumn.tsx # 单阶段列
|
||||
│ │ └── AgentCard.tsx # 智能体卡片
|
||||
│ ├── IntentSearch/
|
||||
│ │ ├── index.tsx # 意图搜索框
|
||||
│ │ └── SuggestionDropdown.tsx # 建议下拉框
|
||||
│ ├── ConversationList/
|
||||
│ │ ├── index.tsx # 历史会话列表
|
||||
│ │ └── ConversationItem.tsx # 会话项
|
||||
│ ├── MessageList/
|
||||
│ │ ├── index.tsx # 消息列表
|
||||
│ │ ├── UserMessage.tsx # 用户消息
|
||||
│ │ ├── AssistantMessage.tsx # AI回复
|
||||
│ │ └── ThinkingBlock.tsx # 深度思考折叠块
|
||||
│ ├── Attachment/
|
||||
│ │ ├── AttachmentUpload.tsx # 附件上传
|
||||
│ │ ├── AttachmentCard.tsx # 附件卡片
|
||||
│ │ └── AttachmentPreview.tsx # 附件预览
|
||||
│ ├── SlashCommands/
|
||||
│ │ └── index.tsx # 快捷指令菜单
|
||||
│ └── ActionBar/
|
||||
│ └── index.tsx # 结果操作栏
|
||||
├── hooks/
|
||||
â”? ├── useConversation.ts # 对è¯<EFBFBD>管ç<EFBFBD>†
|
||||
â”? ├── useAgents.ts # 智能体数æ<EFBFBD>?
|
||||
â”? ├── useIntentRouter.ts # æ„<EFBFBD>图路由
|
||||
â”? ├── useStreamMessage.ts # æµ<EFBFBD>å¼<EFBFBD>消æ<EFBFBD>¯
|
||||
â”? └── useAttachment.ts # 附件上ä¼
|
||||
│ ├── useConversation.ts # 对话管理
|
||||
│ ├── useAgents.ts # 智能体数据
|
||||
│ ├── useIntentRouter.ts # 意图路由
|
||||
│ ├── useStreamMessage.ts # 流式消息
|
||||
│ └── useAttachment.ts # 附件上传
|
||||
├── api/
|
||||
â”? └── index.ts # API å°<EFBFBD>装
|
||||
│ └── index.ts # API 封装
|
||||
├── types/
|
||||
� └── index.ts # TypeScript 类型
|
||||
│ └── index.ts # TypeScript 类型
|
||||
├── styles/
|
||||
â”? ├── dashboard.module.css # Dashboard æ ·å¼<EFBFBD>
|
||||
â”? └── workspace.module.css # Workspace æ ·å¼<EFBFBD>
|
||||
│ ├── dashboard.module.css # Dashboard 样式
|
||||
│ └── workspace.module.css # Workspace 样式
|
||||
└── index.tsx # 模块入口 + 路由
|
||||
```
|
||||
|
||||
@@ -68,7 +68,7 @@ frontend-v2/src/modules/aia/
|
||||
--stage-write: #F59E0B; /* 橙色 - 论文撰写 */
|
||||
--stage-publish: #EF4444; /* 红色 - 成果发布 */
|
||||
|
||||
/* 功能�*/
|
||||
/* 功能色 */
|
||||
--ai-assistant: #6366F1; /* AI助手主色 */
|
||||
--thinking-bg: #F3F4F6; /* 思考块背景 */
|
||||
--thinking-border: #E5E7EB; /* 思考块边框 */
|
||||
@@ -108,7 +108,7 @@ frontend-v2/src/modules/aia/
|
||||
|
||||
## 📄 页面设计
|
||||
|
||||
### 1. Dashboard(智能体大厅�
|
||||
### 1. Dashboard(智能体大厅)
|
||||
|
||||
```tsx
|
||||
// pages/Dashboard.tsx
|
||||
@@ -119,7 +119,7 @@ import { AgentPipeline } from '../components/AgentPipeline';
|
||||
export const Dashboard: React.FC = () => {
|
||||
return (
|
||||
<div className={styles.dashboard}>
|
||||
{/* 顶部æ„<EFBFBD>图æ<EFBFBD>œç´¢æ¡?*/}
|
||||
{/* 顶部意图搜索框 */}
|
||||
<header className={styles.header}>
|
||||
<h1>AI 智能助手</h1>
|
||||
<p>有什么可以帮助您的?</p>
|
||||
@@ -135,14 +135,14 @@ export const Dashboard: React.FC = () => {
|
||||
};
|
||||
```
|
||||
|
||||
**布局特点**�
|
||||
**布局特点**:
|
||||
- 顶部居中大搜索框
|
||||
- 5阶段æµ<EFBFBD>水线横å<EFBFBD>‘平铺(桌é<EFBFBD>¢ï¼? 纵å<C2B5>‘滚动(移动)
|
||||
- Gemini é£Žæ ¼å¤§ç•™ç™?
|
||||
- 5阶段流水线横向平铺(桌面)/ 纵向滚动(移动)
|
||||
- Gemini 风格大留白
|
||||
|
||||
---
|
||||
|
||||
### 2. Workspace(对è¯<EFBFBD>工作å<EFBFBD>°ï¼?
|
||||
### 2. Workspace(对话工作台)
|
||||
|
||||
```tsx
|
||||
// pages/Workspace.tsx
|
||||
@@ -201,7 +201,7 @@ export const Workspace: React.FC = () => {
|
||||
|
||||
## 🧩 组件详细设计
|
||||
|
||||
### 1. AgentPipelineï¼?阶段æµ<C3A6>水线)
|
||||
### 1. AgentPipeline(5阶段流水线)
|
||||
|
||||
```tsx
|
||||
// components/AgentPipeline/index.tsx
|
||||
@@ -238,7 +238,7 @@ export const AgentPipeline: React.FC<AgentPipelineProps> = ({ onAgentClick }) =>
|
||||
};
|
||||
```
|
||||
|
||||
**æ ·å¼<EFBFBD>特点**ï¼?
|
||||
**样式特点**:
|
||||
```css
|
||||
.pipeline {
|
||||
display: flex;
|
||||
@@ -258,7 +258,7 @@ export const AgentPipeline: React.FC<AgentPipelineProps> = ({ onAgentClick }) =>
|
||||
|
||||
---
|
||||
|
||||
### 2. IntentSearch(æ„<EFBFBD>图æ<EFBFBD>œç´¢æ¡†ï¼?
|
||||
### 2. IntentSearch(意图搜索框)
|
||||
|
||||
```tsx
|
||||
// components/IntentSearch/index.tsx
|
||||
@@ -307,14 +307,14 @@ export const IntentSearch: React.FC = () => {
|
||||
|
||||
---
|
||||
|
||||
### 3. ThinkingBlock(深度æ€<EFBFBD>考折å<EFBFBD> å<EFBFBD>—ï¼?
|
||||
### 3. ThinkingBlock(深度思考折叠块)
|
||||
|
||||
```tsx
|
||||
// components/MessageList/ThinkingBlock.tsx
|
||||
|
||||
interface ThinkingBlockProps {
|
||||
content: string;
|
||||
duration?: number; // æ€<EFBFBD>考耗时(秒ï¼?
|
||||
duration?: number; // 思考耗时(秒)
|
||||
isStreaming?: boolean; // 是否正在生成
|
||||
}
|
||||
|
||||
@@ -328,7 +328,7 @@ export const ThinkingBlock: React.FC<ThinkingBlockProps> = ({
|
||||
|
||||
useEffect(() => {
|
||||
if (!isStreaming && expanded) {
|
||||
// 完æˆ<EFBFBD>å<EFBFBD>?1.5s 自动收起
|
||||
// 完成后 1.5s 自动收起
|
||||
const timer = setTimeout(() => setExpanded(false), 1500);
|
||||
return () => clearTimeout(timer);
|
||||
}
|
||||
@@ -344,7 +344,7 @@ export const ThinkingBlock: React.FC<ThinkingBlockProps> = ({
|
||||
{isStreaming ? <LoadingOutlined spin /> : <BulbOutlined />}
|
||||
</span>
|
||||
<span className={styles.title}>
|
||||
{isStreaming ? 'æ£åœ¨æ·±åº¦æ€<EFBFBD>è€?..' : `已深度æ€<EFBFBD>è€?(耗时 ${duration?.toFixed(1)}s)`}
|
||||
{isStreaming ? '正在深度思考...' : `已深度思考 (耗时 ${duration?.toFixed(1)}s)`}
|
||||
</span>
|
||||
<span className={styles.expandIcon}>
|
||||
{expanded ? <UpOutlined /> : <DownOutlined />}
|
||||
@@ -363,7 +363,7 @@ export const ThinkingBlock: React.FC<ThinkingBlockProps> = ({
|
||||
};
|
||||
```
|
||||
|
||||
**æ ·å¼<EFBFBD>**ï¼?
|
||||
**样式**:
|
||||
```css
|
||||
.thinkingBlock {
|
||||
background: var(--thinking-bg);
|
||||
@@ -412,18 +412,18 @@ export const AttachmentUpload: React.FC<AttachmentUploadProps> = ({
|
||||
|
||||
const handleUpload = async (file: File) => {
|
||||
if (attachments.length >= maxCount) {
|
||||
message.error(`最多上�${maxCount} 个附件`);
|
||||
message.error(`最多上传 ${maxCount} 个附件`);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 文件类型校验
|
||||
const allowedTypes = ['application/pdf', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'text/plain', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'];
|
||||
if (!allowedTypes.includes(file.type)) {
|
||||
message.error('仅支æŒ?PDFã€<C3A3>Wordã€<C3A3>TXTã€<C3A3>Excel 文件');
|
||||
message.error('仅支持 PDF、Word、TXT、Excel 文件');
|
||||
return false;
|
||||
}
|
||||
|
||||
// 文件大å°<EFBFBD>æ ¡éªŒï¼?0MBï¼?
|
||||
// 文件大小校验(20MB)
|
||||
if (file.size > 20 * 1024 * 1024) {
|
||||
message.error('文件大小不能超过 20MB');
|
||||
return false;
|
||||
@@ -448,7 +448,7 @@ export const AttachmentUpload: React.FC<AttachmentUploadProps> = ({
|
||||
</Button>
|
||||
</Upload>
|
||||
|
||||
{/* å·²ä¸Šä¼ é™„ä»¶åˆ—è¡?*/}
|
||||
{/* 已上传附件列表 */}
|
||||
<div className={styles.attachmentList}>
|
||||
{attachments.map(att => (
|
||||
<AttachmentCard
|
||||
@@ -483,10 +483,10 @@ interface SlashCommandsProps {
|
||||
}
|
||||
|
||||
const commands: SlashCommand[] = [
|
||||
{ key: 'polish', icon: '�, label: '润色', description: '优化文本表达' },
|
||||
{ key: 'polish', icon: '✨', label: '润色', description: '优化文本表达' },
|
||||
{ key: 'expand', icon: '📝', label: '扩写', description: '扩展内容细节' },
|
||||
{ key: 'translate', icon: '🌐', label: '翻译', description: '中英互译' },
|
||||
{ key: 'export', icon: '📄', label: '导出Word', description: '导出�Word 文档' },
|
||||
{ key: 'export', icon: '📄', label: '导出Word', description: '导出为 Word 文档' },
|
||||
];
|
||||
|
||||
export const SlashCommands: React.FC<SlashCommandsProps> = ({
|
||||
@@ -544,7 +544,7 @@ export const SlashCommands: React.FC<SlashCommandsProps> = ({
|
||||
|
||||
---
|
||||
|
||||
### 6. ActionBar(结果æ“<EFBFBD>作æ <EFBFBD>ï¼?
|
||||
### 6. ActionBar(结果操作栏)
|
||||
|
||||
```tsx
|
||||
// components/ActionBar/index.tsx
|
||||
@@ -623,7 +623,7 @@ export function useConversation(conversationId?: string): UseConversationReturn
|
||||
}
|
||||
}, [conversationId]);
|
||||
|
||||
// å<EFBFBD>‘é€<EFBFBD>消æ<EFBFBD>¯ï¼ˆæµ<EFBFBD>å¼<EFBFBD>ï¼?
|
||||
// 发送消息(流式)
|
||||
const sendMessage = async (content: string, attachmentIds?: string[]) => {
|
||||
setIsLoading(true);
|
||||
|
||||
@@ -637,7 +637,7 @@ export function useConversation(conversationId?: string): UseConversationReturn
|
||||
};
|
||||
setMessages(prev => [...prev, userMessage]);
|
||||
|
||||
// åˆ<EFBFBD>å§‹åŒ?AI 消æ<CB86>¯
|
||||
// 初始化 AI 消息
|
||||
const aiMessage: Message = {
|
||||
id: `temp-ai-${Date.now()}`,
|
||||
role: 'assistant',
|
||||
@@ -769,7 +769,7 @@ export function useStreamMessage() {
|
||||
|
||||
---
|
||||
|
||||
## 📱 å“<C3A5>应å¼<C3A5>设è®?
|
||||
## 📱 响应式设计
|
||||
|
||||
### 断点策略
|
||||
|
||||
@@ -783,19 +783,19 @@ const breakpoints = {
|
||||
};
|
||||
```
|
||||
|
||||
### Dashboard å“<EFBFBD>应å¼?
|
||||
### Dashboard 响应式
|
||||
|
||||
| 断点 | 布局 |
|
||||
|------|------|
|
||||
| `< 768px` | 流水线纵向滚动,卡片单列 |
|
||||
| `â‰?768px` | æµ<EFBFBD>水线横å<EFBFBD>?5 åˆ?|
|
||||
| `≥ 768px` | 流水线横向 5 列 |
|
||||
|
||||
### Workspace å“<EFBFBD>应å¼?
|
||||
### Workspace 响应式
|
||||
|
||||
| 断点 | 布局 |
|
||||
|------|------|
|
||||
| `< 768px` | 侧边栏隐藏(抽屉滑出),输入框键盘适配 |
|
||||
| `â‰?768px` | ä¾§è¾¹æ <EFBFBD>固定显ç¤?240px |
|
||||
| `≥ 768px` | 侧边栏固定显示 240px |
|
||||
|
||||
---
|
||||
|
||||
@@ -883,4 +883,3 @@ export interface SlashCommand {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user