修复弹窗边距问题
This commit is contained in:
parent
d4e4beb3a7
commit
9900602244
@ -1,159 +0,0 @@
|
|||||||
你是一名高级前端开发人员,是ReactJS, NextJS, JavaScript, TypeScript, HTML, CSS和现代UI/UX框架(例如,TailwindCSS, Shadcn, Radix)的专家。你深思熟虑,给出细致入微的答案,并且善于推理。你细心地提供准确、真实、深思熟虑的答案,是推理的天才。
|
|
||||||
# 严格遵循的要求
|
|
||||||
- 不要随意删除代码,请先详细浏览下现在所有的代码,再进行询问修改,得到确认再修改。重点切记
|
|
||||||
- 首先,一步一步地思考——详细描述你在伪代码中构建什么的计划。
|
|
||||||
- 确认,然后写代码!
|
|
||||||
- 始终编写正确、最佳实践、DRY原则(不要重复自己)、无错误、功能齐全且可工作的代码,还应与下面代码实施指南中列出的规则保持一致。
|
|
||||||
- 专注于简单易读的代码,而不是高性能。
|
|
||||||
- 完全实现所有要求的功能。
|
|
||||||
- 不要留下待办事项、占位符或缺失的部分。
|
|
||||||
- 确保代码完整!彻底确认。
|
|
||||||
- 包括所有必需的导入的包,并确保关键组件的正确命名。
|
|
||||||
- 如果你认为可能没有正确答案,你就说出来。
|
|
||||||
- 如果你不知道答案,就说出来,而不是猜测。
|
|
||||||
- 可以提出合理化的建议,但是需要等待是否可以。
|
|
||||||
- 对于新设计的实体类、字段、方法都要写注释,对于实际的逻辑要有逻辑注释。
|
|
||||||
- 不要随意修改现在的接口调用路径,如果不知道接口是什么,可以问。
|
|
||||||
#代码实现指南
|
|
||||||
在编写代码时遵循以下规则:
|
|
||||||
- 尽可能使用早期返回,使代码更具可读性。
|
|
||||||
- 总是使用顺风类样式HTML元素;避免使用CSS或标签。
|
|
||||||
- 尽可能在类标记中使用“ class: ”而不是第三操作符。
|
|
||||||
- 使用描述性变量名和函数/const名。此外,事件函数应该以“handle”前缀命名,就像onClick的“handleClick”和onKeyDown的“handleKeyDown”。
|
|
||||||
- 在元素上实现可访问性特性。例如,一个标签应该有tabindex= " 0 "、aria-label、on:click和on:keydown以及类似的属性。
|
|
||||||
— 使用const代替函数,例如:const toggle =() =>。另外,如果可能的话,定义一个类型。
|
|
||||||
|
|
||||||
# Deploy Ease Platform 前端开发规范
|
|
||||||
|
|
||||||
## 1. 项目结构规范
|
|
||||||
|
|
||||||
```
|
|
||||||
src/
|
|
||||||
├── components/ # 公共组件
|
|
||||||
├── pages/ # 页面组件
|
|
||||||
│ └── System/ # 系统管理模块
|
|
||||||
├── layouts/ # 布局组件
|
|
||||||
├── router/index.ts # 路由配置
|
|
||||||
├── store/ # 状态管理
|
|
||||||
├── services/ # API 服务
|
|
||||||
├── utils/ # 工具函数
|
|
||||||
├── hooks/ # 自定义 Hooks
|
|
||||||
└── types/ # TS 类型定义
|
|
||||||
│ └── pages.ts # 分页基础类
|
|
||||||
|
|
||||||
ModuleName/ # 模块结构
|
|
||||||
├── components/ # 模块私有组件
|
|
||||||
├── type.ts # 类型定义
|
|
||||||
├── service.ts # API 服务
|
|
||||||
└── index.tsx # 模块入口
|
|
||||||
```
|
|
||||||
|
|
||||||
## 2. 命名与类型规范
|
|
||||||
|
|
||||||
1. 文件命名:
|
|
||||||
- 组件:PascalCase(`UserProfile.tsx`)
|
|
||||||
- 工具函数:camelCase(`formatDate.ts`)
|
|
||||||
- 样式:组件同名(`UserProfile.module.css`)
|
|
||||||
- Redux:`xxxSlice.ts`
|
|
||||||
- 类型:`types.ts`
|
|
||||||
- 服务:`service.ts`
|
|
||||||
|
|
||||||
2. 变量命名:
|
|
||||||
- 常量:UPPER_SNAKE_CASE
|
|
||||||
- 变量:camelCase
|
|
||||||
- 接口:以 `I` 开头,PascalCase
|
|
||||||
- 类型:以 `T` 开头,PascalCase
|
|
||||||
- 事件处理:`handle` 前缀
|
|
||||||
- 异步函数:动词开头(`fetchData`)
|
|
||||||
|
|
||||||
3. 已定义了基础类型src\types\base下直接继承即可,把Response query request 的定义都模块的types.ts文件中:
|
|
||||||
|
|
||||||
## 3. 服务层规范
|
|
||||||
|
|
||||||
1. 方法定义:
|
|
||||||
```typescript
|
|
||||||
// 推荐写法 - 使用 request 工具
|
|
||||||
export const resetPassword = (id: number, password: string) =>
|
|
||||||
request.post<void>(`/api/v1/users/${id}/reset-password`, { password });
|
|
||||||
|
|
||||||
// 标准 CRUD 接口
|
|
||||||
export const getList = (params?: Query) =>
|
|
||||||
request.get<Page<Response>>('/api/v1/xxx/page', { params }); // 列表接口统一使用 /page 后缀
|
|
||||||
|
|
||||||
export const create = (data: Request) =>
|
|
||||||
request.post<Response>('/api/v1/xxx', data);
|
|
||||||
|
|
||||||
export const update = (id: number, data: Request) =>
|
|
||||||
request.put<Response>(`/api/v1/xxx/${id}`, data);
|
|
||||||
|
|
||||||
export const remove = (id: number) =>
|
|
||||||
request.delete(`/api/v1/xxx/${id}`);
|
|
||||||
|
|
||||||
export const batchRemove = (ids: number[]) =>
|
|
||||||
request.post('/api/v1/xxx/batch-delete', { ids });
|
|
||||||
|
|
||||||
export const exportData = (params?: Query) =>
|
|
||||||
request.download('/api/v1/xxx/export', undefined, { params });
|
|
||||||
```
|
|
||||||
|
|
||||||
2. 规范要点:
|
|
||||||
- 统一使用 src\utils\request.ts
|
|
||||||
- 使用泛型指定响应数据类型
|
|
||||||
- 错误处理在拦截器中统一处理
|
|
||||||
- 使用模板字符串拼接路径
|
|
||||||
- API 路径使用 `/api/v1/` 前缀
|
|
||||||
- 资源使用复数形式(users, roles)
|
|
||||||
- 特殊操作使用动词(export, import)
|
|
||||||
|
|
||||||
3. 列表数据获取规范:
|
|
||||||
- API 路径必须以 `/page` 结尾,例如:`/api/v1/workflow-definitions/page`
|
|
||||||
- 服务层方法定义示例:
|
|
||||||
```typescript
|
|
||||||
export const getDefinitions = (params?: WorkflowDefinitionQuery) =>
|
|
||||||
request.get<Page<WorkflowDefinitionResponse>>(`${DEFINITION_URL}/page`, { params });
|
|
||||||
```
|
|
||||||
- 组件中获取列表数据:
|
|
||||||
```typescript
|
|
||||||
const response = await getDefinitions();
|
|
||||||
if (response) {
|
|
||||||
setList(response.content); // 使用 response.content 获取列表数据
|
|
||||||
}
|
|
||||||
```
|
|
||||||
- 分页数据结构统一使用 `Page<T>` 类型
|
|
||||||
- 列表数据必须位于返回结果的 `content` 字段中
|
|
||||||
|
|
||||||
## 4. 类型处理规则:
|
|
||||||
- request 工具的泛型参数 T 表示业务数据类型
|
|
||||||
- 响应拦截器负责从 Response<T> 中提取 data
|
|
||||||
- 服务方法直接使用业务数据类型作为泛型参数
|
|
||||||
- 组件中可以直接使用返回的业务数据
|
|
||||||
- TypeScript 类型系统能正确推断类型
|
|
||||||
|
|
||||||
## 5. React 开发规范
|
|
||||||
|
|
||||||
1. 组件开发:
|
|
||||||
- 使用函数组件和箭头函数
|
|
||||||
- Props 类型必须定义
|
|
||||||
- 必须提供默认值
|
|
||||||
- 使用 memo 优化
|
|
||||||
- 复杂组件需拆分
|
|
||||||
|
|
||||||
2. Hooks 使用:
|
|
||||||
- 自定义 hooks 以 `use` 开头
|
|
||||||
- 使用 TypeScript 泛型
|
|
||||||
- 使用 useCallback 和 useMemo
|
|
||||||
- 使用 Promise.all 处理并行请求
|
|
||||||
|
|
||||||
3. 状态管理:
|
|
||||||
- 使用 Redux Toolkit
|
|
||||||
- 持久化数据存储在 localStorage
|
|
||||||
- Token、用户信息、菜单统一管理
|
|
||||||
|
|
||||||
## 6. 样式与布局规范
|
|
||||||
|
|
||||||
1. CSS 规范:
|
|
||||||
- 使用 CSS Modules
|
|
||||||
- 类名使用 kebab-case
|
|
||||||
- 避免内联样式
|
|
||||||
- 响应式适配
|
|
||||||
- 支持暗色主题
|
|
||||||
@ -1,159 +0,0 @@
|
|||||||
你是一名高级前端开发人员,是ReactJS, NextJS, JavaScript, TypeScript, HTML, CSS和现代UI/UX框架(例如,TailwindCSS, Shadcn, Radix)的专家。你深思熟虑,给出细致入微的答案,并且善于推理。你细心地提供准确、真实、深思熟虑的答案,是推理的天才。
|
|
||||||
# 严格遵循的要求
|
|
||||||
- 不要随意删除代码,请先详细浏览下现在所有的代码,再进行询问修改,得到确认再修改。重点切记
|
|
||||||
- 首先,一步一步地思考——详细描述你在伪代码中构建什么的计划。
|
|
||||||
- 确认,然后写代码!
|
|
||||||
- 始终编写正确、最佳实践、DRY原则(不要重复自己)、无错误、功能齐全且可工作的代码,还应与下面代码实施指南中列出的规则保持一致。
|
|
||||||
- 专注于简单易读的代码,而不是高性能。
|
|
||||||
- 完全实现所有要求的功能。
|
|
||||||
- 不要留下待办事项、占位符或缺失的部分。
|
|
||||||
- 确保代码完整!彻底确认。
|
|
||||||
- 包括所有必需的导入的包,并确保关键组件的正确命名。
|
|
||||||
- 如果你认为可能没有正确答案,你就说出来。
|
|
||||||
- 如果你不知道答案,就说出来,而不是猜测。
|
|
||||||
- 可以提出合理化的建议,但是需要等待是否可以。
|
|
||||||
- 对于新设计的实体类、字段、方法都要写注释,对于实际的逻辑要有逻辑注释。
|
|
||||||
- 不要随意修改现在的接口调用路径,如果不知道接口是什么,可以问。
|
|
||||||
#代码实现指南
|
|
||||||
在编写代码时遵循以下规则:
|
|
||||||
- 尽可能使用早期返回,使代码更具可读性。
|
|
||||||
- 总是使用顺风类样式HTML元素;避免使用CSS或标签。
|
|
||||||
- 尽可能在类标记中使用“ class: ”而不是第三操作符。
|
|
||||||
- 使用描述性变量名和函数/const名。此外,事件函数应该以“handle”前缀命名,就像onClick的“handleClick”和onKeyDown的“handleKeyDown”。
|
|
||||||
- 在元素上实现可访问性特性。例如,一个标签应该有tabindex= " 0 "、aria-label、on:click和on:keydown以及类似的属性。
|
|
||||||
— 使用const代替函数,例如:const toggle =() =>。另外,如果可能的话,定义一个类型。
|
|
||||||
|
|
||||||
# Deploy Ease Platform 前端开发规范
|
|
||||||
|
|
||||||
## 1. 项目结构规范
|
|
||||||
|
|
||||||
```
|
|
||||||
src/
|
|
||||||
├── components/ # 公共组件
|
|
||||||
├── pages/ # 页面组件
|
|
||||||
│ └── System/ # 系统管理模块
|
|
||||||
├── layouts/ # 布局组件
|
|
||||||
├── router/index.ts # 路由配置
|
|
||||||
├── store/ # 状态管理
|
|
||||||
├── services/ # API 服务
|
|
||||||
├── utils/ # 工具函数
|
|
||||||
├── hooks/ # 自定义 Hooks
|
|
||||||
└── types/ # TS 类型定义
|
|
||||||
│ └── pages.ts # 分页基础类
|
|
||||||
|
|
||||||
ModuleName/ # 模块结构
|
|
||||||
├── components/ # 模块私有组件
|
|
||||||
├── type.ts # 类型定义
|
|
||||||
├── service.ts # API 服务
|
|
||||||
└── index.tsx # 模块入口
|
|
||||||
```
|
|
||||||
|
|
||||||
## 2. 命名与类型规范
|
|
||||||
|
|
||||||
1. 文件命名:
|
|
||||||
- 组件:PascalCase(`UserProfile.tsx`)
|
|
||||||
- 工具函数:camelCase(`formatDate.ts`)
|
|
||||||
- 样式:组件同名(`UserProfile.module.css`)
|
|
||||||
- Redux:`xxxSlice.ts`
|
|
||||||
- 类型:`types.ts`
|
|
||||||
- 服务:`service.ts`
|
|
||||||
|
|
||||||
2. 变量命名:
|
|
||||||
- 常量:UPPER_SNAKE_CASE
|
|
||||||
- 变量:camelCase
|
|
||||||
- 接口:以 `I` 开头,PascalCase
|
|
||||||
- 类型:以 `T` 开头,PascalCase
|
|
||||||
- 事件处理:`handle` 前缀
|
|
||||||
- 异步函数:动词开头(`fetchData`)
|
|
||||||
|
|
||||||
3. 要使用和继承定义了基础类型src\types\base.ts
|
|
||||||
|
|
||||||
## 3. 服务层规范
|
|
||||||
|
|
||||||
1. 方法定义:
|
|
||||||
```typescript
|
|
||||||
// 推荐写法 - 使用 request 工具
|
|
||||||
export const resetPassword = (id: number, password: string) =>
|
|
||||||
request.post<void>(`/api/v1/users/${id}/reset-password`, { password });
|
|
||||||
|
|
||||||
// 标准 CRUD 接口
|
|
||||||
export const getList = (params?: Query) =>
|
|
||||||
request.get<Page<Response>>('/api/v1/xxx/page', { params }); // 列表接口统一使用 /page 后缀
|
|
||||||
|
|
||||||
export const create = (data: Request) =>
|
|
||||||
request.post<Response>('/api/v1/xxx', data);
|
|
||||||
|
|
||||||
export const update = (id: number, data: Request) =>
|
|
||||||
request.put<Response>(`/api/v1/xxx/${id}`, data);
|
|
||||||
|
|
||||||
export const remove = (id: number) =>
|
|
||||||
request.delete(`/api/v1/xxx/${id}`);
|
|
||||||
|
|
||||||
export const batchRemove = (ids: number[]) =>
|
|
||||||
request.post('/api/v1/xxx/batch-delete', { ids });
|
|
||||||
|
|
||||||
export const exportData = (params?: Query) =>
|
|
||||||
request.download('/api/v1/xxx/export', undefined, { params });
|
|
||||||
```
|
|
||||||
|
|
||||||
2. 规范要点:
|
|
||||||
- 统一使用 src\utils\request.ts
|
|
||||||
- 使用泛型指定响应数据类型
|
|
||||||
- 错误处理在拦截器中统一处理
|
|
||||||
- 使用模板字符串拼接路径
|
|
||||||
- API 路径使用 `/api/v1/` 前缀
|
|
||||||
- 资源使用复数形式(users, roles)
|
|
||||||
- 特殊操作使用动词(export, import)
|
|
||||||
|
|
||||||
3. 列表数据获取规范:
|
|
||||||
- API 路径必须以 `/page` 结尾,例如:`/api/v1/workflow-definitions/page`
|
|
||||||
- 服务层方法定义示例:
|
|
||||||
```typescript
|
|
||||||
export const getDefinitions = (params?: WorkflowDefinitionQuery) =>
|
|
||||||
request.get<Page<WorkflowDefinitionResponse>>(`${DEFINITION_URL}/page`, { params });
|
|
||||||
```
|
|
||||||
- 组件中获取列表数据:
|
|
||||||
```typescript
|
|
||||||
const response = await getDefinitions();
|
|
||||||
if (response) {
|
|
||||||
setList(response.content); // 使用 response.content 获取列表数据
|
|
||||||
}
|
|
||||||
```
|
|
||||||
- 分页数据结构统一使用 `Page<T>` 类型
|
|
||||||
- 列表数据必须位于返回结果的 `content` 字段中
|
|
||||||
|
|
||||||
## 4. 类型处理规则:
|
|
||||||
- request 工具的泛型参数 T 表示业务数据类型
|
|
||||||
- 响应拦截器负责从 Response<T> 中提取 data
|
|
||||||
- 服务方法直接使用业务数据类型作为泛型参数
|
|
||||||
- 组件中可以直接使用返回的业务数据
|
|
||||||
- TypeScript 类型系统能正确推断类型
|
|
||||||
|
|
||||||
## 5. React 开发规范
|
|
||||||
|
|
||||||
1. 组件开发:
|
|
||||||
- 使用函数组件和箭头函数
|
|
||||||
- Props 类型必须定义
|
|
||||||
- 必须提供默认值
|
|
||||||
- 使用 memo 优化
|
|
||||||
- 复杂组件需拆分
|
|
||||||
|
|
||||||
2. Hooks 使用:
|
|
||||||
- 自定义 hooks 以 `use` 开头
|
|
||||||
- 使用 TypeScript 泛型
|
|
||||||
- 使用 useCallback 和 useMemo
|
|
||||||
- 使用 Promise.all 处理并行请求
|
|
||||||
|
|
||||||
3. 状态管理:
|
|
||||||
- 使用 Redux Toolkit
|
|
||||||
- 持久化数据存储在 localStorage
|
|
||||||
- Token、用户信息、菜单统一管理
|
|
||||||
|
|
||||||
## 6. 样式与布局规范
|
|
||||||
|
|
||||||
1. CSS 规范:
|
|
||||||
- 使用 CSS Modules
|
|
||||||
- 类名使用 kebab-case
|
|
||||||
- 避免内联样式
|
|
||||||
- 响应式适配
|
|
||||||
- 支持暗色主题
|
|
||||||
659
frontend/api.md
659
frontend/api.md
@ -1,659 +0,0 @@
|
|||||||
# API 接口文档
|
|
||||||
|
|
||||||
## 1. 通用说明
|
|
||||||
|
|
||||||
### 1.1 接口规范
|
|
||||||
|
|
||||||
- 基础路径: `/api/v1`
|
|
||||||
- 请求方式: REST风格
|
|
||||||
- 数据格式: JSON
|
|
||||||
- 字符编码: UTF-8
|
|
||||||
- 时间格式: ISO8601 (YYYY-MM-DDTHH:mm:ss.SSSZ)
|
|
||||||
|
|
||||||
### 1.2 通用响应格式
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"code": 0, // 响应码,0表示成功,非0表示失败
|
|
||||||
"message": "成功", // 响应消息
|
|
||||||
"data": { // 响应数据
|
|
||||||
// 具体数据结构
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 1.3 通用查询参数
|
|
||||||
|
|
||||||
分页查询参数:
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"page": 1, // 页码,从1开始
|
|
||||||
"size": 10, // 每页大小
|
|
||||||
"sort": ["id,desc"], // 排序字段,可选
|
|
||||||
"keyword": "", // 关键字搜索,可选
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 1.4 通用错误码
|
|
||||||
|
|
||||||
| 错误码 | 说明 | 处理建议 |
|
|
||||||
|--------|------|----------|
|
|
||||||
| 0 | 成功 | - |
|
|
||||||
| 1000 | 系统内部错误 | 联系管理员 |
|
|
||||||
| 1001 | 数据库操作失败 | 重试或联系管理员 |
|
|
||||||
| 1002 | 并发操作冲突 | 刷新后重试 |
|
|
||||||
| 2000 | 参数验证失败 | 检查参数 |
|
|
||||||
| 2001 | 数据不存在 | 检查参数 |
|
|
||||||
| 2002 | 数据已存在 | 检查参数 |
|
|
||||||
|
|
||||||
## 2. 工作流定义<E5AE9A><E4B989>口
|
|
||||||
|
|
||||||
### 2.1 创建工作流定义
|
|
||||||
|
|
||||||
**接口说明**:创建新的工作流定义
|
|
||||||
|
|
||||||
**请求路径**:POST /workflow-definitions
|
|
||||||
|
|
||||||
**请求参数**:
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"code": "string", // 工作流编码,必填,唯一
|
|
||||||
"name": "string", // 工作流名称,必填
|
|
||||||
"description": "string", // 描述,可选
|
|
||||||
"nodeConfig": { // 节点配置,必填
|
|
||||||
"nodes": [{
|
|
||||||
"id": "string", // 节点ID
|
|
||||||
"type": "string", // 节点类型
|
|
||||||
"name": "string", // 节点名称
|
|
||||||
"config": { // 节点配置
|
|
||||||
// 具体配置项根据节点类型定义
|
|
||||||
}
|
|
||||||
}]
|
|
||||||
},
|
|
||||||
"transitionConfig": { // 流转配置,必填
|
|
||||||
"transitions": [{
|
|
||||||
"from": "string", // 来源节点ID
|
|
||||||
"to": "string", // 目标节点ID
|
|
||||||
"condition": "string" // 流转条件
|
|
||||||
}]
|
|
||||||
},
|
|
||||||
"formDefinition": { // 表单定义,必填
|
|
||||||
// 表单配置项
|
|
||||||
},
|
|
||||||
"graphDefinition": { // 图形定义,必填
|
|
||||||
// 图形布局配置
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**响应数据**:
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"code": 0,
|
|
||||||
"message": "成功",
|
|
||||||
"data": {
|
|
||||||
"id": "long", // 工作流定义ID
|
|
||||||
"code": "string", // 工作流编码
|
|
||||||
"name": "string", // 工作流名称
|
|
||||||
"description": "string", // 描述
|
|
||||||
"version": 1, // 版本号
|
|
||||||
"status": "DRAFT", // 状态:DRAFT-草稿、PUBLISHED-已发布、DISABLED-已禁用
|
|
||||||
"enabled": true, // 是否启用
|
|
||||||
"nodeConfig": {}, // 节点配置
|
|
||||||
"transitionConfig": {}, // 流转配置
|
|
||||||
"formDefinition": {}, // 表单定义
|
|
||||||
"graphDefinition": {}, // 图形定义
|
|
||||||
"createTime": "string", // 创建时间
|
|
||||||
"updateTime": "string" // 更新时间
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2.2 更新工作流定义
|
|
||||||
|
|
||||||
**接口说明**:更新工作流定义,仅草稿状态可更新
|
|
||||||
|
|
||||||
**请求路径**:PUT /workflow-definitions/{id}
|
|
||||||
|
|
||||||
**路径参数**:
|
|
||||||
- id: 工作流定义ID
|
|
||||||
|
|
||||||
**请求参数**:同创建接口
|
|
||||||
|
|
||||||
**响应数据**:同创建接口
|
|
||||||
|
|
||||||
### 2.3 发布工作流定义
|
|
||||||
|
|
||||||
**接口说明**:发布工作流定义,使其可被使用
|
|
||||||
|
|
||||||
**请求路径**:POST /workflow-definitions/{id}/publish
|
|
||||||
|
|
||||||
**路径参数**:
|
|
||||||
- id: 工作流定义ID
|
|
||||||
|
|
||||||
**响应数据**:同创建接口
|
|
||||||
|
|
||||||
### 2.4 禁用工作流定义
|
|
||||||
|
|
||||||
**接口说明**:禁用工作流定义,禁止新建实例
|
|
||||||
|
|
||||||
**请求路径**:POST /workflow-definitions/{id}/disable
|
|
||||||
|
|
||||||
**路径参数**:
|
|
||||||
- id: 工作流定义ID
|
|
||||||
|
|
||||||
**响应数据**:同创建接口
|
|
||||||
|
|
||||||
### 2.5 启用工作流定义
|
|
||||||
|
|
||||||
**接口说明**:启用已<E794A8><E5B7B2>用的工作流定义
|
|
||||||
|
|
||||||
**请求路径**:POST /workflow-definitions/{id}/enable
|
|
||||||
|
|
||||||
**路径参数**:
|
|
||||||
- id: 工作流定义ID
|
|
||||||
|
|
||||||
**响应数据**:同创建接口
|
|
||||||
|
|
||||||
### 2.6 创建新版本
|
|
||||||
|
|
||||||
**接口说明**:基于现有工作流定义创建新版本
|
|
||||||
|
|
||||||
**请求路径**:POST /workflow-definitions/{id}/versions
|
|
||||||
|
|
||||||
**路径参数**:
|
|
||||||
- id: 工作流定义ID
|
|
||||||
|
|
||||||
**响应数据**:同创建接口
|
|
||||||
|
|
||||||
### 2.7 查询工作流定义列表
|
|
||||||
|
|
||||||
**接口说明**:分页查询工作流定义列表
|
|
||||||
|
|
||||||
**请求路径**:GET /workflow-definitions
|
|
||||||
|
|
||||||
**请求参数**:
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"page": 1, // 页码,从1开始
|
|
||||||
"size": 10, // 每页大小
|
|
||||||
"sort": ["id,desc"], // 排序字段
|
|
||||||
"keyword": "", // 关键字搜索
|
|
||||||
"status": "DRAFT", // 状态过滤
|
|
||||||
"enabled": true // 是否启用
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**响应数据**:
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"code": 0,
|
|
||||||
"message": "成功",
|
|
||||||
"data": {
|
|
||||||
"content": [{ // 列表数据
|
|
||||||
// 工作流定义数据,同创建接口
|
|
||||||
}],
|
|
||||||
"totalElements": 100, // 总记录数
|
|
||||||
"totalPages": 10, // 总页数
|
|
||||||
"size": 10, // 每页大小
|
|
||||||
"number": 1 // 当前页码
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## 3. 工作流实例接口
|
|
||||||
|
|
||||||
### 3.1 创建工作流实例
|
|
||||||
|
|
||||||
**接口说明**:创建工作流实例
|
|
||||||
|
|
||||||
**请求路径**:POST /workflow-instances
|
|
||||||
|
|
||||||
**请求参数**:
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"definitionId": "long", // 工作流定义ID,必填
|
|
||||||
"businessKey": "string", // 业务标识,可选
|
|
||||||
"variables": { // 初始变量,可选
|
|
||||||
"key": "value"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**响应数据**:
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"code": 0,
|
|
||||||
"message": "成功",
|
|
||||||
"data": {
|
|
||||||
"id": "long", // 实例ID
|
|
||||||
"definitionId": "long", // 定义ID
|
|
||||||
"businessKey": "string",// 业务标识
|
|
||||||
"status": "CREATED", // 状态:CREATED-已创建、RUNNING-运行中、SUSPENDED-已暂停、COMPLETED-已完成、TERMINATED-已终止
|
|
||||||
"startTime": "string", // 开始时间
|
|
||||||
"endTime": "string", // 结束时间
|
|
||||||
"variables": {}, // 变量
|
|
||||||
"createTime": "string", // 创建时间
|
|
||||||
"updateTime": "string" // 更新时间
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3.2 启动工作流实例
|
|
||||||
|
|
||||||
**接口说明**:启动工作流实例
|
|
||||||
|
|
||||||
**请求路径**:POST /workflow-instances/{id}/start
|
|
||||||
|
|
||||||
**路径参数**:
|
|
||||||
- id: 实例ID
|
|
||||||
|
|
||||||
**响应数据**:同创建接口
|
|
||||||
|
|
||||||
### 3.3 暂停工作流实例
|
|
||||||
|
|
||||||
**接口说明**:暂停运行中的工作流实例
|
|
||||||
|
|
||||||
**请求路径**:POST /workflow-instances/{id}/suspend
|
|
||||||
|
|
||||||
**路径参数**:
|
|
||||||
- id: 实例ID
|
|
||||||
|
|
||||||
**响应数据**:同创建接口
|
|
||||||
|
|
||||||
### 3.4 恢复工作流实例
|
|
||||||
|
|
||||||
**接口说明**:恢复已暂停的工作流实例
|
|
||||||
|
|
||||||
**请求路径**:POST /workflow-instances/{id}/resume
|
|
||||||
|
|
||||||
**路径参数**:
|
|
||||||
- id: 实例ID
|
|
||||||
|
|
||||||
**响应数据**:同创建接口
|
|
||||||
|
|
||||||
### 3.5 终止工作流实例
|
|
||||||
|
|
||||||
**接口说明**:强制终止工作流实例
|
|
||||||
|
|
||||||
**请求路径**:POST /workflow-instances/{id}/terminate
|
|
||||||
|
|
||||||
**路径参数**:
|
|
||||||
- id: 实例ID
|
|
||||||
|
|
||||||
**响应数据**:同创建接口
|
|
||||||
|
|
||||||
### 3.6 查询工作流实例列表
|
|
||||||
|
|
||||||
**接口说明**:分页查询工作流实例列表
|
|
||||||
|
|
||||||
**请求路径**:GET /workflow-instances
|
|
||||||
|
|
||||||
**请求参数**:
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"page": 1, // 页码,从1开始
|
|
||||||
"size": 10, // 每页大小
|
|
||||||
"sort": ["id,desc"], // 排序字段
|
|
||||||
"keyword": "", // 关键字搜索
|
|
||||||
"status": "RUNNING", // 状态过滤
|
|
||||||
"definitionId": "long", // 定义ID过滤
|
|
||||||
"businessKey": "string" // 业务标识过滤
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**响应数据**:
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"code": 0,
|
|
||||||
"message": "成功",
|
|
||||||
"data": {
|
|
||||||
"content": [{ // 列表数据
|
|
||||||
// 工作流实例数据,同创建接口
|
|
||||||
}],
|
|
||||||
"totalElements": 100, // 总记录数
|
|
||||||
"totalPages": 10, // 总页数
|
|
||||||
"size": 10, // 每页大小
|
|
||||||
"number": 1 // 当前页码
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## 4. 节点实例接口
|
|
||||||
|
|
||||||
### 4.1 查询节点实例列表
|
|
||||||
|
|
||||||
**接口说明**:查询工作流实例的节点列表
|
|
||||||
|
|
||||||
**请求路径**:GET /workflow-instances/{instanceId}/nodes
|
|
||||||
|
|
||||||
**路径参数**:
|
|
||||||
- instanceId: 工作流实例ID
|
|
||||||
|
|
||||||
**请求参数**:
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"status": "RUNNING" // 状态过滤,可选
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**响应数据**:
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"code": 0,
|
|
||||||
"message": "成功",
|
|
||||||
"data": [{
|
|
||||||
"id": "long", // 节点实例ID
|
|
||||||
"workflowInstanceId": "long", // 工作流实例ID
|
|
||||||
"nodeId": "string", // 节点定义ID
|
|
||||||
"nodeName": "string", // 节点名称
|
|
||||||
"nodeType": "string", // 节点类型
|
|
||||||
"status": "string", // 状态:CREATED、RUNNING、COMPLETED、FAILED
|
|
||||||
"startTime": "string", // 开始时间
|
|
||||||
"endTime": "string", // 结束时间
|
|
||||||
"output": "string", // 输出结果
|
|
||||||
"error": "string", // 错误信息
|
|
||||||
"createTime": "string", // 创建时间
|
|
||||||
"updateTime": "string" // 更新时间
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## 5. 工作流变量接口
|
|
||||||
|
|
||||||
### 5.1 设置变量
|
|
||||||
|
|
||||||
**接口说明**:设置工作流实例变量
|
|
||||||
|
|
||||||
**请求路径**:POST /workflow-instances/{instanceId}/variables
|
|
||||||
|
|
||||||
**路径参数**:
|
|
||||||
- instanceId: 工作流实例ID
|
|
||||||
|
|
||||||
**请求参数**:
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"name": "string", // 变量名,必填
|
|
||||||
"value": "object", // 变量值,必<EFBC8C><E5BF85>
|
|
||||||
"scope": "GLOBAL" // 作用域:GLOBAL-全局、NODE-节点,可选,默认GLOBAL
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**响应数据**:
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"code": 0,
|
|
||||||
"message": "成功"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 5.2 获取变量
|
|
||||||
|
|
||||||
**接口说明**:获取工作流实例变量
|
|
||||||
|
|
||||||
**请求路径**:GET /workflow-instances/{instanceId}/variables
|
|
||||||
|
|
||||||
**路径参数**:
|
|
||||||
- instanceId: 工作流实例ID
|
|
||||||
|
|
||||||
**请求参数**:
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"scope": "GLOBAL" // 作用域过滤,可选
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**响应数据**:
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"code": 0,
|
|
||||||
"message": "成功",
|
|
||||||
"data": {
|
|
||||||
"variableName": "variableValue"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## 6. 工作流日志接口
|
|
||||||
|
|
||||||
### 6.1 查询日志列表
|
|
||||||
|
|
||||||
**接口说明**:查询工作流实例日志
|
|
||||||
|
|
||||||
**请求路径**:GET /workflow-instances/{instanceId}/logs
|
|
||||||
|
|
||||||
**路径参数**:
|
|
||||||
- instanceId: 工作流实例ID
|
|
||||||
|
|
||||||
**请求参数**:
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"nodeId": "string", // 节点ID过滤,可选
|
|
||||||
"level": "INFO", // 日志级别过滤:DEBUG、INFO、WARN、ERROR,可选
|
|
||||||
"startTime": "string", // 开始时间过滤,可选
|
|
||||||
"endTime": "string" // 结束时间过滤,可选
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**响应数据**:
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"code": 0,
|
|
||||||
"message": "成功",
|
|
||||||
"data": [{
|
|
||||||
"id": "long", // 日志ID
|
|
||||||
"workflowInstanceId": "long", // 工作流实例ID
|
|
||||||
"nodeId": "string", // 节点ID
|
|
||||||
"level": "string", // 日志级别
|
|
||||||
"content": "string", // 日志内容
|
|
||||||
"detail": "string", // 详细信息
|
|
||||||
"createTime": "string" // 创建时间
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## 7. 节点类型接口
|
|
||||||
|
|
||||||
### 7.1 查询节点类型列表
|
|
||||||
|
|
||||||
**接口说明**:查询可用的节点类型列表
|
|
||||||
|
|
||||||
**请求路径**:GET /node-types
|
|
||||||
|
|
||||||
**请求参数**:
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"enabled": true, // 是否启用过滤,可选
|
|
||||||
"category": "TASK" // 类型分类过滤:TASK、EVENT、GATEWAY,可选
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**响应数据**:
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"code": 0,
|
|
||||||
"message": "成功",
|
|
||||||
"data": [{
|
|
||||||
"id": "long", // 节点类型ID
|
|
||||||
"code": "string", // 节点类型编码
|
|
||||||
"name": "string", // 节点类型名称
|
|
||||||
"category": "string", // 分类
|
|
||||||
"description": "string", // 描述
|
|
||||||
"enabled": true, // 是否启用
|
|
||||||
"icon": "string", // 图标
|
|
||||||
"color": "string", // 颜色
|
|
||||||
"executors": [{ // 执行器列表(仅TASK类型)
|
|
||||||
"code": "string", // 执行器编码
|
|
||||||
"name": "string", // 执行器名称
|
|
||||||
"description": "string", // 描述
|
|
||||||
"configSchema": {} // 配置模式
|
|
||||||
}],
|
|
||||||
"createTime": "string", // 创建时间
|
|
||||||
"updateTime": "string" // 更新时间
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 7.2 获取节点执行器列表
|
|
||||||
|
|
||||||
**接口说明**:获取指定节点类型支持的执行器列表
|
|
||||||
|
|
||||||
**请求路径**:GET /node-types/{code}/executors
|
|
||||||
|
|
||||||
**路径参数**:
|
|
||||||
- code: 节点类型编码
|
|
||||||
|
|
||||||
**响应数据**:
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"code": 0,
|
|
||||||
"message": "成功",
|
|
||||||
"data": [{
|
|
||||||
"code": "string", // 执行器编码
|
|
||||||
"name": "string", // 执行器名称
|
|
||||||
"description": "string", // 描述
|
|
||||||
"configSchema": { // 配置模式
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
// 具体配置项定义
|
|
||||||
},
|
|
||||||
"required": [] // 必填项
|
|
||||||
}
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## 8. 错误码说明
|
|
||||||
|
|
||||||
### 8.1 系统错误 (1xxx)
|
|
||||||
- 1000: 系统内部错误
|
|
||||||
- 1001: 数据库操作失败
|
|
||||||
- 1002: 并发操作冲突
|
|
||||||
|
|
||||||
### 8.2 通用业务错误 (2xxx)
|
|
||||||
- 2000: 参数验证失败
|
|
||||||
- 2001: 数据不存在
|
|
||||||
- 2002: 数据已存在
|
|
||||||
|
|
||||||
### 8.3 工作流定义错误 (3xxx)
|
|
||||||
- 3000: 工作流定义不存在
|
|
||||||
- 3001: 工作流编码已存在
|
|
||||||
- 3002: 工作流定义非草稿状态
|
|
||||||
- 3003: 工作流定义未发布
|
|
||||||
- 3004: 工作流定义已禁用
|
|
||||||
- 3005: 节点配置无效
|
|
||||||
- 3006: 流转配置无效
|
|
||||||
- 3007: 表单配置无效
|
|
||||||
|
|
||||||
### 8.4 工作流实例错误 (4xxx)
|
|
||||||
- 4000: 工作流实例不存在
|
|
||||||
- 4001: 工作流实例状态无效
|
|
||||||
- 4002: 工作流实例已终止
|
|
||||||
- 4003: 节点实例不存在
|
|
||||||
- 4004: 变量类型无效
|
|
||||||
|
|
||||||
## 9. 数据结构说明
|
|
||||||
|
|
||||||
### 9.1 工作流状态
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
enum WorkflowStatus {
|
|
||||||
DRAFT = "DRAFT", // 草稿
|
|
||||||
PUBLISHED = "PUBLISHED", // 已发布
|
|
||||||
DISABLED = "DISABLED" // 已禁用
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 9.2 实例状态
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
enum InstanceStatus {
|
|
||||||
CREATED = "CREATED", // 已创建
|
|
||||||
RUNNING = "RUNNING", // 运行中
|
|
||||||
SUSPENDED = "SUSPENDED", // 已暂停
|
|
||||||
COMPLETED = "COMPLETED", // 已完成
|
|
||||||
TERMINATED = "TERMINATED"// 已终止
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 9.3 节点状态
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
enum NodeStatus {
|
|
||||||
CREATED = "CREATED", // 已创建
|
|
||||||
RUNNING = "RUNNING", // 运行中
|
|
||||||
COMPLETED = "COMPLETED", // 已完成
|
|
||||||
FAILED = "FAILED" // 失败
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 9.4 日志级别
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
enum LogLevel {
|
|
||||||
DEBUG = "DEBUG",
|
|
||||||
INFO = "INFO",
|
|
||||||
WARN = "WARN",
|
|
||||||
ERROR = "ERROR"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 9.5 变量作用域
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
enum VariableScope {
|
|
||||||
GLOBAL = "GLOBAL", // 全局变量
|
|
||||||
NODE = "NODE" // 节点变量
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## 10. 最佳实践
|
|
||||||
|
|
||||||
### 10.1 工作流设计
|
|
||||||
|
|
||||||
1. 工作流定义创建流程:
|
|
||||||
- 创建草稿
|
|
||||||
- 配置节点和流转
|
|
||||||
- 验证配置
|
|
||||||
- 发布使用
|
|
||||||
|
|
||||||
2. 节点类型选择:
|
|
||||||
- 根据业务场景选择合适的节点类型
|
|
||||||
- 配置合适的执行器
|
|
||||||
- 设置必要的变量
|
|
||||||
|
|
||||||
### 10.2 实例管理
|
|
||||||
|
|
||||||
1. 实例生命周期管理:
|
|
||||||
- 创建 -> 启动 -> 运行 -> 完成
|
|
||||||
- 必要时可暂停或终止
|
|
||||||
- 记录关键节点日志
|
|
||||||
|
|
||||||
2. 变量使用:
|
|
||||||
- 合理使用变量作用域
|
|
||||||
- 及时清理无用变量
|
|
||||||
- 注意变量类型匹配
|
|
||||||
|
|
||||||
### 10.3 错误处理
|
|
||||||
|
|
||||||
1. 异常处理:
|
|
||||||
- 捕获所有可能的错误码
|
|
||||||
- 提供友好的错误提示
|
|
||||||
- 记录详细的错误日志
|
|
||||||
|
|
||||||
2. 重试机制:
|
|
||||||
- 对非致命错误进行重试
|
|
||||||
- 设置合理的重试间隔
|
|
||||||
- 限制最大重试次数
|
|
||||||
|
|
||||||
### 10.4 性能优化
|
|
||||||
|
|
||||||
1. 查询优化:
|
|
||||||
- 合理使用分页
|
|
||||||
- 避免大量数据查询
|
|
||||||
- 使用合适的查询条件
|
|
||||||
|
|
||||||
2. 缓存使用:
|
|
||||||
- 缓存常用数据
|
|
||||||
- 及时更新缓存
|
|
||||||
- 避免缓存穿透
|
|
||||||
@ -196,8 +196,8 @@ const BasicLayout: React.FC = () => {
|
|||||||
</Space>
|
</Space>
|
||||||
<Dropdown menu={{ items: userMenuItems }} trigger={['hover']}>
|
<Dropdown menu={{ items: userMenuItems }} trigger={['hover']}>
|
||||||
<Space className="cursor-pointer">
|
<Space className="cursor-pointer">
|
||||||
<UserOutlined />
|
<UserOutlined className="text-sm" />
|
||||||
<span>{userInfo?.nickname || userInfo?.username}</span>
|
<span className="text-sm">{userInfo?.nickname || userInfo?.username}</span>
|
||||||
</Space>
|
</Space>
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
</Space>
|
</Space>
|
||||||
|
|||||||
@ -34,7 +34,7 @@ const DeleteDialog: React.FC<DeleteDialogProps> = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||||
<DialogContent>
|
<DialogContent className="sm:max-w-[500px]">
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle className="flex items-center gap-2 text-red-600">
|
<DialogTitle className="flex items-center gap-2 text-red-600">
|
||||||
<AlertCircle className="h-5 w-5" /> 确认删除部门?
|
<AlertCircle className="h-5 w-5" /> 确认删除部门?
|
||||||
@ -48,7 +48,7 @@ const DeleteDialog: React.FC<DeleteDialogProps> = ({
|
|||||||
)}
|
)}
|
||||||
</DialogDescription>
|
</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
<div className="space-y-2 text-sm text-muted-foreground">
|
<div className="px-6 py-4 space-y-2 text-sm text-muted-foreground">
|
||||||
<p>
|
<p>
|
||||||
<span className="font-medium">部门编码:</span> <code className="text-sm">{record.code}</code>
|
<span className="font-medium">部门编码:</span> <code className="text-sm">{record.code}</code>
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
@ -181,7 +181,7 @@ const EditDialog: React.FC<EditDialogProps> = ({
|
|||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle>{record ? '编辑部门' : '新增部门'}</DialogTitle>
|
<DialogTitle>{record ? '编辑部门' : '新增部门'}</DialogTitle>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
<div className="grid gap-4 py-4">
|
<div className="grid gap-4 px-6 py-4">
|
||||||
<div className="grid gap-2">
|
<div className="grid gap-2">
|
||||||
<Label htmlFor="parentId">上级部门</Label>
|
<Label htmlFor="parentId">上级部门</Label>
|
||||||
<Select
|
<Select
|
||||||
|
|||||||
@ -46,7 +46,7 @@ const DeleteDialog: React.FC<DeleteDialogProps> = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||||
<DialogContent>
|
<DialogContent className="sm:max-w-[500px]">
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle className="flex items-center gap-2 text-red-600">
|
<DialogTitle className="flex items-center gap-2 text-red-600">
|
||||||
<AlertCircle className="h-5 w-5" /> 确认删除菜单?
|
<AlertCircle className="h-5 w-5" /> 确认删除菜单?
|
||||||
@ -60,7 +60,7 @@ const DeleteDialog: React.FC<DeleteDialogProps> = ({
|
|||||||
)}
|
)}
|
||||||
</DialogDescription>
|
</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
<div className="space-y-2 text-sm text-muted-foreground">
|
<div className="px-6 py-4 space-y-2 text-sm text-muted-foreground">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<span className="font-medium">菜单类型:</span>
|
<span className="font-medium">菜单类型:</span>
|
||||||
<Badge variant={typeInfo.variant}>{typeInfo.text}</Badge>
|
<Badge variant={typeInfo.variant}>{typeInfo.text}</Badge>
|
||||||
|
|||||||
@ -100,7 +100,7 @@ const AssignTagDialog: React.FC<AssignTagDialogProps> = ({
|
|||||||
<TagIcon className="h-5 w-5" /> 分配标签
|
<TagIcon className="h-5 w-5" /> 分配标签
|
||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
<div className="py-4">
|
<div className="px-6 py-4">
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<div className="flex items-center justify-center py-8">
|
<div className="flex items-center justify-center py-8">
|
||||||
<Loader2 className="h-6 w-6 animate-spin mr-2" />
|
<Loader2 className="h-6 w-6 animate-spin mr-2" />
|
||||||
|
|||||||
@ -32,7 +32,7 @@ const DeleteDialog: React.FC<DeleteDialogProps> = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||||
<DialogContent>
|
<DialogContent className="sm:max-w-[450px]">
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle className="flex items-center gap-2 text-red-600">
|
<DialogTitle className="flex items-center gap-2 text-red-600">
|
||||||
<AlertCircle className="h-5 w-5" /> 确认删除角色?
|
<AlertCircle className="h-5 w-5" /> 确认删除角色?
|
||||||
@ -42,7 +42,7 @@ const DeleteDialog: React.FC<DeleteDialogProps> = ({
|
|||||||
此操作不可逆。
|
此操作不可逆。
|
||||||
</DialogDescription>
|
</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
<div className="space-y-2 text-sm text-muted-foreground">
|
<div className="px-6 py-4 space-y-2 text-sm text-muted-foreground">
|
||||||
<p>
|
<p>
|
||||||
<span className="font-medium">角色编码:</span> {record.code}
|
<span className="font-medium">角色编码:</span> {record.code}
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
@ -103,7 +103,7 @@ const EditDialog: React.FC<EditDialogProps> = ({
|
|||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle>{record ? '编辑角色' : '新建角色'}</DialogTitle>
|
<DialogTitle>{record ? '编辑角色' : '新建角色'}</DialogTitle>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
<div className="grid gap-4 py-4">
|
<div className="grid gap-4 px-6 py-4">
|
||||||
<div className="grid gap-2">
|
<div className="grid gap-2">
|
||||||
<Label htmlFor="code">角色编码 *</Label>
|
<Label htmlFor="code">角色编码 *</Label>
|
||||||
<Input
|
<Input
|
||||||
|
|||||||
@ -1,16 +1,16 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState, useMemo } from 'react';
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
DialogHeader,
|
DialogHeader,
|
||||||
DialogTitle,
|
DialogTitle,
|
||||||
DialogFooter,
|
|
||||||
} from '@/components/ui/dialog';
|
} from '@/components/ui/dialog';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Checkbox } from '@/components/ui/checkbox';
|
import { Checkbox } from '@/components/ui/checkbox';
|
||||||
|
import { Input } from '@/components/ui/input';
|
||||||
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@/components/ui/accordion';
|
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@/components/ui/accordion';
|
||||||
import { useToast } from '@/components/ui/use-toast';
|
import { useToast } from '@/components/ui/use-toast';
|
||||||
import { Loader2, KeyRound, Folder, Key } from 'lucide-react';
|
import { Loader2, Folder, Key, Search, Shield } from 'lucide-react';
|
||||||
import { getPermissionTree } from '../service';
|
import { getPermissionTree } from '../service';
|
||||||
|
|
||||||
interface PermissionDialogProps {
|
interface PermissionDialogProps {
|
||||||
@ -52,6 +52,7 @@ const PermissionDialog: React.FC<PermissionDialogProps> = ({
|
|||||||
const [submitting, setSubmitting] = useState(false);
|
const [submitting, setSubmitting] = useState(false);
|
||||||
const [menuTree, setMenuTree] = useState<MenuItem[]>([]);
|
const [menuTree, setMenuTree] = useState<MenuItem[]>([]);
|
||||||
const [checkedPermissionIds, setCheckedPermissionIds] = useState<number[]>([]);
|
const [checkedPermissionIds, setCheckedPermissionIds] = useState<number[]>([]);
|
||||||
|
const [searchText, setSearchText] = useState('');
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const loadPermissionTree = async () => {
|
const loadPermissionTree = async () => {
|
||||||
@ -135,6 +136,45 @@ const PermissionDialog: React.FC<PermissionDialogProps> = ({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 筛选菜单树(根据搜索文本)
|
||||||
|
const filteredMenuTree = useMemo(() => {
|
||||||
|
if (!searchText.trim()) return menuTree;
|
||||||
|
|
||||||
|
const filterMenu = (menu: MenuItem): MenuItem | null => {
|
||||||
|
const matchesName = menu.name.toLowerCase().includes(searchText.toLowerCase());
|
||||||
|
const matchingPermissions = menu.permissions?.filter(p =>
|
||||||
|
p.name.toLowerCase().includes(searchText.toLowerCase())
|
||||||
|
) || [];
|
||||||
|
const matchingChildren = menu.permissionChildren
|
||||||
|
?.map(child => filterMenu(child))
|
||||||
|
.filter(Boolean) as MenuItem[] || [];
|
||||||
|
|
||||||
|
if (matchesName || matchingPermissions.length > 0 || matchingChildren.length > 0) {
|
||||||
|
return {
|
||||||
|
...menu,
|
||||||
|
permissions: matchingPermissions,
|
||||||
|
permissionChildren: matchingChildren
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
return menuTree.map(menu => filterMenu(menu)).filter(Boolean) as MenuItem[];
|
||||||
|
}, [menuTree, searchText]);
|
||||||
|
|
||||||
|
// 统计信息
|
||||||
|
const statistics = useMemo(() => {
|
||||||
|
const countPermissions = (menus: MenuItem[]): number => {
|
||||||
|
return menus.reduce((sum, menu) => {
|
||||||
|
return sum + (menu.permissions?.length || 0) + countPermissions(menu.permissionChildren || []);
|
||||||
|
}, 0);
|
||||||
|
};
|
||||||
|
const total = countPermissions(menuTree);
|
||||||
|
const selected = checkedPermissionIds.length;
|
||||||
|
const percentage = total > 0 ? Math.round((selected / total) * 100) : 0;
|
||||||
|
return { total, selected, percentage };
|
||||||
|
}, [menuTree, checkedPermissionIds]);
|
||||||
|
|
||||||
// 渲染菜单项
|
// 渲染菜单项
|
||||||
const renderMenuItem = (menu: MenuItem, level: number = 0): React.ReactNode => {
|
const renderMenuItem = (menu: MenuItem, level: number = 0): React.ReactNode => {
|
||||||
const hasChildren = menu.permissionChildren && menu.permissionChildren.length > 0;
|
const hasChildren = menu.permissionChildren && menu.permissionChildren.length > 0;
|
||||||
@ -147,7 +187,7 @@ const PermissionDialog: React.FC<PermissionDialogProps> = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div key={menu.id} className={level > 0 ? 'ml-4' : ''}>
|
<div key={menu.id} className={level > 0 ? 'ml-4' : 'mb-2'}>
|
||||||
{hasChildren ? (
|
{hasChildren ? (
|
||||||
<AccordionItem value={`menu-${menu.id}`} className="border-b-0">
|
<AccordionItem value={`menu-${menu.id}`} className="border-b-0">
|
||||||
<AccordionTrigger className="py-2 hover:no-underline hover:bg-muted/50 px-2 rounded">
|
<AccordionTrigger className="py-2 hover:no-underline hover:bg-muted/50 px-2 rounded">
|
||||||
@ -163,16 +203,16 @@ const PermissionDialog: React.FC<PermissionDialogProps> = ({
|
|||||||
onCheckedChange={() => handleToggleMenu(menu)}
|
onCheckedChange={() => handleToggleMenu(menu)}
|
||||||
onClick={(e) => e.stopPropagation()}
|
onClick={(e) => e.stopPropagation()}
|
||||||
/>
|
/>
|
||||||
<Folder className="h-4 w-4 text-blue-500" />
|
<Folder className="h-4 w-4 text-blue-600" />
|
||||||
<span className="text-sm font-medium">{menu.name}</span>
|
<span className="text-sm font-medium">{menu.name}</span>
|
||||||
</div>
|
</div>
|
||||||
</AccordionTrigger>
|
</AccordionTrigger>
|
||||||
<AccordionContent className="pb-0 pt-2">
|
<AccordionContent className="pb-0 pt-2">
|
||||||
{/* 渲染当前菜单的权限 */}
|
{/* 渲染当前菜单的权限 */}
|
||||||
{hasPermissions && (
|
{hasPermissions && (
|
||||||
<div className="space-y-2 mb-2 ml-8">
|
<div className="grid grid-cols-4 gap-x-3 gap-y-1 mb-2 ml-8">
|
||||||
{menu.permissions.map(permission => (
|
{menu.permissions.map(permission => (
|
||||||
<div key={permission.id} className="flex items-center gap-2 py-1">
|
<div key={permission.id} className="flex items-center gap-1.5 py-1.5 px-2 rounded hover:bg-muted/50">
|
||||||
<Checkbox
|
<Checkbox
|
||||||
id={`permission-${permission.id}`}
|
id={`permission-${permission.id}`}
|
||||||
checked={checkedPermissionIds.includes(permission.id)}
|
checked={checkedPermissionIds.includes(permission.id)}
|
||||||
@ -180,10 +220,10 @@ const PermissionDialog: React.FC<PermissionDialogProps> = ({
|
|||||||
/>
|
/>
|
||||||
<label
|
<label
|
||||||
htmlFor={`permission-${permission.id}`}
|
htmlFor={`permission-${permission.id}`}
|
||||||
className="text-sm cursor-pointer flex items-center gap-2"
|
className="text-sm cursor-pointer flex items-center gap-1.5 flex-1"
|
||||||
>
|
>
|
||||||
<Key className="h-3 w-3 text-muted-foreground" />
|
<Key className="h-3 w-3 text-muted-foreground flex-shrink-0" />
|
||||||
{permission.name}
|
<span className="truncate">{permission.name}</span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
@ -209,13 +249,13 @@ const PermissionDialog: React.FC<PermissionDialogProps> = ({
|
|||||||
}}
|
}}
|
||||||
onCheckedChange={() => handleToggleMenu(menu)}
|
onCheckedChange={() => handleToggleMenu(menu)}
|
||||||
/>
|
/>
|
||||||
<Folder className="h-4 w-4 text-blue-500" />
|
<Folder className="h-4 w-4 text-blue-600" />
|
||||||
<span className="text-sm font-medium">{menu.name}</span>
|
<span className="text-sm font-medium">{menu.name}</span>
|
||||||
</div>
|
</div>
|
||||||
{hasPermissions && (
|
{hasPermissions && (
|
||||||
<div className="space-y-2 ml-8">
|
<div className="grid grid-cols-4 gap-x-3 gap-y-1 ml-8">
|
||||||
{menu.permissions.map(permission => (
|
{menu.permissions.map(permission => (
|
||||||
<div key={permission.id} className="flex items-center gap-2 py-1">
|
<div key={permission.id} className="flex items-center gap-1.5 py-1.5 px-2 rounded hover:bg-muted/50">
|
||||||
<Checkbox
|
<Checkbox
|
||||||
id={`permission-${permission.id}`}
|
id={`permission-${permission.id}`}
|
||||||
checked={checkedPermissionIds.includes(permission.id)}
|
checked={checkedPermissionIds.includes(permission.id)}
|
||||||
@ -223,10 +263,10 @@ const PermissionDialog: React.FC<PermissionDialogProps> = ({
|
|||||||
/>
|
/>
|
||||||
<label
|
<label
|
||||||
htmlFor={`permission-${permission.id}`}
|
htmlFor={`permission-${permission.id}`}
|
||||||
className="text-sm cursor-pointer flex items-center gap-2"
|
className="text-sm cursor-pointer flex items-center gap-1.5 flex-1"
|
||||||
>
|
>
|
||||||
<Key className="h-3 w-3 text-muted-foreground" />
|
<Key className="h-3 w-3 text-muted-foreground flex-shrink-0" />
|
||||||
{permission.name}
|
<span className="truncate">{permission.name}</span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
@ -252,43 +292,75 @@ const PermissionDialog: React.FC<PermissionDialogProps> = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||||
<DialogContent className="sm:max-w-[700px] max-h-[80vh]">
|
<DialogContent className="sm:max-w-[700px] max-h-[85vh] overflow-hidden flex flex-col p-0">
|
||||||
<DialogHeader>
|
<DialogHeader className="px-6 pt-6 pb-4 border-b">
|
||||||
<DialogTitle className="flex items-center gap-2">
|
<DialogTitle className="flex items-center justify-between pr-8">
|
||||||
<KeyRound className="h-5 w-5" /> 分配权限
|
<span>分配权限</span>
|
||||||
|
{!loading && (
|
||||||
|
<span className="text-sm font-normal text-muted-foreground">
|
||||||
|
已选择 <span className="font-semibold text-foreground">{statistics.selected}</span> / {statistics.total} 项权限
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
<div className="overflow-y-auto max-h-[500px] pr-2">
|
|
||||||
{loading ? (
|
{/* 搜索框 */}
|
||||||
<div className="flex items-center justify-center py-12">
|
{!loading && menuTree.length > 0 && (
|
||||||
<Loader2 className="h-6 w-6 animate-spin mr-2" />
|
<div className="px-6 py-3 border-b bg-muted/30">
|
||||||
<span className="text-sm text-muted-foreground">加载中...</span>
|
<div className="relative">
|
||||||
|
<Search className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" />
|
||||||
|
<Input
|
||||||
|
placeholder="搜索权限名称或菜单名称..."
|
||||||
|
value={searchText}
|
||||||
|
onChange={(e) => setSearchText(e.target.value)}
|
||||||
|
className="pl-10"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
) : menuTree.length > 0 ? (
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* 权限树 */}
|
||||||
|
<div className="overflow-y-auto flex-1 px-6 py-4">
|
||||||
|
{loading ? (
|
||||||
|
<div className="flex flex-col items-center justify-center py-16">
|
||||||
|
<Loader2 className="h-8 w-8 animate-spin text-muted-foreground mb-3" />
|
||||||
|
<span className="text-sm text-muted-foreground">正在加载权限数据...</span>
|
||||||
|
</div>
|
||||||
|
) : filteredMenuTree.length > 0 ? (
|
||||||
<Accordion type="multiple" className="w-full">
|
<Accordion type="multiple" className="w-full">
|
||||||
{menuTree.map(menu => renderMenuItem(menu))}
|
{filteredMenuTree.map(menu => renderMenuItem(menu))}
|
||||||
</Accordion>
|
</Accordion>
|
||||||
|
) : searchText ? (
|
||||||
|
<div className="flex flex-col items-center justify-center py-16 text-muted-foreground">
|
||||||
|
<Search className="h-12 w-12 mb-3 opacity-30" />
|
||||||
|
<p className="text-sm">未找到匹配的权限</p>
|
||||||
|
<p className="text-xs mt-1">请尝试其他关键词</p>
|
||||||
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="flex items-center justify-center py-12 text-muted-foreground">
|
<div className="flex flex-col items-center justify-center py-16 text-muted-foreground">
|
||||||
暂无权限数据
|
<Shield className="h-12 w-12 mb-3 opacity-30" />
|
||||||
|
<p className="text-sm">暂无权限数据</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<DialogFooter>
|
|
||||||
<div className="flex items-center justify-between w-full">
|
<div className="px-6 py-4 border-t bg-muted/30 flex justify-end gap-2">
|
||||||
<span className="text-sm text-muted-foreground">
|
<Button
|
||||||
已选择 {checkedPermissionIds.length} 个权限
|
type="button"
|
||||||
</span>
|
variant="outline"
|
||||||
<div className="flex gap-2">
|
onClick={() => onOpenChange(false)}
|
||||||
<Button variant="outline" onClick={() => onOpenChange(false)} disabled={submitting}>
|
disabled={submitting}
|
||||||
|
>
|
||||||
取消
|
取消
|
||||||
</Button>
|
</Button>
|
||||||
<Button onClick={handleSubmit} disabled={submitting || loading}>
|
<Button
|
||||||
|
type="button"
|
||||||
|
onClick={handleSubmit}
|
||||||
|
disabled={submitting || loading}
|
||||||
|
>
|
||||||
{submitting ? '保存中...' : '确定'}
|
{submitting ? '保存中...' : '确定'}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</DialogFooter>
|
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -153,7 +153,7 @@ const TagDialog: React.FC<TagDialogProps> = ({
|
|||||||
<Settings className="h-5 w-5" /> 标签管理
|
<Settings className="h-5 w-5" /> 标签管理
|
||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
<div className="py-4">
|
<div className="px-6 py-4">
|
||||||
<div className="mb-4">
|
<div className="mb-4">
|
||||||
<Button onClick={handleAdd}>
|
<Button onClick={handleAdd}>
|
||||||
<Plus className="h-4 w-4 mr-2" />
|
<Plus className="h-4 w-4 mr-2" />
|
||||||
@ -239,7 +239,7 @@ const TagDialog: React.FC<TagDialogProps> = ({
|
|||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle>{editingTag ? '编辑标签' : '新建标签'}</DialogTitle>
|
<DialogTitle>{editingTag ? '编辑标签' : '新建标签'}</DialogTitle>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
<div className="grid gap-4 py-4">
|
<div className="grid gap-4 px-6 py-4">
|
||||||
<div className="grid gap-2">
|
<div className="grid gap-2">
|
||||||
<Label htmlFor="name">标签名称 *</Label>
|
<Label htmlFor="name">标签名称 *</Label>
|
||||||
<Input
|
<Input
|
||||||
|
|||||||
@ -60,7 +60,7 @@ const AssignRolesDialog: React.FC<AssignRolesDialogProps> = ({
|
|||||||
<Users className="h-5 w-5" /> 分配角色
|
<Users className="h-5 w-5" /> 分配角色
|
||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
<div className="grid gap-4 py-4">
|
<div className="grid gap-4 px-6 py-4">
|
||||||
<div className="text-sm text-muted-foreground">
|
<div className="text-sm text-muted-foreground">
|
||||||
为用户 "<span className="font-semibold text-foreground">{record.username}</span>" 分配角色
|
为用户 "<span className="font-semibold text-foreground">{record.username}</span>" 分配角色
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -31,7 +31,7 @@ const DeleteDialog: React.FC<DeleteDialogProps> = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||||
<DialogContent>
|
<DialogContent className="sm:max-w-[450px]">
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle className="flex items-center gap-2 text-red-600">
|
<DialogTitle className="flex items-center gap-2 text-red-600">
|
||||||
<AlertCircle className="h-5 w-5" /> 确认删除用户?
|
<AlertCircle className="h-5 w-5" /> 确认删除用户?
|
||||||
@ -41,7 +41,7 @@ const DeleteDialog: React.FC<DeleteDialogProps> = ({
|
|||||||
此操作不可逆。
|
此操作不可逆。
|
||||||
</DialogDescription>
|
</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
<div className="space-y-2 text-sm text-muted-foreground">
|
<div className="px-6 py-4 space-y-2 text-sm text-muted-foreground">
|
||||||
{record.nickname && (
|
{record.nickname && (
|
||||||
<p>
|
<p>
|
||||||
<span className="font-medium">昵称:</span> {record.nickname}
|
<span className="font-medium">昵称:</span> {record.nickname}
|
||||||
|
|||||||
@ -142,7 +142,7 @@ const EditModal: React.FC<EditModalProps> = ({
|
|||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle>{record ? '编辑用户' : '新增用户'}</DialogTitle>
|
<DialogTitle>{record ? '编辑用户' : '新增用户'}</DialogTitle>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
<div className="grid gap-4 py-4">
|
<div className="grid gap-4 px-6 py-4">
|
||||||
<div className="grid gap-2">
|
<div className="grid gap-2">
|
||||||
<Label htmlFor="username">用户名 *</Label>
|
<Label htmlFor="username">用户名 *</Label>
|
||||||
<Input
|
<Input
|
||||||
|
|||||||
@ -68,7 +68,7 @@ const ResetPasswordDialog: React.FC<ResetPasswordDialogProps> = ({
|
|||||||
<KeyRound className="h-5 w-5" /> 重置密码
|
<KeyRound className="h-5 w-5" /> 重置密码
|
||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
<div className="grid gap-4 py-4">
|
<div className="grid gap-4 px-6 py-4">
|
||||||
<div className="text-sm text-muted-foreground">
|
<div className="text-sm text-muted-foreground">
|
||||||
为用户 "<span className="font-semibold text-foreground">{record.username}</span>" 重置密码
|
为用户 "<span className="font-semibold text-foreground">{record.username}</span>" 重置密码
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -52,7 +52,7 @@ const DeleteDialog: React.FC<DeleteDialogProps> = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||||
<DialogContent>
|
<DialogContent className="sm:max-w-[500px]">
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle className="flex items-center gap-2 text-red-600">
|
<DialogTitle className="flex items-center gap-2 text-red-600">
|
||||||
<AlertCircle className="h-5 w-5" /> 确认删除工作流?
|
<AlertCircle className="h-5 w-5" /> 确认删除工作流?
|
||||||
@ -62,7 +62,7 @@ const DeleteDialog: React.FC<DeleteDialogProps> = ({
|
|||||||
此操作不可逆。
|
此操作不可逆。
|
||||||
</DialogDescription>
|
</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
<div className="space-y-2 text-sm text-muted-foreground">
|
<div className="px-6 py-4 space-y-2 text-sm text-muted-foreground">
|
||||||
<p>
|
<p>
|
||||||
<span className="font-medium">标识:</span>{' '}
|
<span className="font-medium">标识:</span>{' '}
|
||||||
<code className="relative rounded bg-muted px-[0.3rem] py-[0.2rem] font-mono text-sm font-semibold">
|
<code className="relative rounded bg-muted px-[0.3rem] py-[0.2rem] font-mono text-sm font-semibold">
|
||||||
|
|||||||
@ -199,7 +199,7 @@ const EditModal: React.FC<EditModalProps> = ({ visible, onClose, onSuccess, reco
|
|||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle>{isEdit ? '编辑流程' : '新建流程'}</DialogTitle>
|
<DialogTitle>{isEdit ? '编辑流程' : '新建流程'}</DialogTitle>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
<div className="grid gap-6 py-4">
|
<div className="grid gap-6 px-6 py-4">
|
||||||
{/* 流程分类 */}
|
{/* 流程分类 */}
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label htmlFor="categoryId">
|
<Label htmlFor="categoryId">
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user