mirror of
https://gitee.com/JavaLionLi/plus-ui.git
synced 2026-09-13 07:43:43 +08:00
Compare commits
No commits in common. "v6.0.0-React" and "6.X-Vue" have entirely different histories.
v6.0.0-Rea
...
6.X-Vue
@ -1,60 +1,32 @@
|
||||
---
|
||||
name: frontend-api-types
|
||||
description: 前端 API 与类型定义专家。用于当前 plus-ui-react 项目中的 src/api 层、types.ts、返回结构、Query/Form/VO/InfoVO 定义,以及前后端接口映射任务。
|
||||
description: 前端 API 与类型定义专家。用于 plus-ui 前端项目中的 src/api 层、types.ts、返回结构、Query/Form/VO/InfoVO 定义,以及前后端接口映射任务;默认基线为 Gitee 仓库 JavaLionLi/plus-ui 的 6.X-Vue 分支。
|
||||
---
|
||||
|
||||
你负责当前 plus-ui-react 项目中的 API 层和类型定义。
|
||||
你负责 plus-ui 前端项目中的 API 层和类型定义。
|
||||
|
||||
基线仓库:`https://gitee.com/JavaLionLi/plus-ui`
|
||||
默认分支:`6.X-Vue`
|
||||
远端引用必须同时标记仓库、分支和文件路径;不要写本机绝对路径。
|
||||
|
||||
## 核心原则
|
||||
|
||||
1. 先看当前模块已有 `src/api/<module>/<business>`。
|
||||
2. API 路径、返回类型、函数命名、导出风格与当前模块保持一致。
|
||||
2. API 路径、返回类型、命名风格与当前模块保持一致。
|
||||
3. 能明确写出类型时,不要偷懒用 `any`。
|
||||
4. 如果当前模块已有特殊返回结构或聚合导出,继续保持一致。
|
||||
5. 不创建页面,不改路由,除非用户明确要求。
|
||||
6. 如果用户要求维护代码生成模板,优先同步当前仓库 `gen/api.ts.ftl`、`gen/types.ts.ftl`,必要时再看 boot4 后端 generator 字段定义。
|
||||
4. 如果当前模块已有 `export default { ... }`,继续保持一致。
|
||||
|
||||
## API 规则
|
||||
## 重点关注
|
||||
|
||||
- 标准 import:
|
||||
`import type { PageResult, R } from '@/api/types';`
|
||||
`import request from '@/api/request';`
|
||||
`import type { XxxForm, XxxQuery, XxxVO } from './types';`
|
||||
- 分页列表:`GET /<module>/<business>/list`,返回 `R<PageResult<XxxVO>>`。
|
||||
- 树表列表:返回 `R<XxxVO[]>`。
|
||||
- 详情:`GET /<module>/<business>/{id}`,返回 `R<XxxVO>` 或业务 `InfoVO`。
|
||||
- 新增:`POST /<module>/<business>`,请求体用 `data`。
|
||||
- 修改:`PUT /<module>/<business>`,请求体用 `data`。
|
||||
- 删除:`DELETE /<module>/<business>/{id or ids}`。
|
||||
- 状态切换:`PUT /<module>/<business>/changeStatus`,参数形态参考后端和相邻模块。
|
||||
- query string 用 `params`,请求体用 `data`。
|
||||
- 不要从 `axios` 引入 `AxiosPromise`。
|
||||
|
||||
## 类型规则
|
||||
|
||||
- 标准类型定义 `VO`、`Form`、`Query`。
|
||||
- 必要时补 `InfoVO`、`TreeVO`、`ResetPwdForm`、`OptionVO` 等扩展类型。
|
||||
- `Form` 通常继承 `BaseEntity`。
|
||||
- 非树表 `Query` 通常继承 `PageQuery`。
|
||||
- 树表 `Query` 通常不继承 `PageQuery`。
|
||||
- ID 字段通常使用 `string | number`。
|
||||
- 批量删除参数使用 `string | number | Array<string | number>`。
|
||||
- Java 数值类型映射为 `number`,Boolean 映射为 `boolean`,日期和文本默认 `string`。
|
||||
- 日期范围查询保留 `params?: Record<string, unknown>` 或跟随相邻模块,不要无故删除。
|
||||
- 列表对象、表单对象、查询对象职责分开;字段不一致时不要强行复用一个接口。
|
||||
|
||||
## gen 模板规则
|
||||
|
||||
- `gen/api.ts.ftl` 和 `gen/types.ts.ftl` 是当前 React 项目内置模板,优先于外部后端模板。
|
||||
- 保持 Velocity 变量、宏和输出路径约定,不要破坏后端 generator 可替换变量。
|
||||
- 输出类型应继续使用 `R`、`PageResult`、`PageQuery`、`BaseEntity` from `@/api/types` 和 `request` from `@/api/request`。
|
||||
- 不输出 Vue 版 `AxiosPromise`、`@/utils/request` 或 `src/views` 约定。
|
||||
- `Query`
|
||||
- `VO`
|
||||
- `Form`
|
||||
- `InfoVO`
|
||||
- `AxiosPromise<PageResult<T>>`
|
||||
- 详情接口与列表接口返回结构
|
||||
|
||||
## 自检
|
||||
|
||||
- API 路径是否与后端一致。
|
||||
- 类型是否覆盖接口真实结构。
|
||||
- 是否不必要地把类型写宽了。
|
||||
- 是否保留了当前模块的命名和导出风格。
|
||||
- 是否同步维护了当前项目内置 `gen/*.ftl` 模板中对应 API/types 输出。
|
||||
- 是否误用了 Vue 版 `AxiosPromise`、`@/utils/request` 或 `src/views` 约定。
|
||||
- API 路径是否与后端一致
|
||||
- 类型是否覆盖接口真实结构
|
||||
- 是否不必要地把类型写宽了
|
||||
|
||||
@ -1,39 +1,22 @@
|
||||
---
|
||||
name: frontend-crud-coding
|
||||
description: 前端总入口。用于当前 plus-ui-react 项目中的标准 React CRUD 页面、新增 API/types、复杂列表页增强、树筛选、导入导出、权限按钮、状态切换、弹窗表单和项目内置 gen/*.ftl React 代码生成模板维护任务,并根据任务类型选择合适的前端子 agent。
|
||||
description: 前端总入口。用于 plus-ui 前端项目中的标准 CRUD 页面、新增 API/types、复杂列表页增强、树筛选、导入导出、权限按钮与弹窗表单等任务,并根据任务类型选择合适的前端子 agent;默认基线为 Gitee 仓库 JavaLionLi/plus-ui 的 6.X-Vue 分支。
|
||||
---
|
||||
|
||||
你是当前 plus-ui-react 前端项目的总入口 agent。
|
||||
你是 plus-ui 前端项目的总入口 agent。
|
||||
|
||||
基线仓库:`https://gitee.com/JavaLionLi/plus-ui`
|
||||
默认分支:`6.X-Vue`
|
||||
远端引用必须同时标记仓库、分支和文件路径;不要在 agent 文档中写本机绝对路径。
|
||||
|
||||
先判断任务类型,再按下面规则处理:
|
||||
|
||||
1. 如果是新增标准 CRUD 页面、补 `src/api`、`types.ts`、`index.tsx`,优先使用 `frontend-crud-page.md`。
|
||||
2. 如果是修改已有列表页、增强导入导出、树筛选、详情抽屉、更多操作、状态切换,优先使用 `frontend-page-enhancement.md`。
|
||||
1. 如果是新增标准 CRUD 页面、补 `src/api`、`types.ts`、`index.vue`,优先使用 `frontend-crud-page.md`。
|
||||
2. 如果是修改已有列表页、增强导入导出、树筛选、更多菜单、状态切换,优先使用 `frontend-page-enhancement.md`。
|
||||
3. 如果只改接口层和类型定义,优先使用 `frontend-api-types.md`。
|
||||
4. 如果是维护代码生成模板,优先读取当前仓库 `gen/api.ts.ftl`、`gen/types.ts.ftl`、`gen/index.tsx.ftl`、`gen/index-tree.tsx.ftl`,再按 React 项目真实工具和页面规则修改模板。
|
||||
|
||||
通用要求:
|
||||
|
||||
- 先读当前目录下最近似页面和 API,再动代码。
|
||||
- 优先参考 `.codex/skills/frontend-crud-coding/SKILL.md` 和 `references/frontend.md`,保持 Claude agent 与 Codex skill 规则一致。
|
||||
- 冲突时优先相信当前项目真实页面,其次是公共组件、hooks、工具和请求封装,再其次是当前仓库 `gen/*.ftl` React 模板,最后才是 Vue 参考项目和关联后端工程 generator 模板。
|
||||
- 冲突时优先相信当前项目真实页面,其次是公共组件和工具,再其次才是 plus-ui Gitee 仓库 `6.X-Vue` 分支的 `gen` 模板。
|
||||
- 默认直接产出可落地代码,而不是只给抽象建议。
|
||||
- 不要照搬 Vue 项目的 `src/views`、Element Plus、`AxiosPromise`、`v-hasPermi`、`ref/reactive` 写法。
|
||||
|
||||
当前项目关键约定:
|
||||
|
||||
- 页面目录是 `src/pages`。
|
||||
- 请求封装是 `@/api/request`。
|
||||
- API 外层类型是 `R<T>`,分页类型是 `PageResult<T>`,都来自 `@/api/types`。
|
||||
- 标准页面优先使用 `PageContainer`、`ProTable`、`ModalForm`、ProForm 组件。
|
||||
- 权限通过 `useUserStore` 和 `hasPermi` 计算布尔值。
|
||||
- 多选通过 `useTableSelection`,导出通过 `useTableExport`。
|
||||
- 行操作优先使用 `RowActions` 和 `@ant-design/icons`。
|
||||
- 日期范围、字典 options、树表展开、loading、搜索重置和确认弹窗优先使用 `useDateRangeQuery`、`dictOptions`、`useTreeTableExpand`、`useLoading`、`useSearchReset`、`confirmAction`/`confirmTitleSafe`。
|
||||
- `gen/index.tsx.ftl`、`gen/index-tree.tsx.ftl` 内容必须生成 React TSX 页面。
|
||||
|
||||
验证要求:
|
||||
|
||||
- 改 TS/TSX/API/types 后优先运行 `pnpm lint` 或 `pnpm exec tsc --noEmit`。
|
||||
- 改公共组件、hooks、构建配置或较大范围页面后运行 `pnpm build`。
|
||||
- 如果验证失败或无法运行,交付时说明命令和原因。
|
||||
|
||||
@ -1,73 +1,41 @@
|
||||
---
|
||||
name: frontend-crud-page
|
||||
description: 前端标准 React CRUD 页面专家。用于当前 plus-ui-react 项目中的新建列表页、弹窗表单页、标准 API/types/index.tsx 骨架、项目内置 gen/*.ftl React 模板维护,以及后端 generator 字段到 React + ProTable 项目风格的落地任务。
|
||||
description: 前端标准 CRUD 页面专家。用于 plus-ui 前端项目中的新建列表页、弹窗表单页、标准 API/types/index.vue 骨架,以及 gen 模板到项目风格的落地任务;默认参考 Gitee 仓库 JavaLionLi/plus-ui 的 6.X-Vue 分支。
|
||||
---
|
||||
|
||||
你负责当前 plus-ui-react 项目中的标准 CRUD 页面实现。
|
||||
你负责 plus-ui 前端项目中的标准 CRUD 页面实现。
|
||||
|
||||
基线仓库:`https://gitee.com/JavaLionLi/plus-ui`
|
||||
默认分支:`6.X-Vue`
|
||||
远端模板引用必须同时标记仓库、分支和路径,例如 `branch=6.X-Vue, path=gen/index.vue.ftl`;不要写本机绝对路径。
|
||||
|
||||
## 核心原则
|
||||
|
||||
1. 先看当前模块最近似页面。
|
||||
2. 再参考 `.codex/skills/frontend-crud-coding/references/frontend.md`。
|
||||
3. 维护或新增生成能力时,优先参考当前仓库 `gen/api.ts.ftl`、`gen/types.ts.ftl`、`gen/index.tsx.ftl`、`gen/index-tree.tsx.ftl`。
|
||||
4. 必要时参考关联后端工程 generator 模板确认接口、字段、权限和导出能力。
|
||||
5. 默认同时维护:
|
||||
2. 再参考 plus-ui Gitee 仓库 `6.X-Vue` 分支中的 `gen` 模板。
|
||||
3. 默认同时维护:
|
||||
`src/api/<module>/<business>/index.ts`
|
||||
`src/api/<module>/<business>/types.ts`
|
||||
`src/pages/<module>/<business>/index.tsx`
|
||||
|
||||
## 优先参考
|
||||
|
||||
- 标准单表:`src/pages/demo/demo/index.tsx`、`src/api/demo/demo/*`。
|
||||
- 树表:`src/pages/demo/tree/index.tsx`、`src/pages/workflow/category/index.tsx`。
|
||||
- 系统管理:`src/pages/system/user/index.tsx`、`system/role`、`system/post`、`system/config`。
|
||||
- workflow:`src/pages/workflow/*`、`src/api/workflow/*`。
|
||||
`src/views/<module>/<business>/index.vue`
|
||||
|
||||
## 页面规则
|
||||
|
||||
- 页面默认导出函数组件,命名如 `SystemClientPage`、`DemoDemoPage`、`WorkflowCategoryPage`。
|
||||
- 标准结构通常包含 `PageContainer`、`ProTable`、工具栏按钮、`ModalForm`。
|
||||
- 页面优先使用 `<script setup name="Xxx" lang="ts">`
|
||||
- 标准结构通常包含:
|
||||
搜索区、表格区、工具栏、分页、编辑弹窗
|
||||
- 常见状态:
|
||||
`actionRef`、`form`、`modalOpen`、`modalTitle`、`ids`、`selectedOne`、权限布尔值。
|
||||
- 表格列使用 `ProColumns<XxxVO>[]`。
|
||||
- 分页列表的 `request` 中使用 `toPageQuery(params)`、`updateExportParams(query)`、`listXxx(query)`、`toTableData(res)`。
|
||||
- 多选使用 `useTableSelection<XxxVO>(row => row.id)`。
|
||||
- 导出使用 `useTableExport()` 和 `exportFile('/module/business/export', () => 'business_timestamp.xlsx')`。
|
||||
- 行操作使用 `RowActions`,行内删除确认放在 `confirm`。
|
||||
- 新增/编辑弹窗使用 `ModalForm<XxxForm>`,字段优先使用 ProForm 组件。
|
||||
- 日期范围使用 `useDateRangeQuery`,字典 options 使用 `dictOptions`。
|
||||
- 状态切换等 Promise 式确认使用 `confirmAction` 或 `confirmTitleSafe`。
|
||||
- 异步 loading 优先使用 `useLoading`,带额外筛选状态的重置优先使用 `useSearchReset`。
|
||||
`loading`、`showSearch`、`ids`、`single`、`multiple`、`total`
|
||||
- 查询与表单优先使用 `reactive<PageData<Form, Query>>({...})`
|
||||
|
||||
## API / types 规则
|
||||
|
||||
- 请求统一通过 `@/api/request`。
|
||||
- 同目录维护 `index.ts` 与 `types.ts`。
|
||||
- 标准 CRUD 通常包含:列表、详情、新增、修改、删除。
|
||||
- 列表分页接口通常返回 `request<R<PageResult<XxxVO>>>`。
|
||||
- 树表列表接口通常返回 `request<R<XxxVO[]>>`。
|
||||
- 不要从 `axios` 引入 `AxiosPromise`。
|
||||
|
||||
## 树表规则
|
||||
|
||||
- 判断树表后不要生成分页 `PageResult` 页面。
|
||||
- `Query` 通常不继承 `PageQuery`。
|
||||
- 使用 `handleTree`、`pagination={false}`、`useTreeTableExpand`、`ProFormTreeSelect`。
|
||||
- 树表展开/折叠优先使用 `useTreeTableExpand`,不要在页面或模板里重复手写收集 key 的状态逻辑。
|
||||
- 新增子节点时从当前行带入 `parentId`。
|
||||
|
||||
## gen 模板规则
|
||||
|
||||
- `gen/index.tsx.ftl`、`gen/index-tree.tsx.ftl` 内容生成 React TSX。
|
||||
- 保持 Velocity 变量、宏和后端 generator 可识别结构,不为了格式美化破坏模板语法。
|
||||
- 模板应输出当前项目公共工具写法:`useTableSelection`、`useTableExport`、`useDateRangeQuery`、`dictOptions`、`useTreeTableExpand`、`confirmAction`、`toPageQuery`、`toTableData`、`handleTree`。
|
||||
- 不把 Vue `src/views`、Element Plus、`AxiosPromise`、`v-hasPermi` 写入模板。
|
||||
- 请求统一通过 `@/utils/request`
|
||||
- 同目录维护 `index.ts` 与 `types.ts`
|
||||
- 标准 CRUD 通常包含:列表、详情、新增、修改、删除
|
||||
- 列表接口通常返回 `AxiosPromise<PageResult<XxxVO>>`
|
||||
|
||||
## 自检
|
||||
|
||||
- API 路径是否与后端一致。
|
||||
- `index.ts` 与 `types.ts` 是否同步补齐。
|
||||
- 页面是否使用 React 项目的 ProTable/ModalForm 骨架。
|
||||
- 权限标识是否与后端和相邻页面一致。
|
||||
- 是否同步使用当前项目公共 hooks/工具,避免生成重复代码。
|
||||
- 是否误用了 Vue/Element Plus/`src/views` 规则。
|
||||
- API 路径是否与后端一致
|
||||
- `index.ts` 与 `types.ts` 是否同步补齐
|
||||
- 页面是否只是模板裸输出,如果是要继续补强到当前项目风格
|
||||
|
||||
@ -1,58 +1,31 @@
|
||||
---
|
||||
name: frontend-page-enhancement
|
||||
description: 复杂前端页面增强专家。用于修改当前 plus-ui-react 项目中已经存在的列表页、树筛选页、带导入导出、详情抽屉、更多操作和状态切换的页面,强调增量修改和保留现有交互能力。
|
||||
description: 复杂前端页面增强专家。用于修改 plus-ui 前端项目中已经存在的列表页、树筛选页、带导入导出和更多菜单的页面,强调增量修改和保留现有交互能力;默认基线为 Gitee 仓库 JavaLionLi/plus-ui 的 6.X-Vue 分支。
|
||||
---
|
||||
|
||||
你负责当前 plus-ui-react 项目中已有页面的增强,不是重写页面。
|
||||
你负责 plus-ui 前端项目中已有页面的增强,不是重写页面。
|
||||
|
||||
基线仓库:`https://gitee.com/JavaLionLi/plus-ui`
|
||||
默认分支:`6.X-Vue`
|
||||
远端引用必须同时标记仓库、分支和文件路径;不要写本机绝对路径。
|
||||
|
||||
## 核心原则
|
||||
|
||||
1. 优先阅读当前页面完整实现和相关子组件。
|
||||
1. 优先阅读当前页面完整实现。
|
||||
2. 增量修改,不重写整页。
|
||||
3. 保留已有树筛选、导入导出、列显隐、详情抽屉、更多操作、状态切换、路由跳转、权限控制和样式壳。
|
||||
4. 不要把复杂页面退化成 demo/generator 式基础列表页。
|
||||
5. 修改前先看 `.codex/skills/frontend-crud-coding/references/frontend.md` 中对应规则。
|
||||
3. 保留已有树筛选、导入导出、列显隐、更多菜单、状态切换、路由跳转、SCSS 页面壳。
|
||||
4. 不要把复杂页面退化成 generator 式基础列表页。
|
||||
|
||||
## 常见任务
|
||||
|
||||
- 调整工具栏按钮和行操作。
|
||||
- 增加筛选条件和日期范围。
|
||||
- 增加导入、导出能力。
|
||||
- 增加状态切换、快捷操作、确认弹窗。
|
||||
- 增加详情抽屉或增强现有抽屉。
|
||||
- 补复杂页面的小型子功能。
|
||||
- 接入新 API 并保持现有页面状态流。
|
||||
|
||||
## React 项目约定
|
||||
|
||||
- 表格页通常使用 `PageContainer` + `ProTable`。
|
||||
- 复杂系统页可包含 `TreePanel`、子组件弹窗、抽屉、授权路由跳转。
|
||||
- 权限通过 `useUserStore` + `hasPermi` 计算,不使用 Vue 指令。
|
||||
- 字典通过 `useDict` 加载,按现有页面映射为 Ant Design options。
|
||||
- 字典 options 优先使用 `dictOptions` from `@/utils/dict`。
|
||||
- 导出通过 `useTableExport`,不要另写下载封装。
|
||||
- 多选通过 `useTableSelection`,不要手写一套重复状态。
|
||||
- 日期范围优先使用 `useDateRangeQuery`,底层保持 `addDateRange` 参数格式。
|
||||
- 树表展开优先使用 `useTreeTableExpand`。
|
||||
- 异步 loading 优先使用 `useLoading`。
|
||||
- 额外筛选状态的重置优先使用 `useSearchReset`。
|
||||
- Promise 式确认框优先使用 `confirmAction` 或 `confirmTitleSafe`。
|
||||
- 状态切换失败时回滚或刷新,参考 `system/user`。
|
||||
|
||||
## 增量修改规则
|
||||
|
||||
- 如果页面已有子组件,如 `UserFormModal`、`UserImportModal`、`UserDetailDrawer`,优先修改或复用子组件,不把所有逻辑堆回主页面。
|
||||
- 如果页面已有权限布尔值,沿用同一命名风格补新权限。
|
||||
- 如果页面已有导出参数缓存,继续通过 `updateExportParams` 更新。
|
||||
- 如果页面已有树筛选状态,查询参数要合并该状态,不要覆盖掉。
|
||||
- 如果页面已有特殊禁用规则、超级管理员保护或业务状态判断,必须保留。
|
||||
- 如果增强的是标准 CRUD 能力或公共工具约定,要评估当前仓库 `gen/*.ftl` 是否也应同步更新。
|
||||
- 调整工具栏和更多菜单
|
||||
- 增加筛选条件和日期范围
|
||||
- 增加导入导出能力
|
||||
- 增加状态切换、快捷操作、确认弹窗
|
||||
- 补复杂页面的小型子功能
|
||||
|
||||
## 自检
|
||||
|
||||
- 是否破坏了原页面结构和样式。
|
||||
- 是否误删了已有权限控制或交互能力。
|
||||
- 是否保留了树筛选、导入导出、抽屉、路由跳转等复杂能力。
|
||||
- 是否应该拆成子组件而不是继续堆主页面。
|
||||
- 是否使用了当前项目已有工具,避免重复手写日期范围、字典 options、树展开、loading、确认框。
|
||||
- 是否误用了 Vue/Element Plus/generator 的简化逻辑。
|
||||
- 是否破坏了原页面结构和样式
|
||||
- 是否误删了已有权限控制或交互能力
|
||||
- 是否应该拆成子组件而不是继续堆主页面
|
||||
|
||||
@ -1,31 +1,41 @@
|
||||
---
|
||||
name: frontend-crud-coding
|
||||
description: 在当前 plus-ui-react 前端项目中按真实 React + TypeScript + Umi Max + Ant Design ProComponents + oxlint/oxfmt 代码风格生成或修改页面、API、types、hooks 接入、样式和项目内置 gen/*.ftl 代码生成模板。用于新增或修改标准 CRUD 列表页、树表页、系统管理页、监控页、workflow 页面、demo 页面,补齐与 RuoYi-Vue-Plus boot4 后端接口对应的 src/api、types 和 src/pages 代码,或维护 gen/api.ts.ftl、gen/types.ts.ftl、gen/index.tsx.ftl、gen/index-tree.tsx.ftl;触发后应先读取适用 references,再阅读目标模块真实代码和项目内置 gen 模板。
|
||||
description: 在 plus-ui 前端项目中按真实 Vue 3 + TypeScript + Element Plus + oxlint/oxfmt 代码风格生成或修改页面、API、types、hooks 接入和样式壳。用于新增或修改标准 CRUD 列表页、树表页、系统管理页、监控页、workflow 页面、demo 页面,补齐与后端接口对应的 src/api、types 和 src/views 代码;默认参考 Gitee 仓库 JavaLionLi/plus-ui 的 6.X-Vue 分支,触发后应先读取适用 references,再阅读目标模块真实代码和项目内 gen 代码生成模板。
|
||||
---
|
||||
|
||||
# 前端编码规范
|
||||
|
||||
先对齐当前 React 项目里的真实实现,再参考项目内置 `gen/` 代码生成模板。Vue 版本 skill 和关联后端工程的代码生成器模板只用于理解字段、权限、任务分型和迁移意图。不要直接套 Vue 3、Element Plus、AxiosPromise 或 `src/views` 规则;要落成当前仓库的 `src/pages`、`src/api`、ProTable、ModalForm、权限、导出和公共工具方式。
|
||||
先对齐当前前端项目里的真实实现,再参考项目内 `gen` 目录下的代码生成模板。不要只套通用 Vue 模板,也不要把 generator 模板原样复制进来而忽略当前项目已经演进出的 hooks、页面壳、类型入口和下载方式。
|
||||
|
||||
## 项目基线
|
||||
|
||||
- 基线仓库:`https://gitee.com/JavaLionLi/plus-ui`
|
||||
- 默认分支:`6.X-Vue`
|
||||
- 远端引用必须同时标记仓库、分支和文件路径,例如 `https://gitee.com/JavaLionLi/plus-ui/blob/6.X-Vue/gen/index.vue.ftl` 或 `branch=6.X-Vue, path=gen/index.vue.ftl`。
|
||||
- 不要在 skill、reference 或 agent 文档中写本机绝对路径;本地文件读取以运行时工作目录为准。
|
||||
- 如果用户指定其他分支,先按用户分支读取同一仓库的对应文件,并在结果中说明使用的分支。
|
||||
|
||||
## 执行流程
|
||||
|
||||
1. 判断任务类型:新增标准 CRUD、树表、已有页面增强、复杂业务页、只补 API/types。
|
||||
2. 按“文档读取规则”读取必要 reference,不一次性展开所有资料。
|
||||
3. 阅读目标目录下最近似的真实代码:
|
||||
- 标准单表优先看 `src/pages/demo/demo/index.tsx`、`src/api/demo/demo/*`。
|
||||
- 树表优先看 `src/pages/demo/tree/index.tsx`、`src/pages/workflow/category/index.tsx`。
|
||||
- 系统复杂页优先看 `src/pages/system/user/index.tsx`、`system/role`、`system/post`、`system/config`。
|
||||
- workflow 业务页优先看 `src/pages/workflow/*` 与 `src/api/workflow/*` 同类页面。
|
||||
4. 新增或维护代码生成能力时,优先阅读当前仓库 `gen/api.ts.ftl`、`gen/types.ts.ftl`、`gen/index.tsx.ftl`、`gen/index-tree.tsx.ftl`。这些是 React 版内置 FreeMarker 生成模板,页面模板必须输出当前项目 React/TSX 风格。
|
||||
5. 需要从 Vue 版本迁移经验时,参考 Gitee 项目 `https://gitee.com/JavaLionLi/plus-ui` 的 `6.X-Vue` 分支,目录为 `.codex/skills/frontend-crud-coding` 和 `.claude/agents`,只吸收任务分型、增量修改和自检原则。
|
||||
6. 新增标准页面前,可以对照 Gitee 项目 `https://gitee.com/dromara/RuoYi-Vue-Plus` 的 `6.X` 分支,目录为 `ruoyi-modules/ruoyi-gen/src/main/resources/fm`,确认字段、权限和导出能力,但输出和 `gen/` 模板必须保持 React 项目风格。
|
||||
7. 新增代码时通常同步维护 `src/api/<module>/<business>/index.ts`、`types.ts`、`src/pages/<module>/<business>/index.tsx`;如果是生成模板优化,同步维护 `gen/` 对应 `.ftl`。
|
||||
8. 增强已有页面时只做增量修改,保留原页面的树筛选、导入导出、列显隐、权限、字典、弹窗、抽屉和路由跳转能力。
|
||||
9. 修改完成后按影响范围运行验证:优先 `pnpm exec tsc --noEmit` 或 `pnpm lint`;大范围页面、公共组件、构建配置变更再跑 `pnpm build`。
|
||||
- 标准单表优先看 `src/views/demo/demo/index.vue`、`src/api/demo/demo/*`。
|
||||
- 树表优先看 `src/views/demo/tree/index.vue`、`src/views/workflow/category/index.vue`。
|
||||
- 系统复杂页优先看 `src/views/system/user/index.vue`、`system/role`、`system/post`、`system/config`。
|
||||
- workflow 业务页优先看 `src/views/workflow/*` 同类页面。
|
||||
4. 新增标准页面前,对照项目内模板确认基础骨架:
|
||||
- API 模板:`gen/api.ts.ftl`
|
||||
- types 模板:`gen/types.ts.ftl`
|
||||
- 标准单表页模板:`gen/index.vue.ftl`
|
||||
- 树表页模板:`gen/index-tree.vue.ftl`
|
||||
5. 新增代码时通常同步维护 `src/api/<module>/<business>/index.ts`、`types.ts`、`src/views/<module>/<business>/index.vue`。
|
||||
6. 增强已有页面时只做增量修改,保留原页面的树筛选、导入导出、列显隐、权限、字典、弹窗和路由跳转能力。
|
||||
7. 修改完成后按影响范围运行验证:优先 `pnpm exec vue-tsc --noEmit`,改动页面或导入时再跑 `pnpm lint`,大范围变更再跑 `pnpm build`。
|
||||
|
||||
## 文档读取规则
|
||||
|
||||
- 前端 API、types、页面、hooks、样式、权限、导出和验证规则,先读 [references/frontend.md](references/frontend.md)。
|
||||
- 前端 API、types、页面、hooks、样式和验证规则,先读 [references/frontend.md](references/frontend.md)。
|
||||
- 不确定任务边界、需要标准用例或提问方式时,再读 [references/examples.md](references/examples.md)。
|
||||
- reference 只约束实现方式和自检范围;发生冲突时,以当前模块真实代码和实际调用点为准。
|
||||
|
||||
@ -34,46 +44,41 @@ description: 在当前 plus-ui-react 前端项目中按真实 React + TypeScript
|
||||
发生冲突时按下面顺序决策:
|
||||
|
||||
1. 目标目录下最近似页面、API、types 的真实实现。
|
||||
2. 当前项目公共 hooks、组件、工具、样式和请求封装约定。
|
||||
3. 当前项目内置 `gen/*.ftl` React 代码生成模板。
|
||||
4. Gitee 项目 `plus-ui` 的 `6.X-Vue` 分支 `.codex` / `.claude` 中的任务分型和工作流。
|
||||
5. Gitee 项目 `RuoYi-Vue-Plus` 的 `6.X` 分支 generator 模板。
|
||||
6. 通用 React / Ant Design ProComponents 习惯。
|
||||
2. 当前项目公共 hooks、组件、工具和样式约定。
|
||||
3. 项目内 `gen` 代码生成模板。
|
||||
4. 通用 Vue 3 / Element Plus 习惯。
|
||||
|
||||
也就是说:
|
||||
|
||||
- 同模块已有页面怎么写,优先怎么写。
|
||||
- 没有现成页面时,使用 demo 页面作为骨架,再按后端接口字段和权限补齐。
|
||||
- 没有现成页面时,使用项目内 `gen` 模板作为骨架,再改成当前项目风格。
|
||||
- 复杂模块不能为了“标准 CRUD”退化成裸模板页。
|
||||
- Vue 版本只作为迁移参考,不复制 Vue 组件、hooks、指令、样式类和类型导入。
|
||||
|
||||
## 仓库通用规则
|
||||
|
||||
- 遵循 `.editorconfig` 和仓库现状:UTF-8、2 空格缩进、TypeScript、TSX。
|
||||
- 包管理使用 `pnpm`;格式脚本是 `pnpm run fmt`,lint 脚本是 `pnpm lint`。
|
||||
- 技术栈是 React 19 + TypeScript + Umi Max + Ant Design 6 + Ant Design ProComponents + ahooks + TanStack Query + Zustand。
|
||||
- 页面放在 `src/pages`,不是 Vue 项目的 `src/views`。
|
||||
- 请求统一通过 `src/api/request.ts`,API 返回类型使用 `Promise<R<T>>`,不要从 `axios` 或 Vue 项目引入 `AxiosPromise`。
|
||||
- 分页结果使用 `PageResult<T>` from `@/api/types`,接口外层使用 `R<T>` from `@/api/types`。
|
||||
- 标准列表页优先复用 `ProTable`、`ModalForm`、`RowActions`、`useTableSelection`、`useTableExport`、`toPageQuery`、`toTableData`。
|
||||
- 查询、字典、树表、loading、重置和确认弹窗优先复用 `useDateRangeQuery`、`dictOptions`、`useTreeTableExpand`、`useLoading`、`useSearchReset`、`confirmAction`/`confirmTitleSafe`。
|
||||
- 权限用 `const userInfo = useUserStore(state => state.userInfo)` 和 `hasPermi(userInfo, ['module:business:action'])` 计算布尔值。
|
||||
- 新页面不要无故引入另一套状态管理、请求封装、表格封装、样式体系或权限写法。
|
||||
- 遵循 [`.editorconfig`](../../../.editorconfig):UTF-8、LF、2 空格缩进;Markdown 例外。
|
||||
- 当前仓库没有 `.prettierrc`,格式脚本是 `pnpm run fmt` 调用 `oxfmt .`,lint 脚本是 `pnpm lint` 调用 `oxlint src`。
|
||||
- 页面优先使用 `<script setup name="Xxx" lang="ts">`。
|
||||
- API 返回类型优先从 `@/utils/api-types` 引入 `AxiosPromise`,分页结果从 `@/api/types` 引入 `PageResult`。
|
||||
- 请求统一通过 `@/utils/request`,导出下载使用 `import { download as requestDownload } from '@/utils/request';`。
|
||||
- 标准列表页优先复用 `useLoading`、`useSearchToggle`、`useSearchReset`、`useTableSelection`、`useFormDialog`、`useDateRangeQuery`。
|
||||
- 页面壳优先使用 `p-2 app-container <module>-<business>-page`、`search-panel`、`toolbar-shell`、`data-table`、`right-toolbar`、`pagination`。
|
||||
- 新页面不要无故引入另一套状态管理、请求封装、样式体系或权限写法。
|
||||
|
||||
## 目录映射规则
|
||||
|
||||
通常按下面关系组织代码:
|
||||
|
||||
- 后端 `/system/user/*` 对应 `src/api/system/user/*` 与 `src/pages/system/user/*`
|
||||
- 后端 `/monitor/xxx/*` 对应 `src/api/monitor/xxx/*` 与 `src/pages/monitor/xxx/*`
|
||||
- 后端 `/workflow/xxx/*` 对应 `src/api/workflow/xxx/*` 与 `src/pages/workflow/xxx/*`
|
||||
- 后端 `/demo/xxx/*` 对应 `src/api/demo/xxx/*` 与 `src/pages/demo/xxx/*`
|
||||
- 后端 `/system/user/*` 对应 `src/api/system/user/*` 与 `src/views/system/user/*`
|
||||
- 后端 `/monitor/xxx/*` 对应 `src/api/monitor/xxx/*` 与 `src/views/monitor/xxx/*`
|
||||
- 后端 `/workflow/xxx/*` 对应 `src/api/workflow/xxx/*` 与 `src/views/workflow/xxx/*`
|
||||
- 后端 `/demo/xxx/*` 对应 `src/api/demo/xxx/*` 与 `src/views/demo/xxx/*`
|
||||
|
||||
标准新增通常至少包含:
|
||||
|
||||
- `src/api/<module>/<business>/index.ts`
|
||||
- `src/api/<module>/<business>/types.ts`
|
||||
- `src/pages/<module>/<business>/index.tsx`
|
||||
- `src/views/<module>/<business>/index.vue`
|
||||
|
||||
按业务复杂度,可能继续补:
|
||||
|
||||
@ -82,17 +87,17 @@ description: 在当前 plus-ui-react 前端项目中按真实 React + TypeScript
|
||||
- 树筛选面板
|
||||
- 列显隐配置
|
||||
- 分配/授权子页面
|
||||
- 自定义 Less 样式
|
||||
- 自定义 SCSS 样式
|
||||
|
||||
## 任务分型
|
||||
|
||||
### 1. 标准单表 CRUD
|
||||
|
||||
以 `src/pages/demo/demo/index.tsx` 和 `src/api/demo/demo/*` 为主要起点,补齐列表、搜索、分页、新增、编辑、删除、导出、权限、类型和验证。
|
||||
以 `gen/index.vue.ftl`、`gen/api.ts.ftl`、`gen/types.ts.ftl` 和 `src/views/demo/demo/index.vue` 为主要起点,补齐列表、搜索、分页、新增、编辑、删除、导出、权限、类型和验证。
|
||||
|
||||
### 2. 树表 CRUD
|
||||
|
||||
以 `src/pages/demo/tree/index.tsx`、`src/pages/workflow/category/index.tsx` 为主要起点。列表接口通常返回数组,不使用分页 `PageResult`;页面使用 `handleTree`、`useTreeTableExpand`、`ProFormTreeSelect`。
|
||||
以 `src/views/demo/tree/index.vue`、`src/views/workflow/category/index.vue` 为主要起点。列表接口通常返回数组而不是 `PageResult`,页面使用 `handleTree`、`useTreeTableExpand`,`Query` 通常不继承 `PageQuery`。
|
||||
|
||||
### 3. 强业务页面
|
||||
|
||||
@ -100,23 +105,12 @@ description: 在当前 plus-ui-react 前端项目中按真实 React + TypeScript
|
||||
|
||||
### 4. 工作流页面
|
||||
|
||||
workflow 目录优先参考 `src/pages/workflow/*`。流程定义、流程实例、任务列表、请假申请等页面通常有业务按钮、弹窗和路由跳转,不要硬套 system 模块。
|
||||
workflow 目录优先参考 `src/views/workflow/*`。流程定义、流程实例、任务列表、请假申请等页面通常有业务按钮、弹窗和路由跳转,不要硬套 system 模块。
|
||||
|
||||
### 5. 只补 API 和 types
|
||||
|
||||
只维护 `src/api/<module>/<business>/index.ts` 与 `types.ts`,但仍要与后端路由、返回结构、当前模块导入方式和类型入口一致。
|
||||
|
||||
### 6. 维护 React 生成模板
|
||||
|
||||
模板只放在当前仓库 `gen/` 下,除非用户明确要求写入 boot4 后端工程。维护时保持 FreeMarker 变量和文件名不变:
|
||||
|
||||
- `gen/api.ts.ftl`
|
||||
- `gen/types.ts.ftl`
|
||||
- `gen/index.tsx.ftl`
|
||||
- `gen/index-tree.tsx.ftl`
|
||||
|
||||
模板内容必须生成当前 React 项目代码,并优先使用已有公共工具:`useDateRangeQuery`、`dictOptions`、`useTreeTableExpand`、`useTableSelection`、`useTableExport`、`confirmAction`、`toPageQuery`、`toTableData`、`handleTree`、`formatDateTimeFields`、`toDayjsFields`。不要把 Vue 模板、Element Plus 组件或 `src/views` 路径写入这些模板。
|
||||
|
||||
## 输出要求
|
||||
|
||||
使用本 skill 时,默认期望产出应满足:
|
||||
@ -125,26 +119,21 @@ workflow 目录优先参考 `src/pages/workflow/*`。流程定义、流程实例
|
||||
- API 路径、函数名、权限标识与后端接口保持一致。
|
||||
- 标准页查询、重置、分页、弹窗、提交、删除、导出流程闭环完整。
|
||||
- 复杂页面保留原有交互能力和业务约束。
|
||||
- 代码体现当前项目 hooks、页面壳和下载方式,而不是 Vue 版本或 generator 裸输出。
|
||||
- 生成模板变更要同步反映当前项目公共 hooks/工具,避免生成的新页面重复手写日期范围、字典 options、树展开、loading、搜索重置和确认框逻辑。
|
||||
- 代码体现当前项目 hooks、页面壳和下载方式,而不是 `gen` 模板裸输出。
|
||||
- 交付前说明运行过的验证命令;如果无法验证,说明原因。
|
||||
|
||||
## 快速检查清单
|
||||
|
||||
- API 是否从 `@/api/request` 引入 `request`。
|
||||
- `R`、`PageResult`、`PageQuery`、`BaseEntity` 是否来自 `@/api/types`。
|
||||
- `AxiosPromise` 是否来自 `@/utils/api-types`。
|
||||
- `PageResult` 是否来自 `@/api/types`。
|
||||
- API `params` 和 `data` 是否与后端方法一致。
|
||||
- 分页表格 request 是否通过 `toPageQuery(params)` 和 `toTableData(res)`。
|
||||
- 导出是否通过 `useTableExport` 和 `exportFile('/module/business/export', () => 'name_timestamp.xlsx')`。
|
||||
- 多选是否通过 `useTableSelection<T>(row => row.id)`。
|
||||
- 行操作是否优先使用 `RowActions` 和 Ant Design 图标。
|
||||
- 权限是否通过 `useUserStore` + `hasPermi` 计算,不要写 Vue 指令。
|
||||
- 日期范围是否通过 `useDateRangeQuery` 或 `addDateRange` 相关工具处理。
|
||||
- 字典 options 是否通过 `dictOptions`。
|
||||
- 树表是否使用 `handleTree`、`pagination={false}`、`useTreeTableExpand`、`ProFormTreeSelect`。
|
||||
- loading 是否优先使用 `useLoading`,搜索重置是否优先使用 `useSearchReset`。
|
||||
- 需要 Promise 式确认时是否使用 `confirmAction` 或 `confirmTitleSafe`。
|
||||
- 代码生成模板是否仍输出 React TSX 项目风格。
|
||||
- 日期范围是否通过 `useDateRangeQuery` 或附近页面现有方式处理。
|
||||
- 列表 loading 是否通过 `useLoading` 或原页面方式维护。
|
||||
- 弹窗是否通过 `useFormDialog` 或原页面方式维护。
|
||||
- 多选状态是否通过 `useTableSelection` 或原页面方式维护。
|
||||
- 权限指令是否保持同文件一致,默认使用当前项目主流 `v-hasPermi`。
|
||||
- 导出是否使用 `requestDownload('<module>/<business>/export', { ...queryParams.value }, '<name>_<time>.xlsx')`。
|
||||
- 页面壳是否保留 `search-panel`、`table-panel`、`toolbar-shell`、`data-table`、`right-toolbar`、`pagination`。
|
||||
|
||||
## 推荐提问方式
|
||||
|
||||
@ -158,5 +147,5 @@ workflow 目录优先参考 `src/pages/workflow/*`。流程定义、流程实例
|
||||
|
||||
例如:
|
||||
|
||||
- 使用 `$frontend-crud-coding` 为 `/system/client` 补一套 React 标准 CRUD 页面,参考 `demo/demo`、现有 `system/client` 和 boot4 generator 模板。
|
||||
- 使用 `$frontend-crud-coding` 修改 `workflow/category` 列表页,增加导出按钮和状态筛选,保持当前 workflow 树表风格。
|
||||
- 使用 `$frontend-crud-coding` 为 `/system/client` 补一套标准 CRUD 页面,参考 `gen` 模板、`demo/demo` 和 `system/client`。
|
||||
- 使用 `$frontend-crud-coding` 修改 `workflow/category` 列表页,增加导出按钮和状态筛选,保持当前 workflow 风格。
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
interface:
|
||||
display_name: "前端编码"
|
||||
short_description: "按 plus-ui-react 真实代码和 gen 模板编写 React CRUD"
|
||||
default_prompt: "使用 $frontend-crud-coding 先读取适用 reference,再按 plus-ui-react 真实页面、项目内置 gen 模板、API/types、ProTable、ModalForm、权限、导出和公共工具约定实现前端修改。"
|
||||
short_description: "按 plus-ui 6.X-Vue 真实代码和 gen 模板编写前端 CRUD"
|
||||
default_prompt: "使用 $frontend-crud-coding 先读取适用 reference,再按 plus-ui Gitee 仓库 6.X-Vue 分支的真实页面、hooks、API/types 约定和项目内 gen 模板实现前端修改。"
|
||||
|
||||
policy:
|
||||
allow_implicit_invocation: true
|
||||
|
||||
@ -5,19 +5,18 @@
|
||||
### 用户提问示例
|
||||
|
||||
```text
|
||||
使用 $frontend-crud-coding 为 system/client 补一套 React 前端 CRUD 页面。
|
||||
使用 $frontend-crud-coding 为 system/client 补一套前端 CRUD 页面。
|
||||
后端接口已经有 /system/client/list、/system/client/{id}、POST /system/client、PUT /system/client、DELETE /system/client/{ids}。
|
||||
请参考 src/pages/demo/demo/index.tsx 和现有 system/client 风格实现。
|
||||
请参考 plus-ui Gitee 仓库 6.X-Vue 分支的项目内 gen 模板、src/views/demo/demo/index.vue 和现有 system/client 风格实现。
|
||||
```
|
||||
|
||||
### 期望执行方式
|
||||
|
||||
- 先看 `src/api/system/client/*` 和 `src/pages/system/client/index.tsx` 是否已存在。
|
||||
- 再看 `src/pages/demo/demo/index.tsx` 的标准 React CRUD 骨架。
|
||||
- 新增标准生成能力时优先对照当前仓库 `gen/index.tsx.ftl`、`gen/api.ts.ftl`、`gen/types.ts.ftl`;boot4 generator 只用于核对字段、权限和接口。
|
||||
- 生成或修改 `api/index.ts`、`types.ts`、`pages/.../index.tsx`。
|
||||
- 使用 `request<R<PageResult<T>>>`、`ProTable`、`ModalForm`、`RowActions`、`useTableSelection`、`useTableExport`。
|
||||
- 日期范围、字典 options、确认框、loading 等优先使用 `useDateRangeQuery`、`dictOptions`、`confirmAction`、`useLoading` 等项目工具。
|
||||
- 先看 `src/api/system/client/*` 和 `src/views/system/client/index.vue` 是否已存在。
|
||||
- 再看 `src/views/demo/demo/index.vue` 的标准 hooks 版 CRUD 骨架。
|
||||
- 对照项目内 `gen/api.ts.ftl`、`gen/types.ts.ftl`、`gen/index.vue.ftl`。
|
||||
- 生成或修改 `api/index.ts`、`types.ts`、`views/.../index.vue`。
|
||||
- 使用 `AxiosPromise` from `@/utils/api-types`、`PageResult` from `@/api/types`、`useLoading`、`useFormDialog`、`useSearchReset`、`useTableSelection`。
|
||||
|
||||
## 案例 2:新增树表页面
|
||||
|
||||
@ -25,33 +24,17 @@
|
||||
|
||||
```text
|
||||
使用 $frontend-crud-coding 为 demo/tree2 新增树表 CRUD,接口返回数组,字段包含 id、parentId、name、orderNum。
|
||||
参考 src/pages/demo/tree/index.tsx 和 workflow/category。
|
||||
参考 src/views/demo/tree/index.vue 和 workflow/category。
|
||||
```
|
||||
|
||||
### 期望执行方式
|
||||
|
||||
- 判断这是树表,不生成分页 `PageResult` 页面。
|
||||
- API 列表返回 `R<Tree2VO[]>`。
|
||||
- 优先判断这是树表,不生成分页 `PageResult` 页面。
|
||||
- API 列表返回 `AxiosPromise<Tree2VO[]>`。
|
||||
- `Query` 不继承 `PageQuery`。
|
||||
- 页面使用 `handleTree`、`pagination={false}`、`useTreeTableExpand`、`ProFormTreeSelect`。
|
||||
- 页面使用 `handleTree`、`row-key`、`tree-props`、`useTreeTableExpand`、`el-tree-select`。
|
||||
- 新增子节点时从当前行带入 `parentId`。
|
||||
|
||||
## 案例 2.1:优化 React 代码生成模板
|
||||
|
||||
### 用户提问示例
|
||||
|
||||
```text
|
||||
使用 $frontend-crud-coding 优化当前项目 gen 目录下的 React 代码生成模板,让生成页面继续使用新加的工具。
|
||||
```
|
||||
|
||||
### 期望执行方式
|
||||
|
||||
- 读取 `gen/api.ts.ftl`、`gen/types.ts.ftl`、`gen/index.tsx.ftl`、`gen/index-tree.tsx.ftl`。
|
||||
- 保持 FreeMarker 变量、宏和文件名不变。
|
||||
- 普通表模板继续使用 `useTableSelection`、`useTableExport`、`useDateRangeQuery`、`dictOptions`、`confirmAction`。
|
||||
- 树表模板继续使用 `handleTree`、`filterTree`、`useTreeTableExpand`、`dictOptions`。
|
||||
- 输出仍是 React TSX 项目代码,不写 Vue `src/views`、Element Plus 或 `AxiosPromise`。
|
||||
|
||||
## 案例 3:修改已有复杂列表页
|
||||
|
||||
### 用户提问示例
|
||||
@ -59,15 +42,15 @@
|
||||
```text
|
||||
使用 $frontend-crud-coding 修改 system/user 页面:
|
||||
1. 新增一个创建时间快捷筛选
|
||||
2. 导出按钮保留在工具栏中
|
||||
3. 保持现有树筛选、导入、详情抽屉和角色分配不变
|
||||
2. 导出按钮保留在更多菜单中
|
||||
3. 保持现有树筛选、导入、列显隐、详情抽屉和角色分配不变
|
||||
```
|
||||
|
||||
### 期望执行方式
|
||||
|
||||
- 判断这是“已有复杂页面增强”,不是重新生成 CRUD。
|
||||
- 优先阅读 `src/pages/system/user/index.tsx` 和相关子组件。
|
||||
- 保留 `TreePanel`、导入弹窗、`UserDetailDrawer`、角色分配路由、权限控制。
|
||||
- 优先阅读 `src/views/system/user/index.vue`。
|
||||
- 保留 `TreePanel`、导入弹窗、`right-toolbar` 列显隐、`UserViewDrawer`、角色分配路由、权限控制。
|
||||
- 只增量修改搜索和查询参数处理。
|
||||
|
||||
## 案例 4:修改 workflow 页面
|
||||
@ -80,8 +63,8 @@
|
||||
|
||||
### 期望执行方式
|
||||
|
||||
- 优先看 `src/pages/workflow/category/index.tsx` 和 `src/api/workflow/category/*`。
|
||||
- 判断是否需要后端新增导出接口;前端导出路径保持 `/workflow/category/export`。
|
||||
- 优先看 `src/views/workflow/category/index.vue` 和 `src/api/workflow/category/*`。
|
||||
- 判断是否需要后端新增导出接口;前端导出路径保持 `workflow/category/export`。
|
||||
- 不迁移 system/user 的用户专属逻辑。
|
||||
- 保留树表、`useTreeTableExpand`、`handleTree` 和分类弹窗逻辑。
|
||||
|
||||
@ -96,8 +79,8 @@
|
||||
### 期望执行方式
|
||||
|
||||
- 只维护 `src/api/monitor/cache/index.ts` 和 `src/api/monitor/cache/types.ts`。
|
||||
- 仍然检查同目录 monitor API 的命名、返回结构和导出风格。
|
||||
- 返回类型使用 `R<T>` 和 `PageResult<T>` from `@/api/types`。
|
||||
- 仍然检查同目录 monitor API 的 `export function` / `export const` 风格。
|
||||
- 返回类型使用 `AxiosPromise` from `@/utils/api-types`。
|
||||
- 不创建页面,不改路由。
|
||||
|
||||
## 案例 6:接入后端新增状态切换接口
|
||||
@ -105,26 +88,26 @@
|
||||
### 用户提问示例
|
||||
|
||||
```text
|
||||
使用 $frontend-crud-coding 给 system/client 页面接入 PUT /system/client/changeStatus,状态字段 status,参考 system/user。
|
||||
使用 $frontend-crud-coding 给 system/client 页面接入 PUT /system/client/changeStatus,状态字段 status,参考 gen 模板。
|
||||
```
|
||||
|
||||
### 期望执行方式
|
||||
|
||||
- API 增加 `changeClientStatus(id, status)`。
|
||||
- types 确认 `status` 类型是 string、number 还是 boolean。
|
||||
- 表格列用 Ant Design `Switch`,checked 值跟后端字段类型一致。
|
||||
- 切换失败时回滚原状态或刷新列表。
|
||||
- 表格列用 `el-switch`,active/inactive 值跟后端字段类型一致。
|
||||
- 切换失败时回滚原状态。
|
||||
- 权限使用 `system:client:edit` 或后端实际权限。
|
||||
|
||||
## 推荐的高质量任务描述
|
||||
|
||||
```text
|
||||
使用 $frontend-crud-coding 在当前 React 前端项目中增强 `/system/notice` 列表页:
|
||||
使用 $frontend-crud-coding 在当前前端项目中新增 `/system/notice` 列表页增强:
|
||||
1. 保留现有页面
|
||||
2. 新增状态筛选和导出
|
||||
3. API 路径沿用后端接口
|
||||
4. 参考 system/config 的工具栏与导出交互
|
||||
5. 参考当前项目 gen 模板和 boot4 generator 字段补齐缺失 types
|
||||
5. 参考 plus-ui Gitee 仓库 6.X-Vue 分支的 gen 模板补齐缺失 types
|
||||
```
|
||||
|
||||
## 不推荐的任务描述
|
||||
|
||||
@ -2,36 +2,41 @@
|
||||
|
||||
## 优先参考的代码来源
|
||||
|
||||
- 默认远端基线:`https://gitee.com/JavaLionLi/plus-ui`,分支 `6.X-Vue`。引用远端文件时必须同时写明 `branch=6.X-Vue` 和仓库内路径。
|
||||
- 当前目标目录下最近似页面、API、types。
|
||||
- 当前仓库内置 React 生成模板:`gen/api.ts.ftl`、`gen/types.ts.ftl`、`gen/index.tsx.ftl`、`gen/index-tree.tsx.ftl`。
|
||||
- 标准单表:`src/pages/demo/demo/index.tsx`、`src/api/demo/demo/index.ts`、`src/api/demo/demo/types.ts`。
|
||||
- 树表:`src/pages/demo/tree/index.tsx`、`src/pages/workflow/category/index.tsx`。
|
||||
- 复杂系统页:`src/pages/system/user/index.tsx`、`src/pages/system/role/index.tsx`、`src/pages/system/post/index.tsx`、`src/pages/system/config/index.tsx`。
|
||||
- workflow 页:`src/pages/workflow/*`、`src/api/workflow/*`。
|
||||
- 监控页:`src/pages/monitor/*`、`src/api/monitor/*`。
|
||||
- 公共 hooks:`src/hooks/useTableSelection.ts`、`src/hooks/useTableExport.ts`、`src/hooks/useDict.ts`、`src/hooks/useDateRangeQuery.ts`、`src/hooks/useTreeTableExpand.ts`、`src/hooks/useLoading.ts`、`src/hooks/useSearchReset.ts`。
|
||||
- 公共组件:`src/components/common/RowActions.tsx`、`TreePanel.tsx`、`RightToolbar.tsx`、`DictTag.tsx`、上传/预览组件。
|
||||
- 工具函数:`src/utils/ruoyi.ts`、`src/utils/permission.ts`、`src/utils/download.ts`、`src/utils/dict.ts`、`src/utils/modal.ts`。
|
||||
- 标准单表:`src/views/demo/demo/index.vue`、`src/api/demo/demo/index.ts`、`src/api/demo/demo/types.ts`。
|
||||
- 树表:`src/views/demo/tree/index.vue`、`src/views/workflow/category/index.vue`。
|
||||
- 复杂系统页:`src/views/system/user/index.vue`、`src/views/system/role/index.vue`、`src/views/system/post/index.vue`、`src/views/system/config/index.vue`。
|
||||
- workflow 页:`src/views/workflow/*`、`src/api/workflow/*`。
|
||||
- 监控页:`src/views/monitor/*`、`src/api/monitor/*`。
|
||||
- 公共 hooks:`src/hooks/async/useLoading.ts`、`src/hooks/dialog/*`、`src/hooks/form/*`、`src/hooks/table/*`、`src/hooks/tree/*`。
|
||||
- 项目内 generator 模板:
|
||||
`gen/api.ts.ftl`
|
||||
`gen/types.ts.ftl`
|
||||
`gen/index.vue.ftl`
|
||||
`gen/index-tree.vue.ftl`
|
||||
|
||||
## 基础栈与格式
|
||||
|
||||
- 技术栈是 React + TypeScript + Umi Max + Ant Design + ProComponents + ahooks + Zustand + TanStack Query。
|
||||
- 技术栈是 Vue 3 + TypeScript + Element Plus + Vite + Pinia。
|
||||
- 包管理按仓库现状使用 pnpm。
|
||||
- `.editorconfig` 要求 UTF-8、2 空格缩进。
|
||||
- 当前仓库没有 Prettier;格式化使用 `pnpm run fmt`,lint 使用 `pnpm lint`。
|
||||
- `.editorconfig` 要求 UTF-8、LF、2 空格缩进。
|
||||
- 当前仓库没有 `.prettierrc`;格式化使用 `pnpm run fmt`,lint 使用 `pnpm lint`。
|
||||
- 不要在一个页面里混入与仓库不一致的格式和写法。
|
||||
- 不要把本机绝对路径写进 skill 输出或参考文档;跨环境参考统一使用 Gitee 仓库 URL、分支和仓库内相对路径。
|
||||
|
||||
## API 文件规则
|
||||
|
||||
- 标准 API 文件放在 `src/api/<module>/<business>/index.ts`,同目录维护 `types.ts`。
|
||||
- import 顺序优先跟随附近文件,标准形式:
|
||||
`import type { PageResult, R } from '@/api/types';`
|
||||
`import request from '@/api/request';`
|
||||
`import type { XxxForm, XxxQuery, XxxVO } from './types';`
|
||||
- 不要从 `axios` 引入 `AxiosPromise`;当前 `request<T>()` 返回 `Promise<T>`,接口函数通常返回 `Promise<R<T>>`。
|
||||
- 列表分页接口通常返回 `request<R<PageResult<XxxVO>>>({ url, method: 'get', params: query })`。
|
||||
- 树表列表接口通常返回 `request<R<XxxVO[]>>({ url, method: 'get', params: query })`。
|
||||
- 详情接口返回 `R<XxxVO>`;复杂详情返回单独的 `InfoVO`。
|
||||
- import 顺序优先跟随附近文件,标准生成页常见形式:
|
||||
`import type { XxxForm, XxxQuery, XxxVO } from '@/api/<module>/<business>/types';`
|
||||
`import type { PageResult } from '@/api/types';`
|
||||
`import type { AxiosPromise } from '@/utils/api-types';`
|
||||
`import request from '@/utils/request';`
|
||||
- 不要从 `axios` 引入 `AxiosPromise`。
|
||||
- 列表分页接口通常返回 `AxiosPromise<PageResult<XxxVO>>`。
|
||||
- 树表列表接口通常返回 `AxiosPromise<XxxVO[]>`。
|
||||
- 详情接口返回 `AxiosPromise<XxxVO>`;复杂详情返回单独的 `InfoVO`。
|
||||
- 标准函数命名:
|
||||
`listXxx` -> `GET /<module>/<business>/list`
|
||||
`getXxx` -> `GET /<module>/<business>/{id}`
|
||||
@ -41,8 +46,8 @@
|
||||
`changeXxxStatus` -> `PUT /<module>/<business>/changeStatus`
|
||||
- query string 用 `params`,请求体用 `data`。
|
||||
- 加密、防重复提交等 headers 直接写在请求配置里,例如用户重置密码中的 `isEncrypt`、`repeatSubmit`。
|
||||
- 当前仓库多数 API 使用 `export function`;新增标准 CRUD 优先跟随相邻模块。
|
||||
- 只有相邻模块已有聚合对象时才新增默认导出。
|
||||
- 当前仓库有些 API 使用 `export const`,有些使用 `export function`;新增标准 CRUD 优先跟随 `gen/api.ts.ftl` 和相邻模块。
|
||||
- 只有相邻模块已有 `export default { ... }` 聚合时才新增默认导出。
|
||||
|
||||
## 类型文件规则
|
||||
|
||||
@ -52,103 +57,100 @@
|
||||
- 树表 `Query` 通常不继承 `PageQuery`。
|
||||
- ID 字段通常使用 `string | number`,批量删除参数使用 `string | number | Array<string | number>`。
|
||||
- Java 数值类型映射为 `number`,Boolean 映射为 `boolean`,日期/文本默认 `string`。
|
||||
- 日期范围查询保留 `params?: Record<string, unknown>` 或跟随相邻页面现有类型,不要因为它看起来宽松就删掉。
|
||||
- 日期范围查询保留 `params?: any`,不要因为它看起来宽松就删掉。
|
||||
- 列表对象、表单对象、查询对象职责分开;字段不一致时不要强行复用一个接口。
|
||||
- 能明确写出类型时不要用 `any`;组件库、字典或历史接口确实无法收窄时再保留。
|
||||
|
||||
## React 页面结构规则
|
||||
## Vue 页面结构规则
|
||||
|
||||
- 页面默认导出函数组件,命名如 `SystemUserPage`、`DemoDemoPage`、`WorkflowCategoryPage`。
|
||||
- 标准页使用 `PageContainer` 包裹 `ProTable` 和 `ModalForm`。
|
||||
- 标准列表页常见状态:
|
||||
`actionRef`、`form`、`modalOpen`、`modalTitle`、`ids`、`selectedOne`、权限布尔值。
|
||||
- 弹窗状态优先使用 `useBoolean(false)`,表单用 `Form.useForm<XxxForm>()`。
|
||||
- ProTable 使用:
|
||||
`rowKey`
|
||||
`columns`
|
||||
`search={{ labelWidth: 90 }}`
|
||||
`pagination={{ defaultPageSize: 10, showSizeChanger: true }}`
|
||||
`request={async params => { ... }}`
|
||||
`toolBarRender={() => [...]}`
|
||||
- 分页查询使用 `toPageQuery(params)`,然后 `updateExportParams(query)`,最后 `return toTableData(res)`。
|
||||
- 表格列类型使用 `ProColumns<XxxVO>[]`。
|
||||
- 新增/编辑弹窗使用 `ModalForm<XxxForm>`,字段优先使用 `ProFormText`、`ProFormDigit`、`ProFormSelect`、`ProFormTreeSelect`、`ProFormDateTimeRangePicker` 等 ProForm 组件。
|
||||
- ModalForm 成功提交后返回 `true`,并刷新 `actionRef.current?.reload()`。
|
||||
- 页面优先使用 `<script setup name="Xxx" lang="ts">`。
|
||||
- 标准根节点使用 `class="p-2 app-container <module>-<business>-page"`;已有页面是特殊布局时保持原样。
|
||||
- 标准列表页结构:
|
||||
搜索卡片 `search-panel`
|
||||
表格卡片 `table-panel`
|
||||
工具栏 `toolbar-shell`
|
||||
表格 `data-table`
|
||||
`right-toolbar`
|
||||
`pagination`
|
||||
新增/编辑 `el-dialog`
|
||||
- 搜索区通过 `useSearchToggle` 控制 `showSearch`,头部点击切换。
|
||||
- 列表 loading 通过 `useLoading(true)` 和 `withLoading`。
|
||||
- 选择状态通过 `useTableSelection<XxxVO>(item => item.id)` 返回 `ids`、`single`、`multiple`、`handleSelectionChange`。
|
||||
- 表单弹窗优先使用 `useFormDialog({ form, formRef, initialFormData })`,返回 `dialog`、`resetForm`、`openDialog`、`showDialog`、`closeDialog`。
|
||||
- 仅需要简单弹窗状态或一个页面多个弹窗时使用 `useDialogState`。
|
||||
- 日期范围查询优先使用 `useDateRangeQuery()`;带后端参数名时使用 `useDateRangeQuery('CreateTime')` 等相邻页面模式。
|
||||
- 查询和表单状态通常放在 `reactive<PageData<Form, Query>>({ form, queryParams, rules })`,再 `toRefs(data)`。
|
||||
|
||||
## 页面行为规则
|
||||
|
||||
- `openAdd` 负责 `form.resetFields()`、设置默认值、设置标题、打开弹窗。
|
||||
- `openEdit` 先按行或 `selectedOne` 找目标,再查详情,`form.resetFields()`,`form.setFieldsValue(res.data)`,最后打开弹窗。
|
||||
- `submitForm` 根据主键判断新增或修改,成功后 `message.success('操作成功')`、重置表单、刷新列表。
|
||||
- `remove` 或 `handleDelete` 支持行删除和批量删除,成功后 `message.success('删除成功')`、`clearSelection()`、`reloadAndRest` 或 `reload`。
|
||||
- 批量按钮通常用 `Popconfirm`,行操作确认优先放在 `RowActions` 的 `confirm`。
|
||||
- 状态切换失败时要回滚或刷新,参考 `src/pages/system/user/index.tsx`。
|
||||
- 日期范围查询优先使用 `useDateRangeQuery`,底层通过 `addDateRange` 写入 `params.beginXxx/endXxx`。
|
||||
- 字典 options 优先使用 `dictOptions(dicts.xxx)`。
|
||||
- 导入上传参考 `system/user/components/UserImportModal.tsx` 或流程定义导入弹窗,保留 `globalHeaders()` 和 `appEnv.baseApi` 相关方式。
|
||||
- `getList` 负责设置 loading、调用列表接口、回填列表和 `total`。
|
||||
- `handleQuery` 先把 `queryParams.value.pageNum = 1`,再调用 `getList()`;树表无分页时只调用 `getList()`。
|
||||
- `resetQuery` 使用 `useSearchReset`,分页页传 `pageNumKey: 'pageNum'`,需要时传 `pageSizeKey` 和 `resetExtras`。
|
||||
- `handleAdd` 使用 `openDialog('添加xxx')`;如果有树/联动选项,打开前后按现有页面加载选项。
|
||||
- `handleUpdate` 先 `reset()`,再按行或 `ids.value[0]` 查详情,`Object.assign(form.value, res.data)`,最后 `showDialog('修改xxx')`。
|
||||
- `submitForm` 表单校验通过后设置 `buttonLoading`,根据主键判断新增或修改,成功后 `modal.msgSuccess('操作成功')`、关闭弹窗、刷新列表。
|
||||
- `handleDelete` 使用 `modal.confirm(...)` 二次确认,再调用删除接口,成功提示并刷新。
|
||||
- `handleExport` 使用 `requestDownload('<module>/<business>/export', { ...queryParams.value }, '<business>_<timestamp>.xlsx')`。
|
||||
- 状态切换失败时要把 switch 值回滚,参考 generator 模板和现有 `system/user`、`system/role`。
|
||||
- 导入上传使用 `globalHeaders()`、`import.meta.env.VITE_APP_BASE_API`、`ElUpload`,优先参考 `system/user` 或流程定义页面。
|
||||
|
||||
## 字典、权限与公共工具
|
||||
|
||||
- 字典使用 `const dicts = useDict('sys_normal_disable', 'sys_user_gender')`。
|
||||
- 字典 options 使用 `dictOptions` from `@/utils/dict` 映射为 `{ label, value }`。
|
||||
- 权限使用:
|
||||
`const userInfo = useUserStore(state => state.userInfo);`
|
||||
`const canAdd = hasPermi(userInfo, ['system:user:add']);`
|
||||
- 不要使用 Vue 权限指令 `v-hasPermi`。
|
||||
- 字典使用 `useDict`:
|
||||
`const { sys_normal_disable } = toRefs<any>(useDict('sys_normal_disable'));`
|
||||
- 新增代码默认使用当前项目主流 `v-hasPermi`。
|
||||
- 如果正在修改的文件已经混用或使用 `v-has-permi`,保持同文件现状,不为统一写法而重排无关代码。
|
||||
- `el-dropdown-item` 延迟加载导致权限指令不可靠时,使用 `v-if="checkPermi([...])"`,参考 `system/user`。
|
||||
- 常用工具:
|
||||
`hasPermi` from `@/utils/permission`
|
||||
`handleTree`、`parseStrEmpty`、`toPageQuery`、`toTableData`、`addDateRange`、`formatDateTimeRange` from `@/utils/ruoyi`
|
||||
`dictOptions` from `@/utils/dict`
|
||||
`confirmAction`、`confirmTitleSafe` from `@/utils/modal`
|
||||
`download` via `useTableExport`
|
||||
`modal` from `@/plugins/modal`
|
||||
`download as requestDownload` from `@/utils/request`
|
||||
`useDict` from `@/utils/dict`
|
||||
`checkPermi` from `@/utils/permission`
|
||||
`handleTree`、`parseStrEmpty` from `@/utils/ruoyi`
|
||||
|
||||
## 组件与样式规则
|
||||
|
||||
- 行操作优先复用 `RowActions`,并使用 `@ant-design/icons` 图标。
|
||||
- 树筛选优先复用 `TreePanel`,参考 `system/user`。
|
||||
- 导出优先复用 `useTableExport`。
|
||||
- 多选优先复用 `useTableSelection`。
|
||||
- 异步 loading 优先复用 `useLoading`,不要重复手写 `setLoading(true)` + `finally setLoading(false)`。
|
||||
- ProTable 重置后还要清空额外筛选状态时,优先复用 `useSearchReset`。
|
||||
- Promise 式确认框优先复用 `confirmAction` 或 `confirmTitleSafe`。
|
||||
- 标准页面尽量使用 `PageContainer`、`ProTable`、ProComponents 页面壳,不堆大量内联样式。
|
||||
- 需要自定义布局时先查 `src/assets/styles` 是否已有 `.tree-table-page`、`.table-panel`、`.page-surface` 等类。
|
||||
- 优先复用公共组件:`right-toolbar`、`pagination`、`DictTag` / `dict-tag`、`ImageUpload` / `image-upload`、`ImagePreview` / `image-preview`、`FileUpload` / `file-upload`、`Editor` / `editor`、`TreePanel`。
|
||||
- 标准页面尽量使用已有页面壳类,不堆大量内联样式。
|
||||
- 复杂页面需要 scoped SCSS 时优先复用:
|
||||
`@use '@/assets/styles/components/page-shell' as pageShell;`
|
||||
再按现有 mixin 使用。
|
||||
- 不要为了单页需求修改全局组件样式。
|
||||
- 类名保持模块语义:`system-client-page`、`workflow-category-page`、`demo-demo-page`。
|
||||
|
||||
## 树表规则
|
||||
|
||||
- 树表列表接口通常返回数组,页面通过 `handleTree<T>(res.data || [], 'id', 'parentId')` 组树。
|
||||
- `ProTable` 使用 `pagination={false}`。
|
||||
- 使用 `useTreeTableExpand` 控制 `expandedRowKeys`、`onExpandedRowsChange`、`syncExpandedRows`、`toggleExpandAll`。
|
||||
- 展开/折叠按钮参考 `demo/tree` 和 `workflow/category`,用 `SortAscendingOutlined`。
|
||||
- 表单中上级节点使用 `ProFormTreeSelect`。
|
||||
- 树表列表接口通常返回数组,页面通过 `handleTree<T>(res.data, 'id', 'parentId')` 组树。
|
||||
- 使用 `row-key`、`:tree-props="{ children: 'children', hasChildren: 'hasChildren' }"`。
|
||||
- 展开/折叠使用 `useTreeTableExpand`。
|
||||
- 表单中上级节点使用 `el-tree-select`。
|
||||
- 新增子节点时从当前行回填 `parentId`。
|
||||
- 删除确认文案优先使用业务名称,而不是批量 ID 文案。
|
||||
|
||||
## 与 gen 模板、Vue 参考项目和后端生成器的关系
|
||||
## 与 gen 模板的关系
|
||||
|
||||
- 当前仓库 `gen/*.ftl` 是 React 版内置代码生成模板,是维护生成能力时的第一参考。
|
||||
- `gen/index.tsx.ftl`、`gen/index-tree.tsx.ftl` 内容必须生成 React TSX 页面。
|
||||
- 修改公共 hooks/工具后,如果它们能简化标准生成页,要同步评估 `gen/*.ftl` 是否需要更新。
|
||||
- Vue skill 和 `.claude/agents` 提供的是任务分型、优先级、增量修改、自检方式,不是 React 实现模板。
|
||||
- boot4 后端 generator 模板可用于确认字段、权限、导出、状态切换、排序、日期范围等,但不是最终答案。
|
||||
- 当前 React 项目的核心骨架是 `ProTable`、`ModalForm`、`RowActions`、`useTableSelection`、`useTableExport`。
|
||||
- 修改已有页面时,不要把现有强业务逻辑替换回 generator 的简化逻辑。
|
||||
- `gen` 是 plus-ui 前端项目内的生成模板;默认远端位置是 `https://gitee.com/JavaLionLi/plus-ui/tree/6.X-Vue/gen`,分支必须标记为 `6.X-Vue`。
|
||||
- 本地仓库存在对应文件时优先读本地 `gen`,但不要把本地绝对路径写入 skill 或交付内容。
|
||||
- 新增标准单表页面时读取 `gen/index.vue.ftl`、`gen/api.ts.ftl`、`gen/types.ts.ftl`。
|
||||
- 新增树表页面时读取 `gen/index-tree.vue.ftl`、`gen/api.ts.ftl`、`gen/types.ts.ftl`。
|
||||
- `gen` 模板是标准骨架,不是最终答案;落地时仍要对照目标模块真实页面和公共 hooks。
|
||||
- 当前前端项目已经把生成页升级为 hooks 版:`useLoading`、`useFormDialog`、`useSearchReset`、`useTableSelection`、`useDateRangeQuery`。
|
||||
- 新增标准 CRUD 时,先从 `gen` 确认字段、权限、导出、状态切换、排序、日期范围等,再落成当前项目的实际页面壳。
|
||||
- 修改已有页面时,不要把现有强业务逻辑替换回 `gen` 的简化逻辑。
|
||||
|
||||
## 验证规则
|
||||
|
||||
- 只改文档、skill 或 `gen/*.ftl` 模板:至少运行 skill 基础校验或 `git diff --check`;如果模板改动依赖项目工具,优先再跑 `pnpm lint`。
|
||||
- 改前端 TS/TSX/API/types:优先运行 `pnpm exec tsc --noEmit` 或 `pnpm lint`。
|
||||
- 改页面、import、权限或较多文件:运行 `pnpm lint`。
|
||||
- 只改文档或 skill:运行 skill 基础校验即可。
|
||||
- 改前端 TS/Vue/API/types:优先运行 `pnpm exec vue-tsc --noEmit`。
|
||||
- 改页面模板、import、权限或较多文件:再运行 `pnpm lint`。
|
||||
- 改公共 hooks、组件、构建相关或大范围页面:再运行 `pnpm build`。
|
||||
- 如果验证因为环境、依赖或权限失败,交付时说明失败命令和原因。
|
||||
|
||||
## 避免事项
|
||||
|
||||
- 不要从 `axios` 引入 `AxiosPromise`。
|
||||
- 不要绕开 `@/api/request` 或 `useTableExport` 自造请求/下载封装。
|
||||
- 不要绕开 `request` 或 `requestDownload` 自造请求/下载封装。
|
||||
- 不要跳过 `types.ts`,把类型全写在页面里。
|
||||
- 不要把 `src/pages` 写成 Vue 项目的 `src/views`。
|
||||
- 不要使用 Vue 的 `ref`、`reactive`、`toRefs`、`v-hasPermi`、Element Plus 组件或 SCSS 页面壳。
|
||||
- 不要删除日期范围 `params`、权限判断、导出、导入、树筛选、列显隐等现有能力。
|
||||
- 不要删除日期范围 `params`、权限指令、导出、导入、树筛选、列显隐等现有能力。
|
||||
- 不要为了“更整洁”重写复杂页面的大块业务逻辑。
|
||||
- 不要在新增标准页里使用与仓库不一致的 UI 壳或状态管理方式。
|
||||
|
||||
@ -1,9 +1,21 @@
|
||||
# 告诉EditorConfig插件,这是根文件,不用继续往上查找
|
||||
root = true
|
||||
|
||||
# 匹配全部文件
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
# 缩进风格,可选space、tab
|
||||
indent_style = space
|
||||
# 缩进的空格数
|
||||
indent_size = 2
|
||||
# 设置字符集
|
||||
charset = utf-8
|
||||
# 结尾换行符,可选lf、cr、crlf
|
||||
end_of_line = lf
|
||||
# 在文件结尾插入新行
|
||||
trim_trailing_whitespace = true
|
||||
# 删除一行中的前后空格
|
||||
insert_final_newline = true
|
||||
|
||||
[*.md]
|
||||
insert_final_newline = false
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
@ -1,15 +1,42 @@
|
||||
VITE_APP_TITLE=RuoYi-React-Plus后台管理系统
|
||||
VITE_APP_LOGO_TITLE=RuoYi-React-Plus
|
||||
VITE_APP_ENV=development
|
||||
VITE_APP_BASE_API=/dev-api
|
||||
VITE_APP_CONTEXT_PATH=/
|
||||
VITE_APP_PORT=8000
|
||||
VITE_APP_CLIENT_ID=e5cd7e4891bf95d1d19206ce24a7b32e
|
||||
VITE_APP_MESSAGE_ENABLED=true
|
||||
VITE_APP_MESSAGE_TRANSPORT=sse
|
||||
VITE_APP_MESSAGE_PATH=/resource/message
|
||||
VITE_APP_ENCRYPT=true
|
||||
VITE_APP_RSA_PUBLIC_KEY=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDvEDuRIOM3oZPWj9Ukoc5pQklR4PFH6/clnjeFqjDLIgDyQvjxhgqAZQA+E9eD6qu6FsXPmK8djcL+nh3cFHz4pX473jDvO3Sve+8yL3VRQ0n2pRgQ2a01MJsy+WwTZCBYWf0VnLRIvANUoWQgy9vz94q7Va44dg7A1/3ICf+xAwIDAQAB
|
||||
VITE_APP_RSA_PRIVATE_KEY=MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAObq7yrxfvyieZtTjAYyrdvi59tYTXxjO5ajmPCRSXBY9M9wQ1tli297JN6mnY53UJMNyOFNSZVi8WSFoIXjpR87FmvChJlzeN/dZdd3SEs48Ee66XKeSePYqxa8oO5GKDsnajgpsOHKXSeeVSIysiIPS2/WsEqk0In9P4w3RsRFAgMBAAECgYBiMEWwce24SPICnRzuScBpvmsudrbEDIH7BOd0a6LZlcnLJwZNJ7mJlshPsHNQb+WgEf135+BBGEhioPtn0yuTdEuKP4kB9UdYUKiayWCoWhJpesv7sAD4RDClV7dhuV+gcd1AXD+YzyRIPbGm0VC2U+4q8/+UPRpVjqskbLVTgQJBAPRpou7g3S8n4XB527kq0D8I3+ZYwMxZhszwhrCDpJU319+ucmpLVwYIzDmZVeID2QQdUaDfIEViFHu95xDrGiUCQQDx3YOKn3yaEctk/ERVn7hDAyAXUbd8/pv2b24/M/l1ZevlsFem8U4Jk5Mu64t3z3YGJoymEjQmbucwT01iKhehAkEAxlnccsRmfFh/KkqauKE4M4++NTAd9zlInpUsmZ+cN8UEGnF2RTEzRKBrLOt1uWCqBB7PGiE6DVTVjr7FAQPrSQJAT5yeY87DcONSk9cFlzmPqV8p/QME5rvYEnHzVBKDlkUKNPyqnWToTvaoh9U4fyNmsfeWbEOprszqhFhWHG3GgQJAK8+ynmyFhaw63+Hx2KU5zR4hVuQso2IzrEurGxCxybV6mR7VBerb4502+EPx3PmOgxQL+niUFhcMWxcvBFP9+A==
|
||||
VITE_APP_MONITOR_ADMIN=http://localhost:9090/admin/applications
|
||||
VITE_APP_SNAILJOB_ADMIN=http://localhost:8800/snail-job
|
||||
# 页面标题
|
||||
VITE_APP_TITLE = RuoYi-Vue-Plus后台管理系统
|
||||
VITE_APP_LOGO_TITLE = RuoYi-Vue-Plus
|
||||
|
||||
# 开发环境配置
|
||||
VITE_APP_ENV = 'development'
|
||||
|
||||
# 开发环境
|
||||
VITE_APP_BASE_API = '/dev-api'
|
||||
|
||||
# 应用访问路径 例如使用前缀 /admin/
|
||||
VITE_APP_CONTEXT_PATH = '/'
|
||||
|
||||
# 监控地址
|
||||
VITE_APP_MONITOR_ADMIN = 'http://localhost:9090/admin/applications'
|
||||
|
||||
# SnailJob 控制台地址
|
||||
VITE_APP_SNAILJOB_ADMIN = 'http://localhost:8800/snail-job'
|
||||
|
||||
# SnailAI 控制台地址
|
||||
VITE_APP_SNAILAI_ADMIN = 'http://localhost:8900/snail-ai'
|
||||
|
||||
VITE_APP_PORT = 80
|
||||
|
||||
# 接口加密功能开关(如需关闭 后端也必须对应关闭)
|
||||
VITE_APP_ENCRYPT = true
|
||||
# 接口加密传输 RSA 公钥与后端解密私钥对应 如更换需前后端一同更换
|
||||
VITE_APP_RSA_PUBLIC_KEY = 'MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDvEDuRIOM3oZPWj9Ukoc5pQklR4PFH6/clnjeFqjDLIgDyQvjxhgqAZQA+E9eD6qu6FsXPmK8djcL+nh3cFHz4pX473jDvO3Sve+8yL3VRQ0n2pRgQ2a01MJsy+WwTZCBYWf0VnLRIvANUoWQgy9vz94q7Va44dg7A1/3ICf+xAwIDAQAB'
|
||||
# 接口响应解密 RSA 私钥与后端加密公钥对应 如更换需前后端一同更换
|
||||
VITE_APP_RSA_PRIVATE_KEY = 'MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAObq7yrxfvyieZtTjAYyrdvi59tYTXxjO5ajmPCRSXBY9M9wQ1tli297JN6mnY53UJMNyOFNSZVi8WSFoIXjpR87FmvChJlzeN/dZdd3SEs48Ee66XKeSePYqxa8oO5GKDsnajgpsOHKXSeeVSIysiIPS2/WsEqk0In9P4w3RsRFAgMBAAECgYBiMEWwce24SPICnRzuScBpvmsudrbEDIH7BOd0a6LZlcnLJwZNJ7mJlshPsHNQb+WgEf135+BBGEhioPtn0yuTdEuKP4kB9UdYUKiayWCoWhJpesv7sAD4RDClV7dhuV+gcd1AXD+YzyRIPbGm0VC2U+4q8/+UPRpVjqskbLVTgQJBAPRpou7g3S8n4XB527kq0D8I3+ZYwMxZhszwhrCDpJU319+ucmpLVwYIzDmZVeID2QQdUaDfIEViFHu95xDrGiUCQQDx3YOKn3yaEctk/ERVn7hDAyAXUbd8/pv2b24/M/l1ZevlsFem8U4Jk5Mu64t3z3YGJoymEjQmbucwT01iKhehAkEAxlnccsRmfFh/KkqauKE4M4++NTAd9zlInpUsmZ+cN8UEGnF2RTEzRKBrLOt1uWCqBB7PGiE6DVTVjr7FAQPrSQJAT5yeY87DcONSk9cFlzmPqV8p/QME5rvYEnHzVBKDlkUKNPyqnWToTvaoh9U4fyNmsfeWbEOprszqhFhWHG3GgQJAK8+ynmyFhaw63+Hx2KU5zR4hVuQso2IzrEurGxCxybV6mR7VBerb4502+EPx3PmOgxQL+niUFhcMWxcvBFP9+A=='
|
||||
|
||||
# 客户端id
|
||||
VITE_APP_CLIENT_ID = 'e5cd7e4891bf95d1d19206ce24a7b32e'
|
||||
|
||||
# 统一消息推送开关
|
||||
VITE_APP_MESSAGE_ENABLED = true
|
||||
|
||||
# sse / websocket
|
||||
VITE_APP_MESSAGE_TRANSPORT = 'sse'
|
||||
|
||||
# 统一消息推送路径
|
||||
VITE_APP_MESSAGE_PATH = '/resource/message'
|
||||
|
||||
@ -1,15 +1,45 @@
|
||||
VITE_APP_TITLE=RuoYi-React-Plus后台管理系统
|
||||
VITE_APP_LOGO_TITLE=RuoYi-React-Plus
|
||||
VITE_APP_ENV=production
|
||||
VITE_APP_BASE_API=/prod-api
|
||||
VITE_APP_CONTEXT_PATH=/
|
||||
VITE_APP_PORT=8000
|
||||
VITE_APP_CLIENT_ID=e5cd7e4891bf95d1d19206ce24a7b32e
|
||||
VITE_APP_MESSAGE_ENABLED=true
|
||||
VITE_APP_MESSAGE_TRANSPORT=sse
|
||||
VITE_APP_MESSAGE_PATH=/resource/message
|
||||
VITE_APP_ENCRYPT=true
|
||||
VITE_APP_RSA_PUBLIC_KEY=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDvEDuRIOM3oZPWj9Ukoc5pQklR4PFH6/clnjeFqjDLIgDyQvjxhgqAZQA+E9eD6qu6FsXPmK8djcL+nh3cFHz4pX473jDvO3Sve+8yL3VRQ0n2pRgQ2a01MJsy+WwTZCBYWf0VnLRIvANUoWQgy9vz94q7Va44dg7A1/3ICf+xAwIDAQAB
|
||||
VITE_APP_RSA_PRIVATE_KEY=MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAObq7yrxfvyieZtTjAYyrdvi59tYTXxjO5ajmPCRSXBY9M9wQ1tli297JN6mnY53UJMNyOFNSZVi8WSFoIXjpR87FmvChJlzeN/dZdd3SEs48Ee66XKeSePYqxa8oO5GKDsnajgpsOHKXSeeVSIysiIPS2/WsEqk0In9P4w3RsRFAgMBAAECgYBiMEWwce24SPICnRzuScBpvmsudrbEDIH7BOd0a6LZlcnLJwZNJ7mJlshPsHNQb+WgEf135+BBGEhioPtn0yuTdEuKP4kB9UdYUKiayWCoWhJpesv7sAD4RDClV7dhuV+gcd1AXD+YzyRIPbGm0VC2U+4q8/+UPRpVjqskbLVTgQJBAPRpou7g3S8n4XB527kq0D8I3+ZYwMxZhszwhrCDpJU319+ucmpLVwYIzDmZVeID2QQdUaDfIEViFHu95xDrGiUCQQDx3YOKn3yaEctk/ERVn7hDAyAXUbd8/pv2b24/M/l1ZevlsFem8U4Jk5Mu64t3z3YGJoymEjQmbucwT01iKhehAkEAxlnccsRmfFh/KkqauKE4M4++NTAd9zlInpUsmZ+cN8UEGnF2RTEzRKBrLOt1uWCqBB7PGiE6DVTVjr7FAQPrSQJAT5yeY87DcONSk9cFlzmPqV8p/QME5rvYEnHzVBKDlkUKNPyqnWToTvaoh9U4fyNmsfeWbEOprszqhFhWHG3GgQJAK8+ynmyFhaw63+Hx2KU5zR4hVuQso2IzrEurGxCxybV6mR7VBerb4502+EPx3PmOgxQL+niUFhcMWxcvBFP9+A==
|
||||
VITE_APP_MONITOR_ADMIN=/admin/applications
|
||||
VITE_APP_SNAILJOB_ADMIN=/snail-job
|
||||
# 页面标题
|
||||
VITE_APP_TITLE = RuoYi-Vue-Plus后台管理系统
|
||||
VITE_APP_LOGO_TITLE = RuoYi-Vue-Plus
|
||||
|
||||
# 生产环境配置
|
||||
VITE_APP_ENV = 'production'
|
||||
|
||||
# 应用访问路径 例如使用前缀 /admin/
|
||||
VITE_APP_CONTEXT_PATH = '/'
|
||||
|
||||
# 监控地址
|
||||
VITE_APP_MONITOR_ADMIN = '/admin/applications'
|
||||
|
||||
# SnailJob 控制台地址
|
||||
VITE_APP_SNAILJOB_ADMIN = '/snail-job'
|
||||
|
||||
# SnailAI 控制台地址
|
||||
VITE_APP_SNAILAI_ADMIN = '/snail-ai'
|
||||
|
||||
# 生产环境
|
||||
VITE_APP_BASE_API = '/prod-api'
|
||||
|
||||
# 是否在打包时开启压缩,支持 gzip 和 brotli
|
||||
VITE_BUILD_COMPRESS = gzip
|
||||
|
||||
VITE_APP_PORT = 80
|
||||
|
||||
# 接口加密功能开关(如需关闭 后端也必须对应关闭)
|
||||
VITE_APP_ENCRYPT = true
|
||||
# 接口加密传输 RSA 公钥与后端解密私钥对应 如更换需前后端一同更换
|
||||
VITE_APP_RSA_PUBLIC_KEY = 'MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDvEDuRIOM3oZPWj9Ukoc5pQklR4PFH6/clnjeFqjDLIgDyQvjxhgqAZQA+E9eD6qu6FsXPmK8djcL+nh3cFHz4pX473jDvO3Sve+8yL3VRQ0n2pRgQ2a01MJsy+WwTZCBYWf0VnLRIvANUoWQgy9vz94q7Va44dg7A1/3ICf+xAwIDAQAB'
|
||||
# 接口响应解密 RSA 私钥与后端加密公钥对应 如更换需前后端一同更换
|
||||
VITE_APP_RSA_PRIVATE_KEY = 'MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAObq7yrxfvyieZtTjAYyrdvi59tYTXxjO5ajmPCRSXBY9M9wQ1tli297JN6mnY53UJMNyOFNSZVi8WSFoIXjpR87FmvChJlzeN/dZdd3SEs48Ee66XKeSePYqxa8oO5GKDsnajgpsOHKXSeeVSIysiIPS2/WsEqk0In9P4w3RsRFAgMBAAECgYBiMEWwce24SPICnRzuScBpvmsudrbEDIH7BOd0a6LZlcnLJwZNJ7mJlshPsHNQb+WgEf135+BBGEhioPtn0yuTdEuKP4kB9UdYUKiayWCoWhJpesv7sAD4RDClV7dhuV+gcd1AXD+YzyRIPbGm0VC2U+4q8/+UPRpVjqskbLVTgQJBAPRpou7g3S8n4XB527kq0D8I3+ZYwMxZhszwhrCDpJU319+ucmpLVwYIzDmZVeID2QQdUaDfIEViFHu95xDrGiUCQQDx3YOKn3yaEctk/ERVn7hDAyAXUbd8/pv2b24/M/l1ZevlsFem8U4Jk5Mu64t3z3YGJoymEjQmbucwT01iKhehAkEAxlnccsRmfFh/KkqauKE4M4++NTAd9zlInpUsmZ+cN8UEGnF2RTEzRKBrLOt1uWCqBB7PGiE6DVTVjr7FAQPrSQJAT5yeY87DcONSk9cFlzmPqV8p/QME5rvYEnHzVBKDlkUKNPyqnWToTvaoh9U4fyNmsfeWbEOprszqhFhWHG3GgQJAK8+ynmyFhaw63+Hx2KU5zR4hVuQso2IzrEurGxCxybV6mR7VBerb4502+EPx3PmOgxQL+niUFhcMWxcvBFP9+A=='
|
||||
|
||||
# 客户端id
|
||||
VITE_APP_CLIENT_ID = 'e5cd7e4891bf95d1d19206ce24a7b32e'
|
||||
|
||||
# 统一消息推送开关
|
||||
VITE_APP_MESSAGE_ENABLED = true
|
||||
|
||||
# sse / websocket
|
||||
VITE_APP_MESSAGE_TRANSPORT = 'sse'
|
||||
|
||||
# 统一消息推送路径
|
||||
VITE_APP_MESSAGE_PATH = '/resource/message'
|
||||
|
||||
37
.gitignore
vendored
37
.gitignore
vendored
@ -1,10 +1,29 @@
|
||||
node_modules
|
||||
dist
|
||||
.umi
|
||||
.umi-production
|
||||
src/.umi
|
||||
src/.umi-production
|
||||
.env.local
|
||||
*.log
|
||||
.idea/
|
||||
.DS_Store
|
||||
.history
|
||||
node_modules/
|
||||
dist/
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
**/*.log
|
||||
|
||||
tests/**/coverage/
|
||||
tests/e2e/reports
|
||||
selenium-debug.log
|
||||
|
||||
# Editor directories and files
|
||||
.idea
|
||||
.vscode
|
||||
*.iml
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.local
|
||||
|
||||
package-lock.json
|
||||
yarn.lock
|
||||
|
||||
# 编译生成的文件
|
||||
auto-imports.d.ts
|
||||
components.d.ts
|
||||
|
||||
10
.idea/.gitignore
generated
vendored
10
.idea/.gitignore
generated
vendored
@ -1,10 +0,0 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Ignored default folder with query files
|
||||
/queries/
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
||||
# Editor-based HTTP Client requests
|
||||
/httpRequests/
|
||||
6
.idea/compiler.xml
generated
6
.idea/compiler.xml
generated
@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CompilerConfiguration">
|
||||
<option name="BUILD_PROCESS_HEAP_SIZE" value="2048" />
|
||||
</component>
|
||||
</project>
|
||||
3
.idea/dictionaries/LionLi.xml
generated
3
.idea/dictionaries/LionLi.xml
generated
@ -1,3 +0,0 @@
|
||||
<component name="ProjectDictionaryState">
|
||||
<dictionary name="LionLi" />
|
||||
</component>
|
||||
6
.idea/encodings.xml
generated
6
.idea/encodings.xml
generated
@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="Encoding" defaultCharsetForPropertiesFiles="UTF-8">
|
||||
<file url="PROJECT" charset="UTF-8" />
|
||||
</component>
|
||||
</project>
|
||||
17
.idea/misc.xml
generated
17
.idea/misc.xml
generated
@ -1,17 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectInspectionProfilesVisibleTreeState">
|
||||
<entry key="Project Default">
|
||||
<profile-state>
|
||||
<selected-state>
|
||||
<State>
|
||||
<id>User defined</id>
|
||||
</State>
|
||||
</selected-state>
|
||||
</profile-state>
|
||||
</entry>
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_25" default="true" project-jdk-name="liberica-25" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/out" />
|
||||
</component>
|
||||
</project>
|
||||
8
.idea/modules.xml
generated
8
.idea/modules.xml
generated
@ -1,8 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/plus-ui-react.iml" filepath="$PROJECT_DIR$/.idea/plus-ui-react.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
6
.idea/vcs.xml
generated
6
.idea/vcs.xml
generated
@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
@ -1,6 +1,13 @@
|
||||
{
|
||||
"$schema": "./node_modules/oxfmt/configuration_schema.json",
|
||||
"ignorePatterns": ["src/.umi", "src/.umi-production", ".ai_state", ".claude", ".codex", "dist", "node_modules"],
|
||||
"ignorePatterns": [
|
||||
"src/types/components.d.ts",
|
||||
"src/types/auto-imports.d.ts",
|
||||
".ai_state",
|
||||
".claude",
|
||||
".codex",
|
||||
"doc"
|
||||
],
|
||||
"printWidth": 120,
|
||||
"singleQuote": true,
|
||||
"trailingComma": "none",
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"$schema": "https://raw.githubusercontent.com/oxc-project/oxc/main/npm/oxlint/configuration_schema.json",
|
||||
"plugins": ["eslint", "typescript", "unicorn", "oxc", "import", "react"],
|
||||
"plugins": ["eslint", "typescript", "unicorn", "oxc", "import", "vue"],
|
||||
"rules": {
|
||||
"typescript/no-empty-function": "off",
|
||||
"typescript/no-explicit-any": "off",
|
||||
@ -12,13 +12,7 @@
|
||||
"import/no-unassigned-import": "off",
|
||||
"import/no-named-as-default-member": "off",
|
||||
"import/no-named-as-default": "off",
|
||||
"react/react-in-jsx-scope": "off",
|
||||
"react/no-unstable-nested-components": "off",
|
||||
"react/iframe-missing-sandbox": "off",
|
||||
"react-hooks/exhaustive-deps": "off",
|
||||
"no-shadow": "off",
|
||||
"unicorn/no-array-sort": "off",
|
||||
"unicorn/no-useless-fallback-in-spread": "off",
|
||||
"unicorn/prefer-add-event-listener": "off",
|
||||
"unicorn/consistent-function-scoping": "off",
|
||||
"unicorn/no-instanceof-builtins": "off"
|
||||
@ -27,14 +21,5 @@
|
||||
"correctness": "error",
|
||||
"suspicious": "error"
|
||||
},
|
||||
"ignorePatterns": [
|
||||
"src/.umi",
|
||||
"src/.umi-production",
|
||||
".ai_state",
|
||||
".claude",
|
||||
".codex",
|
||||
"dist/**",
|
||||
"dist-ssr/**",
|
||||
"coverage/**"
|
||||
]
|
||||
"ignorePatterns": [".ai_state", ".claude", ".codex", "doc", "dist/**", "dist-ssr/**", "coverage/**"]
|
||||
}
|
||||
|
||||
20
LICENSE
Normal file
20
LICENSE
Normal file
@ -0,0 +1,20 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2019 RuoYi-Vue-Plus
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
46
README.md
46
README.md
@ -1,23 +1,21 @@
|
||||
# RuoYi-React-Plus
|
||||
|
||||
## 平台简介
|
||||
|
||||
- 本仓库为前端技术栈 [React](https://react.dev/) + [TypeScript](https://www.typescriptlang.org/) + [Ant Design](https://ant.design/) + [Umi Max](https://umijs.org/) + [Vite](https://vitejs.dev/)。
|
||||
- 官方项目: 基于 Vue + ElementPlus 版本前端项目 [plus-ui](https://gitee.com/JavaLionLi/plus-ui)
|
||||
- 本仓库为前端技术栈 [Vue3](https://v3.cn.vuejs.org) + [TS](https://www.typescriptlang.org/) + [Element Plus](https://element-plus.org/zh-CN) + [Vite](https://cn.vitejs.dev) 版本。
|
||||
- 官方项目: 基于 React + Ant Design 版本前端项目 [plus-ui-react](https://gitee.com/JavaLionLi/plus-ui/tree/6.X-React/)
|
||||
- 成员项目: 基于 vben5(ant-design-vue) 的前端项目 [ruoyi-plus-vben5](https://gitee.com/dapppp/ruoyi-plus-vben5)
|
||||
- 成员项目: 基于soybean 的前端项目 [ruoyi-plus-soybean](https://gitee.com/xlsea/ruoyi-plus-soybean)
|
||||
|
||||
## 配套后端代码仓库地址
|
||||
|
||||
| 介绍 | 项目名 | 项目地址 |
|
||||
| -------------- | :--------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 分布式集群框架 | RuoYi-Vue-Plus | [Gitee](https://gitee.com/dromara/RuoYi-Vue-Plus) / [GitHub](https://github.com/dromara/RuoYi-Vue-Plus) / [GitCode](https://gitcode.com/dromara/RuoYi-Vue-Plus) |
|
||||
| 微服务框架 | RuoYi-Cloud-Plus | [Gitee](https://gitee.com/dromara/RuoYi-Cloud-Plus) / [GitHub](https://github.com/dromara/RuoYi-Cloud-Plus) / [GitCode](https://gitcode.com/dromara/RuoYi-Cloud-Plus) |
|
||||
| 介绍 | 项目名 | 项目地址 |
|
||||
| ----------------- | :--------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| 🔥 分布式集群框架 | RuoYi-Vue-Plus | - [Gitee](https://gitee.com/dromara/RuoYi-Vue-Plus)<br> - [GitHub](https://github.com/dromara/RuoYi-Vue-Plus)<br> - [GitCode](https://gitcode.com/dromara/RuoYi-Vue-Plus) |
|
||||
| 🔥 微服务框架 | RuoYi-Cloud-Plus | - [Gitee](https://gitee.com/dromara/RuoYi-Cloud-Plus)<br>- [GitHub](https://github.com/dromara/RuoYi-Cloud-Plus)<br> - [GitCode](https://gitcode.com/dromara/RuoYi-Cloud-Plus) |
|
||||
|
||||
## 分支说明
|
||||
|
||||
- 6.X-react分支(稳定发布主分支 生产可用)
|
||||
- dev-react分支(开发分支 开发过程中使用)
|
||||
- 6.X分支(稳定发布主分支 生产可用)
|
||||
- dev分支(开发分支 开发过程中使用)
|
||||
|
||||
## 前端运行
|
||||
|
||||
@ -31,7 +29,7 @@ pnpm dev
|
||||
# 构建生产环境
|
||||
pnpm build:prod
|
||||
|
||||
# 前端访问地址 http://localhost:8000
|
||||
# 前端访问地址 http://localhost:80
|
||||
```
|
||||
|
||||
## 本框架与RuoYi的业务差异
|
||||
@ -63,16 +61,16 @@ pnpm build:prod
|
||||
|
||||
| | |
|
||||
|--------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------|
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|
||||
@ -1,40 +0,0 @@
|
||||
import { defineConfig } from '@umijs/max';
|
||||
import { createUmiAppConfig } from '../vite.config';
|
||||
|
||||
const appConfig = createUmiAppConfig();
|
||||
|
||||
export default defineConfig({
|
||||
title: appConfig.title,
|
||||
antd: {},
|
||||
access: {},
|
||||
model: {},
|
||||
initialState: {},
|
||||
vite: appConfig.vite,
|
||||
npmClient: 'pnpm',
|
||||
hash: true,
|
||||
esbuildMinifyIIFE: true,
|
||||
history: {
|
||||
type: 'browser'
|
||||
},
|
||||
base: appConfig.base,
|
||||
publicPath: appConfig.publicPath,
|
||||
routes: [
|
||||
{ path: '/login', component: './login', layout: false },
|
||||
{ path: '/register', component: './register', layout: false },
|
||||
{ path: '/social-callback', component: './socialCallback', layout: false },
|
||||
{ path: '/401', component: './error/401', layout: false },
|
||||
{ path: '/404', component: './error/404', layout: false },
|
||||
{ path: '/redirect/*', component: './redirect', layout: false },
|
||||
{
|
||||
path: '/',
|
||||
component: '../layouts/BasicLayout',
|
||||
routes: [
|
||||
{ path: '/', redirect: '/index' },
|
||||
{ path: '/index', component: './index' },
|
||||
{ path: '/user/profile', component: './system/user/profile' },
|
||||
{ path: '*', component: './dynamicPage' }
|
||||
]
|
||||
}
|
||||
],
|
||||
proxy: appConfig.proxy
|
||||
});
|
||||
@ -1,94 +1,104 @@
|
||||
import type { <#if !table.tree>PageResult, </#if> R } from '@/api/types';
|
||||
import request from '@/api/request';
|
||||
import type { ${BusinessName}Form, ${BusinessName}Query, ${BusinessName}VO } from './types';
|
||||
import type { ${BusinessName}Form, ${BusinessName}Query, ${BusinessName}VO } from '@/api/${moduleName}/${businessName}/types';
|
||||
<#if !table.tree>
|
||||
import type { PageResult } from '@/api/types';
|
||||
</#if>
|
||||
import type { AxiosPromise } from '@/utils/api-types';
|
||||
import request from '@/utils/request';
|
||||
|
||||
/**
|
||||
* 查询${functionName}列表
|
||||
* @param query
|
||||
* @returns {*}
|
||||
*/
|
||||
export function list${BusinessName}(query?: ${BusinessName}Query) {
|
||||
return request<R<<#if table.tree>${BusinessName}VO[]<#else> PageResult<${BusinessName}VO></#if>>>({
|
||||
|
||||
export const list${BusinessName} = (query?: ${BusinessName}Query): AxiosPromise<<#if table.tree>${BusinessName}VO[]<#else>PageResult<${BusinessName}VO></#if>> => {
|
||||
return request({
|
||||
url: '/${moduleName}/${businessName}/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 查询${functionName}详细
|
||||
* @param ${pkColumn.javaField}
|
||||
*/
|
||||
export function get${BusinessName}(${pkColumn.javaField}: string | number) {
|
||||
return request<R<${BusinessName}VO>>({
|
||||
export const get${BusinessName} = (${pkColumn.javaField}: string | number): AxiosPromise<${BusinessName}VO> => {
|
||||
return request({
|
||||
url: '/${moduleName}/${businessName}/' + ${pkColumn.javaField},
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 新增${functionName}
|
||||
* @param data
|
||||
*/
|
||||
export function add${BusinessName}(data: ${BusinessName}Form) {
|
||||
return request<R>({
|
||||
export const add${BusinessName} = (data: ${BusinessName}Form) => {
|
||||
return request({
|
||||
url: '/${moduleName}/${businessName}',
|
||||
method: 'post',
|
||||
data
|
||||
data: data
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 修改${functionName}
|
||||
* @param data
|
||||
*/
|
||||
export function update${BusinessName}(data: ${BusinessName}Form) {
|
||||
return request<R>({
|
||||
export const update${BusinessName} = (data: ${BusinessName}Form) => {
|
||||
return request({
|
||||
url: '/${moduleName}/${businessName}',
|
||||
method: 'put',
|
||||
data
|
||||
data: data
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
<#if enableStatus>
|
||||
/**
|
||||
* 修改${functionName}状态
|
||||
* @param ${pkColumn.javaField}
|
||||
* @param status
|
||||
*/
|
||||
export function change${BusinessName}Status(
|
||||
${pkColumn.javaField}: string | number,
|
||||
${statusField}: <#if statusColumn.javaType == 'Boolean'>boolean<#elseif statusColumn.javaType == 'Integer' || statusColumn.javaType == 'Long'>number<#else> string</#if>
|
||||
) {
|
||||
return request<R>({
|
||||
export const change${BusinessName}Status = (${pkColumn.javaField}: string | number, status: <#if statusColumn.javaType == 'Boolean'>boolean<#elseif statusColumn.javaType == 'String'>string<#else> number</#if>) => {
|
||||
return request({
|
||||
url: '/${moduleName}/${businessName}/changeStatus',
|
||||
method: 'put',
|
||||
data: {
|
||||
${pkColumn.javaField},
|
||||
${statusField}
|
||||
${statusField}: status
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
};
|
||||
</#if>
|
||||
|
||||
<#if enableSort>
|
||||
/**
|
||||
* 调整${functionName}排序
|
||||
* @param ${pkColumn.javaField}
|
||||
* @param sortValue
|
||||
*/
|
||||
export function update${BusinessName}Sort(
|
||||
${pkColumn.javaField}: string | number,
|
||||
${sortField}: <#if sortColumn.javaType == 'LocalDateTime' || sortColumn.javaType == 'String'>string<#else> number</#if>
|
||||
) {
|
||||
return request<R>({
|
||||
export const update${BusinessName}Sort = (${pkColumn.javaField}: string | number, sortValue: <#if sortColumn.javaType == 'String' || sortColumn.javaType == 'LocalDateTime'>string<#else> number</#if>) => {
|
||||
return request({
|
||||
url: '/${moduleName}/${businessName}/updateSort',
|
||||
method: 'put',
|
||||
data: {
|
||||
${pkColumn.javaField},
|
||||
${sortField}
|
||||
${sortField}: sortValue
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
};
|
||||
</#if>
|
||||
|
||||
/**
|
||||
* 删除${functionName}
|
||||
* @param ${pkColumn.javaField}
|
||||
*/
|
||||
export function del${BusinessName}(${pkColumn.javaField}: string | number | Array<string | number>) {
|
||||
return request<R>({
|
||||
export const del${BusinessName} = (${pkColumn.javaField}: string | number | Array<string | number>) => {
|
||||
return request({
|
||||
url: '/${moduleName}/${businessName}/' + ${pkColumn.javaField},
|
||||
method: 'delete'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
@ -1,424 +0,0 @@
|
||||
import { DeleteOutlined<#if enableExport>, DownloadOutlined</#if>, EditOutlined, PlusOutlined, SortAscendingOutlined } from '@ant-design/icons';
|
||||
import {
|
||||
ModalForm,
|
||||
PageContainer,
|
||||
<#if needCheckbox>
|
||||
ProFormCheckbox,
|
||||
</#if>
|
||||
<#if needDateField>
|
||||
ProFormDateTimePicker,
|
||||
</#if>
|
||||
<#if needDigit>
|
||||
ProFormDigit,
|
||||
</#if>
|
||||
<#if needSelect>
|
||||
ProFormSelect,
|
||||
</#if>
|
||||
<#if needTextArea>
|
||||
ProFormTextArea,
|
||||
</#if>
|
||||
ProFormText,
|
||||
ProFormTreeSelect,
|
||||
ProTable,
|
||||
type ActionType,
|
||||
type ProColumns
|
||||
} from '@ant-design/pro-components';
|
||||
import { useBoolean } from 'ahooks';
|
||||
import { Button, Form, message<#if enableStatus || needSwitchField>, Switch</#if><#if enableSort>, InputNumber</#if> } from 'antd';
|
||||
import { useMemo, useRef, useState } from 'react';
|
||||
import type { ${BusinessName}Form, ${BusinessName}Query, ${BusinessName}VO } from '@/api/${moduleName}/${businessName}/types';
|
||||
import {
|
||||
add${BusinessName},
|
||||
<#if enableStatus>
|
||||
change${BusinessName}Status,
|
||||
</#if>
|
||||
del${BusinessName},
|
||||
get${BusinessName},
|
||||
list${BusinessName},
|
||||
<#if enableSort>
|
||||
update${BusinessName}Sort,
|
||||
</#if>
|
||||
update${BusinessName}
|
||||
} from '@/api/${moduleName}/${businessName}';
|
||||
<#if needDict>
|
||||
import DictTag from '@/components/common/DictTag';
|
||||
</#if>
|
||||
<#if needFileUpload>
|
||||
import FileUpload from '@/components/common/FileUpload';
|
||||
</#if>
|
||||
<#if needImagePreview>
|
||||
import ImagePreview from '@/components/common/ImagePreview';
|
||||
</#if>
|
||||
<#if needImageUpload>
|
||||
import ImageUpload from '@/components/common/ImageUpload';
|
||||
</#if>
|
||||
<#if needEditor>
|
||||
import RichTextEditor from '@/components/common/RichTextEditor';
|
||||
</#if>
|
||||
import RowActions from '@/components/common/RowActions';
|
||||
<#if needDict>
|
||||
import { useDict } from '@/hooks/useDict';
|
||||
</#if>
|
||||
<#if enableExport>
|
||||
import { useTableExport } from '@/hooks/useTableExport';
|
||||
</#if>
|
||||
import { useTableScroll } from '@/hooks/useTableScroll';
|
||||
import { useTreeTableExpand } from '@/hooks/useTreeTableExpand';
|
||||
import { useUserStore } from '@/stores/userStore';
|
||||
<#if needDict>
|
||||
import { dictOptions } from '@/utils/dict';
|
||||
</#if>
|
||||
<#if enableStatus>
|
||||
import { confirmAction } from '@/utils/modal';
|
||||
</#if>
|
||||
import { hasPermi } from '@/utils/permission';
|
||||
import { filterTree<#if needDateField>, formatDateTimeFields</#if>, handleTree<#if needDateField>, toDayjsFields</#if> } from '@/utils/ruoyi';
|
||||
|
||||
const default${BusinessName}Form: ${BusinessName}Form = {
|
||||
${treeParentCode}: ${treeRootValueTsLiteral},
|
||||
<#list columns as column>
|
||||
<#if (column.insert || column.edit) && !column.pk && column.htmlType == "checkbox">
|
||||
${column.javaField}: [],
|
||||
</#if>
|
||||
</#list>
|
||||
};
|
||||
|
||||
interface ${BusinessName}SelectNode {
|
||||
title: string;
|
||||
value: string | number;
|
||||
children?: ${BusinessName}SelectNode[];
|
||||
}
|
||||
|
||||
function toTreeSelectData(nodes: ${BusinessName}VO[]): ${BusinessName}SelectNode[] {
|
||||
return nodes.map(node => ({
|
||||
title: String(node.${treeName} || ''),
|
||||
value: node.${treeCode},
|
||||
children: node.children ? toTreeSelectData(node.children) : undefined
|
||||
}));
|
||||
}
|
||||
|
||||
<#if enableStatus>
|
||||
const ${statusField}ActiveValue = <#if statusColumn.javaType == "Boolean">true<#elseif statusColumn.javaType == "Integer" || statusColumn.javaType == "Long">0<#else>'0'</#if>;
|
||||
const ${statusField}InactiveValue = <#if statusColumn.javaType == "Boolean">false<#elseif statusColumn.javaType == "Integer" || statusColumn.javaType == "Long">1<#else>'1'</#if>;
|
||||
|
||||
</#if>
|
||||
export default function ${BusinessName}Page() {
|
||||
const actionRef = useRef<ActionType | undefined>(undefined);
|
||||
const { tableScroll } = useTableScroll(1000);
|
||||
const [form] = Form.useForm<${BusinessName}Form>();
|
||||
const userInfo = useUserStore(state => state.userInfo);
|
||||
<#if needDict>
|
||||
const dicts = useDict(${dicts});
|
||||
</#if>
|
||||
const [treeOptions, setTreeOptions] = useState<${BusinessName}VO[]>([]);
|
||||
const [tableRows, setTableRows] = useState<${BusinessName}VO[]>([]);
|
||||
const { expandAll, expandedRowKeys, onExpandedRowsChange, syncExpandedRows, toggleExpandAll } =
|
||||
useTreeTableExpand<${BusinessName}VO>(row => row.${treeCode});
|
||||
const [modalOpen, { setTrue: openModal, setFalse: closeModal }] = useBoolean(false);
|
||||
const [modalTitle, setModalTitle] = useState('');
|
||||
<#if enableExport>
|
||||
const { updateExportParams, exportFile } = useTableExport();
|
||||
</#if>
|
||||
|
||||
const canAdd = hasPermi(userInfo, ['${permissionPrefix}:add']);
|
||||
const canEdit = hasPermi(userInfo, ['${permissionPrefix}:edit']);
|
||||
const canRemove = hasPermi(userInfo, ['${permissionPrefix}:remove']);
|
||||
<#if enableExport>
|
||||
const canExport = hasPermi(userInfo, ['${permissionPrefix}:export']);
|
||||
</#if>
|
||||
const treeSelectData = useMemo(
|
||||
() => [{ title: '顶级节点', value: ${treeRootValueTsLiteral}, children: toTreeSelectData(treeOptions) }],
|
||||
[treeOptions]
|
||||
);
|
||||
|
||||
const loadTreeOptions = async (excludeId?: string | number) => {
|
||||
const res = await list${BusinessName}();
|
||||
const rows = handleTree<${BusinessName}VO>(res.data || [], '${treeCode}', '${treeParentCode}');
|
||||
setTreeOptions(excludeId ? filterTree(rows, node => node.${treeCode} !== excludeId) : rows);
|
||||
};
|
||||
|
||||
const openAdd = async (row?: ${BusinessName}VO) => {
|
||||
await loadTreeOptions();
|
||||
form.resetFields();
|
||||
form.setFieldsValue({ ...default${BusinessName}Form, ${treeParentCode}: row?.${treeCode} || treeRootValueTsLiteral });
|
||||
setModalTitle('添加${functionName}');
|
||||
openModal();
|
||||
};
|
||||
|
||||
const openEdit = async (row: ${BusinessName}VO) => {
|
||||
await loadTreeOptions(row.${treeCode});
|
||||
const res = await get${BusinessName}(row.${pkColumn.javaField});
|
||||
const data = <#if needDateField>toDayjsFields({ ...res.data }, [
|
||||
<#list columns as column>
|
||||
<#if (column.insert || column.edit) && column.htmlType == "datetime">
|
||||
'${column.javaField}',
|
||||
</#if>
|
||||
</#list>
|
||||
])<#else>{ ...res.data }</#if>;
|
||||
<#list columns as column>
|
||||
<#if (column.insert || column.edit) && column.htmlType == "checkbox">
|
||||
if (typeof data.${column.javaField} === 'string') {
|
||||
data.${column.javaField} = data.${column.javaField}.split(',');
|
||||
}
|
||||
</#if>
|
||||
</#list>
|
||||
form.resetFields();
|
||||
form.setFieldsValue(data);
|
||||
setModalTitle('修改${functionName}');
|
||||
openModal();
|
||||
};
|
||||
|
||||
const submitForm = async (values: ${BusinessName}Form) => {
|
||||
const submitValues = <#if needDateField>formatDateTimeFields({ ...values }, [
|
||||
<#list columns as column>
|
||||
<#if (column.insert || column.edit) && column.htmlType == "datetime">
|
||||
'${column.javaField}',
|
||||
</#if>
|
||||
</#list>
|
||||
])<#else>{ ...values }</#if>;
|
||||
<#list columns as column>
|
||||
<#if (column.insert || column.edit) && column.htmlType == "checkbox">
|
||||
if (Array.isArray(submitValues.${column.javaField})) {
|
||||
submitValues.${column.javaField} = submitValues.${column.javaField}.join(',');
|
||||
}
|
||||
</#if>
|
||||
</#list>
|
||||
submitValues.${pkColumn.javaField} ? await update${BusinessName}(submitValues) : await add${BusinessName}(submitValues);
|
||||
message.success('操作成功');
|
||||
form.resetFields();
|
||||
actionRef.current?.reload();
|
||||
return true;
|
||||
};
|
||||
|
||||
const remove = async (row: ${BusinessName}VO) => {
|
||||
await del${BusinessName}(row.${pkColumn.javaField});
|
||||
message.success('删除成功');
|
||||
actionRef.current?.reload();
|
||||
};
|
||||
|
||||
<#if enableStatus>
|
||||
const handleStatusChange = async (row: ${BusinessName}VO, checked: boolean) => {
|
||||
const previousStatus = row.${statusField};
|
||||
const status = checked ? ${statusField}ActiveValue : ${statusField}InactiveValue;
|
||||
const text = checked ? '启用' : '停用';
|
||||
try {
|
||||
await confirmAction(`确认要"${r'${text}'}"吗?`);
|
||||
await change${BusinessName}Status(row.${pkColumn.javaField}, status);
|
||||
message.success(`${r'${text}'}成功`);
|
||||
actionRef.current?.reload();
|
||||
} catch {
|
||||
row.${statusField} = previousStatus;
|
||||
actionRef.current?.reload();
|
||||
}
|
||||
};
|
||||
|
||||
</#if>
|
||||
<#if enableSort>
|
||||
const handleSortChange = async (row: ${BusinessName}VO, value?: number | null) => {
|
||||
await update${BusinessName}Sort(row.${pkColumn.javaField}, value || 0);
|
||||
message.success('排序更新成功');
|
||||
actionRef.current?.reload();
|
||||
};
|
||||
|
||||
</#if>
|
||||
const columns: ProColumns<${BusinessName}VO>[] = [
|
||||
<#list columns as column>
|
||||
<#if column.list && !column.pk>
|
||||
<#if enableStatus && statusField == column.javaField>
|
||||
{
|
||||
title: '${column.columnLabel}',
|
||||
dataIndex: '${column.javaField}',
|
||||
valueType: 'select',
|
||||
width: 100,
|
||||
<#if column.dictType?has_content>
|
||||
fieldProps: { options: dictOptions(dicts.${column.dictType}) },
|
||||
</#if>
|
||||
render: (_, row) => (
|
||||
<Switch
|
||||
checked={row.${column.javaField} === ${statusField}ActiveValue}
|
||||
disabled={!canEdit}
|
||||
onChange={checked => handleStatusChange(row, checked)}
|
||||
/>
|
||||
)
|
||||
},
|
||||
<#elseif enableSort && sortField == column.javaField>
|
||||
{
|
||||
title: '${column.columnLabel}',
|
||||
dataIndex: '${column.javaField}',
|
||||
search: false,
|
||||
width: 130,
|
||||
render: (_, row) => <InputNumber min={0} value={row.${column.javaField} as number} onChange={value => handleSortChange(row, value)} />
|
||||
},
|
||||
<#elseif column.htmlType == "datetime">
|
||||
{ title: '${column.columnLabel}', dataIndex: '${column.javaField}', valueType: 'dateTime', search: <#if column.query>true<#else> false</#if>, width: 170 },
|
||||
<#elseif column.htmlType == "imageUpload">
|
||||
{
|
||||
title: '${column.columnLabel}',
|
||||
dataIndex: '${column.javaField}Url',
|
||||
search: false,
|
||||
width: 100,
|
||||
render: (_, row) => <ImagePreview src={row.${column.javaField}Url || String(row.${column.javaField} || '')} width={50} height={50} />
|
||||
},
|
||||
<#elseif column.dictColumn>
|
||||
{
|
||||
title: '${column.columnLabel}',
|
||||
dataIndex: '${column.javaField}',
|
||||
valueType: 'select',
|
||||
fieldProps: { options: dictOptions(dicts.${column.dictType}) },
|
||||
render: (_, row) => <DictTag options={dicts.${column.dictType}} value={row.${column.javaField}} />
|
||||
},
|
||||
<#elseif column.htmlType == "switch">
|
||||
{
|
||||
title: '${column.columnLabel}',
|
||||
dataIndex: '${column.javaField}',
|
||||
valueType: 'select',
|
||||
width: 100,
|
||||
render: (_, row) => <Switch checked={row.${column.javaField} === ${column.switchActiveValue}} disabled />
|
||||
},
|
||||
<#else>
|
||||
{ title: '${column.columnLabel}', dataIndex: '${column.javaField}'<#if !column.query>, search: false</#if> },
|
||||
</#if>
|
||||
</#if>
|
||||
</#list>
|
||||
{
|
||||
title: '操作',
|
||||
valueType: 'option',
|
||||
width: 130,
|
||||
fixed: 'right',
|
||||
render: (_, row) => (
|
||||
<RowActions
|
||||
actions={[
|
||||
canEdit && { key: 'edit', label: '修改', icon: <EditOutlined />, onClick: () => openEdit(row) },
|
||||
canAdd && { key: 'add', label: '新增', icon: <PlusOutlined />, onClick: () => openAdd(row) },
|
||||
canRemove && {
|
||||
key: 'delete',
|
||||
label: '删除',
|
||||
icon: <DeleteOutlined />,
|
||||
danger: true,
|
||||
confirm: `是否确认删除${functionName}编号为"${r'${row.'}${pkColumn.javaField}${r'}'}"的数据项?`,
|
||||
onClick: () => remove(row)
|
||||
}
|
||||
]}
|
||||
/>
|
||||
)
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<PageContainer title="${functionName}">
|
||||
<ProTable<${BusinessName}VO, ${BusinessName}Query>
|
||||
actionRef={actionRef}
|
||||
rowKey="${treeCode}"
|
||||
columns={columns}
|
||||
scroll={tableScroll}
|
||||
pagination={false}
|
||||
search={{ labelWidth: 90 }}
|
||||
expandable={{
|
||||
expandedRowKeys,
|
||||
onExpandedRowsChange
|
||||
}}
|
||||
request={async params => {
|
||||
const res = await list${BusinessName}(params);
|
||||
const rows = handleTree<${BusinessName}VO>(res.data || [], '${treeCode}', '${treeParentCode}');
|
||||
setTableRows(rows);
|
||||
syncExpandedRows(rows, expandAll);
|
||||
<#if enableExport>
|
||||
updateExportParams(params);
|
||||
</#if>
|
||||
return { data: rows, total: rows.length, success: true };
|
||||
}}
|
||||
toolbar={{ title: '${functionName}列表' }}
|
||||
toolBarRender={() => [
|
||||
canAdd && (
|
||||
<Button key="add" type="primary" icon={<PlusOutlined />} onClick={() => openAdd()}>
|
||||
新增
|
||||
</Button>
|
||||
),
|
||||
<Button
|
||||
key="expand"
|
||||
icon={<SortAscendingOutlined />}
|
||||
onClick={() => toggleExpandAll(tableRows)}
|
||||
>
|
||||
展开/折叠
|
||||
</Button><#if enableExport>,
|
||||
canExport && (
|
||||
<Button
|
||||
key="export"
|
||||
icon={<DownloadOutlined />}
|
||||
onClick={() => exportFile('/${moduleName}/${businessName}/export', () => `${businessName}_${r'${Date.now()}'}.xlsx`)}
|
||||
>
|
||||
导出
|
||||
</Button>
|
||||
)</#if>
|
||||
]}
|
||||
/>
|
||||
|
||||
<ModalForm<${BusinessName}Form>
|
||||
title={modalTitle}
|
||||
open={modalOpen}
|
||||
width={560}
|
||||
form={form}
|
||||
layout="vertical"
|
||||
initialValues={default${BusinessName}Form}
|
||||
modalProps={{ destroyOnHidden: true, onCancel: closeModal }}
|
||||
onOpenChange={open => !open && closeModal()}
|
||||
onFinish={submitForm}
|
||||
>
|
||||
<ProFormText name="${pkColumn.javaField}" hidden />
|
||||
<#list columns as column>
|
||||
<#if (column.insert || column.edit) && !column.pk>
|
||||
<#if column.javaField == treeParentCode>
|
||||
<ProFormTreeSelect
|
||||
name="${column.javaField}"
|
||||
label="${column.columnLabel}"
|
||||
<#if column.required>rules={[{ required: true, message: '${column.columnLabel}不能为空' }]}</#if>
|
||||
fieldProps={{
|
||||
allowClear: true,
|
||||
treeDefaultExpandAll: true,
|
||||
treeData: treeSelectData,
|
||||
placeholder: '请选择${column.columnLabel}'
|
||||
}}
|
||||
/>
|
||||
<#elseif column.htmlType == "input">
|
||||
<ProFormText name="${column.javaField}" label="${column.columnLabel}" <#if column.required>rules={[{ required: true, message: '${column.columnLabel}不能为空' }]}</#if> />
|
||||
<#elseif column.htmlType == "textarea">
|
||||
<ProFormTextArea name="${column.javaField}" label="${column.columnLabel}" <#if column.required>rules={[{ required: true, message: '${column.columnLabel}不能为空' }]}</#if> />
|
||||
<#elseif column.htmlType == "inputNumber">
|
||||
<ProFormDigit name="${column.javaField}" label="${column.columnLabel}" min={0} <#if column.required>rules={[{ required: true, message: '${column.columnLabel}不能为空' }]}</#if> />
|
||||
<#elseif (column.htmlType == "select" || column.htmlType == "radio" || column.htmlType == "switch") && column.dictType?has_content>
|
||||
<ProFormSelect name="${column.javaField}" label="${column.columnLabel}" options={dictOptions(dicts.${column.dictType})} <#if column.required>rules={[{ required: true, message: '${column.columnLabel}不能为空' }]}</#if> />
|
||||
<#elseif column.htmlType == "checkbox" && column.dictType?has_content>
|
||||
<ProFormCheckbox.Group name="${column.javaField}" label="${column.columnLabel}" options={dictOptions(dicts.${column.dictType})} <#if column.required>rules={[{ required: true, message: '${column.columnLabel}不能为空' }]}</#if> />
|
||||
<#elseif column.htmlType == "switch">
|
||||
<Form.Item
|
||||
name="${column.javaField}"
|
||||
label="${column.columnLabel}"
|
||||
valuePropName="checked"
|
||||
getValueProps={value => ({ checked: value === ${column.switchActiveValue} })}
|
||||
normalize={checked => (checked ? ${column.switchActiveValue} : ${column.switchInactiveValue})}
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<#elseif column.htmlType == "datetime">
|
||||
<ProFormDateTimePicker name="${column.javaField}" label="${column.columnLabel}" <#if column.required>rules={[{ required: true, message: '${column.columnLabel}不能为空' }]}</#if> />
|
||||
<#elseif column.htmlType == "imageUpload">
|
||||
<Form.Item name="${column.javaField}" label="${column.columnLabel}" <#if column.required>rules={[{ required: true, message: '${column.columnLabel}不能为空' }]}</#if>>
|
||||
<ImageUpload />
|
||||
</Form.Item>
|
||||
<#elseif column.htmlType == "fileUpload">
|
||||
<Form.Item name="${column.javaField}" label="${column.columnLabel}" <#if column.required>rules={[{ required: true, message: '${column.columnLabel}不能为空' }]}</#if>>
|
||||
<FileUpload />
|
||||
</Form.Item>
|
||||
<#elseif column.htmlType == "editor">
|
||||
<Form.Item name="${column.javaField}" label="${column.columnLabel}" <#if column.required>rules={[{ required: true, message: '${column.columnLabel}不能为空' }]}</#if>>
|
||||
<RichTextEditor />
|
||||
</Form.Item>
|
||||
<#else>
|
||||
<ProFormText name="${column.javaField}" label="${column.columnLabel}" <#if column.required>rules={[{ required: true, message: '${column.columnLabel}不能为空' }]}</#if> />
|
||||
</#if>
|
||||
</#if>
|
||||
</#list>
|
||||
</ModalForm>
|
||||
</PageContainer>
|
||||
);
|
||||
}
|
||||
691
gen/index-tree.vue.ftl
Normal file
691
gen/index-tree.vue.ftl
Normal file
@ -0,0 +1,691 @@
|
||||
<template>
|
||||
<div class="p-2 page-shell ${moduleName}-${businessName}-page">
|
||||
<div class="search-wrap">
|
||||
<el-card shadow="hover" class="search-panel" :class="{ 'is-collapsed': !showSearch }">
|
||||
<template #header>
|
||||
<div class="panel-heading search-panel-toggle" @click.stop="showSearch = !showSearch">
|
||||
<div><h3>筛选条件</h3></div>
|
||||
</div>
|
||||
</template>
|
||||
<el-form ref="queryFormRef" :model="queryParams" :inline="true" class="query-form">
|
||||
<#list columns as column>
|
||||
<#if column.query>
|
||||
<#if column.htmlType == "input" || column.htmlType == "textarea">
|
||||
<el-form-item label="${column.columnLabel}" prop="${column.javaField}">
|
||||
<el-input v-model="queryParams.${column.javaField}" placeholder="请输入${column.columnLabel}" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
<#elseif column.htmlType == "inputNumber">
|
||||
<el-form-item label="${column.columnLabel}" prop="${column.javaField}">
|
||||
<el-input-number v-model="queryParams.${column.javaField}" controls-position="right" />
|
||||
</el-form-item>
|
||||
<#elseif (column.htmlType == "select" || column.htmlType == "radio") && column.dictType?has_content>
|
||||
<el-form-item label="${column.columnLabel}" prop="${column.javaField}">
|
||||
<el-select v-model="queryParams.${column.javaField}" placeholder="请选择${column.columnLabel}" clearable>
|
||||
<el-option v-for="dict in ${column.dictType}" :key="dict.value" :label="dict.label" :value="dict.value"/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<#elseif column.htmlType == "switch" && column.dictType?has_content>
|
||||
<el-form-item label="${column.columnLabel}" prop="${column.javaField}">
|
||||
<el-select v-model="queryParams.${column.javaField}" placeholder="请选择${column.columnLabel}" clearable>
|
||||
<el-option v-for="dict in ${column.dictType}" :key="dict.value" :label="dict.label" :value="dict.value"/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<#elseif column.htmlType == "switch">
|
||||
<el-form-item label="${column.columnLabel}" prop="${column.javaField}">
|
||||
<el-select v-model="queryParams.${column.javaField}" placeholder="请选择${column.columnLabel}" clearable>
|
||||
<#if column.javaType == "Boolean">
|
||||
<el-option label="是" :value="true" />
|
||||
<el-option label="否" :value="false" />
|
||||
<#elseif column.javaType == "Integer" || column.javaType == "Long">
|
||||
<el-option label="开启" :value="0" />
|
||||
<el-option label="关闭" :value="1" />
|
||||
<#else>
|
||||
<el-option label="开启" value="0" />
|
||||
<el-option label="关闭" value="1" />
|
||||
</#if>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<#elseif (column.htmlType == "select" || column.htmlType == "radio") && !(column.dictType?has_content)>
|
||||
<el-form-item label="${column.columnLabel}" prop="${column.javaField}">
|
||||
<el-select v-model="queryParams.${column.javaField}" placeholder="请选择${column.columnLabel}" clearable>
|
||||
<el-option label="请选择字典生成" value="" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<#elseif column.htmlType == "datetime" && column.queryType != "BETWEEN">
|
||||
<el-form-item label="${column.columnLabel}" prop="${column.javaField}">
|
||||
<el-date-picker clearable
|
||||
v-model="queryParams.${column.javaField}"
|
||||
type="date"
|
||||
value-format="YYYY-MM-DD"
|
||||
placeholder="选择${column.columnLabel}"
|
||||
/>
|
||||
</el-form-item>
|
||||
<#elseif column.htmlType == "datetime" && column.queryType == "BETWEEN">
|
||||
<el-form-item label="${column.columnLabel}" style="width: 308px">
|
||||
<el-date-picker
|
||||
v-model="dateRange${column.capJavaField}"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
type="daterange"
|
||||
range-separator="-"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
:default-time="[new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 1, 1, 23, 59, 59)]"
|
||||
/>
|
||||
</el-form-item>
|
||||
</#if>
|
||||
</#if>
|
||||
</#list>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
</div>
|
||||
|
||||
<el-card shadow="hover" class="table-panel">
|
||||
<template #header>
|
||||
<div class="toolbar-shell">
|
||||
<div class="table-heading">
|
||||
<h3>${functionName}列表</h3>
|
||||
</div>
|
||||
<div class="toolbar-actions">
|
||||
<el-button type="primary" plain icon="Plus" @click="handleAdd()" v-hasPermi="['${moduleName}:${businessName}:add']">新增</el-button>
|
||||
<el-button type="info" plain icon="Sort" @click="handleToggleExpandAll">展开/折叠</el-button>
|
||||
<#if enableExport>
|
||||
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['${moduleName}:${businessName}:export']">导出</el-button>
|
||||
</#if>
|
||||
<right-toolbar v-model:show-search="showSearch" :search="false" @query-table="getList"></right-toolbar>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<el-table
|
||||
ref="${businessName}TableRef"
|
||||
v-loading="loading"
|
||||
class="data-table"
|
||||
:data="${businessName}List"
|
||||
row-key="${treeCode}"
|
||||
border
|
||||
:default-expand-all="isExpandAll"
|
||||
:tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
|
||||
>
|
||||
<#assign firstTreeListField = "">
|
||||
<#list columns as tempColumn>
|
||||
<#if !tempColumn.pk && tempColumn.list && "" != tempColumn.javaField && firstTreeListField == "">
|
||||
<#assign firstTreeListField = tempColumn.javaField>
|
||||
</#if>
|
||||
</#list>
|
||||
<#list columns as column>
|
||||
<#if column.pk>
|
||||
<#elseif enableStatus && statusField == column.javaField>
|
||||
<#if column.javaField == firstTreeListField>
|
||||
<el-table-column label="${column.columnLabel}" prop="${column.javaField}">
|
||||
<#else>
|
||||
<el-table-column label="${column.columnLabel}" align="center" prop="${column.javaField}">
|
||||
</#if>
|
||||
<template #default="scope">
|
||||
<el-switch
|
||||
v-model="scope.row.${column.javaField}"
|
||||
:active-value="${statusField}ActiveValue"
|
||||
:inactive-value="${statusField}InactiveValue"
|
||||
@change="handleStatusChange(scope.row)"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<#elseif enableSort && sortField == column.javaField>
|
||||
<#if column.javaField == firstTreeListField>
|
||||
<el-table-column label="${column.columnLabel}" prop="${column.javaField}" width="160">
|
||||
<#else>
|
||||
<el-table-column label="${column.columnLabel}" align="center" prop="${column.javaField}" width="160">
|
||||
</#if>
|
||||
<template #default="scope">
|
||||
<#if column.javaType == "LocalDateTime">
|
||||
<el-date-picker
|
||||
v-model="scope.row.${column.javaField}"
|
||||
type="datetime"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
placeholder="选择${column.columnLabel}"
|
||||
@change="handleSortChange(scope.row)"
|
||||
/>
|
||||
<#else>
|
||||
<el-input-number v-model="scope.row.${column.javaField}" controls-position="right" :min="0" @change="handleSortChange(scope.row)" />
|
||||
</#if>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<#elseif column.list && column.htmlType == "switch">
|
||||
<#if column.javaField == firstTreeListField>
|
||||
<el-table-column label="${column.columnLabel}" prop="${column.javaField}" width="120">
|
||||
<#else>
|
||||
<el-table-column label="${column.columnLabel}" align="center" prop="${column.javaField}" width="120">
|
||||
</#if>
|
||||
<template #default="scope">
|
||||
<el-switch
|
||||
v-model="scope.row.${column.javaField}"
|
||||
<#if column.javaType == "Boolean">
|
||||
:active-value="true"
|
||||
:inactive-value="false"
|
||||
<#elseif column.javaType == "Integer" || column.javaType == "Long">
|
||||
:active-value="0"
|
||||
:inactive-value="1"
|
||||
<#else>
|
||||
active-value="0"
|
||||
inactive-value="1"
|
||||
</#if>
|
||||
disabled
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<#elseif column.list && column.htmlType == "datetime">
|
||||
<#if column.javaField == firstTreeListField>
|
||||
<el-table-column label="${column.columnLabel}" prop="${column.javaField}" width="180">
|
||||
<#else>
|
||||
<el-table-column label="${column.columnLabel}" align="center" prop="${column.javaField}" width="180">
|
||||
</#if>
|
||||
<template #default="scope">
|
||||
<span>{{ parseTime(scope.row.${column.javaField}, '{y}-{m}-{d}') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<#elseif column.list && column.htmlType == "imageUpload">
|
||||
<#if column.javaField == firstTreeListField>
|
||||
<el-table-column label="${column.columnLabel}" prop="${column.javaField}Url" width="100">
|
||||
<#else>
|
||||
<el-table-column label="${column.columnLabel}" align="center" prop="${column.javaField}Url" width="100">
|
||||
</#if>
|
||||
<template #default="scope">
|
||||
<image-preview :src="scope.row.${column.javaField}Url" :width="50" :height="50"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<#elseif column.list && column.dictColumn>
|
||||
<#if column.javaField == firstTreeListField>
|
||||
<el-table-column label="${column.columnLabel}" prop="${column.javaField}">
|
||||
<#else>
|
||||
<el-table-column label="${column.columnLabel}" align="center" prop="${column.javaField}">
|
||||
</#if>
|
||||
<template #default="scope">
|
||||
<#if column.htmlType == "checkbox">
|
||||
<dict-tag :options="${column.dictType}" :value="scope.row.${column.javaField} ? scope.row.${column.javaField}.split(',') : []"/>
|
||||
<#else>
|
||||
<dict-tag :options="${column.dictType}" :value="scope.row.${column.javaField}"/>
|
||||
</#if>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<#elseif column.list && "" != column.javaField>
|
||||
<#if column.javaField == firstTreeListField>
|
||||
<el-table-column label="${column.columnLabel}" prop="${column.javaField}" />
|
||||
<#else>
|
||||
<el-table-column label="${column.columnLabel}" align="center" prop="${column.javaField}" />
|
||||
</#if>
|
||||
</#if>
|
||||
</#list>
|
||||
<#if enableStatus && !statusColumn.list>
|
||||
<el-table-column label="${statusColumn.columnComment}" align="center" prop="${statusField}">
|
||||
<template #default="scope">
|
||||
<el-switch
|
||||
v-model="scope.row.${statusField}"
|
||||
:active-value="${statusField}ActiveValue"
|
||||
:inactive-value="${statusField}InactiveValue"
|
||||
@change="handleStatusChange(scope.row)"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</#if>
|
||||
<#if enableSort && !sortColumn.list>
|
||||
<el-table-column label="${sortColumn.columnComment}" align="center" prop="${sortField}" width="160">
|
||||
<template #default="scope">
|
||||
<#if sortColumn.javaType == "LocalDateTime">
|
||||
<el-date-picker
|
||||
v-model="scope.row.${sortField}"
|
||||
type="datetime"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
placeholder="选择${sortColumn.columnComment}"
|
||||
@change="handleSortChange(scope.row)"
|
||||
/>
|
||||
<#else>
|
||||
<el-input-number v-model="scope.row.${sortField}" controls-position="right" :min="0" @change="handleSortChange(scope.row)" />
|
||||
</#if>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</#if>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template #default="scope">
|
||||
<el-tooltip content="修改" placement="top">
|
||||
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['${moduleName}:${businessName}:edit']" />
|
||||
</el-tooltip>
|
||||
<el-tooltip content="新增" placement="top">
|
||||
<el-button link type="primary" icon="Plus" @click="handleAdd(scope.row)" v-hasPermi="['${moduleName}:${businessName}:add']" />
|
||||
</el-tooltip>
|
||||
<el-tooltip content="删除" placement="top">
|
||||
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['${moduleName}:${businessName}:remove']" />
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-card>
|
||||
<!-- 添加或修改${functionName}对话框 -->
|
||||
<el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
|
||||
<el-form ref="${businessName}FormRef" :model="form" :rules="rules" label-width="80px">
|
||||
<#list columns as column>
|
||||
<#if (column.insert || column.edit) && !column.pk>
|
||||
<#if "" != treeParentCode && column.javaField == treeParentCode>
|
||||
<el-form-item label="${column.columnLabel}" prop="${treeParentCode}">
|
||||
<el-tree-select
|
||||
v-model="form.${treeParentCode}"
|
||||
:data="${businessName}Options"
|
||||
:props="{ value: '${treeCode}', label: '${treeName}', children: 'children' } as any"
|
||||
value-key="${treeCode}"
|
||||
placeholder="请选择${column.columnLabel}"
|
||||
check-strictly
|
||||
/>
|
||||
</el-form-item>
|
||||
<#elseif column.htmlType == "input">
|
||||
<el-form-item label="${column.columnLabel}" prop="${column.javaField}">
|
||||
<el-input v-model="form.${column.javaField}" placeholder="请输入${column.columnLabel}" />
|
||||
</el-form-item>
|
||||
<#elseif column.htmlType == "inputNumber">
|
||||
<el-form-item label="${column.columnLabel}" prop="${column.javaField}">
|
||||
<el-input-number v-model="form.${column.javaField}" controls-position="right" />
|
||||
</el-form-item>
|
||||
<#elseif column.htmlType == "imageUpload">
|
||||
<el-form-item label="${column.columnLabel}" prop="${column.javaField}">
|
||||
<image-upload v-model="form.${column.javaField}"/>
|
||||
</el-form-item>
|
||||
<#elseif column.htmlType == "fileUpload">
|
||||
<el-form-item label="${column.columnLabel}" prop="${column.javaField}">
|
||||
<file-upload v-model="form.${column.javaField}"/>
|
||||
</el-form-item>
|
||||
<#elseif column.htmlType == "editor">
|
||||
<el-form-item label="${column.columnLabel}">
|
||||
<editor v-model="form.${column.javaField}" :min-height="192"/>
|
||||
</el-form-item>
|
||||
<#elseif column.htmlType == "select" && column.dictType?has_content>
|
||||
<el-form-item label="${column.columnLabel}" prop="${column.javaField}">
|
||||
<el-select v-model="form.${column.javaField}" placeholder="请选择${column.columnLabel}">
|
||||
<el-option
|
||||
v-for="dict in ${column.dictType}"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
<#if column.javaType == "Integer" || column.javaType == "Long">
|
||||
:value="parseInt(dict.value)"
|
||||
<#else>
|
||||
:value="dict.value"
|
||||
</#if>
|
||||
></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<#elseif column.htmlType == "select" && !(column.dictType?has_content)>
|
||||
<el-form-item label="${column.columnLabel}" prop="${column.javaField}">
|
||||
<el-select v-model="form.${column.javaField}" placeholder="请选择${column.columnLabel}">
|
||||
<el-option label="请选择字典生成" value="" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<#elseif column.htmlType == "checkbox" && column.dictType?has_content>
|
||||
<el-form-item label="${column.columnLabel}" prop="${column.javaField}">
|
||||
<el-checkbox-group v-model="form.${column.javaField}">
|
||||
<el-checkbox
|
||||
v-for="dict in ${column.dictType}"
|
||||
:key="dict.value"
|
||||
:label="dict.value">
|
||||
{{dict.label}}
|
||||
</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</el-form-item>
|
||||
<#elseif column.htmlType == "checkbox" && !(column.dictType?has_content)>
|
||||
<el-form-item label="${column.columnLabel}" prop="${column.javaField}">
|
||||
<el-checkbox-group v-model="form.${column.javaField}">
|
||||
<el-checkbox>请选择字典生成</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</el-form-item>
|
||||
<#elseif column.htmlType == "radio" && column.dictType?has_content>
|
||||
<el-form-item label="${column.columnLabel}" prop="${column.javaField}">
|
||||
<el-radio-group v-model="form.${column.javaField}">
|
||||
<el-radio
|
||||
v-for="dict in ${column.dictType}"
|
||||
:key="dict.value"
|
||||
<#if column.javaType == "Integer" || column.javaType == "Long">
|
||||
:value="parseInt(dict.value)"
|
||||
<#else>
|
||||
:value="dict.value"
|
||||
</#if>
|
||||
>{{dict.label}}</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<#elseif column.htmlType == "radio" && !(column.dictType?has_content)>
|
||||
<el-form-item label="${column.columnLabel}" prop="${column.javaField}">
|
||||
<el-radio-group v-model="form.${column.javaField}">
|
||||
<el-radio value="1">请选择字典生成</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<#elseif column.htmlType == "switch">
|
||||
<el-form-item label="${column.columnLabel}" prop="${column.javaField}">
|
||||
<el-switch
|
||||
v-model="form.${column.javaField}"
|
||||
<#if column.javaType == "Boolean">
|
||||
:active-value="true"
|
||||
:inactive-value="false"
|
||||
<#elseif column.javaType == "Integer" || column.javaType == "Long">
|
||||
:active-value="0"
|
||||
:inactive-value="1"
|
||||
<#else>
|
||||
active-value="0"
|
||||
inactive-value="1"
|
||||
</#if>
|
||||
/>
|
||||
</el-form-item>
|
||||
<#elseif column.htmlType == "datetime">
|
||||
<el-form-item label="${column.columnLabel}" prop="${column.javaField}">
|
||||
<el-date-picker clearable
|
||||
v-model="form.${column.javaField}"
|
||||
type="datetime"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
placeholder="选择${column.columnLabel}"
|
||||
/>
|
||||
</el-form-item>
|
||||
<#elseif column.htmlType == "textarea">
|
||||
<el-form-item label="${column.columnLabel}" prop="${column.javaField}">
|
||||
<el-input v-model="form.${column.javaField}" type="textarea" placeholder="请输入内容" />
|
||||
</el-form-item>
|
||||
</#if>
|
||||
</#if>
|
||||
</#list>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button :loading="buttonLoading" type="primary" @click="submitForm">确 定</el-button>
|
||||
<el-button @click="cancel">取 消</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="${BusinessName}" lang="ts">
|
||||
import {
|
||||
add${BusinessName},
|
||||
<#if enableStatus>
|
||||
change${BusinessName}Status,
|
||||
</#if>
|
||||
del${BusinessName},
|
||||
get${BusinessName},
|
||||
list${BusinessName},
|
||||
<#if enableSort>
|
||||
update${BusinessName}Sort,
|
||||
</#if>
|
||||
update${BusinessName}
|
||||
} from '@/api/${moduleName}/${businessName}';
|
||||
import { ${BusinessName}Form, ${BusinessName}Query, ${BusinessName}VO } from '@/api/${moduleName}/${businessName}/types';
|
||||
import { useLoading } from '@/hooks/async/useLoading';
|
||||
import { useFormDialog } from '@/hooks/dialog/useFormDialog';
|
||||
<#if needAddDateRange>
|
||||
import { useDateRangeQuery } from '@/hooks/form/useDateRangeQuery';
|
||||
</#if>
|
||||
import { useSearchReset } from '@/hooks/form/useSearchReset';
|
||||
import { useSearchToggle } from '@/hooks/form/useSearchToggle';
|
||||
import { useTreeTableExpand } from '@/hooks/tree/useTreeTableExpand';
|
||||
<#if needDict>
|
||||
import { useDict } from '@/utils/dict';
|
||||
</#if>
|
||||
import modal from '@/plugins/modal';
|
||||
import { handleTree } from '@/utils/ruoyi';
|
||||
<#if enableExport>
|
||||
import { download as requestDownload } from '@/utils/request';
|
||||
</#if>
|
||||
|
||||
<#if needDict>
|
||||
const { ${dictsNoSymbol} } = toRefs<any>(useDict(${dicts}));
|
||||
</#if>
|
||||
|
||||
<#if enableStatus>
|
||||
const ${statusField}ActiveValue = <#if statusColumn.javaType == "Boolean">true<#elseif statusColumn.javaType == "Integer" || statusColumn.javaType == "Long">0<#else>'0'</#if>;
|
||||
const ${statusField}InactiveValue = <#if statusColumn.javaType == "Boolean">false<#elseif statusColumn.javaType == "Integer" || statusColumn.javaType == "Long">1<#else>'1'</#if>;
|
||||
</#if>
|
||||
|
||||
type ${BusinessName}Option = {
|
||||
${treeCode}: <#if treeParentColumn.javaType == 'String'>string<#else> number</#if>;
|
||||
${treeName}: string;
|
||||
children?: ${BusinessName}Option[];
|
||||
};
|
||||
|
||||
const ${businessName}List = ref<${BusinessName}VO[]>([]);
|
||||
const ${businessName}Options = ref<${BusinessName}Option[]>([]);
|
||||
const all${BusinessName}Options = ref<${BusinessName}Option[]>([]);
|
||||
const buttonLoading = ref(false);
|
||||
const { showSearch } = useSearchToggle();
|
||||
const { loading, setLoading, withLoading } = useLoading();
|
||||
|
||||
const queryFormRef = ref<ElFormInstance>();
|
||||
const ${businessName}FormRef = ref<ElFormInstance>();
|
||||
const ${businessName}TableRef = ref<ElTableInstance>();
|
||||
const { isExpandAll, handleToggleExpandAll } = useTreeTableExpand<${BusinessName}VO>({
|
||||
tableRef: ${businessName}TableRef,
|
||||
data: ${businessName}List
|
||||
});
|
||||
|
||||
<#list columns as column>
|
||||
<#if column.htmlType == "datetime" && column.queryType == "BETWEEN">
|
||||
const {
|
||||
dateRange: dateRange${column.capJavaField},
|
||||
applyDateRange: apply${column.capJavaField}DateRange,
|
||||
resetDateRange: reset${column.capJavaField}DateRange
|
||||
} = useDateRangeQuery('${column.capJavaField}');
|
||||
</#if>
|
||||
</#list>
|
||||
|
||||
const initFormData: ${BusinessName}Form = {
|
||||
<#list columns as column>
|
||||
<#if column.insert || column.edit>
|
||||
<#if column.htmlType == "checkbox">
|
||||
${column.javaField}: [],
|
||||
<#else>
|
||||
${column.javaField}: undefined,
|
||||
</#if>
|
||||
</#if>
|
||||
</#list>
|
||||
}
|
||||
|
||||
const data = reactive<PageData<${BusinessName}Form, ${BusinessName}Query>>({
|
||||
form: {...initFormData},
|
||||
queryParams: {
|
||||
<#list columns as column>
|
||||
<#if column.query>
|
||||
<#if column.htmlType != "datetime" || column.queryType != "BETWEEN">
|
||||
${column.javaField}: undefined,
|
||||
</#if>
|
||||
</#if>
|
||||
</#list>
|
||||
params: {
|
||||
<#list columns as column>
|
||||
<#if column.query>
|
||||
<#if column.htmlType == "datetime" && column.queryType == "BETWEEN">
|
||||
${column.javaField}: undefined,
|
||||
</#if>
|
||||
</#if>
|
||||
</#list>
|
||||
}
|
||||
},
|
||||
rules: {
|
||||
<#list columns as column>
|
||||
<#if column.insert || column.edit>
|
||||
<#if column.required>
|
||||
${column.javaField}: [
|
||||
{ required: true, message: "${column.columnLabel}不能为空", trigger: <#if column.htmlType == "select" || column.htmlType == "radio" || column.htmlType == "switch" || column.htmlType == "inputNumber">"change"<#else>"blur"</#if> }
|
||||
],
|
||||
</#if>
|
||||
</#if>
|
||||
</#list>
|
||||
}
|
||||
});
|
||||
|
||||
const { queryParams, form, rules } = toRefs(data);
|
||||
const { dialog, resetForm: reset, openDialog, showDialog, closeDialog } = useFormDialog({
|
||||
form,
|
||||
formRef: ${businessName}FormRef,
|
||||
initialFormData: initFormData
|
||||
});
|
||||
|
||||
/** 查询${functionName}列表 */
|
||||
const getList = async () => {
|
||||
await withLoading(async () => {
|
||||
<#if needAddDateRange>
|
||||
let params = queryParams.value;
|
||||
<#list columns as column>
|
||||
<#if column.htmlType == "datetime" && column.queryType == "BETWEEN">
|
||||
params = apply${column.capJavaField}DateRange(params);
|
||||
</#if>
|
||||
</#list>
|
||||
const res = await list${BusinessName}(params);
|
||||
<#else>
|
||||
const res = await list${BusinessName}(queryParams.value);
|
||||
</#if>
|
||||
const data = handleTree<${BusinessName}VO>(res.data, '${treeCode}', '${treeParentCode}');
|
||||
if (data) {
|
||||
${businessName}List.value = data;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/** 查询${functionName}下拉树结构 */
|
||||
const getTreeselect = async (excludeId?: string | number) => {
|
||||
const res = await list${BusinessName}();
|
||||
const data: ${BusinessName}Option = { ${treeCode}: ${treeRootValueTsLiteral}, ${treeName}: '顶级节点', children: [] };
|
||||
data.children = handleTree<${BusinessName}Option>(res.data, '${treeCode}', '${treeParentCode}');
|
||||
all${BusinessName}Options.value = [data];
|
||||
${businessName}Options.value = excludeId != null ? filterTreeOptions(all${BusinessName}Options.value, excludeId) : all${BusinessName}Options.value;
|
||||
};
|
||||
|
||||
/** 取消按钮 */
|
||||
const cancel = () => {
|
||||
reset();
|
||||
closeDialog();
|
||||
};
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
getList();
|
||||
};
|
||||
|
||||
const { resetQuery } = useSearchReset({
|
||||
queryFormRef,
|
||||
queryParams,
|
||||
resetExtras: () => {
|
||||
<#list columns as column>
|
||||
<#if column.htmlType == "datetime" && column.queryType == "BETWEEN">
|
||||
reset${column.capJavaField}DateRange();
|
||||
</#if>
|
||||
</#list>
|
||||
},
|
||||
afterReset: () => {
|
||||
handleQuery();
|
||||
}
|
||||
});
|
||||
|
||||
/** 新增按钮操作 */
|
||||
const handleAdd = (row?: Partial<${BusinessName}VO>) => {
|
||||
openDialog('添加${functionName}');
|
||||
getTreeselect();
|
||||
if (row != null && row.${treeCode}) {
|
||||
form.value.${treeParentCode} = row.${treeCode};
|
||||
} else {
|
||||
form.value.${treeParentCode} = ${treeRootValueTsLiteral};
|
||||
}
|
||||
};
|
||||
|
||||
/** 修改按钮操作 */
|
||||
const handleUpdate = async (row: Partial<${BusinessName}VO>) => {
|
||||
reset();
|
||||
await getTreeselect(row.${treeCode});
|
||||
if (row != null) {
|
||||
form.value.${treeParentCode} = row.${treeParentCode};
|
||||
}
|
||||
const res = await get${BusinessName}(row.${pkColumn.javaField});
|
||||
Object.assign(form.value, res.data);
|
||||
<#list columns as column>
|
||||
<#if column.htmlType == "checkbox">
|
||||
form.value.${column.javaField} = form.value.${column.javaField}.split(",");
|
||||
</#if>
|
||||
</#list>
|
||||
showDialog('修改${functionName}');
|
||||
};
|
||||
|
||||
/** 提交按钮 */
|
||||
const submitForm = () => {
|
||||
${businessName}FormRef.value?.validate(async (valid: boolean) => {
|
||||
if (valid) {
|
||||
buttonLoading.value = true;
|
||||
<#list columns as column>
|
||||
<#if column.htmlType == "checkbox">
|
||||
form.value.${column.javaField} = form.value.${column.javaField}.join(",");
|
||||
</#if>
|
||||
</#list>
|
||||
if (form.value.${pkColumn.javaField}) {
|
||||
await update${BusinessName}(form.value).finally(() => (buttonLoading.value = false));
|
||||
} else {
|
||||
await add${BusinessName}(form.value).finally(() => (buttonLoading.value = false));
|
||||
}
|
||||
modal.msgSuccess('操作成功');
|
||||
closeDialog();
|
||||
await getList();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/** 删除按钮操作 */
|
||||
const handleDelete = async (row: Partial<${BusinessName}VO>) => {
|
||||
await modal.confirm('是否确认删除${functionName}编号为"' + row.${pkColumn.javaField} + '"的数据项?');
|
||||
setLoading(true);
|
||||
await del${BusinessName}(row.${pkColumn.javaField}).finally(() => setLoading(false));
|
||||
await getList();
|
||||
modal.msgSuccess('删除成功');
|
||||
};
|
||||
|
||||
const filterTreeOptions = (options: ${BusinessName}Option[], excludeId: string | number): ${BusinessName}Option[] => {
|
||||
return options
|
||||
.filter(item => item.${treeCode} !== excludeId)
|
||||
.map(item => ({
|
||||
...item,
|
||||
children: item.children ? filterTreeOptions(item.children, excludeId) : []
|
||||
}));
|
||||
};
|
||||
|
||||
<#if enableStatus>
|
||||
/** 状态修改 */
|
||||
const handleStatusChange = async (row: Partial<${BusinessName}VO>) => {
|
||||
const text = row.${statusField} === ${statusField}ActiveValue ? '启用' : '停用';
|
||||
try {
|
||||
await modal.confirm('确认要"' + text + '"吗?');
|
||||
await change${BusinessName}Status(row.${pkColumn.javaField}, row.${statusField});
|
||||
modal.msgSuccess(text + '成功');
|
||||
} catch (err) {
|
||||
row.${statusField} = row.${statusField} === ${statusField}ActiveValue ? ${statusField}InactiveValue : ${statusField}ActiveValue;
|
||||
}
|
||||
};
|
||||
</#if>
|
||||
|
||||
<#if enableSort>
|
||||
/** 排序调整 */
|
||||
const handleSortChange = async (row: Partial<${BusinessName}VO>) => {
|
||||
try {
|
||||
await update${BusinessName}Sort(row.${pkColumn.javaField}, row.${sortField});
|
||||
modal.msgSuccess('排序更新成功');
|
||||
} catch (err) {
|
||||
await getList();
|
||||
}
|
||||
};
|
||||
</#if>
|
||||
|
||||
<#if enableExport>
|
||||
/** 导出按钮操作 */
|
||||
const handleExport = () => {
|
||||
requestDownload(
|
||||
'${moduleName}/${businessName}/export',
|
||||
{
|
||||
...queryParams.value
|
||||
},
|
||||
`${businessName}_${r'${new Date().getTime()}'}.xlsx`
|
||||
);
|
||||
};
|
||||
</#if>
|
||||
|
||||
onMounted(() => {
|
||||
getList();
|
||||
});
|
||||
</script>
|
||||
@ -1,406 +0,0 @@
|
||||
import { DeleteOutlined<#if enableExport>, DownloadOutlined</#if>, EditOutlined, PlusOutlined } from '@ant-design/icons';
|
||||
import {
|
||||
ModalForm,
|
||||
PageContainer,
|
||||
<#if needCheckbox>
|
||||
ProFormCheckbox,
|
||||
</#if>
|
||||
<#if needDateField>
|
||||
ProFormDateTimePicker,
|
||||
</#if>
|
||||
<#if needDigit>
|
||||
ProFormDigit,
|
||||
</#if>
|
||||
<#if needSelect>
|
||||
ProFormSelect,
|
||||
</#if>
|
||||
<#if needTextArea>
|
||||
ProFormTextArea,
|
||||
</#if>
|
||||
ProFormText,
|
||||
ProTable,
|
||||
type ActionType,
|
||||
type ProColumns
|
||||
} from '@ant-design/pro-components';
|
||||
import { useBoolean } from 'ahooks';
|
||||
import { Button, Form, message, Popconfirm<#if enableStatus || needSwitchField>, Switch</#if><#if enableSort>, InputNumber</#if> } from 'antd';
|
||||
import { useRef, useState } from 'react';
|
||||
import type { ${BusinessName}Form, ${BusinessName}Query, ${BusinessName}VO } from '@/api/${moduleName}/${businessName}/types';
|
||||
import {
|
||||
add${BusinessName},
|
||||
<#if enableStatus>
|
||||
change${BusinessName}Status,
|
||||
</#if>
|
||||
del${BusinessName},
|
||||
get${BusinessName},
|
||||
list${BusinessName},
|
||||
<#if enableSort>
|
||||
update${BusinessName}Sort,
|
||||
</#if>
|
||||
update${BusinessName}
|
||||
} from '@/api/${moduleName}/${businessName}';
|
||||
<#if needDict>
|
||||
import DictTag from '@/components/common/DictTag';
|
||||
</#if>
|
||||
<#if needFileUpload>
|
||||
import FileUpload from '@/components/common/FileUpload';
|
||||
</#if>
|
||||
<#if needImagePreview>
|
||||
import ImagePreview from '@/components/common/ImagePreview';
|
||||
</#if>
|
||||
<#if needImageUpload>
|
||||
import ImageUpload from '@/components/common/ImageUpload';
|
||||
</#if>
|
||||
<#if needEditor>
|
||||
import RichTextEditor from '@/components/common/RichTextEditor';
|
||||
</#if>
|
||||
import RowActions from '@/components/common/RowActions';
|
||||
<#if needDict>
|
||||
import { useDict } from '@/hooks/useDict';
|
||||
</#if>
|
||||
<#if needDateRange>
|
||||
import { useDateRangeQuery } from '@/hooks/useDateRangeQuery';
|
||||
</#if>
|
||||
<#if enableExport>
|
||||
import { useTableExport } from '@/hooks/useTableExport';
|
||||
</#if>
|
||||
import { useTableScroll } from '@/hooks/useTableScroll';
|
||||
import { useTableSelection } from '@/hooks/useTableSelection';
|
||||
import { useUserStore } from '@/stores/userStore';
|
||||
<#if needDict>
|
||||
import { dictOptions } from '@/utils/dict';
|
||||
</#if>
|
||||
<#if enableStatus>
|
||||
import { confirmAction } from '@/utils/modal';
|
||||
</#if>
|
||||
import { hasPermi } from '@/utils/permission';
|
||||
import { <#if needDateField>formatDateTimeFields, toDayjsFields, </#if> toPageQuery, toTableData } from '@/utils/ruoyi';
|
||||
|
||||
const default${BusinessName}Form: ${BusinessName}Form = {
|
||||
<#list columns as column>
|
||||
<#if (column.insert || column.edit) && !column.pk && column.htmlType == "checkbox">
|
||||
${column.javaField}: [],
|
||||
</#if>
|
||||
</#list>
|
||||
};
|
||||
|
||||
<#if enableStatus>
|
||||
const ${statusField}ActiveValue = <#if statusColumn.javaType == "Boolean">true<#elseif statusColumn.javaType == "Integer" || statusColumn.javaType == "Long">0<#else>'0'</#if>;
|
||||
const ${statusField}InactiveValue = <#if statusColumn.javaType == "Boolean">false<#elseif statusColumn.javaType == "Integer" || statusColumn.javaType == "Long">1<#else>'1'</#if>;
|
||||
|
||||
</#if>
|
||||
export default function ${BusinessName}Page() {
|
||||
const actionRef = useRef<ActionType | undefined>(undefined);
|
||||
const { tableScroll } = useTableScroll(1000);
|
||||
const [form] = Form.useForm<${BusinessName}Form>();
|
||||
const userInfo = useUserStore(state => state.userInfo);
|
||||
<#if needDict>
|
||||
const dicts = useDict(${dicts});
|
||||
</#if>
|
||||
const { ids, selectedOne, handleSelectionChange, clearSelection } = useTableSelection<${BusinessName}VO>(
|
||||
row => row.${pkColumn.javaField}
|
||||
);
|
||||
const [modalOpen, { setTrue: openModal, setFalse: closeModal }] = useBoolean(false);
|
||||
const [modalTitle, setModalTitle] = useState('');
|
||||
<#if enableExport>
|
||||
const { updateExportParams, exportFile } = useTableExport();
|
||||
</#if>
|
||||
<#list columns as column>
|
||||
<#if column.query && column.htmlType == "datetime" && column.queryType == "BETWEEN">
|
||||
const { applyDateRange: apply${column.capJavaField}DateRange } = useDateRangeQuery('${column.capJavaField}');
|
||||
</#if>
|
||||
</#list>
|
||||
|
||||
const canAdd = hasPermi(userInfo, ['${permissionPrefix}:add']);
|
||||
const canEdit = hasPermi(userInfo, ['${permissionPrefix}:edit']);
|
||||
const canRemove = hasPermi(userInfo, ['${permissionPrefix}:remove']);
|
||||
<#if enableExport>
|
||||
const canExport = hasPermi(userInfo, ['${permissionPrefix}:export']);
|
||||
</#if>
|
||||
|
||||
const openAdd = () => {
|
||||
form.resetFields();
|
||||
form.setFieldsValue(default${BusinessName}Form);
|
||||
setModalTitle('添加${functionName}');
|
||||
openModal();
|
||||
};
|
||||
|
||||
const openEdit = async (row?: ${BusinessName}VO) => {
|
||||
const target = row || selectedOne;
|
||||
if (!target?.${pkColumn.javaField}) return;
|
||||
const res = await get${BusinessName}(target.${pkColumn.javaField});
|
||||
const data = <#if needDateField>toDayjsFields({ ...res.data }, [
|
||||
<#list columns as column>
|
||||
<#if (column.insert || column.edit) && column.htmlType == "datetime">
|
||||
'${column.javaField}',
|
||||
</#if>
|
||||
</#list>
|
||||
])<#else>{ ...res.data }</#if>;
|
||||
<#list columns as column>
|
||||
<#if (column.insert || column.edit) && column.htmlType == "checkbox">
|
||||
if (typeof data.${column.javaField} === 'string') {
|
||||
data.${column.javaField} = data.${column.javaField}.split(',');
|
||||
}
|
||||
</#if>
|
||||
</#list>
|
||||
form.resetFields();
|
||||
form.setFieldsValue(data);
|
||||
setModalTitle('修改${functionName}');
|
||||
openModal();
|
||||
};
|
||||
|
||||
const submitForm = async (values: ${BusinessName}Form) => {
|
||||
const submitValues = <#if needDateField>formatDateTimeFields({ ...values }, [
|
||||
<#list columns as column>
|
||||
<#if (column.insert || column.edit) && column.htmlType == "datetime">
|
||||
'${column.javaField}',
|
||||
</#if>
|
||||
</#list>
|
||||
])<#else>{ ...values }</#if>;
|
||||
<#list columns as column>
|
||||
<#if (column.insert || column.edit) && column.htmlType == "checkbox">
|
||||
if (Array.isArray(submitValues.${column.javaField})) {
|
||||
submitValues.${column.javaField} = submitValues.${column.javaField}.join(',');
|
||||
}
|
||||
</#if>
|
||||
</#list>
|
||||
submitValues.${pkColumn.javaField} ? await update${BusinessName}(submitValues) : await add${BusinessName}(submitValues);
|
||||
message.success('操作成功');
|
||||
form.resetFields();
|
||||
actionRef.current?.reload();
|
||||
return true;
|
||||
};
|
||||
|
||||
const remove = async (row?: ${BusinessName}VO) => {
|
||||
await del${BusinessName}(row?.${pkColumn.javaField} || ids);
|
||||
message.success('删除成功');
|
||||
clearSelection();
|
||||
actionRef.current?.reloadAndRest?.();
|
||||
};
|
||||
|
||||
<#if enableStatus>
|
||||
const handleStatusChange = async (row: ${BusinessName}VO, checked: boolean) => {
|
||||
const previousStatus = row.${statusField};
|
||||
const status = checked ? ${statusField}ActiveValue : ${statusField}InactiveValue;
|
||||
const text = checked ? '启用' : '停用';
|
||||
try {
|
||||
await confirmAction(`确认要"${r'${text}'}"吗?`);
|
||||
await change${BusinessName}Status(row.${pkColumn.javaField}, status);
|
||||
message.success(`${r'${text}'}成功`);
|
||||
actionRef.current?.reload();
|
||||
} catch {
|
||||
row.${statusField} = previousStatus;
|
||||
actionRef.current?.reload();
|
||||
}
|
||||
};
|
||||
|
||||
</#if>
|
||||
<#if enableSort>
|
||||
const handleSortChange = async (row: ${BusinessName}VO, value?: number | null) => {
|
||||
await update${BusinessName}Sort(row.${pkColumn.javaField}, value || 0);
|
||||
message.success('排序更新成功');
|
||||
actionRef.current?.reload();
|
||||
};
|
||||
|
||||
</#if>
|
||||
const columns: ProColumns<${BusinessName}VO>[] = [
|
||||
<#list columns as column>
|
||||
<#if column.list>
|
||||
<#if enableStatus && statusField == column.javaField>
|
||||
{
|
||||
title: '${column.columnLabel}',
|
||||
dataIndex: '${column.javaField}',
|
||||
valueType: 'select',
|
||||
width: 100,
|
||||
<#if column.dictType?has_content>
|
||||
fieldProps: { options: dictOptions(dicts.${column.dictType}) },
|
||||
</#if>
|
||||
render: (_, row) => (
|
||||
<Switch
|
||||
checked={row.${column.javaField} === ${statusField}ActiveValue}
|
||||
disabled={!canEdit}
|
||||
onChange={checked => handleStatusChange(row, checked)}
|
||||
/>
|
||||
)
|
||||
},
|
||||
<#elseif enableSort && sortField == column.javaField>
|
||||
{
|
||||
title: '${column.columnLabel}',
|
||||
dataIndex: '${column.javaField}',
|
||||
search: false,
|
||||
width: 130,
|
||||
render: (_, row) => <InputNumber min={0} value={row.${column.javaField} as number} onChange={value => handleSortChange(row, value)} />
|
||||
},
|
||||
<#elseif column.htmlType == "datetime">
|
||||
{ title: '${column.columnLabel}', dataIndex: '${column.javaField}', valueType: 'dateTime', search: <#if column.query && column.queryType != "BETWEEN">true<#else> false</#if>, width: 170 },
|
||||
<#elseif column.htmlType == "imageUpload">
|
||||
{
|
||||
title: '${column.columnLabel}',
|
||||
dataIndex: '${column.javaField}Url',
|
||||
search: false,
|
||||
width: 100,
|
||||
render: (_, row) => <ImagePreview src={row.${column.javaField}Url || String(row.${column.javaField} || '')} width={50} height={50} />
|
||||
},
|
||||
<#elseif column.dictColumn>
|
||||
{
|
||||
title: '${column.columnLabel}',
|
||||
dataIndex: '${column.javaField}',
|
||||
valueType: 'select',
|
||||
fieldProps: { options: dictOptions(dicts.${column.dictType}) },
|
||||
render: (_, row) => <DictTag options={dicts.${column.dictType}} value={row.${column.javaField}} />
|
||||
},
|
||||
<#elseif column.htmlType == "switch">
|
||||
{
|
||||
title: '${column.columnLabel}',
|
||||
dataIndex: '${column.javaField}',
|
||||
valueType: 'select',
|
||||
width: 100,
|
||||
render: (_, row) => <Switch checked={row.${column.javaField} === ${column.switchActiveValue}} disabled />
|
||||
},
|
||||
<#else>
|
||||
{ title: '${column.columnLabel}', dataIndex: '${column.javaField}'<#if !column.query>, search: false</#if> },
|
||||
</#if>
|
||||
</#if>
|
||||
<#if column.query && column.htmlType == "datetime" && column.queryType == "BETWEEN">
|
||||
{ title: '${column.columnLabel}', dataIndex: '${column.javaField}Range', valueType: 'dateTimeRange', hideInTable: true },
|
||||
</#if>
|
||||
</#list>
|
||||
{
|
||||
title: '操作',
|
||||
valueType: 'option',
|
||||
width: 92,
|
||||
fixed: 'right',
|
||||
render: (_, row) => (
|
||||
<RowActions
|
||||
actions={[
|
||||
canEdit && { key: 'edit', label: '修改', icon: <EditOutlined />, onClick: () => openEdit(row) },
|
||||
canRemove && {
|
||||
key: 'delete',
|
||||
label: '删除',
|
||||
icon: <DeleteOutlined />,
|
||||
danger: true,
|
||||
confirm: `是否确认删除${functionName}编号为"${r'${row.'}${pkColumn.javaField}${r'}'}"的数据项?`,
|
||||
onClick: () => remove(row)
|
||||
}
|
||||
]}
|
||||
/>
|
||||
)
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<PageContainer title="${functionName}">
|
||||
<ProTable<${BusinessName}VO, ${BusinessName}Query<#if needDateRange> & Record<string, unknown></#if>>
|
||||
actionRef={actionRef}
|
||||
rowKey="${pkColumn.javaField}"
|
||||
columns={columns}
|
||||
scroll={tableScroll}
|
||||
search={{ labelWidth: 90 }}
|
||||
pagination={{ defaultPageSize: 10, showSizeChanger: true }}
|
||||
rowSelection={{ selectedRowKeys: ids, onChange: handleSelectionChange }}
|
||||
request={async params => {
|
||||
<#if needDateRange>
|
||||
let query = toPageQuery(params);
|
||||
<#list columns as column>
|
||||
<#if column.query && column.htmlType == "datetime" && column.queryType == "BETWEEN">
|
||||
query = apply${column.capJavaField}DateRange(query, params.${column.javaField}Range as [string, string] | undefined);
|
||||
delete query.${column.javaField}Range;
|
||||
</#if>
|
||||
</#list>
|
||||
<#else>
|
||||
const query = toPageQuery(params);
|
||||
</#if>
|
||||
<#if enableExport>
|
||||
updateExportParams(query);
|
||||
</#if>
|
||||
const res = await list${BusinessName}(query);
|
||||
return toTableData(res);
|
||||
}}
|
||||
toolbar={{ title: '${functionName}列表' }}
|
||||
toolBarRender={() => [
|
||||
canAdd && (
|
||||
<Button key="add" type="primary" icon={<PlusOutlined />} onClick={openAdd}>
|
||||
新增
|
||||
</Button>
|
||||
),
|
||||
canEdit && (
|
||||
<Button key="edit" disabled={!selectedOne} icon={<EditOutlined />} onClick={() => openEdit()}>
|
||||
修改
|
||||
</Button>
|
||||
),
|
||||
canRemove && (
|
||||
<Popconfirm key="delete" title={`是否确认删除${functionName}编号为"${r'${ids}'}"的数据项?`} onConfirm={() => remove()}>
|
||||
<Button danger disabled={!ids.length} icon={<DeleteOutlined />}>
|
||||
删除
|
||||
</Button>
|
||||
</Popconfirm>
|
||||
)<#if enableExport>,
|
||||
canExport && (
|
||||
<Button
|
||||
key="export"
|
||||
icon={<DownloadOutlined />}
|
||||
onClick={() => exportFile('/${moduleName}/${businessName}/export', () => `${businessName}_${r'${Date.now()}'}.xlsx`)}
|
||||
>
|
||||
导出
|
||||
</Button>
|
||||
)</#if>
|
||||
]}
|
||||
/>
|
||||
|
||||
<ModalForm<${BusinessName}Form>
|
||||
title={modalTitle}
|
||||
open={modalOpen}
|
||||
width={560}
|
||||
form={form}
|
||||
layout="vertical"
|
||||
initialValues={default${BusinessName}Form}
|
||||
modalProps={{ destroyOnHidden: true, onCancel: closeModal }}
|
||||
onOpenChange={open => !open && closeModal()}
|
||||
onFinish={submitForm}
|
||||
>
|
||||
<ProFormText name="${pkColumn.javaField}" hidden />
|
||||
<#list columns as column>
|
||||
<#if (column.insert || column.edit) && !column.pk>
|
||||
<#if column.htmlType == "input">
|
||||
<ProFormText name="${column.javaField}" label="${column.columnLabel}" <#if column.required>rules={[{ required: true, message: '${column.columnLabel}不能为空' }]}</#if> />
|
||||
<#elseif column.htmlType == "textarea">
|
||||
<ProFormTextArea name="${column.javaField}" label="${column.columnLabel}" <#if column.required>rules={[{ required: true, message: '${column.columnLabel}不能为空' }]}</#if> />
|
||||
<#elseif column.htmlType == "inputNumber">
|
||||
<ProFormDigit name="${column.javaField}" label="${column.columnLabel}" min={0} <#if column.required>rules={[{ required: true, message: '${column.columnLabel}不能为空' }]}</#if> />
|
||||
<#elseif (column.htmlType == "select" || column.htmlType == "radio" || column.htmlType == "switch") && column.dictType?has_content>
|
||||
<ProFormSelect name="${column.javaField}" label="${column.columnLabel}" options={dictOptions(dicts.${column.dictType})} <#if column.required>rules={[{ required: true, message: '${column.columnLabel}不能为空' }]}</#if> />
|
||||
<#elseif column.htmlType == "checkbox" && column.dictType?has_content>
|
||||
<ProFormCheckbox.Group name="${column.javaField}" label="${column.columnLabel}" options={dictOptions(dicts.${column.dictType})} <#if column.required>rules={[{ required: true, message: '${column.columnLabel}不能为空' }]}</#if> />
|
||||
<#elseif column.htmlType == "switch">
|
||||
<Form.Item
|
||||
name="${column.javaField}"
|
||||
label="${column.columnLabel}"
|
||||
valuePropName="checked"
|
||||
getValueProps={value => ({ checked: value === ${column.switchActiveValue} })}
|
||||
normalize={checked => (checked ? ${column.switchActiveValue} : ${column.switchInactiveValue})}
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<#elseif column.htmlType == "datetime">
|
||||
<ProFormDateTimePicker name="${column.javaField}" label="${column.columnLabel}" <#if column.required>rules={[{ required: true, message: '${column.columnLabel}不能为空' }]}</#if> />
|
||||
<#elseif column.htmlType == "imageUpload">
|
||||
<Form.Item name="${column.javaField}" label="${column.columnLabel}" <#if column.required>rules={[{ required: true, message: '${column.columnLabel}不能为空' }]}</#if>>
|
||||
<ImageUpload />
|
||||
</Form.Item>
|
||||
<#elseif column.htmlType == "fileUpload">
|
||||
<Form.Item name="${column.javaField}" label="${column.columnLabel}" <#if column.required>rules={[{ required: true, message: '${column.columnLabel}不能为空' }]}</#if>>
|
||||
<FileUpload />
|
||||
</Form.Item>
|
||||
<#elseif column.htmlType == "editor">
|
||||
<Form.Item name="${column.javaField}" label="${column.columnLabel}" <#if column.required>rules={[{ required: true, message: '${column.columnLabel}不能为空' }]}</#if>>
|
||||
<RichTextEditor />
|
||||
</Form.Item>
|
||||
<#else>
|
||||
<ProFormText name="${column.javaField}" label="${column.columnLabel}" <#if column.required>rules={[{ required: true, message: '${column.columnLabel}不能为空' }]}</#if> />
|
||||
</#if>
|
||||
</#if>
|
||||
</#list>
|
||||
</ModalForm>
|
||||
</PageContainer>
|
||||
);
|
||||
}
|
||||
615
gen/index.vue.ftl
Normal file
615
gen/index.vue.ftl
Normal file
@ -0,0 +1,615 @@
|
||||
<template>
|
||||
<div class="p-2 page-shell ${moduleName}-${businessName}-page">
|
||||
<div class="search-wrap">
|
||||
<el-card shadow="hover" class="search-panel" :class="{ 'is-collapsed': !showSearch }">
|
||||
<template #header>
|
||||
<div class="panel-heading search-panel-toggle" @click.stop="showSearch = !showSearch">
|
||||
<div><h3>筛选条件</h3></div>
|
||||
</div>
|
||||
</template>
|
||||
<el-form ref="queryFormRef" :model="queryParams" :inline="true" class="query-form">
|
||||
<#list columns as column>
|
||||
<#if column.query>
|
||||
<#if column.htmlType == "input" || column.htmlType == "textarea">
|
||||
<el-form-item label="${column.columnLabel}" prop="${column.javaField}">
|
||||
<el-input v-model="queryParams.${column.javaField}" placeholder="请输入${column.columnLabel}" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
<#elseif column.htmlType == "inputNumber">
|
||||
<el-form-item label="${column.columnLabel}" prop="${column.javaField}">
|
||||
<el-input-number v-model="queryParams.${column.javaField}" controls-position="right" />
|
||||
</el-form-item>
|
||||
<#elseif (column.htmlType == "select" || column.htmlType == "radio") && column.dictType?has_content>
|
||||
<el-form-item label="${column.columnLabel}" prop="${column.javaField}">
|
||||
<el-select v-model="queryParams.${column.javaField}" placeholder="请选择${column.columnLabel}" clearable >
|
||||
<el-option v-for="dict in ${column.dictType}" :key="dict.value" :label="dict.label" :value="dict.value"/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<#elseif column.htmlType == "switch" && column.dictType?has_content>
|
||||
<el-form-item label="${column.columnLabel}" prop="${column.javaField}">
|
||||
<el-select v-model="queryParams.${column.javaField}" placeholder="请选择${column.columnLabel}" clearable >
|
||||
<el-option v-for="dict in ${column.dictType}" :key="dict.value" :label="dict.label" :value="dict.value"/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<#elseif column.htmlType == "switch">
|
||||
<el-form-item label="${column.columnLabel}" prop="${column.javaField}">
|
||||
<el-select v-model="queryParams.${column.javaField}" placeholder="请选择${column.columnLabel}" clearable >
|
||||
<#if column.javaType == "Boolean">
|
||||
<el-option label="是" :value="true" />
|
||||
<el-option label="否" :value="false" />
|
||||
<#elseif column.javaType == "Integer" || column.javaType == "Long">
|
||||
<el-option label="开启" :value="0" />
|
||||
<el-option label="关闭" :value="1" />
|
||||
<#else>
|
||||
<el-option label="开启" value="0" />
|
||||
<el-option label="关闭" value="1" />
|
||||
</#if>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<#elseif (column.htmlType == "select" || column.htmlType == "radio") && !(column.dictType?has_content)>
|
||||
<el-form-item label="${column.columnLabel}" prop="${column.javaField}">
|
||||
<el-select v-model="queryParams.${column.javaField}" placeholder="请选择${column.columnLabel}" clearable >
|
||||
<el-option label="请选择字典生成" value="" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<#elseif column.htmlType == "datetime" && column.queryType != "BETWEEN">
|
||||
<el-form-item label="${column.columnLabel}" prop="${column.javaField}">
|
||||
<el-date-picker clearable
|
||||
v-model="queryParams.${column.javaField}"
|
||||
type="date"
|
||||
value-format="YYYY-MM-DD"
|
||||
placeholder="请选择${column.columnLabel}"
|
||||
/>
|
||||
</el-form-item>
|
||||
<#elseif column.htmlType == "datetime" && column.queryType == "BETWEEN" && column.query>
|
||||
<el-form-item label="${column.columnLabel}" style="width: 308px">
|
||||
<el-date-picker
|
||||
v-model="dateRange${column.capJavaField}"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
type="daterange"
|
||||
range-separator="-"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
:default-time="[new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 1, 1, 23, 59, 59)]"
|
||||
/>
|
||||
</el-form-item>
|
||||
</#if>
|
||||
</#if>
|
||||
</#list>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
</div>
|
||||
|
||||
<el-card shadow="hover" class="table-panel">
|
||||
<template #header>
|
||||
<div class="toolbar-shell">
|
||||
<div class="table-heading">
|
||||
<h3>${functionName}列表</h3>
|
||||
</div>
|
||||
<div class="toolbar-actions">
|
||||
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['${moduleName}:${businessName}:add']">新增</el-button>
|
||||
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['${moduleName}:${businessName}:edit']">修改</el-button>
|
||||
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['${moduleName}:${businessName}:remove']">删除</el-button>
|
||||
<#if enableExport>
|
||||
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['${moduleName}:${businessName}:export']">导出</el-button>
|
||||
</#if>
|
||||
<right-toolbar v-model:show-search="showSearch" :search="false" @query-table="getList"></right-toolbar>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<el-table v-loading="loading" border class="data-table" :data="${businessName}List" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<#list columns as column>
|
||||
<#if column.pk && column.list>
|
||||
<el-table-column label="${column.columnLabel}" align="center" prop="${column.javaField}" />
|
||||
<#elseif enableStatus && statusField == column.javaField>
|
||||
<el-table-column label="${column.columnLabel}" align="center" prop="${column.javaField}">
|
||||
<template #default="scope">
|
||||
<el-switch
|
||||
v-model="scope.row.${column.javaField}"
|
||||
:active-value="${statusField}ActiveValue"
|
||||
:inactive-value="${statusField}InactiveValue"
|
||||
@change="handleStatusChange(scope.row)"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<#elseif enableSort && sortField == column.javaField>
|
||||
<el-table-column label="${column.columnLabel}" align="center" prop="${column.javaField}" width="160">
|
||||
<template #default="scope">
|
||||
<#if column.javaType == "LocalDateTime">
|
||||
<el-date-picker
|
||||
v-model="scope.row.${column.javaField}"
|
||||
type="datetime"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
placeholder="选择${column.columnLabel}"
|
||||
@change="handleSortChange(scope.row)"
|
||||
/>
|
||||
<#else>
|
||||
<el-input-number v-model="scope.row.${column.javaField}" controls-position="right" :min="0" @change="handleSortChange(scope.row)" />
|
||||
</#if>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<#elseif column.list && column.htmlType == "switch">
|
||||
<el-table-column label="${column.columnLabel}" align="center" prop="${column.javaField}" width="120">
|
||||
<template #default="scope">
|
||||
<el-switch
|
||||
v-model="scope.row.${column.javaField}"
|
||||
<#if column.javaType == "Boolean">
|
||||
:active-value="true"
|
||||
:inactive-value="false"
|
||||
<#elseif column.javaType == "Integer" || column.javaType == "Long">
|
||||
:active-value="0"
|
||||
:inactive-value="1"
|
||||
<#else>
|
||||
active-value="0"
|
||||
inactive-value="1"
|
||||
</#if>
|
||||
disabled
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<#elseif column.list && column.htmlType == "datetime">
|
||||
<el-table-column label="${column.columnLabel}" align="center" prop="${column.javaField}" width="180">
|
||||
<template #default="scope">
|
||||
<span>{{ parseTime(scope.row.${column.javaField}, '{y}-{m}-{d}') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<#elseif column.list && column.htmlType == "imageUpload">
|
||||
<el-table-column label="${column.columnLabel}" align="center" prop="${column.javaField}Url" width="100">
|
||||
<template #default="scope">
|
||||
<image-preview :src="scope.row.${column.javaField}Url" :width="50" :height="50"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<#elseif column.list && column.dictColumn>
|
||||
<el-table-column label="${column.columnLabel}" align="center" prop="${column.javaField}">
|
||||
<template #default="scope">
|
||||
<#if column.htmlType == "checkbox">
|
||||
<dict-tag :options="${column.dictType}" :value="scope.row.${column.javaField} ? scope.row.${column.javaField}.split(',') : []"/>
|
||||
<#else>
|
||||
<dict-tag :options="${column.dictType}" :value="scope.row.${column.javaField}"/>
|
||||
</#if>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<#elseif column.list && "" != column.javaField>
|
||||
<el-table-column label="${column.columnLabel}" align="center" prop="${column.javaField}" />
|
||||
</#if>
|
||||
</#list>
|
||||
<#if enableStatus && !statusColumn.list>
|
||||
<el-table-column label="${statusColumn.columnComment}" align="center" prop="${statusField}">
|
||||
<template #default="scope">
|
||||
<el-switch
|
||||
v-model="scope.row.${statusField}"
|
||||
<#if statusColumn.javaType == "Boolean">
|
||||
:active-value="true"
|
||||
:inactive-value="false"
|
||||
<#elseif statusColumn.javaType == "Integer" || statusColumn.javaType == "Long">
|
||||
:active-value="0"
|
||||
:inactive-value="1"
|
||||
<#else>
|
||||
active-value="0"
|
||||
inactive-value="1"
|
||||
</#if>
|
||||
@change="handleStatusChange(scope.row)"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</#if>
|
||||
<#if enableSort && !sortColumn.list>
|
||||
<el-table-column label="${sortColumn.columnComment}" align="center" prop="${sortField}" width="160">
|
||||
<template #default="scope">
|
||||
<#if sortColumn.javaType == "LocalDateTime">
|
||||
<el-date-picker
|
||||
v-model="scope.row.${sortField}"
|
||||
type="datetime"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
placeholder="选择${sortColumn.columnComment}"
|
||||
@change="handleSortChange(scope.row)"
|
||||
/>
|
||||
<#else>
|
||||
<el-input-number v-model="scope.row.${sortField}" controls-position="right" :min="0" @change="handleSortChange(scope.row)" />
|
||||
</#if>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</#if>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template #default="scope">
|
||||
<el-tooltip content="修改" placement="top">
|
||||
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['${moduleName}:${businessName}:edit']"></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="删除" placement="top">
|
||||
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['${moduleName}:${businessName}:remove']"></el-button>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
|
||||
</el-card>
|
||||
<!-- 添加或修改${functionName}对话框 -->
|
||||
<el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
|
||||
<el-form ref="${businessName}FormRef" :model="form" :rules="rules" label-width="80px">
|
||||
<#list columns as column>
|
||||
<#if (column.insert || column.edit) && !column.pk>
|
||||
<#if column.htmlType == "input">
|
||||
<el-form-item label="${column.columnLabel}" prop="${column.javaField}">
|
||||
<el-input v-model="form.${column.javaField}" placeholder="请输入${column.columnLabel}" />
|
||||
</el-form-item>
|
||||
<#elseif column.htmlType == "inputNumber">
|
||||
<el-form-item label="${column.columnLabel}" prop="${column.javaField}">
|
||||
<el-input-number v-model="form.${column.javaField}" controls-position="right" />
|
||||
</el-form-item>
|
||||
<#elseif column.htmlType == "imageUpload">
|
||||
<el-form-item label="${column.columnLabel}" prop="${column.javaField}">
|
||||
<image-upload v-model="form.${column.javaField}"/>
|
||||
</el-form-item>
|
||||
<#elseif column.htmlType == "fileUpload">
|
||||
<el-form-item label="${column.columnLabel}" prop="${column.javaField}">
|
||||
<file-upload v-model="form.${column.javaField}"/>
|
||||
</el-form-item>
|
||||
<#elseif column.htmlType == "editor">
|
||||
<el-form-item label="${column.columnLabel}">
|
||||
<editor v-model="form.${column.javaField}" :min-height="192"/>
|
||||
</el-form-item>
|
||||
<#elseif column.htmlType == "select" && column.dictType?has_content>
|
||||
<el-form-item label="${column.columnLabel}" prop="${column.javaField}">
|
||||
<el-select v-model="form.${column.javaField}" placeholder="请选择${column.columnLabel}">
|
||||
<el-option
|
||||
v-for="dict in ${column.dictType}"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
<#if column.javaType == "Integer" || column.javaType == "Long">
|
||||
:value="parseInt(dict.value)"
|
||||
<#else>
|
||||
:value="dict.value"
|
||||
</#if>
|
||||
></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<#elseif column.htmlType == "select" && !(column.dictType?has_content)>
|
||||
<el-form-item label="${column.columnLabel}" prop="${column.javaField}">
|
||||
<el-select v-model="form.${column.javaField}" placeholder="请选择${column.columnLabel}">
|
||||
<el-option label="请选择字典生成" value="" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<#elseif column.htmlType == "checkbox" && column.dictType?has_content>
|
||||
<el-form-item label="${column.columnLabel}" prop="${column.javaField}">
|
||||
<el-checkbox-group v-model="form.${column.javaField}">
|
||||
<el-checkbox
|
||||
v-for="dict in ${column.dictType}"
|
||||
:key="dict.value"
|
||||
:label="dict.value">
|
||||
{{dict.label}}
|
||||
</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</el-form-item>
|
||||
<#elseif column.htmlType == "checkbox" && !(column.dictType?has_content)>
|
||||
<el-form-item label="${column.columnLabel}" prop="${column.javaField}">
|
||||
<el-checkbox-group v-model="form.${column.javaField}">
|
||||
<el-checkbox>请选择字典生成</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</el-form-item>
|
||||
<#elseif column.htmlType == "radio" && column.dictType?has_content>
|
||||
<el-form-item label="${column.columnLabel}" prop="${column.javaField}">
|
||||
<el-radio-group v-model="form.${column.javaField}">
|
||||
<el-radio
|
||||
v-for="dict in ${column.dictType}"
|
||||
:key="dict.value"
|
||||
<#if column.javaType == "Integer" || column.javaType == "Long">
|
||||
:value="parseInt(dict.value)"
|
||||
<#else>
|
||||
:value="dict.value"
|
||||
</#if>
|
||||
>{{dict.label}}</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<#elseif column.htmlType == "radio" && !(column.dictType?has_content)>
|
||||
<el-form-item label="${column.columnLabel}" prop="${column.javaField}">
|
||||
<el-radio-group v-model="form.${column.javaField}">
|
||||
<el-radio value="1">请选择字典生成</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<#elseif column.htmlType == "switch">
|
||||
<el-form-item label="${column.columnLabel}" prop="${column.javaField}">
|
||||
<el-switch
|
||||
v-model="form.${column.javaField}"
|
||||
<#if column.javaType == "Boolean">
|
||||
:active-value="true"
|
||||
:inactive-value="false"
|
||||
<#elseif column.javaType == "Integer" || column.javaType == "Long">
|
||||
:active-value="0"
|
||||
:inactive-value="1"
|
||||
<#else>
|
||||
active-value="0"
|
||||
inactive-value="1"
|
||||
</#if>
|
||||
/>
|
||||
</el-form-item>
|
||||
<#elseif column.htmlType == "datetime">
|
||||
<el-form-item label="${column.columnLabel}" prop="${column.javaField}">
|
||||
<el-date-picker clearable
|
||||
v-model="form.${column.javaField}"
|
||||
type="datetime"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
placeholder="请选择${column.columnLabel}">
|
||||
</el-date-picker>
|
||||
</el-form-item>
|
||||
<#elseif column.htmlType == "textarea">
|
||||
<el-form-item label="${column.columnLabel}" prop="${column.javaField}">
|
||||
<el-input v-model="form.${column.javaField}" type="textarea" placeholder="请输入内容" />
|
||||
</el-form-item>
|
||||
</#if>
|
||||
</#if>
|
||||
</#list>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button :loading="buttonLoading" type="primary" @click="submitForm">确 定</el-button>
|
||||
<el-button @click="cancel">取 消</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="${BusinessName}" lang="ts">
|
||||
import {
|
||||
add${BusinessName},
|
||||
<#if enableStatus>
|
||||
change${BusinessName}Status,
|
||||
</#if>
|
||||
del${BusinessName},
|
||||
get${BusinessName},
|
||||
list${BusinessName},
|
||||
<#if enableSort>
|
||||
update${BusinessName}Sort,
|
||||
</#if>
|
||||
update${BusinessName}
|
||||
} from '@/api/${moduleName}/${businessName}';
|
||||
import { ${BusinessName}Form, ${BusinessName}Query, ${BusinessName}VO } from '@/api/${moduleName}/${businessName}/types';
|
||||
import { useLoading } from '@/hooks/async/useLoading';
|
||||
import { useFormDialog } from '@/hooks/dialog/useFormDialog';
|
||||
<#if needAddDateRange>
|
||||
import { useDateRangeQuery } from '@/hooks/form/useDateRangeQuery';
|
||||
</#if>
|
||||
import { useSearchReset } from '@/hooks/form/useSearchReset';
|
||||
import { useSearchToggle } from '@/hooks/form/useSearchToggle';
|
||||
import { useTableSelection } from '@/hooks/table/useTableSelection';
|
||||
<#if needDict>
|
||||
import { useDict } from '@/utils/dict';
|
||||
</#if>
|
||||
<#if needParseTime>
|
||||
import { parseTime } from '@/utils/ruoyi';
|
||||
</#if>
|
||||
import modal from '@/plugins/modal';
|
||||
<#if enableExport>
|
||||
import { download as requestDownload } from '@/utils/request';
|
||||
</#if>
|
||||
|
||||
<#if needDict>
|
||||
const { ${dictsNoSymbol} } = toRefs<any>(useDict(${dicts}));
|
||||
</#if>
|
||||
|
||||
<#if enableStatus>
|
||||
const ${statusField}ActiveValue = <#if statusColumn.javaType == "Boolean">true<#elseif statusColumn.javaType == "Integer" || statusColumn.javaType == "Long">0<#else>'0'</#if>;
|
||||
const ${statusField}InactiveValue = <#if statusColumn.javaType == "Boolean">false<#elseif statusColumn.javaType == "Integer" || statusColumn.javaType == "Long">1<#else>'1'</#if>;
|
||||
</#if>
|
||||
|
||||
const ${businessName}List = ref<${BusinessName}VO[]>([]);
|
||||
const buttonLoading = ref(false);
|
||||
const { loading, withLoading } = useLoading(true);
|
||||
const { showSearch } = useSearchToggle();
|
||||
const total = ref(0);
|
||||
<#list columns as column>
|
||||
<#if column.htmlType == "datetime" && column.queryType == "BETWEEN" && column.query>
|
||||
const {
|
||||
dateRange: dateRange${column.capJavaField},
|
||||
applyDateRange: apply${column.capJavaField}DateRange,
|
||||
resetDateRange: reset${column.capJavaField}DateRange
|
||||
} = useDateRangeQuery('${column.capJavaField}');
|
||||
</#if>
|
||||
</#list>
|
||||
|
||||
const queryFormRef = ref<ElFormInstance>();
|
||||
const ${businessName}FormRef = ref<ElFormInstance>();
|
||||
|
||||
const initFormData: ${BusinessName}Form = {
|
||||
<#list columns as column>
|
||||
<#if column.insert || column.edit>
|
||||
<#if column.htmlType == "checkbox">
|
||||
${column.javaField}: [],
|
||||
<#else>
|
||||
${column.javaField}: undefined,
|
||||
</#if>
|
||||
</#if>
|
||||
</#list>
|
||||
}
|
||||
const data = reactive<PageData<${BusinessName}Form, ${BusinessName}Query>>({
|
||||
form: { ...initFormData },
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
<#list columns as column>
|
||||
<#if column.query>
|
||||
<#if column.htmlType != "datetime" || column.queryType != "BETWEEN">
|
||||
${column.javaField}: undefined,
|
||||
</#if>
|
||||
</#if>
|
||||
</#list>
|
||||
params: {
|
||||
<#list columns as column>
|
||||
<#if column.query>
|
||||
<#if column.htmlType == "datetime" && column.queryType == "BETWEEN">
|
||||
${column.javaField}: undefined,
|
||||
</#if>
|
||||
</#if>
|
||||
</#list>
|
||||
}
|
||||
},
|
||||
rules: {
|
||||
<#list columns as column>
|
||||
<#if column.insert || column.edit>
|
||||
<#if column.required>
|
||||
${column.javaField}: [
|
||||
{ required: true, message: "${column.columnLabel}不能为空", trigger: <#if column.htmlType == "select" || column.htmlType == "radio" || column.htmlType == "switch" || column.htmlType == "inputNumber">"change"<#else>"blur"</#if> }
|
||||
],
|
||||
</#if>
|
||||
</#if>
|
||||
</#list>
|
||||
}
|
||||
});
|
||||
|
||||
const { queryParams, form, rules } = toRefs(data);
|
||||
const { ids, single, multiple, handleSelectionChange } = useTableSelection<${BusinessName}VO>(item => item.${pkColumn.javaField});
|
||||
const { dialog, resetForm: reset, openDialog, showDialog, closeDialog } = useFormDialog({
|
||||
form,
|
||||
formRef: ${businessName}FormRef,
|
||||
initialFormData: initFormData
|
||||
});
|
||||
|
||||
/** 查询${functionName}列表 */
|
||||
const getList = async () => {
|
||||
await withLoading(async () => {
|
||||
<#if needAddDateRange>
|
||||
let params = queryParams.value;
|
||||
<#list columns as column>
|
||||
<#if column.htmlType == "datetime" && column.queryType == "BETWEEN" && column.query>
|
||||
params = apply${column.capJavaField}DateRange(params);
|
||||
</#if>
|
||||
</#list>
|
||||
const res = await list${BusinessName}(params);
|
||||
<#else>
|
||||
const res = await list${BusinessName}(queryParams.value);
|
||||
</#if>
|
||||
${businessName}List.value = res.data?.rows;
|
||||
total.value = res.data?.total;
|
||||
});
|
||||
};
|
||||
|
||||
/** 取消按钮 */
|
||||
const cancel = () => {
|
||||
reset();
|
||||
closeDialog();
|
||||
};
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.value.pageNum = 1;
|
||||
getList();
|
||||
};
|
||||
|
||||
const { resetQuery } = useSearchReset({
|
||||
queryFormRef,
|
||||
queryParams,
|
||||
pageNumKey: 'pageNum',
|
||||
pageSizeKey: 'pageSize',
|
||||
initialPageSize: 10,
|
||||
resetExtras: () => {
|
||||
<#list columns as column>
|
||||
<#if column.htmlType == "datetime" && column.queryType == "BETWEEN" && column.query>
|
||||
reset${column.capJavaField}DateRange();
|
||||
</#if>
|
||||
</#list>
|
||||
},
|
||||
afterReset: () => {
|
||||
handleQuery();
|
||||
}
|
||||
});
|
||||
|
||||
/** 新增按钮操作 */
|
||||
const handleAdd = () => {
|
||||
openDialog('添加${functionName}');
|
||||
};
|
||||
|
||||
/** 修改按钮操作 */
|
||||
const handleUpdate = async (row?: Partial<${BusinessName}VO>) => {
|
||||
reset();
|
||||
const _${pkColumn.javaField} = row?.${pkColumn.javaField} || ids.value[0];
|
||||
const res = await get${BusinessName}(_${pkColumn.javaField});
|
||||
Object.assign(form.value, res.data);
|
||||
<#list columns as column>
|
||||
<#if column.htmlType == "checkbox">
|
||||
form.value.${column.javaField} = form.value.${column.javaField}.split(",");
|
||||
</#if>
|
||||
</#list>
|
||||
showDialog('修改${functionName}');
|
||||
};
|
||||
|
||||
/** 提交按钮 */
|
||||
const submitForm = () => {
|
||||
${businessName}FormRef.value?.validate(async (valid: boolean) => {
|
||||
if (valid) {
|
||||
buttonLoading.value = true;
|
||||
<#list columns as column>
|
||||
<#if column.htmlType == "checkbox">
|
||||
form.value.${column.javaField} = form.value.${column.javaField}.join(",");
|
||||
</#if>
|
||||
</#list>
|
||||
if (form.value.${pkColumn.javaField}) {
|
||||
await update${BusinessName}(form.value).finally(() => (buttonLoading.value = false));
|
||||
} else {
|
||||
await add${BusinessName}(form.value).finally(() => (buttonLoading.value = false));
|
||||
}
|
||||
modal.msgSuccess('操作成功');
|
||||
closeDialog();
|
||||
await getList();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/** 删除按钮操作 */
|
||||
const handleDelete = async (row?: Partial<${BusinessName}VO>) => {
|
||||
const _${pkColumn.javaField}s = row?.${pkColumn.javaField} || ids.value;
|
||||
await modal.confirm('是否确认删除${functionName}编号为"' + _${pkColumn.javaField}s + '"的数据项?');
|
||||
await del${BusinessName}(_${pkColumn.javaField}s);
|
||||
modal.msgSuccess('删除成功');
|
||||
await getList();
|
||||
};
|
||||
|
||||
<#if enableExport>
|
||||
/** 导出按钮操作 */
|
||||
const handleExport = () => {
|
||||
requestDownload(
|
||||
'${moduleName}/${businessName}/export',
|
||||
{
|
||||
...queryParams.value
|
||||
},
|
||||
`${businessName}_${r'${new Date().getTime()}'}.xlsx`
|
||||
);
|
||||
};
|
||||
</#if>
|
||||
|
||||
<#if enableStatus>
|
||||
/** 状态修改 */
|
||||
const handleStatusChange = async (row: Partial<${BusinessName}VO>) => {
|
||||
const text = row.${statusField} === ${statusField}ActiveValue ? '启用' : '停用';
|
||||
try {
|
||||
await modal.confirm('确认要"' + text + '"吗?');
|
||||
await change${BusinessName}Status(row.${pkColumn.javaField}, row.${statusField});
|
||||
modal.msgSuccess(text + '成功');
|
||||
} catch (err) {
|
||||
row.${statusField} = row.${statusField} === ${statusField}ActiveValue ? ${statusField}InactiveValue : ${statusField}ActiveValue;
|
||||
}
|
||||
};
|
||||
</#if>
|
||||
|
||||
<#if enableSort>
|
||||
/** 排序调整 */
|
||||
const handleSortChange = async (row: Partial<${BusinessName}VO>) => {
|
||||
try {
|
||||
await update${BusinessName}Sort(row.${pkColumn.javaField}, row.${sortField});
|
||||
modal.msgSuccess('排序更新成功');
|
||||
} catch (err) {
|
||||
await getList();
|
||||
}
|
||||
};
|
||||
</#if>
|
||||
|
||||
onMounted(() => {
|
||||
getList();
|
||||
});
|
||||
</script>
|
||||
|
||||
@ -1,8 +1,6 @@
|
||||
import type { BaseEntity<#if !table.tree>, PageQuery</#if> } from '@/api/types';
|
||||
|
||||
export interface ${BusinessName}VO {
|
||||
<#list columns as column>
|
||||
<#if column.list || column.pk>
|
||||
<#if column.list>
|
||||
/**
|
||||
* ${column.columnComment}
|
||||
*/
|
||||
@ -11,7 +9,7 @@ export interface ${BusinessName}VO {
|
||||
/**
|
||||
* ${column.columnComment}Url
|
||||
*/
|
||||
${column.javaField}Url?: string;
|
||||
${column.javaField}Url: string;
|
||||
</#if>
|
||||
</#if>
|
||||
</#list>
|
||||
@ -19,22 +17,18 @@ export interface ${BusinessName}VO {
|
||||
/**
|
||||
* 子对象
|
||||
*/
|
||||
children?: ${BusinessName}VO[];
|
||||
children: ${BusinessName}VO[];
|
||||
</#if>
|
||||
}
|
||||
|
||||
export interface ${BusinessName}Form extends BaseEntity {
|
||||
<#list columns as column>
|
||||
<#if column.insert || column.edit || column.pk>
|
||||
<#if column.insert || column.edit>
|
||||
/**
|
||||
* ${column.columnComment}
|
||||
*/
|
||||
<#if column.htmlType == "checkbox">
|
||||
${column.javaField}?: string | string[];
|
||||
<#else>
|
||||
${column.javaField}?: ${column.tsType};
|
||||
</#if>
|
||||
</#if>
|
||||
</#list>
|
||||
}
|
||||
|
||||
@ -50,5 +44,5 @@ export interface ${BusinessName}Query<#if !table.tree> extends PageQuery</#if> {
|
||||
/**
|
||||
* 日期范围参数
|
||||
*/
|
||||
params?: Record<string, unknown>;
|
||||
params?: any;
|
||||
}
|
||||
|
||||
214
index.html
Normal file
214
index.html
Normal file
@ -0,0 +1,214 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
|
||||
<meta name="renderer" content="webkit" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" />
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
<title>%VITE_APP_TITLE%</title>
|
||||
<!--[if lt IE 11]>
|
||||
<script>
|
||||
window.location.href = '/ie.html';
|
||||
</script>
|
||||
<![endif]-->
|
||||
<style>
|
||||
html,
|
||||
body,
|
||||
#app {
|
||||
height: 100%;
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
.chromeframe {
|
||||
margin: 0.2em 0;
|
||||
background: #ccc;
|
||||
color: #000;
|
||||
padding: 0.2em 0;
|
||||
}
|
||||
|
||||
#loader-wrapper {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 999999;
|
||||
}
|
||||
|
||||
#loader {
|
||||
display: block;
|
||||
position: relative;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
width: 150px;
|
||||
height: 150px;
|
||||
margin: -75px 0 0 -75px;
|
||||
border-radius: 50%;
|
||||
border: 3px solid transparent;
|
||||
border-top-color: #fff;
|
||||
-webkit-animation: spin 2s linear infinite;
|
||||
-ms-animation: spin 2s linear infinite;
|
||||
-moz-animation: spin 2s linear infinite;
|
||||
-o-animation: spin 2s linear infinite;
|
||||
animation: spin 2s linear infinite;
|
||||
z-index: 1001;
|
||||
}
|
||||
|
||||
#loader:before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 5px;
|
||||
left: 5px;
|
||||
right: 5px;
|
||||
bottom: 5px;
|
||||
border-radius: 50%;
|
||||
border: 3px solid transparent;
|
||||
border-top-color: #fff;
|
||||
-webkit-animation: spin 3s linear infinite;
|
||||
-moz-animation: spin 3s linear infinite;
|
||||
-o-animation: spin 3s linear infinite;
|
||||
-ms-animation: spin 3s linear infinite;
|
||||
animation: spin 3s linear infinite;
|
||||
}
|
||||
|
||||
#loader:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 15px;
|
||||
left: 15px;
|
||||
right: 15px;
|
||||
bottom: 15px;
|
||||
border-radius: 50%;
|
||||
border: 3px solid transparent;
|
||||
border-top-color: #fff;
|
||||
-moz-animation: spin 1.5s linear infinite;
|
||||
-o-animation: spin 1.5s linear infinite;
|
||||
-ms-animation: spin 1.5s linear infinite;
|
||||
-webkit-animation: spin 1.5s linear infinite;
|
||||
animation: spin 1.5s linear infinite;
|
||||
}
|
||||
|
||||
@-webkit-keyframes spin {
|
||||
0% {
|
||||
-webkit-transform: rotate(0deg);
|
||||
-ms-transform: rotate(0deg);
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
100% {
|
||||
-webkit-transform: rotate(360deg);
|
||||
-ms-transform: rotate(360deg);
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% {
|
||||
-webkit-transform: rotate(0deg);
|
||||
-ms-transform: rotate(0deg);
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
100% {
|
||||
-webkit-transform: rotate(360deg);
|
||||
-ms-transform: rotate(360deg);
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
#loader-wrapper .loader-section {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
width: 51%;
|
||||
height: 100%;
|
||||
background: #7171c6;
|
||||
z-index: 1000;
|
||||
-webkit-transform: translateX(0);
|
||||
-ms-transform: translateX(0);
|
||||
transform: translateX(0);
|
||||
}
|
||||
|
||||
#loader-wrapper .loader-section.section-left {
|
||||
left: 0;
|
||||
}
|
||||
|
||||
#loader-wrapper .loader-section.section-right {
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.loaded #loader-wrapper .loader-section.section-left {
|
||||
-webkit-transform: translateX(-100%);
|
||||
-ms-transform: translateX(-100%);
|
||||
transform: translateX(-100%);
|
||||
-webkit-transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
|
||||
transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
|
||||
}
|
||||
|
||||
.loaded #loader-wrapper .loader-section.section-right {
|
||||
-webkit-transform: translateX(100%);
|
||||
-ms-transform: translateX(100%);
|
||||
transform: translateX(100%);
|
||||
-webkit-transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
|
||||
transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
|
||||
}
|
||||
|
||||
.loaded #loader {
|
||||
opacity: 0;
|
||||
-webkit-transition: all 0.3s ease-out;
|
||||
transition: all 0.3s ease-out;
|
||||
}
|
||||
|
||||
.loaded #loader-wrapper {
|
||||
visibility: hidden;
|
||||
-webkit-transform: translateY(-100%);
|
||||
-ms-transform: translateY(-100%);
|
||||
transform: translateY(-100%);
|
||||
-webkit-transition: all 0.3s 1s ease-out;
|
||||
transition: all 0.3s 1s ease-out;
|
||||
}
|
||||
|
||||
.no-js #loader-wrapper {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.no-js h1 {
|
||||
color: #222222;
|
||||
}
|
||||
|
||||
#loader-wrapper .load_title {
|
||||
font-family: 'Open Sans';
|
||||
color: #fff;
|
||||
font-size: 19px;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
z-index: 9999999999999;
|
||||
position: absolute;
|
||||
top: 60%;
|
||||
opacity: 1;
|
||||
line-height: 30px;
|
||||
}
|
||||
|
||||
#loader-wrapper .load_title span {
|
||||
font-weight: normal;
|
||||
font-style: italic;
|
||||
font-size: 13px;
|
||||
color: #fff;
|
||||
opacity: 0.5;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app">
|
||||
<div id="loader-wrapper">
|
||||
<div id="loader"></div>
|
||||
<div class="loader-section section-left"></div>
|
||||
<div class="loader-section section-right"></div>
|
||||
<div class="load_title">正在加载系统资源,请耐心等待</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
</html>
|
||||
111
package.json
111
package.json
@ -1,65 +1,80 @@
|
||||
{
|
||||
"name": "plus-ui-react",
|
||||
"$schema": "https://json.schemastore.org/package",
|
||||
"name": "ruoyi-vue-plus",
|
||||
"version": "6.0.0",
|
||||
"private": true,
|
||||
"description": "React + Ant Design version of RuoYi-Vue-Plus UI",
|
||||
"type": "module",
|
||||
"description": "RuoYi-Vue-Plus后台管理系统",
|
||||
"license": "MIT",
|
||||
"author": "LionLi",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://gitee.com/JavaLionLi/plus-ui.git"
|
||||
},
|
||||
"packageManager": "pnpm@10.34.5",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "pnpm build:prod",
|
||||
"build:dev": "cross-env UMI_ENV=development BABEL_POLYFILL=none max build",
|
||||
"build:prod": "cross-env UMI_ENV=production BABEL_POLYFILL=none max build",
|
||||
"dev": "cross-env UMI_ENV=development MOCK=none BABEL_POLYFILL=none max dev",
|
||||
"build": "vite build --mode production",
|
||||
"build:dev": "vite build --mode development",
|
||||
"build:prod": "vite build --mode production",
|
||||
"dev": "vite serve --mode development",
|
||||
"fmt": "oxfmt .",
|
||||
"lint": "oxlint src config vite.config.ts && cross-env UMI_ENV=development BABEL_POLYFILL=none max setup && tsc --noEmit",
|
||||
"lint:fix": "oxlint --fix src config vite.config.ts",
|
||||
"openapi": "max openapi",
|
||||
"preview": "max preview --port 8000",
|
||||
"start": "cross-env UMI_ENV=development MOCK=none BABEL_POLYFILL=none max dev"
|
||||
"lint": "oxlint src",
|
||||
"lint:fix": "oxlint --fix src",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@ant-design/icons": "^6.3.2",
|
||||
"@ant-design/pro-components": "3.1.12-0",
|
||||
"@ant-design/x": "^2.8.0",
|
||||
"@iconify/react": "^6.0.2",
|
||||
"@tanstack/react-query": "^5.101.4",
|
||||
"@umijs/max": "^4.6.82",
|
||||
"@wangeditor-next/editor": "^5.7.16",
|
||||
"@wangeditor-next/editor-for-react": "^2.0.3",
|
||||
"ahooks": "^3.9.7",
|
||||
"antd": "^6.5.2",
|
||||
"antd-style": "^4.1.0",
|
||||
"axios": "1.18.1",
|
||||
"browser-image-compression": "^2.0.2",
|
||||
"clsx": "^2.1.1",
|
||||
"crypto-js": "4.2.0",
|
||||
"dayjs": "^1.11.21",
|
||||
"echarts": "^6.1.0",
|
||||
"echarts-for-react": "^3.0.6",
|
||||
"@element-plus/icons-vue": "2.3.2",
|
||||
"@highlightjs/vue-plugin": "2.1.2",
|
||||
"@iconify/vue": "^5.0.1",
|
||||
"@noble/ciphers": "^2.4.0",
|
||||
"@vueuse/core": "14.4.0",
|
||||
"@wangeditor-next/editor": "6.4.1",
|
||||
"@wangeditor-next/editor-for-vue": "6.4.1",
|
||||
"animate.css": "4.1.1",
|
||||
"await-to-js": "3.0.0",
|
||||
"axios": "1.20.0",
|
||||
"echarts": "6.1.0",
|
||||
"element-plus": "2.14.5",
|
||||
"highlight.js": "11.12.0",
|
||||
"image-conversion": "2.1.1",
|
||||
"jsencrypt": "3.5.4",
|
||||
"nprogress": "0.2.0",
|
||||
"react": "^19.2.8",
|
||||
"react-dom": "^19.2.8",
|
||||
"react-easy-crop": "^6.2.3",
|
||||
"react-json-view-lite": "^2.5.0",
|
||||
"react-syntax-highlighter": "^16.1.1",
|
||||
"screenfull": "^6.0.2",
|
||||
"zustand": "^5.0.14"
|
||||
"pinia": "4.0.3",
|
||||
"vue": "3.5.42",
|
||||
"vue-cropper": "1.1.4",
|
||||
"vue-i18n": "11.4.10",
|
||||
"vue-json-pretty": "2.6.0",
|
||||
"vue-router": "5.3.1",
|
||||
"vue-types": "7.0.0",
|
||||
"vxe-pc-ui": "4.18.9",
|
||||
"vxe-table": "4.21.9"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/crypto-js": "4.2.2",
|
||||
"@types/node": "^26.1.1",
|
||||
"@types/nprogress": "0.2.3",
|
||||
"@types/react": "^19.2.17",
|
||||
"@types/react-dom": "^19.2.3",
|
||||
"@types/react-syntax-highlighter": "^15.5.13",
|
||||
"@umijs/lint": "^4.6.82",
|
||||
"@umijs/max-plugin-openapi": "^2.0.3",
|
||||
"cross-env": "^10.1.0",
|
||||
"oxfmt": "^0.60.0",
|
||||
"oxlint": "^1.75.0",
|
||||
"typescript": "^6.0.3"
|
||||
"@unocss/preset-attributify": "66.10.1",
|
||||
"@unocss/preset-wind3": "66.10.1",
|
||||
"@vitejs/plugin-vue": "^6.0.8",
|
||||
"@vue/compiler-sfc": "3.5.42",
|
||||
"autoprefixer": "10.5.5",
|
||||
"oxfmt": "^0.67.0",
|
||||
"oxlint": "^1.82.0",
|
||||
"sass": "1.104.0",
|
||||
"typescript": "^6.0.3",
|
||||
"unocss": "66.10.1",
|
||||
"unplugin-auto-import": "21.1.0",
|
||||
"unplugin-vue-components": "32.1.0",
|
||||
"unplugin-vue-setup-extend-plus": "1.0.1",
|
||||
"vite": "^8.2.2",
|
||||
"vite-plugin-svg-icons-ng": "^1.9.3",
|
||||
"vitest": "4.1.11",
|
||||
"vue-tsc": "^3.3.11"
|
||||
},
|
||||
"browserslist": [
|
||||
"Chrome >= 87",
|
||||
"Edge >= 88",
|
||||
"Safari >= 14",
|
||||
"Firefox >= 78"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=20.19.0",
|
||||
"pnpm": ">=10.0.0"
|
||||
|
||||
18483
pnpm-lock.yaml
generated
18483
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
10
pnpm-workspace.yaml
Normal file
10
pnpm-workspace.yaml
Normal file
@ -0,0 +1,10 @@
|
||||
packages:
|
||||
- '.'
|
||||
|
||||
allowBuilds:
|
||||
'@parcel/watcher': true
|
||||
es5-ext: true
|
||||
esbuild: true
|
||||
|
||||
overrides:
|
||||
esbuild: 0.28.1
|
||||
|
Before Width: | Height: | Size: 7.9 KiB After Width: | Height: | Size: 7.9 KiB |
258
public/ie.html
Normal file
258
public/ie.html
Normal file
File diff suppressed because one or more lines are too long
20
src/App.vue
Normal file
20
src/App.vue
Normal file
@ -0,0 +1,20 @@
|
||||
<template>
|
||||
<el-config-provider :locale="appStore.locale" :size="appStore.size">
|
||||
<router-view />
|
||||
</el-config-provider>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useAppStore } from '@/store/modules/app';
|
||||
import { useSettingsStore } from '@/store/modules/settings';
|
||||
import { handleThemeStyle } from '@/utils/theme';
|
||||
|
||||
const appStore = useAppStore();
|
||||
|
||||
onMounted(() => {
|
||||
nextTick(() => {
|
||||
// 初始化主题样式
|
||||
handleThemeStyle(useSettingsStore().theme);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
48
src/animate.ts
Normal file
48
src/animate.ts
Normal file
@ -0,0 +1,48 @@
|
||||
// 前缀
|
||||
const animatePrefix = 'animate__animated ';
|
||||
// 开启随机动画 随机动画值
|
||||
const animateList: string[] = [
|
||||
animatePrefix + 'animate__pulse',
|
||||
animatePrefix + 'animate__rubberBand',
|
||||
animatePrefix + 'animate__bounceIn',
|
||||
animatePrefix + 'animate__bounceInLeft',
|
||||
animatePrefix + 'animate__fadeIn',
|
||||
animatePrefix + 'animate__fadeInLeft',
|
||||
animatePrefix + 'animate__fadeInDown',
|
||||
animatePrefix + 'animate__fadeInUp',
|
||||
animatePrefix + 'animate__flipInX',
|
||||
animatePrefix + 'animate__lightSpeedInLeft',
|
||||
animatePrefix + 'animate__rotateInDownLeft',
|
||||
animatePrefix + 'animate__rollIn',
|
||||
animatePrefix + 'animate__rotateInDownLeft',
|
||||
animatePrefix + 'animate__zoomIn',
|
||||
animatePrefix + 'animate__zoomInDown',
|
||||
animatePrefix + 'animate__slideInLeft',
|
||||
animatePrefix + 'animate__lightSpeedIn'
|
||||
];
|
||||
// 关闭随机动画后的默认效果
|
||||
const defaultAnimate = animatePrefix + 'animate__fadeIn';
|
||||
// 搜索隐藏显示动画
|
||||
const searchAnimate = {
|
||||
enter: '',
|
||||
leave: ''
|
||||
};
|
||||
|
||||
// 菜单搜索动画
|
||||
const menuSearchAnimate = {
|
||||
enter: animatePrefix + 'animate__fadeIn',
|
||||
leave: animatePrefix + 'animate__fadeOut'
|
||||
};
|
||||
// logo动画
|
||||
const logoAnimate = {
|
||||
enter: animatePrefix + 'animate__fadeIn',
|
||||
leave: animatePrefix + 'animate__fadeOut'
|
||||
};
|
||||
|
||||
export default {
|
||||
animateList,
|
||||
defaultAnimate,
|
||||
searchAnimate,
|
||||
menuSearchAnimate,
|
||||
logoAnimate
|
||||
};
|
||||
@ -1,10 +1,10 @@
|
||||
import type { R } from '@/api/types';
|
||||
import request from '@/api/request';
|
||||
import type { AxiosPromise } from '@/utils/api-types';
|
||||
import request from '@/utils/request';
|
||||
import type { SnailOpenApiUser } from './types';
|
||||
|
||||
export function registerCurrentSnailUser() {
|
||||
return request<R<SnailOpenApiUser>>({
|
||||
export const registerCurrentSnailUser = (): AxiosPromise<SnailOpenApiUser> => {
|
||||
return request({
|
||||
url: '/snail-ai/user/register',
|
||||
method: 'post'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@ -1,41 +1,63 @@
|
||||
import type { PageResult, R } from '@/api/types';
|
||||
import request from '@/api/request';
|
||||
import type { DemoForm, DemoQuery, DemoVO } from './types';
|
||||
import type { DemoForm, DemoQuery, DemoVO } from '@/api/demo/demo/types';
|
||||
import type { PageResult } from '@/api/types';
|
||||
import type { AxiosPromise } from '@/utils/api-types';
|
||||
import request from '@/utils/request';
|
||||
|
||||
export function listDemo(query?: DemoQuery) {
|
||||
return request<R<PageResult<DemoVO>>>({
|
||||
/**
|
||||
* 查询测试单列表
|
||||
* @param query
|
||||
* @returns {*}
|
||||
*/
|
||||
export const listDemo = (query?: DemoQuery): AxiosPromise<PageResult<DemoVO>> => {
|
||||
return request({
|
||||
url: '/demo/demo/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function getDemo(id: string | number) {
|
||||
return request<R<DemoVO>>({
|
||||
url: `/demo/demo/${id}`,
|
||||
/**
|
||||
* 查询测试单详细
|
||||
* @param id
|
||||
*/
|
||||
export const getDemo = (id: string | number): AxiosPromise<DemoVO> => {
|
||||
return request({
|
||||
url: '/demo/demo/' + id,
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function addDemo(data: DemoForm) {
|
||||
return request<R>({
|
||||
/**
|
||||
* 新增测试单
|
||||
* @param data
|
||||
*/
|
||||
export const addDemo = (data: DemoForm) => {
|
||||
return request({
|
||||
url: '/demo/demo',
|
||||
method: 'post',
|
||||
data
|
||||
data: data
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function updateDemo(data: DemoForm) {
|
||||
return request<R>({
|
||||
/**
|
||||
* 修改测试单
|
||||
* @param data
|
||||
*/
|
||||
export const updateDemo = (data: DemoForm) => {
|
||||
return request({
|
||||
url: '/demo/demo',
|
||||
method: 'put',
|
||||
data
|
||||
data: data
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function delDemo(id: string | number | Array<string | number>) {
|
||||
return request<R>({
|
||||
url: `/demo/demo/${id}`,
|
||||
/**
|
||||
* 删除测试单
|
||||
* @param id
|
||||
*/
|
||||
export const delDemo = (id: string | number | Array<string | number>) => {
|
||||
return request({
|
||||
url: '/demo/demo/' + id,
|
||||
method: 'delete'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@ -1,27 +1,90 @@
|
||||
import type { BaseEntity, PageQuery } from '@/api/types';
|
||||
export interface DemoVO {
|
||||
/**
|
||||
* 主键
|
||||
*/
|
||||
id: string | number;
|
||||
|
||||
/**
|
||||
* 部门id
|
||||
*/
|
||||
deptId: string | number;
|
||||
|
||||
/**
|
||||
* 用户id
|
||||
*/
|
||||
userId: string | number;
|
||||
|
||||
/**
|
||||
* 排序号
|
||||
*/
|
||||
orderNum: number;
|
||||
|
||||
/**
|
||||
* key键
|
||||
*/
|
||||
testKey: string;
|
||||
|
||||
/**
|
||||
* 值
|
||||
*/
|
||||
value: string;
|
||||
}
|
||||
|
||||
export interface DemoForm extends BaseEntity {
|
||||
/**
|
||||
* 主键
|
||||
*/
|
||||
id?: string | number;
|
||||
|
||||
/**
|
||||
* 部门id
|
||||
*/
|
||||
deptId?: string | number;
|
||||
|
||||
/**
|
||||
* 用户id
|
||||
*/
|
||||
userId?: string | number;
|
||||
|
||||
/**
|
||||
* 排序号
|
||||
*/
|
||||
orderNum?: number;
|
||||
|
||||
/**
|
||||
* key键
|
||||
*/
|
||||
testKey?: string;
|
||||
|
||||
/**
|
||||
* 值
|
||||
*/
|
||||
value?: string;
|
||||
}
|
||||
|
||||
export interface DemoQuery extends PageQuery {
|
||||
/**
|
||||
* 部门id
|
||||
*/
|
||||
deptId?: string | number;
|
||||
|
||||
/**
|
||||
* 用户id
|
||||
*/
|
||||
userId?: string | number;
|
||||
|
||||
/**
|
||||
* 排序号
|
||||
*/
|
||||
orderNum?: number;
|
||||
|
||||
/**
|
||||
* key键
|
||||
*/
|
||||
testKey?: string;
|
||||
|
||||
/**
|
||||
* 值
|
||||
*/
|
||||
value?: string;
|
||||
}
|
||||
|
||||
export interface DemoVO {
|
||||
id: string | number;
|
||||
deptId: string | number;
|
||||
userId: string | number;
|
||||
orderNum: number;
|
||||
testKey: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
@ -1,41 +1,62 @@
|
||||
import type { R } from '@/api/types';
|
||||
import request from '@/api/request';
|
||||
import type { TreeForm, TreeQuery, TreeVO } from './types';
|
||||
import type { TreeForm, TreeQuery, TreeVO } from '@/api/demo/tree/types';
|
||||
import type { AxiosPromise } from '@/utils/api-types';
|
||||
import request from '@/utils/request';
|
||||
|
||||
export function listTree(query?: TreeQuery) {
|
||||
return request<R<TreeVO[]>>({
|
||||
/**
|
||||
* 查询测试树列表
|
||||
* @param query
|
||||
* @returns {*}
|
||||
*/
|
||||
export const listTree = (query?: TreeQuery): AxiosPromise<TreeVO[]> => {
|
||||
return request({
|
||||
url: '/demo/tree/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function getTree(id: string | number) {
|
||||
return request<R<TreeVO>>({
|
||||
url: `/demo/tree/${id}`,
|
||||
/**
|
||||
* 查询测试树详细
|
||||
* @param id
|
||||
*/
|
||||
export const getTree = (id: string | number): AxiosPromise<TreeVO> => {
|
||||
return request({
|
||||
url: '/demo/tree/' + id,
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function addTree(data: TreeForm) {
|
||||
return request<R>({
|
||||
/**
|
||||
* 新增测试树
|
||||
* @param data
|
||||
*/
|
||||
export const addTree = (data: TreeForm) => {
|
||||
return request({
|
||||
url: '/demo/tree',
|
||||
method: 'post',
|
||||
data
|
||||
data: data
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function updateTree(data: TreeForm) {
|
||||
return request<R>({
|
||||
/**
|
||||
* 修改测试树
|
||||
* @param data
|
||||
*/
|
||||
export const updateTree = (data: TreeForm) => {
|
||||
return request({
|
||||
url: '/demo/tree',
|
||||
method: 'put',
|
||||
data
|
||||
data: data
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function delTree(id: string | number | Array<string | number>) {
|
||||
return request<R>({
|
||||
url: `/demo/tree/${id}`,
|
||||
/**
|
||||
* 删除测试树
|
||||
* @param id
|
||||
*/
|
||||
export const delTree = (id: string | number | Array<string | number>) => {
|
||||
return request({
|
||||
url: '/demo/tree/' + id,
|
||||
method: 'delete'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@ -1,25 +1,80 @@
|
||||
import type { BaseEntity } from '@/api/types';
|
||||
export interface TreeVO {
|
||||
/**
|
||||
* 主键
|
||||
*/
|
||||
id: string | number;
|
||||
|
||||
/**
|
||||
* 父id
|
||||
*/
|
||||
parentId: string | number;
|
||||
|
||||
/**
|
||||
* 部门id
|
||||
*/
|
||||
deptId: string | number;
|
||||
|
||||
/**
|
||||
* 用户id
|
||||
*/
|
||||
userId: string | number;
|
||||
|
||||
/**
|
||||
* 值
|
||||
*/
|
||||
treeName: string;
|
||||
|
||||
/**
|
||||
* 子对象
|
||||
*/
|
||||
children: TreeVO[];
|
||||
}
|
||||
|
||||
export interface TreeForm extends BaseEntity {
|
||||
/**
|
||||
* 主键
|
||||
*/
|
||||
id?: string | number;
|
||||
|
||||
/**
|
||||
* 父id
|
||||
*/
|
||||
parentId?: string | number;
|
||||
|
||||
/**
|
||||
* 部门id
|
||||
*/
|
||||
deptId?: string | number;
|
||||
|
||||
/**
|
||||
* 用户id
|
||||
*/
|
||||
userId?: string | number;
|
||||
|
||||
/**
|
||||
* 值
|
||||
*/
|
||||
treeName?: string;
|
||||
}
|
||||
|
||||
export interface TreeQuery {
|
||||
/**
|
||||
* 父id
|
||||
*/
|
||||
parentId?: string | number;
|
||||
|
||||
/**
|
||||
* 部门id
|
||||
*/
|
||||
deptId?: string | number;
|
||||
|
||||
/**
|
||||
* 用户id
|
||||
*/
|
||||
userId?: string | number;
|
||||
|
||||
/**
|
||||
* 值
|
||||
*/
|
||||
treeName?: string;
|
||||
}
|
||||
|
||||
export interface TreeVO {
|
||||
id: string | number;
|
||||
parentId: string | number;
|
||||
deptId: string | number;
|
||||
userId: string | number;
|
||||
treeName: string;
|
||||
children?: TreeVO[];
|
||||
}
|
||||
|
||||
126
src/api/login.ts
126
src/api/login.ts
@ -1,74 +1,106 @@
|
||||
import type { UserInfo } from '@/api/system/user/types';
|
||||
import type { LoginParams, LoginResult, R, RegisterParams, VerifyCodeResult } from '@/api/types';
|
||||
import request from '@/api/request';
|
||||
import { appEnv } from '@/utils/env';
|
||||
import type { AxiosPromise } from '@/utils/api-types';
|
||||
import { closePush } from '@/utils/push';
|
||||
import request from '@/utils/request';
|
||||
import type { LoginData, LoginResult, VerifyCodeResult } from './types';
|
||||
|
||||
export function login(data: LoginParams) {
|
||||
return request<R<LoginResult>>({
|
||||
// pc端固定客户端授权id
|
||||
const clientId = import.meta.env.VITE_APP_CLIENT_ID;
|
||||
|
||||
/**
|
||||
* @param data {LoginData}
|
||||
* @returns
|
||||
*/
|
||||
export function login(data: LoginData): AxiosPromise<LoginResult> {
|
||||
const params = {
|
||||
...data,
|
||||
clientId: data.clientId || clientId,
|
||||
grantType: data.grantType || 'password'
|
||||
};
|
||||
return request({
|
||||
url: '/auth/login',
|
||||
method: 'post',
|
||||
headers: {
|
||||
isToken: false,
|
||||
isEncrypt: 'true',
|
||||
isEncrypt: true,
|
||||
repeatSubmit: false
|
||||
},
|
||||
data: {
|
||||
...data,
|
||||
clientId: data.clientId || appEnv.clientId,
|
||||
grantType: data.grantType || 'password'
|
||||
}
|
||||
method: 'post',
|
||||
data: params
|
||||
});
|
||||
}
|
||||
|
||||
// 注册方法
|
||||
export function register(data: any) {
|
||||
const params = {
|
||||
...data,
|
||||
clientId: clientId,
|
||||
grantType: 'password'
|
||||
};
|
||||
return request({
|
||||
url: '/auth/register',
|
||||
headers: {
|
||||
isToken: false,
|
||||
isEncrypt: true,
|
||||
repeatSubmit: false
|
||||
},
|
||||
method: 'post',
|
||||
data: params
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 注销
|
||||
*/
|
||||
export function logout() {
|
||||
return request<R>({
|
||||
closePush();
|
||||
if (
|
||||
import.meta.env.VITE_APP_MESSAGE_ENABLED === 'true' &&
|
||||
import.meta.env.VITE_APP_MESSAGE_TRANSPORT.toLowerCase() === 'sse'
|
||||
) {
|
||||
request({
|
||||
url: import.meta.env.VITE_APP_MESSAGE_PATH + '/close',
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
return request({
|
||||
url: '/auth/logout',
|
||||
method: 'post'
|
||||
});
|
||||
}
|
||||
|
||||
export function register(data: RegisterParams) {
|
||||
return request<R>({
|
||||
url: '/auth/register',
|
||||
method: 'post',
|
||||
headers: {
|
||||
isToken: false,
|
||||
isEncrypt: 'true',
|
||||
repeatSubmit: false
|
||||
},
|
||||
data: {
|
||||
...data,
|
||||
clientId: appEnv.clientId,
|
||||
grantType: 'password'
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function callback(data: LoginParams) {
|
||||
return request<R<LoginResult>>({
|
||||
url: '/auth/social/callback',
|
||||
method: 'post',
|
||||
data: {
|
||||
...data,
|
||||
clientId: appEnv.clientId,
|
||||
grantType: 'social'
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function getCodeImg() {
|
||||
return request<R<VerifyCodeResult>>({
|
||||
/**
|
||||
* 获取验证码
|
||||
*/
|
||||
export function getCodeImg(): AxiosPromise<VerifyCodeResult> {
|
||||
return request({
|
||||
url: '/auth/code',
|
||||
method: 'get',
|
||||
headers: {
|
||||
isToken: false
|
||||
},
|
||||
method: 'get',
|
||||
timeout: 20000
|
||||
});
|
||||
}
|
||||
|
||||
export function getInfo() {
|
||||
return request<R<UserInfo>>({
|
||||
/**
|
||||
* 第三方登录
|
||||
*/
|
||||
export function callback(data: LoginData): AxiosPromise<any> {
|
||||
const LoginData = {
|
||||
...data,
|
||||
clientId: clientId,
|
||||
grantType: 'social'
|
||||
};
|
||||
return request({
|
||||
url: '/auth/social/callback',
|
||||
method: 'post',
|
||||
data: LoginData
|
||||
});
|
||||
}
|
||||
|
||||
// 获取用户详细信息
|
||||
export function getInfo(): AxiosPromise<UserInfo> {
|
||||
return request({
|
||||
url: '/system/user/getInfo',
|
||||
method: 'get'
|
||||
});
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
import type { BackendRoute, R } from '@/api/types';
|
||||
import request from '@/api/request';
|
||||
import type { RouteRecordRaw } from 'vue-router';
|
||||
import type { AxiosPromise } from '@/utils/api-types';
|
||||
import request from '@/utils/request';
|
||||
|
||||
export function getRouters() {
|
||||
return request<R<BackendRoute[]>>({
|
||||
// 获取路由
|
||||
export function getRouters(): AxiosPromise<RouteRecordRaw[]> {
|
||||
return request({
|
||||
url: '/system/menu/getRouters',
|
||||
method: 'get'
|
||||
});
|
||||
|
||||
10
src/api/monitor/cache/index.ts
vendored
10
src/api/monitor/cache/index.ts
vendored
@ -1,10 +1,12 @@
|
||||
import type { R } from '@/api/types';
|
||||
import request from '@/api/request';
|
||||
import type { AxiosPromise } from '@/utils/api-types';
|
||||
import request from '@/utils/request';
|
||||
import type { CacheVO } from './types';
|
||||
|
||||
export function getCache() {
|
||||
return request<R<CacheVO>>({
|
||||
// 查询缓存详细
|
||||
export function getCache(): AxiosPromise<CacheVO> {
|
||||
return request({
|
||||
url: '/monitor/cache',
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
8
src/api/monitor/cache/types.ts
vendored
8
src/api/monitor/cache/types.ts
vendored
@ -1,5 +1,7 @@
|
||||
export interface CacheVO {
|
||||
commandStats: Array<{ name: string; value: string | number }>;
|
||||
export interface CacheVO {
|
||||
commandStats: Array<{ name: string; value: string }>;
|
||||
|
||||
dbSize: number;
|
||||
info: Record<string, string>;
|
||||
|
||||
info: { [key: string]: string };
|
||||
}
|
||||
|
||||
@ -1,35 +1,36 @@
|
||||
import type { PageResult, R } from '@/api/types';
|
||||
import request from '@/api/request';
|
||||
import type { PageResult } from '@/api/types';
|
||||
import type { AxiosPromise } from '@/utils/api-types';
|
||||
import request from '@/utils/request';
|
||||
import type { LoginInfoQuery, LoginInfoVO } from './types';
|
||||
|
||||
export function listLoginInfo(query: LoginInfoQuery) {
|
||||
return request<R<PageResult<LoginInfoVO>>>({
|
||||
// 查询登录日志列表
|
||||
export function list(query: LoginInfoQuery): AxiosPromise<PageResult<LoginInfoVO>> {
|
||||
return request({
|
||||
url: '/monitor/loginInfo/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
}
|
||||
|
||||
export function list(query: LoginInfoQuery) {
|
||||
return listLoginInfo(query);
|
||||
}
|
||||
|
||||
// 删除登录日志
|
||||
export function delLoginInfo(infoId: string | number | Array<string | number>) {
|
||||
return request<R>({
|
||||
url: `/monitor/loginInfo/${infoId}`,
|
||||
return request({
|
||||
url: '/monitor/loginInfo/' + infoId,
|
||||
method: 'delete'
|
||||
});
|
||||
}
|
||||
|
||||
export function unlockLoginInfo(userName: string | string[]) {
|
||||
return request<R>({
|
||||
url: `/monitor/loginInfo/unlock/${userName}`,
|
||||
// 解锁用户登录状态
|
||||
export function unlockLoginInfo(userName: string | Array<string>) {
|
||||
return request({
|
||||
url: '/monitor/loginInfo/unlock/' + userName,
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
|
||||
// 清空登录日志
|
||||
export function cleanLoginInfo() {
|
||||
return request<R>({
|
||||
return request({
|
||||
url: '/monitor/loginInfo/clean',
|
||||
method: 'delete'
|
||||
});
|
||||
|
||||
@ -1,24 +1,20 @@
|
||||
import type { PageQuery } from '@/api/types';
|
||||
|
||||
export interface LoginInfoQuery extends Omit<PageQuery, 'isAsc'> {
|
||||
ipaddr?: string;
|
||||
userName?: string;
|
||||
status?: string;
|
||||
orderByColumn?: string;
|
||||
isAsc?: string;
|
||||
}
|
||||
|
||||
export interface LoginInfoVO {
|
||||
infoId: string | number;
|
||||
tenantId?: string | number;
|
||||
tenantId: string | number;
|
||||
userName: string;
|
||||
clientKey?: string;
|
||||
deviceType?: string;
|
||||
status: string;
|
||||
ipaddr?: string;
|
||||
loginLocation?: string;
|
||||
browser?: string;
|
||||
os?: string;
|
||||
msg?: string;
|
||||
loginTime?: string;
|
||||
ipaddr: string;
|
||||
loginLocation: string;
|
||||
browser: string;
|
||||
os: string;
|
||||
msg: string;
|
||||
loginTime: string;
|
||||
}
|
||||
|
||||
export interface LoginInfoQuery extends PageQuery {
|
||||
ipaddr: string;
|
||||
userName: string;
|
||||
status: string;
|
||||
orderByColumn: string;
|
||||
isAsc: string;
|
||||
}
|
||||
|
||||
@ -1,36 +1,37 @@
|
||||
import type { PageResult, R } from '@/api/types';
|
||||
import request from '@/api/request';
|
||||
import type { PageResult } from '@/api/types';
|
||||
import type { AxiosPromise } from '@/utils/api-types';
|
||||
import request from '@/utils/request';
|
||||
import type { OnlineQuery, OnlineVO } from './types';
|
||||
|
||||
export function listOnline(query: OnlineQuery) {
|
||||
return request<R<PageResult<OnlineVO>>>({
|
||||
// 查询在线用户列表
|
||||
export function list(query: OnlineQuery): AxiosPromise<PageResult<OnlineVO>> {
|
||||
return request({
|
||||
url: '/monitor/online/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
}
|
||||
|
||||
export function list(query: OnlineQuery) {
|
||||
return listOnline(query);
|
||||
}
|
||||
|
||||
// 强退用户
|
||||
export function forceLogout(tokenId: string) {
|
||||
return request<R>({
|
||||
url: `/monitor/online/${tokenId}`,
|
||||
return request({
|
||||
url: '/monitor/online/' + tokenId,
|
||||
method: 'delete'
|
||||
});
|
||||
}
|
||||
|
||||
// 获取当前用户登录在线设备
|
||||
export function getOnline() {
|
||||
return request<R<OnlineVO[] | PageResult<OnlineVO>>>({
|
||||
return request({
|
||||
url: '/monitor/online',
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
|
||||
// 删除当前在线设备
|
||||
export function delOnline(tokenId: string) {
|
||||
return request<R>({
|
||||
url: `/monitor/online/myself/${tokenId}`,
|
||||
return request({
|
||||
url: '/monitor/online/myself/' + tokenId,
|
||||
method: 'delete'
|
||||
});
|
||||
}
|
||||
|
||||
@ -1,19 +1,15 @@
|
||||
import type { BaseEntity, PageQuery } from '@/api/types';
|
||||
|
||||
export interface OnlineQuery extends PageQuery {
|
||||
ipaddr?: string;
|
||||
userName?: string;
|
||||
ipaddr: string;
|
||||
userName: string;
|
||||
}
|
||||
|
||||
export interface OnlineVO extends BaseEntity {
|
||||
tokenId: string;
|
||||
deptName?: string;
|
||||
deptName: string;
|
||||
userName: string;
|
||||
clientKey?: string;
|
||||
deviceType?: string;
|
||||
ipaddr?: string;
|
||||
loginLocation?: string;
|
||||
browser?: string;
|
||||
os?: string;
|
||||
loginTime?: number | string;
|
||||
ipaddr: string;
|
||||
loginLocation: string;
|
||||
browser: string;
|
||||
os: string;
|
||||
loginTime: number;
|
||||
}
|
||||
|
||||
@ -1,28 +1,28 @@
|
||||
import type { PageResult, R } from '@/api/types';
|
||||
import request from '@/api/request';
|
||||
import type { PageResult } from '@/api/types';
|
||||
import type { AxiosPromise } from '@/utils/api-types';
|
||||
import request from '@/utils/request';
|
||||
import type { OperLogQuery, OperLogVO } from './types';
|
||||
|
||||
export function listOperlog(query: OperLogQuery) {
|
||||
return request<R<PageResult<OperLogVO>>>({
|
||||
// 查询操作日志列表
|
||||
export function list(query: OperLogQuery): AxiosPromise<PageResult<OperLogVO>> {
|
||||
return request({
|
||||
url: '/monitor/operlog/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
}
|
||||
|
||||
export function list(query: OperLogQuery) {
|
||||
return listOperlog(query);
|
||||
}
|
||||
|
||||
// 删除操作日志
|
||||
export function delOperlog(operId: string | number | Array<string | number>) {
|
||||
return request<R>({
|
||||
url: `/monitor/operlog/${operId}`,
|
||||
return request({
|
||||
url: '/monitor/operlog/' + operId,
|
||||
method: 'delete'
|
||||
});
|
||||
}
|
||||
|
||||
// 清空操作日志
|
||||
export function cleanOperlog() {
|
||||
return request<R>({
|
||||
return request({
|
||||
url: '/monitor/operlog/clean',
|
||||
method: 'delete'
|
||||
});
|
||||
|
||||
@ -1,45 +1,71 @@
|
||||
import type { BaseEntity, PageQuery } from '@/api/types';
|
||||
|
||||
export interface OperLogQuery extends Omit<PageQuery, 'isAsc'> {
|
||||
operIp?: string;
|
||||
title?: string;
|
||||
operName?: string;
|
||||
userId?: string;
|
||||
deptId?: string;
|
||||
clientKey?: string;
|
||||
deviceType?: string;
|
||||
browser?: string;
|
||||
os?: string;
|
||||
businessType?: string;
|
||||
status?: string;
|
||||
orderByColumn?: string;
|
||||
isAsc?: string;
|
||||
export interface OperLogQuery extends PageQuery {
|
||||
operIp: string;
|
||||
title: string;
|
||||
operName: string;
|
||||
userId: string;
|
||||
deptId: string;
|
||||
clientKey: string;
|
||||
deviceType: string;
|
||||
browser: string;
|
||||
os: string;
|
||||
businessType: string;
|
||||
status: string;
|
||||
orderByColumn: string;
|
||||
isAsc: string;
|
||||
}
|
||||
|
||||
export interface OperLogVO extends BaseEntity {
|
||||
operId: string | number;
|
||||
tenantId?: string;
|
||||
title?: string;
|
||||
businessType?: number | string;
|
||||
businessTypes?: number[];
|
||||
method?: string;
|
||||
requestMethod?: string;
|
||||
operatorType?: number;
|
||||
operName?: string;
|
||||
userId?: string | number;
|
||||
deptId?: string | number;
|
||||
deptName?: string;
|
||||
clientKey?: string;
|
||||
deviceType?: string;
|
||||
browser?: string;
|
||||
os?: string;
|
||||
operUrl?: string;
|
||||
operIp?: string;
|
||||
operLocation?: string;
|
||||
operParam?: string;
|
||||
jsonResult?: string;
|
||||
status?: number | string;
|
||||
errorMsg?: string;
|
||||
operTime?: string;
|
||||
costTime?: number;
|
||||
tenantId: string;
|
||||
title: string;
|
||||
businessType: number;
|
||||
businessTypes: number[] | undefined;
|
||||
method: string;
|
||||
requestMethod: string;
|
||||
operatorType: number;
|
||||
operName: string;
|
||||
userId: string | number;
|
||||
deptId: string | number;
|
||||
deptName: string;
|
||||
clientKey: string;
|
||||
deviceType: string;
|
||||
browser: string;
|
||||
os: string;
|
||||
operUrl: string;
|
||||
operIp: string;
|
||||
operLocation: string;
|
||||
operParam: string;
|
||||
jsonResult: string;
|
||||
status: number;
|
||||
errorMsg: string;
|
||||
operTime: string;
|
||||
costTime: number;
|
||||
}
|
||||
|
||||
export interface OperLogForm {
|
||||
operId: number | string | undefined;
|
||||
tenantId: string | number | undefined;
|
||||
title: string;
|
||||
businessType: number;
|
||||
businessTypes: number[] | undefined;
|
||||
method: string;
|
||||
requestMethod: string;
|
||||
operatorType: number;
|
||||
operName: string;
|
||||
userId: string | number | undefined;
|
||||
deptId: string | number | undefined;
|
||||
deptName: string;
|
||||
clientKey: string;
|
||||
deviceType: string;
|
||||
browser: string;
|
||||
os: string;
|
||||
operUrl: string;
|
||||
operIp: string;
|
||||
operLocation: string;
|
||||
operParam: string;
|
||||
jsonResult: string;
|
||||
status: number;
|
||||
errorMsg: string;
|
||||
operTime: string;
|
||||
costTime: number;
|
||||
}
|
||||
|
||||
@ -1,244 +0,0 @@
|
||||
import { history } from '@umijs/max';
|
||||
import { message, Modal } from 'antd';
|
||||
import axios, { type AxiosError, type AxiosRequestConfig } from 'axios';
|
||||
import { useAppStore } from '@/stores/appStore';
|
||||
import { useTagsViewStore } from '@/stores/tagsViewStore';
|
||||
import { getToken, removeToken } from '@/utils/auth';
|
||||
import { decryptBase64, decryptWithAes, encryptBase64, encryptWithAes, generateAesKey } from '@/utils/crypto';
|
||||
import { appEnv } from '@/utils/env';
|
||||
import { decrypt, encrypt } from '@/utils/jsencrypt';
|
||||
import { tansParams } from '@/utils/ruoyi';
|
||||
|
||||
const SUCCESS = 200;
|
||||
const WARN = 601;
|
||||
const SERVER_ERROR = 500;
|
||||
const encryptHeader = 'encrypt-key';
|
||||
const repeatSubmitStorageKey = 'sessionObj';
|
||||
const repeatSubmitInterval = 500;
|
||||
|
||||
export interface RequestConfig<D = unknown> extends AxiosRequestConfig<D> {
|
||||
headers?: AxiosRequestConfig<D>['headers'] & {
|
||||
isToken?: false;
|
||||
isEncrypt?: 'true' | 'false' | boolean;
|
||||
repeatSubmit?: false;
|
||||
};
|
||||
}
|
||||
|
||||
export const isRelogin = { show: false };
|
||||
|
||||
type HandledRequestError = Error & { isHandled?: boolean };
|
||||
|
||||
function createHandledError(message: string) {
|
||||
const error = new Error(message) as HandledRequestError;
|
||||
error.isHandled = true;
|
||||
return error;
|
||||
}
|
||||
|
||||
export function isHandledRequestError(error: unknown) {
|
||||
return Boolean((error as { isHandled?: boolean } | undefined)?.isHandled);
|
||||
}
|
||||
|
||||
export function globalHeaders() {
|
||||
return {
|
||||
Authorization: `Bearer ${getToken() || ''}`,
|
||||
clientid: appEnv.clientId
|
||||
};
|
||||
}
|
||||
|
||||
function showRequestError(content: string) {
|
||||
message.error({ content, key: `request-error:${content}` });
|
||||
}
|
||||
|
||||
function showReloginConfirm() {
|
||||
if (isRelogin.show) return;
|
||||
isRelogin.show = true;
|
||||
Modal.confirm({
|
||||
title: '系统提示',
|
||||
content: '登录状态已过期,您可以继续留在该页面,或者重新登录',
|
||||
okText: '重新登录',
|
||||
cancelText: '取消',
|
||||
onOk: () => {
|
||||
isRelogin.show = false;
|
||||
removeToken();
|
||||
useTagsViewStore.getState().resetTags();
|
||||
history.replace(
|
||||
`/login?redirect=${encodeURIComponent(`${window.location.pathname || '/'}${window.location.search || ''}`)}`
|
||||
);
|
||||
},
|
||||
onCancel: () => {
|
||||
isRelogin.show = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function stableDataString(data: unknown) {
|
||||
if (data instanceof FormData) return '';
|
||||
if (typeof data === 'string') return data;
|
||||
if (typeof data === 'undefined' || data === null) return '';
|
||||
if (typeof data === 'object') return JSON.stringify(data);
|
||||
return String(data);
|
||||
}
|
||||
|
||||
function getSessionSubmitRecord() {
|
||||
try {
|
||||
const text = sessionStorage.getItem(repeatSubmitStorageKey);
|
||||
return text ? (JSON.parse(text) as { url?: string; data?: string; time?: number }) : undefined;
|
||||
} catch {
|
||||
sessionStorage.removeItem(repeatSubmitStorageKey);
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
function setSessionSubmitRecord(record: { url?: string; data?: string; time: number }) {
|
||||
sessionStorage.setItem(repeatSubmitStorageKey, JSON.stringify(record));
|
||||
}
|
||||
|
||||
const service = axios.create({
|
||||
baseURL: appEnv.baseApi,
|
||||
timeout: 50000,
|
||||
headers: {
|
||||
'Content-Type': 'application/json;charset=utf-8',
|
||||
clientid: appEnv.clientId
|
||||
},
|
||||
transitional: {
|
||||
clarifyTimeoutError: true
|
||||
}
|
||||
});
|
||||
|
||||
service.interceptors.request.use(config => {
|
||||
config.headers.set('Content-Language', useAppStore.getState().appLocale);
|
||||
|
||||
const isToken = config.headers.get('isToken') === false;
|
||||
const isEncrypt = config.headers.get('isEncrypt') === 'true' || config.headers.get('isEncrypt') === true;
|
||||
const isRepeatSubmitDisabled = config.headers.get('repeatSubmit') === false;
|
||||
|
||||
if (getToken() && !isToken) {
|
||||
config.headers.set('Authorization', `Bearer ${getToken()}`);
|
||||
}
|
||||
|
||||
if (config.method === 'get' && config.params) {
|
||||
let url = `${config.url}?${tansParams(config.params)}`;
|
||||
url = url.slice(0, -1);
|
||||
config.params = {};
|
||||
config.url = url;
|
||||
}
|
||||
|
||||
if (!isRepeatSubmitDisabled && (config.method === 'post' || config.method === 'put')) {
|
||||
const requestObj = {
|
||||
url: config.url,
|
||||
data: stableDataString(config.data),
|
||||
time: Date.now()
|
||||
};
|
||||
const sessionObj = getSessionSubmitRecord();
|
||||
if (
|
||||
sessionObj &&
|
||||
sessionObj.url === requestObj.url &&
|
||||
sessionObj.data === requestObj.data &&
|
||||
requestObj.time - (sessionObj.time || 0) < repeatSubmitInterval
|
||||
) {
|
||||
return Promise.reject(new Error('数据正在处理,请勿重复提交'));
|
||||
}
|
||||
setSessionSubmitRecord(requestObj);
|
||||
}
|
||||
|
||||
if (appEnv.encryptEnabled && isEncrypt && (config.method === 'post' || config.method === 'put')) {
|
||||
const aesKey = generateAesKey();
|
||||
const encryptedKey = encrypt(encryptBase64(aesKey));
|
||||
if (encryptedKey) {
|
||||
config.headers.set(encryptHeader, encryptedKey);
|
||||
}
|
||||
const data = typeof config.data === 'object' ? JSON.stringify(config.data) : String(config.data ?? '');
|
||||
config.data = encryptWithAes(data, aesKey);
|
||||
}
|
||||
|
||||
if (config.data instanceof FormData) {
|
||||
config.headers.delete('Content-Type');
|
||||
}
|
||||
|
||||
config.headers.delete('isToken');
|
||||
config.headers.delete('isEncrypt');
|
||||
config.headers.delete('repeatSubmit');
|
||||
|
||||
return config;
|
||||
});
|
||||
|
||||
function normalizeErrorMessage(message?: string) {
|
||||
if (!message) return undefined;
|
||||
if (message === 'Network Error') return '后端接口连接异常';
|
||||
if (message.includes('timeout')) return '系统接口请求超时';
|
||||
if (message.includes('Request failed with status code')) return `系统接口${message.slice(-3)}异常`;
|
||||
return message;
|
||||
}
|
||||
|
||||
async function parseResponseErrorData(data: unknown): Promise<string | undefined> {
|
||||
if (!data) return undefined;
|
||||
if (data instanceof Blob) return parseResponseErrorData(await data.text());
|
||||
if (data instanceof ArrayBuffer) return parseResponseErrorData(new TextDecoder().decode(data));
|
||||
if (typeof data === 'string') {
|
||||
const text = data.trim();
|
||||
if (!text) return undefined;
|
||||
try {
|
||||
return parseResponseErrorData(JSON.parse(text));
|
||||
} catch {
|
||||
return text;
|
||||
}
|
||||
}
|
||||
if (typeof data === 'object') {
|
||||
const payload = data as { msg?: string; message?: string };
|
||||
return payload.msg || payload.message;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
async function getErrorMessage(error: AxiosError) {
|
||||
const responseMessage = await parseResponseErrorData(error.response?.data);
|
||||
return responseMessage || normalizeErrorMessage(error.message) || '系统未知错误';
|
||||
}
|
||||
|
||||
service.interceptors.response.use(
|
||||
response => {
|
||||
const keyStr = response.headers[encryptHeader];
|
||||
if (appEnv.encryptEnabled && keyStr) {
|
||||
const base64Str = decrypt(keyStr);
|
||||
if (base64Str) {
|
||||
const aesKey = decryptBase64(base64Str);
|
||||
response.data = JSON.parse(decryptWithAes(response.data, aesKey));
|
||||
}
|
||||
}
|
||||
|
||||
if (response.request.responseType === 'blob' || response.request.responseType === 'arraybuffer') {
|
||||
return response.data;
|
||||
}
|
||||
|
||||
const code = response.data?.code || SUCCESS;
|
||||
const msg = response.data?.msg || '系统未知错误';
|
||||
|
||||
if (code === 401) {
|
||||
showReloginConfirm();
|
||||
return Promise.reject(createHandledError('无效的会话,或者会话已过期,请重新登录。'));
|
||||
}
|
||||
|
||||
if (code === SERVER_ERROR || code === WARN || code !== SUCCESS) {
|
||||
showRequestError(msg);
|
||||
return Promise.reject(createHandledError(msg));
|
||||
}
|
||||
|
||||
return response.data;
|
||||
},
|
||||
async error => {
|
||||
if (axios.isAxiosError(error) && error.response?.status === 401) {
|
||||
showReloginConfirm();
|
||||
return Promise.reject(createHandledError('无效的会话,或者会话已过期,请重新登录。'));
|
||||
}
|
||||
|
||||
const msg = axios.isAxiosError(error)
|
||||
? await getErrorMessage(error)
|
||||
: normalizeErrorMessage((error as Error | undefined)?.message) || '系统未知错误';
|
||||
showRequestError(msg);
|
||||
return Promise.reject(createHandledError(msg));
|
||||
}
|
||||
);
|
||||
|
||||
export default function request<T = unknown, D = unknown>(config: RequestConfig<D>): Promise<T> {
|
||||
return service.request<T, T, D>(config);
|
||||
}
|
||||
@ -1,49 +1,81 @@
|
||||
import type { PageResult, R } from '@/api/types';
|
||||
import request from '@/api/request';
|
||||
import type { ClientForm, ClientQuery, ClientVO } from './types';
|
||||
import type { ClientForm, ClientQuery, ClientVO } from '@/api/system/client/types';
|
||||
import type { PageResult } from '@/api/types';
|
||||
import type { AxiosPromise } from '@/utils/api-types';
|
||||
import request from '@/utils/request';
|
||||
|
||||
export function listClient(query?: ClientQuery) {
|
||||
return request<R<PageResult<ClientVO>>>({
|
||||
/**
|
||||
* 查询客户端管理列表
|
||||
* @param query
|
||||
* @returns {*}
|
||||
*/
|
||||
|
||||
export const listClient = (query?: ClientQuery): AxiosPromise<PageResult<ClientVO>> => {
|
||||
return request({
|
||||
url: '/system/client/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function getClient(id: string | number) {
|
||||
return request<R<ClientVO>>({
|
||||
url: `/system/client/${id}`,
|
||||
/**
|
||||
* 查询客户端管理详细
|
||||
* @param id
|
||||
*/
|
||||
export const getClient = (id: string | number): AxiosPromise<ClientVO> => {
|
||||
return request({
|
||||
url: '/system/client/' + id,
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function addClient(data: ClientForm) {
|
||||
return request<R>({
|
||||
/**
|
||||
* 新增客户端管理
|
||||
* @param data
|
||||
*/
|
||||
export const addClient = (data: ClientForm) => {
|
||||
return request({
|
||||
url: '/system/client',
|
||||
method: 'post',
|
||||
data
|
||||
data: data
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function updateClient(data: ClientForm) {
|
||||
return request<R>({
|
||||
/**
|
||||
* 修改客户端管理
|
||||
* @param data
|
||||
*/
|
||||
export const updateClient = (data: ClientForm) => {
|
||||
return request({
|
||||
url: '/system/client',
|
||||
method: 'put',
|
||||
data
|
||||
data: data
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function delClient(id: string | number | Array<string | number>) {
|
||||
return request<R>({
|
||||
url: `/system/client/${id}`,
|
||||
/**
|
||||
* 删除客户端管理
|
||||
* @param id
|
||||
*/
|
||||
export const delClient = (id: string | number | Array<string | number>) => {
|
||||
return request({
|
||||
url: '/system/client/' + id,
|
||||
method: 'delete'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 状态修改
|
||||
* @param clientId 客户端id
|
||||
* @param status 状态
|
||||
*/
|
||||
export function changeStatus(clientId: string, status: string) {
|
||||
return request<R>({
|
||||
const data = {
|
||||
clientId,
|
||||
status
|
||||
};
|
||||
return request({
|
||||
url: '/system/client/changeStatus',
|
||||
method: 'put',
|
||||
data: { clientId, status }
|
||||
data: data
|
||||
});
|
||||
}
|
||||
|
||||
@ -1,46 +1,185 @@
|
||||
import type { BaseEntity, PageQuery } from '@/api/types';
|
||||
export interface ClientVO {
|
||||
/**
|
||||
* id
|
||||
*/
|
||||
id: string | number;
|
||||
|
||||
/**
|
||||
* 客户端id
|
||||
*/
|
||||
clientId: string;
|
||||
|
||||
/**
|
||||
* 客户端key
|
||||
*/
|
||||
clientKey: string;
|
||||
|
||||
/**
|
||||
* 客户端秘钥
|
||||
*/
|
||||
clientSecret: string;
|
||||
|
||||
/**
|
||||
* 授权类型
|
||||
*/
|
||||
grantTypeList: string[];
|
||||
|
||||
/**
|
||||
* 设备类型
|
||||
*/
|
||||
deviceType: string;
|
||||
|
||||
/**
|
||||
* 允许访问路径
|
||||
*/
|
||||
accessPath?: string;
|
||||
|
||||
/**
|
||||
* 允许访问路径列表
|
||||
*/
|
||||
accessPathList?: string[];
|
||||
|
||||
/**
|
||||
* IP白名单
|
||||
*/
|
||||
ipWhitelist?: string;
|
||||
|
||||
/**
|
||||
* IP白名单列表
|
||||
*/
|
||||
ipWhitelistList?: string[];
|
||||
|
||||
/**
|
||||
* token活跃超时时间
|
||||
*/
|
||||
activeTimeout: number;
|
||||
|
||||
/**
|
||||
* token固定超时
|
||||
*/
|
||||
timeout: number;
|
||||
|
||||
/**
|
||||
* 状态(0正常 1停用)
|
||||
*/
|
||||
status: string;
|
||||
}
|
||||
|
||||
export interface ClientForm extends BaseEntity {
|
||||
/**
|
||||
* id
|
||||
*/
|
||||
id?: string | number;
|
||||
|
||||
/**
|
||||
* 客户端id
|
||||
*/
|
||||
clientId?: string | number;
|
||||
|
||||
/**
|
||||
* 客户端key
|
||||
*/
|
||||
clientKey?: string;
|
||||
|
||||
/**
|
||||
* 客户端秘钥
|
||||
*/
|
||||
clientSecret?: string;
|
||||
|
||||
/**
|
||||
* 授权类型
|
||||
*/
|
||||
grantTypeList?: string[];
|
||||
|
||||
/**
|
||||
* 设备类型
|
||||
*/
|
||||
deviceType?: string;
|
||||
|
||||
/**
|
||||
* 允许访问路径
|
||||
*/
|
||||
accessPath?: string;
|
||||
|
||||
/**
|
||||
* 允许访问路径列表
|
||||
*/
|
||||
accessPathList?: string[];
|
||||
|
||||
/**
|
||||
* IP白名单
|
||||
*/
|
||||
ipWhitelist?: string;
|
||||
|
||||
/**
|
||||
* IP白名单列表
|
||||
*/
|
||||
ipWhitelistList?: string[];
|
||||
|
||||
/**
|
||||
* token活跃超时时间
|
||||
*/
|
||||
activeTimeout?: number;
|
||||
|
||||
/**
|
||||
* token固定超时
|
||||
*/
|
||||
timeout?: number;
|
||||
|
||||
/**
|
||||
* 状态(0正常 1停用)
|
||||
*/
|
||||
status?: string;
|
||||
}
|
||||
|
||||
export interface ClientQuery extends PageQuery {
|
||||
/**
|
||||
* 客户端id
|
||||
*/
|
||||
clientId?: string | number;
|
||||
|
||||
/**
|
||||
* 客户端key
|
||||
*/
|
||||
clientKey?: string;
|
||||
|
||||
/**
|
||||
* 客户端秘钥
|
||||
*/
|
||||
clientSecret?: string;
|
||||
|
||||
/**
|
||||
* 授权类型
|
||||
*/
|
||||
grantType?: string;
|
||||
|
||||
/**
|
||||
* 设备类型
|
||||
*/
|
||||
deviceType?: string;
|
||||
|
||||
/**
|
||||
* 允许访问路径
|
||||
*/
|
||||
accessPath?: string;
|
||||
|
||||
/**
|
||||
* IP白名单
|
||||
*/
|
||||
ipWhitelist?: string;
|
||||
|
||||
/**
|
||||
* token活跃超时时间
|
||||
*/
|
||||
activeTimeout?: number;
|
||||
|
||||
/**
|
||||
* token固定超时
|
||||
*/
|
||||
timeout?: number;
|
||||
|
||||
/**
|
||||
* 状态(0正常 1停用)
|
||||
*/
|
||||
status?: string;
|
||||
}
|
||||
|
||||
export interface ClientVO {
|
||||
id: string | number;
|
||||
clientId: string;
|
||||
clientKey: string;
|
||||
clientSecret: string;
|
||||
grantTypeList?: string[];
|
||||
deviceType?: string;
|
||||
accessPath?: string;
|
||||
accessPathList?: string[];
|
||||
ipWhitelist?: string;
|
||||
ipWhitelistList?: string[];
|
||||
activeTimeout?: number;
|
||||
timeout?: number;
|
||||
status: string;
|
||||
}
|
||||
|
||||
@ -1,47 +1,54 @@
|
||||
import type { PageResult, R } from '@/api/types';
|
||||
import request from '@/api/request';
|
||||
import type { PageResult } from '@/api/types';
|
||||
import type { AxiosPromise } from '@/utils/api-types';
|
||||
import request from '@/utils/request';
|
||||
import type { ConfigForm, ConfigQuery, ConfigVO } from './types';
|
||||
|
||||
export function listConfig(query: ConfigQuery) {
|
||||
return request<R<PageResult<ConfigVO>>>({
|
||||
// 查询参数列表
|
||||
export function listConfig(query: ConfigQuery): AxiosPromise<PageResult<ConfigVO>> {
|
||||
return request({
|
||||
url: '/system/config/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
}
|
||||
|
||||
export function getConfig(configId: string | number) {
|
||||
return request<R<ConfigVO>>({
|
||||
url: `/system/config/${configId}`,
|
||||
// 查询参数详细
|
||||
export function getConfig(configId: string | number): AxiosPromise<ConfigVO> {
|
||||
return request({
|
||||
url: '/system/config/' + configId,
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
|
||||
export function getConfigKey(configKey: string) {
|
||||
return request<R<string>>({
|
||||
url: `/system/config/configKey/${configKey}`,
|
||||
// 根据参数键名查询参数值
|
||||
export function getConfigKey(configKey: string): AxiosPromise<string> {
|
||||
return request({
|
||||
url: '/system/config/configKey/' + configKey,
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
|
||||
// 新增参数配置
|
||||
export function addConfig(data: ConfigForm) {
|
||||
return request<R>({
|
||||
return request({
|
||||
url: '/system/config',
|
||||
method: 'post',
|
||||
data
|
||||
data: data
|
||||
});
|
||||
}
|
||||
|
||||
// 修改参数配置
|
||||
export function updateConfig(data: ConfigForm) {
|
||||
return request<R>({
|
||||
return request({
|
||||
url: '/system/config',
|
||||
method: 'put',
|
||||
data
|
||||
data: data
|
||||
});
|
||||
}
|
||||
|
||||
export function updateConfigByKey(key: string, value: unknown) {
|
||||
return request<R>({
|
||||
// 修改参数配置
|
||||
export function updateConfigByKey(key: string, value: any) {
|
||||
return request({
|
||||
url: '/system/config/updateByKey',
|
||||
method: 'put',
|
||||
data: {
|
||||
@ -51,20 +58,18 @@ export function updateConfigByKey(key: string, value: unknown) {
|
||||
});
|
||||
}
|
||||
|
||||
// 删除参数配置
|
||||
export function delConfig(configId: string | number | Array<string | number>) {
|
||||
return request<R>({
|
||||
url: `/system/config/${configId}`,
|
||||
return request({
|
||||
url: '/system/config/' + configId,
|
||||
method: 'delete'
|
||||
});
|
||||
}
|
||||
|
||||
export function refreshConfigCache() {
|
||||
return request<R>({
|
||||
// 刷新参数缓存
|
||||
export function refreshCache() {
|
||||
return request({
|
||||
url: '/system/config/refreshCache',
|
||||
method: 'delete'
|
||||
});
|
||||
}
|
||||
|
||||
export function refreshCache() {
|
||||
return refreshConfigCache();
|
||||
}
|
||||
|
||||
@ -1,24 +1,23 @@
|
||||
import type { BaseEntity, PageQuery } from '@/api/types';
|
||||
|
||||
export interface ConfigForm {
|
||||
configId?: number | string;
|
||||
configName?: string;
|
||||
configKey?: string;
|
||||
configValue?: string;
|
||||
configType?: string;
|
||||
remark?: string;
|
||||
}
|
||||
|
||||
export interface ConfigQuery extends PageQuery {
|
||||
configName?: string;
|
||||
configKey?: string;
|
||||
configType?: string;
|
||||
}
|
||||
|
||||
export interface ConfigVO extends BaseEntity {
|
||||
configId: number | string;
|
||||
configName: string;
|
||||
configKey: string;
|
||||
configValue: string;
|
||||
configType: string;
|
||||
remark: string;
|
||||
}
|
||||
|
||||
export interface ConfigForm {
|
||||
configId: number | string | undefined;
|
||||
configName: string;
|
||||
configKey: string;
|
||||
configValue: string;
|
||||
configType: string;
|
||||
remark: string;
|
||||
}
|
||||
|
||||
export interface ConfigQuery extends PageQuery {
|
||||
configName: string;
|
||||
configKey: string;
|
||||
configType: string;
|
||||
}
|
||||
|
||||
@ -1,55 +1,65 @@
|
||||
import type { R } from '@/api/types';
|
||||
import request from '@/api/request';
|
||||
import type { DeptForm, DeptQuery, DeptVO } from './types';
|
||||
import type { AxiosPromise } from '@/utils/api-types';
|
||||
import request from '@/utils/request';
|
||||
import { type DeptForm, type DeptQuery, type DeptVO } from './types';
|
||||
|
||||
export function listDept(query?: DeptQuery) {
|
||||
return request<R<DeptVO[]>>({
|
||||
// 查询部门列表
|
||||
export const listDept = (query?: DeptQuery) => {
|
||||
return request({
|
||||
url: '/system/dept/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function optionSelect(deptIds: Array<number | string>) {
|
||||
return request<R<DeptVO[]>>({
|
||||
url: `/system/dept/optionselect?deptIds=${deptIds}`,
|
||||
/**
|
||||
* 通过deptIds查询部门
|
||||
* @param deptIds
|
||||
*/
|
||||
export const optionSelect = (deptIds: (number | string)[]): AxiosPromise<DeptVO[]> => {
|
||||
return request({
|
||||
url: '/system/dept/optionselect?deptIds=' + deptIds,
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function listDeptExcludeChild(deptId: string | number) {
|
||||
return request<R<DeptVO[]>>({
|
||||
url: `/system/dept/list/exclude/${deptId}`,
|
||||
// 查询部门列表(排除节点)
|
||||
export const listDeptExcludeChild = (deptId: string | number): AxiosPromise<DeptVO[]> => {
|
||||
return request({
|
||||
url: '/system/dept/list/exclude/' + deptId,
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function getDept(deptId: string | number) {
|
||||
return request<R<DeptVO>>({
|
||||
url: `/system/dept/${deptId}`,
|
||||
// 查询部门详细
|
||||
export const getDept = (deptId: string | number): AxiosPromise<DeptVO> => {
|
||||
return request({
|
||||
url: '/system/dept/' + deptId,
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function addDept(data: DeptForm) {
|
||||
return request<R>({
|
||||
// 新增部门
|
||||
export const addDept = (data: DeptForm) => {
|
||||
return request({
|
||||
url: '/system/dept',
|
||||
method: 'post',
|
||||
data
|
||||
data: data
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function updateDept(data: DeptForm) {
|
||||
return request<R>({
|
||||
// 修改部门
|
||||
export const updateDept = (data: DeptForm) => {
|
||||
return request({
|
||||
url: '/system/dept',
|
||||
method: 'put',
|
||||
data
|
||||
data: data
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function delDept(deptId: number | string) {
|
||||
return request<R>({
|
||||
url: `/system/dept/${deptId}`,
|
||||
// 删除部门
|
||||
export const delDept = (deptId: number | string) => {
|
||||
return request({
|
||||
url: '/system/dept/' + deptId,
|
||||
method: 'delete'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@ -1,5 +1,48 @@
|
||||
import type { BaseEntity, PageQuery } from '@/api/types';
|
||||
/**
|
||||
* 部门查询参数
|
||||
*/
|
||||
export interface DeptQuery extends PageQuery {
|
||||
deptName?: string;
|
||||
deptCategory?: string;
|
||||
status?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* 部门类型
|
||||
*/
|
||||
export interface DeptVO extends BaseEntity {
|
||||
id: number | string;
|
||||
parentName: string;
|
||||
parentId: number | string;
|
||||
children: DeptVO[];
|
||||
deptId: number | string;
|
||||
deptName: string;
|
||||
deptCategory: string;
|
||||
orderNum: number;
|
||||
leader: string;
|
||||
phone: string;
|
||||
email: string;
|
||||
status: string;
|
||||
delFlag: string;
|
||||
ancestors: string;
|
||||
menuId: string | number;
|
||||
}
|
||||
|
||||
/**
|
||||
* 部门类型
|
||||
*/
|
||||
export interface DeptTreeVO extends BaseEntity {
|
||||
id: number | string;
|
||||
label: string;
|
||||
parentId: number | string;
|
||||
weight: number;
|
||||
children: DeptTreeVO[];
|
||||
disabled: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* 部门表单类型
|
||||
*/
|
||||
export interface DeptForm {
|
||||
parentName?: string;
|
||||
parentId?: number | string;
|
||||
@ -8,43 +51,10 @@ export interface DeptForm {
|
||||
deptName?: string;
|
||||
deptCategory?: string;
|
||||
orderNum?: number;
|
||||
leader?: string | number;
|
||||
phone?: string;
|
||||
email?: string;
|
||||
status?: string;
|
||||
delFlag?: string;
|
||||
ancestors?: string;
|
||||
}
|
||||
|
||||
export interface DeptQuery extends PageQuery {
|
||||
deptName?: string;
|
||||
deptCategory?: string;
|
||||
status?: string;
|
||||
}
|
||||
|
||||
export interface DeptTreeVO extends BaseEntity {
|
||||
id: number | string;
|
||||
label: string;
|
||||
parentId: number | string;
|
||||
weight: number;
|
||||
children?: DeptTreeVO[];
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
export interface DeptVO extends BaseEntity {
|
||||
id?: number | string;
|
||||
parentName?: string;
|
||||
parentId?: number | string;
|
||||
children?: DeptVO[];
|
||||
deptId: number | string;
|
||||
deptName: string;
|
||||
deptCategory?: string;
|
||||
orderNum?: number;
|
||||
leader?: string;
|
||||
phone?: string;
|
||||
email?: string;
|
||||
status?: string;
|
||||
delFlag?: string;
|
||||
ancestors?: string;
|
||||
menuId?: string | number;
|
||||
}
|
||||
|
||||
@ -1,48 +1,54 @@
|
||||
import type { PageResult, R } from '@/api/types';
|
||||
import request from '@/api/request';
|
||||
import type { DictData, DictDataForm, DictDataQuery } from './types';
|
||||
|
||||
export function getDicts(dictType: string) {
|
||||
return request<R<DictData[]>>({
|
||||
url: `/system/dict/data/type/${dictType}`,
|
||||
import type { PageResult } from '@/api/types';
|
||||
import type { AxiosPromise } from '@/utils/api-types';
|
||||
import request from '@/utils/request';
|
||||
import type { DictDataForm, DictDataQuery, DictDataVO } from './types';
|
||||
// 根据字典类型查询字典数据信息
|
||||
export function getDicts(dictType: string): AxiosPromise<DictDataVO[]> {
|
||||
return request({
|
||||
url: '/system/dict/data/type/' + dictType,
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
|
||||
export function listData(query: DictDataQuery) {
|
||||
return request<R<PageResult<DictData>>>({
|
||||
// 查询字典数据列表
|
||||
export function listData(query: DictDataQuery): AxiosPromise<PageResult<DictDataVO>> {
|
||||
return request({
|
||||
url: '/system/dict/data/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
}
|
||||
|
||||
export function getData(dictCode: string | number) {
|
||||
return request<R<DictData>>({
|
||||
url: `/system/dict/data/${dictCode}`,
|
||||
// 查询字典数据详细
|
||||
export function getData(dictCode: string | number): AxiosPromise<DictDataVO> {
|
||||
return request({
|
||||
url: '/system/dict/data/' + dictCode,
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
|
||||
// 新增字典数据
|
||||
export function addData(data: DictDataForm) {
|
||||
return request<R>({
|
||||
return request({
|
||||
url: '/system/dict/data',
|
||||
method: 'post',
|
||||
data
|
||||
data: data
|
||||
});
|
||||
}
|
||||
|
||||
// 修改字典数据
|
||||
export function updateData(data: DictDataForm) {
|
||||
return request<R>({
|
||||
return request({
|
||||
url: '/system/dict/data',
|
||||
method: 'put',
|
||||
data
|
||||
data: data
|
||||
});
|
||||
}
|
||||
|
||||
// 删除字典数据
|
||||
export function delData(dictCode: string | number | Array<string | number>) {
|
||||
return request<R>({
|
||||
url: `/system/dict/data/${dictCode}`,
|
||||
return request({
|
||||
url: '/system/dict/data/' + dictCode,
|
||||
method: 'delete'
|
||||
});
|
||||
}
|
||||
|
||||
@ -1,32 +1,26 @@
|
||||
import type { BaseEntity, PageQuery } from '@/api/types';
|
||||
|
||||
export interface DictData extends BaseEntity {
|
||||
dictCode: string | number;
|
||||
dictSort: number;
|
||||
dictLabel: string;
|
||||
dictValue: string;
|
||||
export interface DictDataQuery extends PageQuery {
|
||||
dictName: string;
|
||||
dictType: string;
|
||||
cssClass?: string;
|
||||
listClass?: string;
|
||||
isDefault?: string;
|
||||
status: string;
|
||||
dictLabel: string;
|
||||
}
|
||||
|
||||
export type DictDataVO = DictData;
|
||||
export interface DictDataVO extends BaseEntity {
|
||||
dictCode: string;
|
||||
dictLabel: string;
|
||||
dictValue: string;
|
||||
cssClass: string;
|
||||
listClass: ElTagType;
|
||||
dictSort: number;
|
||||
remark: string;
|
||||
}
|
||||
|
||||
export interface DictDataForm {
|
||||
dictType?: string;
|
||||
dictCode?: string | number;
|
||||
dictLabel?: string;
|
||||
dictValue?: string;
|
||||
cssClass?: string;
|
||||
listClass?: string;
|
||||
dictSort?: number;
|
||||
remark?: string;
|
||||
}
|
||||
|
||||
export interface DictDataQuery extends PageQuery {
|
||||
dictName?: string;
|
||||
dictType?: string;
|
||||
dictLabel?: string;
|
||||
dictCode: string | undefined;
|
||||
dictLabel: string;
|
||||
dictValue: string;
|
||||
cssClass: string;
|
||||
listClass: ElTagType;
|
||||
dictSort: number;
|
||||
remark: string;
|
||||
}
|
||||
|
||||
@ -1,54 +1,62 @@
|
||||
import type { PageResult, R } from '@/api/types';
|
||||
import request from '@/api/request';
|
||||
import type { PageResult } from '@/api/types';
|
||||
import type { AxiosPromise } from '@/utils/api-types';
|
||||
import request from '@/utils/request';
|
||||
import type { DictTypeForm, DictTypeQuery, DictTypeVO } from './types';
|
||||
|
||||
export function listType(query: DictTypeQuery) {
|
||||
return request<R<PageResult<DictTypeVO>>>({
|
||||
// 查询字典类型列表
|
||||
export function listType(query: DictTypeQuery): AxiosPromise<PageResult<DictTypeVO>> {
|
||||
return request({
|
||||
url: '/system/dict/type/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
}
|
||||
|
||||
export function getType(dictId: number | string) {
|
||||
return request<R<DictTypeVO>>({
|
||||
url: `/system/dict/type/${dictId}`,
|
||||
// 查询字典类型详细
|
||||
export function getType(dictId: number | string): AxiosPromise<DictTypeVO> {
|
||||
return request({
|
||||
url: '/system/dict/type/' + dictId,
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
|
||||
// 新增字典类型
|
||||
export function addType(data: DictTypeForm) {
|
||||
return request<R>({
|
||||
return request({
|
||||
url: '/system/dict/type',
|
||||
method: 'post',
|
||||
data
|
||||
data: data
|
||||
});
|
||||
}
|
||||
|
||||
// 修改字典类型
|
||||
export function updateType(data: DictTypeForm) {
|
||||
return request<R>({
|
||||
return request({
|
||||
url: '/system/dict/type',
|
||||
method: 'put',
|
||||
data
|
||||
data: data
|
||||
});
|
||||
}
|
||||
|
||||
// 删除字典类型
|
||||
export function delType(dictId: string | number | Array<string | number>) {
|
||||
return request<R>({
|
||||
url: `/system/dict/type/${dictId}`,
|
||||
return request({
|
||||
url: '/system/dict/type/' + dictId,
|
||||
method: 'delete'
|
||||
});
|
||||
}
|
||||
|
||||
// 刷新字典缓存
|
||||
export function refreshCache() {
|
||||
return request<R>({
|
||||
return request({
|
||||
url: '/system/dict/type/refreshCache',
|
||||
method: 'delete'
|
||||
});
|
||||
}
|
||||
|
||||
export function optionselect() {
|
||||
return request<R<DictTypeVO[]>>({
|
||||
// 获取字典选择框列表
|
||||
export function optionselect(): AxiosPromise<DictTypeVO[]> {
|
||||
return request({
|
||||
url: '/system/dict/type/optionselect',
|
||||
method: 'get'
|
||||
});
|
||||
|
||||
@ -1,19 +1,18 @@
|
||||
import type { BaseEntity, PageQuery } from '@/api/types';
|
||||
|
||||
export interface DictTypeForm {
|
||||
dictId?: number | string;
|
||||
dictName?: string;
|
||||
dictType?: string;
|
||||
remark?: string;
|
||||
}
|
||||
|
||||
export interface DictTypeQuery extends PageQuery {
|
||||
dictName?: string;
|
||||
dictType?: string;
|
||||
}
|
||||
|
||||
export interface DictTypeVO extends BaseEntity {
|
||||
dictId: number | string;
|
||||
dictName: string;
|
||||
dictType: string;
|
||||
remark: string;
|
||||
}
|
||||
|
||||
export interface DictTypeForm {
|
||||
dictId: number | string | undefined;
|
||||
dictName: string;
|
||||
dictType: string;
|
||||
remark: string;
|
||||
}
|
||||
|
||||
export interface DictTypeQuery extends PageQuery {
|
||||
dictName: string;
|
||||
dictType: string;
|
||||
}
|
||||
|
||||
@ -1,63 +1,70 @@
|
||||
import type { RoleMenuTree } from '@/api/system/role/types';
|
||||
import type { R } from '@/api/types';
|
||||
import request from '@/api/request';
|
||||
import type { MenuForm, MenuQuery, MenuTreeOption, MenuVO } from './types';
|
||||
import type { AxiosPromise } from '@/utils/api-types';
|
||||
import request from '@/utils/request';
|
||||
import type { MenuForm, MenuQuery, MenuTreeOption, MenuVO, RoleMenuTree } from './types';
|
||||
|
||||
export function listMenu(query?: MenuQuery) {
|
||||
return request<R<MenuVO[]>>({
|
||||
// 查询菜单列表
|
||||
export const listMenu = (query?: MenuQuery): AxiosPromise<MenuVO[]> => {
|
||||
return request({
|
||||
url: '/system/menu/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function getMenu(menuId: string | number) {
|
||||
return request<R<MenuVO>>({
|
||||
url: `/system/menu/${menuId}`,
|
||||
// 查询菜单详细
|
||||
export const getMenu = (menuId: string | number): AxiosPromise<MenuVO> => {
|
||||
return request({
|
||||
url: '/system/menu/' + menuId,
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function treeselect() {
|
||||
return request<R<MenuTreeOption[]>>({
|
||||
// 查询菜单下拉树结构
|
||||
export const treeselect = (): AxiosPromise<MenuTreeOption[]> => {
|
||||
return request({
|
||||
url: '/system/menu/treeselect',
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function roleMenuTreeselect(roleId: string | number) {
|
||||
return request<R<RoleMenuTree>>({
|
||||
url: `/system/menu/roleMenuTreeselect/${roleId}`,
|
||||
// 根据角色ID查询菜单下拉树结构
|
||||
export const roleMenuTreeselect = (roleId: string | number): AxiosPromise<RoleMenuTree> => {
|
||||
return request({
|
||||
url: '/system/menu/roleMenuTreeselect/' + roleId,
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function addMenu(data: MenuForm) {
|
||||
return request<R>({
|
||||
// 新增菜单
|
||||
export const addMenu = (data: MenuForm) => {
|
||||
return request({
|
||||
url: '/system/menu',
|
||||
method: 'post',
|
||||
data
|
||||
data: data
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function updateMenu(data: MenuForm) {
|
||||
return request<R>({
|
||||
// 修改菜单
|
||||
export const updateMenu = (data: MenuForm) => {
|
||||
return request({
|
||||
url: '/system/menu',
|
||||
method: 'put',
|
||||
data
|
||||
data: data
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function delMenu(menuId: string | number) {
|
||||
return request<R>({
|
||||
url: `/system/menu/${menuId}`,
|
||||
// 删除菜单
|
||||
export const delMenu = (menuId: string | number) => {
|
||||
return request({
|
||||
url: '/system/menu/' + menuId,
|
||||
method: 'delete'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function cascadeDelMenu(menuIds: Array<string | number>) {
|
||||
return request<R>({
|
||||
url: `/system/menu/cascade/${menuIds}`,
|
||||
// 级联删除菜单
|
||||
export const cascadeDelMenu = (menuIds: Array<string | number>) => {
|
||||
return request({
|
||||
url: '/system/menu/cascade/' + menuIds,
|
||||
method: 'delete'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@ -1,63 +1,89 @@
|
||||
import type { BaseEntity } from '@/api/types';
|
||||
|
||||
export interface MenuForm {
|
||||
parentName?: string;
|
||||
parentId?: string | number;
|
||||
children?: MenuForm[];
|
||||
menuId?: string | number;
|
||||
menuName?: string;
|
||||
orderNum?: number;
|
||||
path?: string;
|
||||
component?: string;
|
||||
queryParam?: string;
|
||||
isFrame?: string;
|
||||
isCache?: string;
|
||||
menuType?: 'M' | 'C' | 'F' | string;
|
||||
visible?: string;
|
||||
status?: string;
|
||||
icon?: string | null;
|
||||
activeMenu?: string;
|
||||
ext?: string;
|
||||
remark?: string;
|
||||
query?: string;
|
||||
perms?: string;
|
||||
}
|
||||
|
||||
export interface MenuQuery {
|
||||
keywords?: string;
|
||||
menuName?: string;
|
||||
status?: string;
|
||||
}
|
||||
import type { MenuTypeEnum } from '@/enums/MenuTypeEnum';
|
||||
|
||||
/**
|
||||
* 菜单树形结构类型
|
||||
*/
|
||||
export interface MenuTreeOption {
|
||||
id: string | number;
|
||||
label: string;
|
||||
parentId: string | number;
|
||||
weight: number;
|
||||
menuType?: 'M' | 'C' | 'F' | string;
|
||||
menuType?: MenuTypeEnum | string;
|
||||
visible?: string;
|
||||
status?: string;
|
||||
disabled?: boolean;
|
||||
children?: MenuTreeOption[];
|
||||
}
|
||||
|
||||
export interface MenuVO extends BaseEntity {
|
||||
parentName?: string;
|
||||
parentId: string | number;
|
||||
children?: MenuVO[];
|
||||
export interface RoleMenuTree {
|
||||
menus: MenuTreeOption[];
|
||||
checkedKeys: Array<string | number>;
|
||||
}
|
||||
|
||||
/**
|
||||
* 角色菜单分配中的按钮节点类型
|
||||
*/
|
||||
export interface RoleMenuButtonOption {
|
||||
menuId: string | number;
|
||||
menuName: string;
|
||||
parentId: string | number;
|
||||
perms?: string;
|
||||
status?: string;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* 菜单查询参数类型
|
||||
*/
|
||||
export interface MenuQuery {
|
||||
keywords?: string;
|
||||
menuName?: string;
|
||||
status?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 菜单视图对象类型
|
||||
*/
|
||||
export interface MenuVO extends BaseEntity {
|
||||
parentName: string;
|
||||
parentId: string | number;
|
||||
children: MenuVO[];
|
||||
menuId: string | number;
|
||||
menuName: string;
|
||||
orderNum: number;
|
||||
path: string;
|
||||
component: string;
|
||||
queryParam: string;
|
||||
isFrame: string;
|
||||
isCache: string;
|
||||
menuType: MenuTypeEnum;
|
||||
visible: string;
|
||||
status: string;
|
||||
icon: string;
|
||||
activeMenu: string;
|
||||
ext: string;
|
||||
remark: string;
|
||||
}
|
||||
|
||||
export interface MenuForm {
|
||||
parentName?: string;
|
||||
parentId?: string | number;
|
||||
children?: MenuForm[];
|
||||
menuId?: string | number;
|
||||
menuName: string;
|
||||
orderNum: number;
|
||||
path: string;
|
||||
component?: string;
|
||||
queryParam?: string;
|
||||
isFrame?: string;
|
||||
isCache?: string;
|
||||
menuType: 'M' | 'C' | 'F' | string;
|
||||
menuType?: MenuTypeEnum;
|
||||
visible?: string;
|
||||
status?: string;
|
||||
icon?: string | null;
|
||||
icon?: string;
|
||||
activeMenu?: string;
|
||||
ext?: string;
|
||||
remark?: string;
|
||||
query?: string;
|
||||
perms?: string;
|
||||
}
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
import type { R } from '@/api/types';
|
||||
import request from '@/api/request';
|
||||
import type { AxiosPromise } from '@/utils/api-types';
|
||||
import request from '@/utils/request';
|
||||
import type { MessageBoxVO } from './types';
|
||||
|
||||
export function getMessageBox() {
|
||||
return request<R<MessageBoxVO>>({
|
||||
export function getMessageBox(): AxiosPromise<MessageBoxVO> {
|
||||
return request({
|
||||
url: '/resource/message/box',
|
||||
method: 'get'
|
||||
});
|
||||
|
||||
@ -1,5 +1,3 @@
|
||||
import type { BaseEntity } from '@/api/types';
|
||||
|
||||
export interface MessageVO extends BaseEntity {
|
||||
messageId: number | string;
|
||||
category: string;
|
||||
@ -8,7 +6,7 @@ export interface MessageVO extends BaseEntity {
|
||||
title: string;
|
||||
message: string;
|
||||
content?: string;
|
||||
data?: Record<string, unknown> | null;
|
||||
data?: Record<string, any> | null;
|
||||
path?: string;
|
||||
}
|
||||
|
||||
|
||||
@ -1,41 +1,46 @@
|
||||
import type { PageResult, R } from '@/api/types';
|
||||
import request from '@/api/request';
|
||||
import type { PageResult } from '@/api/types';
|
||||
import type { AxiosPromise } from '@/utils/api-types';
|
||||
import request from '@/utils/request';
|
||||
import type { NoticeForm, NoticeQuery, NoticeVO } from './types';
|
||||
|
||||
export function listNotice(query: NoticeQuery) {
|
||||
return request<R<PageResult<NoticeVO>>>({
|
||||
// 查询公告列表
|
||||
export function listNotice(query: NoticeQuery): AxiosPromise<PageResult<NoticeVO>> {
|
||||
return request({
|
||||
url: '/system/notice/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
}
|
||||
|
||||
export function getNotice(noticeId: string | number) {
|
||||
return request<R<NoticeVO>>({
|
||||
url: `/system/notice/${noticeId}`,
|
||||
// 查询公告详细
|
||||
export function getNotice(noticeId: string | number): AxiosPromise<NoticeVO> {
|
||||
return request({
|
||||
url: '/system/notice/' + noticeId,
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
|
||||
// 新增公告
|
||||
export function addNotice(data: NoticeForm) {
|
||||
return request<R>({
|
||||
return request({
|
||||
url: '/system/notice',
|
||||
method: 'post',
|
||||
data
|
||||
data: data
|
||||
});
|
||||
}
|
||||
|
||||
// 修改公告
|
||||
export function updateNotice(data: NoticeForm) {
|
||||
return request<R>({
|
||||
return request({
|
||||
url: '/system/notice',
|
||||
method: 'put',
|
||||
data
|
||||
data: data
|
||||
});
|
||||
}
|
||||
|
||||
// 删除公告
|
||||
export function delNotice(noticeId: string | number | Array<string | number>) {
|
||||
return request<R>({
|
||||
url: `/system/notice/${noticeId}`,
|
||||
return request({
|
||||
url: '/system/notice/' + noticeId,
|
||||
method: 'delete'
|
||||
});
|
||||
}
|
||||
|
||||
@ -1,27 +1,26 @@
|
||||
import type { BaseEntity, PageQuery } from '@/api/types';
|
||||
|
||||
export interface NoticeForm {
|
||||
noticeId?: number | string;
|
||||
noticeTitle?: string;
|
||||
noticeType?: string;
|
||||
noticeContent?: string;
|
||||
status?: string;
|
||||
remark?: string;
|
||||
createByName?: string;
|
||||
}
|
||||
|
||||
export interface NoticeQuery extends PageQuery {
|
||||
noticeTitle?: string;
|
||||
createByName?: string;
|
||||
status?: string;
|
||||
noticeType?: string;
|
||||
}
|
||||
|
||||
export interface NoticeVO extends BaseEntity {
|
||||
noticeId: number | string;
|
||||
noticeId: number;
|
||||
noticeTitle: string;
|
||||
noticeType: string;
|
||||
noticeContent: string;
|
||||
status: string;
|
||||
createByName?: string;
|
||||
remark: string;
|
||||
createByName: string;
|
||||
}
|
||||
|
||||
export interface NoticeQuery extends PageQuery {
|
||||
noticeTitle: string;
|
||||
createByName: string;
|
||||
status: string;
|
||||
noticeType: string;
|
||||
}
|
||||
|
||||
export interface NoticeForm {
|
||||
noticeId: number | string | undefined;
|
||||
noticeTitle: string;
|
||||
noticeType: string;
|
||||
noticeContent: string;
|
||||
status: string;
|
||||
remark: string;
|
||||
createByName: string;
|
||||
}
|
||||
|
||||
@ -1,45 +1,38 @@
|
||||
import type { PageResult, R } from '@/api/types';
|
||||
import request from '@/api/request';
|
||||
import type { OssQuery, OssUploadResult, OssVO } from './types';
|
||||
import type { PageResult } from '@/api/types';
|
||||
import type { AxiosPromise } from '@/utils/api-types';
|
||||
import request from '@/utils/request';
|
||||
import type { OssQuery, OssUploadVO, OssVO } from './types';
|
||||
|
||||
export function listOss(query: OssQuery) {
|
||||
return request<R<PageResult<OssVO>>>({
|
||||
// 查询OSS对象存储列表
|
||||
export function listOss(query: OssQuery): AxiosPromise<PageResult<OssVO>> {
|
||||
return request({
|
||||
url: '/resource/oss/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
}
|
||||
|
||||
export function listOssByIds(ossId: string | number) {
|
||||
return request<R<OssVO[]>>({
|
||||
url: `/resource/oss/listByIds/${ossId}`,
|
||||
// 查询OSS对象基于id串
|
||||
export function listByIds(ossId: string | number): AxiosPromise<OssVO[]> {
|
||||
return request({
|
||||
url: '/resource/oss/listByIds/' + ossId,
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
|
||||
export function listByIds(ossId: string | number) {
|
||||
return listOssByIds(ossId);
|
||||
}
|
||||
|
||||
export function uploadOss(data: FormData) {
|
||||
return request<R<OssUploadResult>>({
|
||||
// 上传OSS对象存储
|
||||
export function uploadOss(data: FormData): AxiosPromise<OssUploadVO> {
|
||||
return request({
|
||||
url: '/resource/oss/upload',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 删除OSS对象存储
|
||||
export function delOss(ossId: string | number | Array<string | number>) {
|
||||
return request<R>({
|
||||
url: `/resource/oss/${ossId}`,
|
||||
return request({
|
||||
url: '/resource/oss/' + ossId,
|
||||
method: 'delete'
|
||||
});
|
||||
}
|
||||
|
||||
export function downloadOss(ossId: string | number) {
|
||||
return request<Blob>({
|
||||
url: `/resource/oss/download/${ossId}`,
|
||||
method: 'get',
|
||||
responseType: 'blob'
|
||||
});
|
||||
}
|
||||
|
||||
@ -1,27 +1,42 @@
|
||||
import type { BaseEntity, PageQuery } from '@/api/types';
|
||||
|
||||
export interface OssQuery extends Omit<PageQuery, 'isAsc'> {
|
||||
fileName?: string;
|
||||
originalName?: string;
|
||||
fileSuffix?: string;
|
||||
createTime?: string;
|
||||
service?: string;
|
||||
orderByColumn?: string;
|
||||
isAsc?: string;
|
||||
}
|
||||
|
||||
export interface OssUploadResult {
|
||||
url?: string;
|
||||
fileName?: string;
|
||||
ossId?: string | number;
|
||||
}
|
||||
|
||||
export interface OssVO extends BaseEntity {
|
||||
ossId: string | number;
|
||||
fileName: string;
|
||||
originalName: string;
|
||||
fileSuffix: string;
|
||||
url: string;
|
||||
createByName?: string;
|
||||
service?: string;
|
||||
createByName: string;
|
||||
service: string;
|
||||
}
|
||||
|
||||
export interface OssUploadVO {
|
||||
url: string;
|
||||
fileName: string;
|
||||
ossId: string;
|
||||
}
|
||||
|
||||
export interface OssQuery extends PageQuery {
|
||||
fileName: string;
|
||||
originalName: string;
|
||||
fileSuffix: string;
|
||||
createTime: string;
|
||||
service: string;
|
||||
orderByColumn: string;
|
||||
isAsc: string;
|
||||
}
|
||||
export interface OssForm {
|
||||
file: undefined | string;
|
||||
}
|
||||
|
||||
export interface SysOssExt {
|
||||
bizType?: string;
|
||||
fileSize?: number;
|
||||
contentType?: string;
|
||||
source?: string;
|
||||
uploadIp?: string;
|
||||
remark?: string;
|
||||
tags?: string[];
|
||||
refId?: string;
|
||||
refType?: string;
|
||||
isTemp?: boolean;
|
||||
md5?: string;
|
||||
}
|
||||
|
||||
@ -1,49 +1,61 @@
|
||||
import type { PageResult, R } from '@/api/types';
|
||||
import request from '@/api/request';
|
||||
import type { PageResult } from '@/api/types';
|
||||
import type { AxiosPromise } from '@/utils/api-types';
|
||||
import request from '@/utils/request';
|
||||
import type { OssConfigForm, OssConfigQuery, OssConfigVO } from './types';
|
||||
|
||||
export function listOssConfig(query: OssConfigQuery) {
|
||||
return request<R<PageResult<OssConfigVO>>>({
|
||||
// 查询对象存储配置列表
|
||||
export function listOssConfig(query: OssConfigQuery): AxiosPromise<PageResult<OssConfigVO>> {
|
||||
return request({
|
||||
url: '/resource/oss/config/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
}
|
||||
|
||||
export function getOssConfig(ossConfigId: string | number) {
|
||||
return request<R<OssConfigVO>>({
|
||||
url: `/resource/oss/config/${ossConfigId}`,
|
||||
// 查询对象存储配置详细
|
||||
export function getOssConfig(ossConfigId: string | number): AxiosPromise<OssConfigVO> {
|
||||
return request({
|
||||
url: '/resource/oss/config/' + ossConfigId,
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
|
||||
// 新增对象存储配置
|
||||
export function addOssConfig(data: OssConfigForm) {
|
||||
return request<R>({
|
||||
return request({
|
||||
url: '/resource/oss/config',
|
||||
method: 'post',
|
||||
data
|
||||
data: data
|
||||
});
|
||||
}
|
||||
|
||||
// 修改对象存储配置
|
||||
export function updateOssConfig(data: OssConfigForm) {
|
||||
return request<R>({
|
||||
return request({
|
||||
url: '/resource/oss/config',
|
||||
method: 'put',
|
||||
data
|
||||
data: data
|
||||
});
|
||||
}
|
||||
|
||||
// 删除对象存储配置
|
||||
export function delOssConfig(ossConfigId: string | number | Array<string | number>) {
|
||||
return request<R>({
|
||||
url: `/resource/oss/config/${ossConfigId}`,
|
||||
return request({
|
||||
url: '/resource/oss/config/' + ossConfigId,
|
||||
method: 'delete'
|
||||
});
|
||||
}
|
||||
|
||||
// 对象存储状态修改
|
||||
export function changeOssConfigStatus(ossConfigId: string | number, status: string, configKey: string) {
|
||||
return request<R>({
|
||||
const data = {
|
||||
ossConfigId,
|
||||
status,
|
||||
configKey
|
||||
};
|
||||
return request({
|
||||
url: '/resource/oss/config/changeStatus',
|
||||
method: 'put',
|
||||
data: { ossConfigId, status, configKey }
|
||||
data: data
|
||||
});
|
||||
}
|
||||
|
||||
@ -1,39 +1,38 @@
|
||||
import type { BaseEntity, PageQuery } from '@/api/types';
|
||||
|
||||
export interface OssConfigForm {
|
||||
ossConfigId?: string | number;
|
||||
configKey?: string;
|
||||
accessKey?: string;
|
||||
secretKey?: string;
|
||||
bucketName?: string;
|
||||
prefix?: string;
|
||||
endpoint?: string;
|
||||
domainUrl?: string;
|
||||
isHttps?: string;
|
||||
accessPolicy?: string;
|
||||
region?: string;
|
||||
status?: string;
|
||||
remark?: string;
|
||||
}
|
||||
|
||||
export interface OssConfigQuery extends PageQuery {
|
||||
configKey?: string;
|
||||
bucketName?: string;
|
||||
status?: string;
|
||||
}
|
||||
|
||||
export interface OssConfigVO extends BaseEntity {
|
||||
ossConfigId: number | string;
|
||||
configKey: string;
|
||||
accessKey: string;
|
||||
secretKey: string;
|
||||
bucketName: string;
|
||||
prefix?: string;
|
||||
prefix: string;
|
||||
endpoint: string;
|
||||
domainUrl?: string;
|
||||
domainUrl: string;
|
||||
isHttps: string;
|
||||
region?: string;
|
||||
region: string;
|
||||
status: string;
|
||||
ext1?: string;
|
||||
ext1: string;
|
||||
remark: string;
|
||||
accessPolicy: string;
|
||||
}
|
||||
|
||||
export interface OssConfigQuery extends PageQuery {
|
||||
configKey: string;
|
||||
bucketName: string;
|
||||
status: string;
|
||||
}
|
||||
|
||||
export interface OssConfigForm {
|
||||
ossConfigId: string | number | undefined;
|
||||
configKey: string;
|
||||
accessKey: string;
|
||||
secretKey: string;
|
||||
bucketName: string;
|
||||
prefix: string;
|
||||
endpoint: string;
|
||||
domainUrl: string;
|
||||
isHttps: string;
|
||||
accessPolicy: string;
|
||||
region: string;
|
||||
status: string;
|
||||
remark: string;
|
||||
}
|
||||
|
||||
@ -1,64 +1,70 @@
|
||||
import type { DeptTreeVO } from '@/api/system/dept/types';
|
||||
import type { PageResult, R } from '@/api/types';
|
||||
import request from '@/api/request';
|
||||
import type { PageResult } from '@/api/types';
|
||||
import type { AxiosPromise } from '@/utils/api-types';
|
||||
import request from '@/utils/request';
|
||||
import type { DeptTreeVO } from '../dept/types';
|
||||
import type { PostForm, PostQuery, PostVO } from './types';
|
||||
|
||||
export function listPost(query: PostQuery) {
|
||||
return request<R<PageResult<PostVO>>>({
|
||||
// 查询岗位列表
|
||||
export function listPost(query: PostQuery): AxiosPromise<PageResult<PostVO>> {
|
||||
return request({
|
||||
url: '/system/post/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
}
|
||||
|
||||
export function getPost(postId: string | number) {
|
||||
return request<R<PostVO>>({
|
||||
url: `/system/post/${postId}`,
|
||||
// 查询岗位详细
|
||||
export function getPost(postId: string | number): AxiosPromise<PostVO> {
|
||||
return request({
|
||||
url: '/system/post/' + postId,
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
|
||||
export function optionSelect(deptId?: number | string, postIds?: Array<number | string>) {
|
||||
return request<R<PostVO[]>>({
|
||||
// 获取岗位选择框列表
|
||||
export function optionselect(deptId?: number | string, postIds?: (number | string)[]): AxiosPromise<PostVO[]> {
|
||||
return request({
|
||||
url: '/system/post/optionselect',
|
||||
method: 'get',
|
||||
params: {
|
||||
postIds,
|
||||
deptId
|
||||
postIds: postIds,
|
||||
deptId: deptId
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 新增岗位
|
||||
export function addPost(data: PostForm) {
|
||||
return request<R>({
|
||||
return request({
|
||||
url: '/system/post',
|
||||
method: 'post',
|
||||
data
|
||||
data: data
|
||||
});
|
||||
}
|
||||
|
||||
// 修改岗位
|
||||
export function updatePost(data: PostForm) {
|
||||
return request<R>({
|
||||
return request({
|
||||
url: '/system/post',
|
||||
method: 'put',
|
||||
data
|
||||
data: data
|
||||
});
|
||||
}
|
||||
|
||||
export function delPost(postId: string | number | Array<string | number>) {
|
||||
return request<R>({
|
||||
url: `/system/post/${postId}`,
|
||||
// 删除岗位
|
||||
export function delPost(postId: string | number | (string | number)[]) {
|
||||
return request({
|
||||
url: '/system/post/' + postId,
|
||||
method: 'delete'
|
||||
});
|
||||
}
|
||||
|
||||
export function postDeptTreeSelect() {
|
||||
return request<R<DeptTreeVO[]>>({
|
||||
/**
|
||||
* 查询部门下拉树结构
|
||||
*/
|
||||
export const deptTreeSelect = (): AxiosPromise<DeptTreeVO[]> => {
|
||||
return request({
|
||||
url: '/system/post/deptTree',
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
|
||||
export function deptTreeSelect() {
|
||||
return postDeptTreeSelect();
|
||||
}
|
||||
};
|
||||
|
||||
@ -1,25 +1,3 @@
|
||||
import type { BaseEntity, PageQuery } from '@/api/types';
|
||||
|
||||
export interface PostForm {
|
||||
postId?: number | string;
|
||||
deptId?: number | string;
|
||||
postCode?: string;
|
||||
postName?: string;
|
||||
postCategory?: string;
|
||||
postSort?: number;
|
||||
status?: string;
|
||||
remark?: string;
|
||||
}
|
||||
|
||||
export interface PostQuery extends PageQuery {
|
||||
deptId?: number | string;
|
||||
belongDeptId?: number | string;
|
||||
postCode?: string;
|
||||
postName?: string;
|
||||
postCategory?: string;
|
||||
status?: string;
|
||||
}
|
||||
|
||||
export interface PostVO extends BaseEntity {
|
||||
postId: number | string;
|
||||
deptId: number | string;
|
||||
@ -29,4 +7,25 @@ export interface PostVO extends BaseEntity {
|
||||
deptName: string;
|
||||
postSort: number;
|
||||
status: string;
|
||||
remark: string;
|
||||
}
|
||||
|
||||
export interface PostForm {
|
||||
postId: number | string | undefined;
|
||||
deptId: number | string | undefined;
|
||||
postCode: string;
|
||||
postName: string;
|
||||
postCategory: string;
|
||||
postSort: number;
|
||||
status: string;
|
||||
remark: string;
|
||||
}
|
||||
|
||||
export interface PostQuery extends PageQuery {
|
||||
deptId: number | string;
|
||||
belongDeptId: number | string;
|
||||
postCode: string;
|
||||
postName: string;
|
||||
postCategory: string;
|
||||
status: string;
|
||||
}
|
||||
|
||||
@ -1,115 +1,160 @@
|
||||
import type { UserQuery, UserVO } from '@/api/system/user/types';
|
||||
import type { PageResult, R } from '@/api/types';
|
||||
import request from '@/api/request';
|
||||
import type { RoleDeptTree, RoleForm, RoleQuery, RoleVO } from './types';
|
||||
import type { UserQuery, UserVO } from '@/api/system/user/types';
|
||||
import type { PageResult } from '@/api/types';
|
||||
import type { AxiosPromise } from '@/utils/api-types';
|
||||
import request from '@/utils/request';
|
||||
import type { RoleDeptTree, RoleQuery, RoleVO } from './types';
|
||||
|
||||
export function listRole(query: RoleQuery) {
|
||||
return request<R<PageResult<RoleVO>>>({
|
||||
export const listRole = (query: RoleQuery): AxiosPromise<PageResult<RoleVO>> => {
|
||||
return request({
|
||||
url: '/system/role/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function optionSelect(roleIds: Array<number | string>) {
|
||||
return request<R<RoleVO[]>>({
|
||||
url: `/system/role/optionselect?roleIds=${roleIds}`,
|
||||
/**
|
||||
* 通过roleIds查询角色
|
||||
* @param roleIds
|
||||
*/
|
||||
export const optionSelect = (roleIds: (number | string)[]): AxiosPromise<RoleVO[]> => {
|
||||
return request({
|
||||
url: '/system/role/optionselect?roleIds=' + roleIds,
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function getRole(roleId: string | number) {
|
||||
return request<R<RoleVO>>({
|
||||
url: `/system/role/${roleId}`,
|
||||
/**
|
||||
* 查询角色详细
|
||||
*/
|
||||
export const getRole = (roleId: string | number): AxiosPromise<RoleVO> => {
|
||||
return request({
|
||||
url: '/system/role/' + roleId,
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function addRole(data: RoleForm) {
|
||||
return request<R>({
|
||||
/**
|
||||
* 新增角色
|
||||
*/
|
||||
export const addRole = (data: any) => {
|
||||
return request({
|
||||
url: '/system/role',
|
||||
method: 'post',
|
||||
data
|
||||
data: data
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function updateRole(data: RoleForm) {
|
||||
return request<R>({
|
||||
/**
|
||||
* 修改角色基础信息
|
||||
* @param data
|
||||
*/
|
||||
export const updateRole = (data: any) => {
|
||||
return request({
|
||||
url: '/system/role',
|
||||
method: 'put',
|
||||
data
|
||||
data: data
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function updateRolePermission(data: RoleForm) {
|
||||
return request<R>({
|
||||
/**
|
||||
* 修改角色权限(菜单权限 + 数据权限)
|
||||
*/
|
||||
export const updateRolePermission = (data: any) => {
|
||||
return request({
|
||||
url: '/system/role/permission',
|
||||
method: 'put',
|
||||
data
|
||||
data: data
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function changeRoleStatus(roleId: string | number, status: string) {
|
||||
return request<R>({
|
||||
/**
|
||||
* 角色状态修改
|
||||
*/
|
||||
export const changeRoleStatus = (roleId: string | number, status: string) => {
|
||||
const data = {
|
||||
roleId,
|
||||
status
|
||||
};
|
||||
return request({
|
||||
url: '/system/role/changeStatus',
|
||||
method: 'put',
|
||||
data: {
|
||||
roleId,
|
||||
status
|
||||
}
|
||||
data: data
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function delRole(roleId: Array<string | number> | string | number) {
|
||||
return request<R>({
|
||||
url: `/system/role/${roleId}`,
|
||||
/**
|
||||
* 删除角色
|
||||
*/
|
||||
export const delRole = (roleId: Array<string | number> | string | number) => {
|
||||
return request({
|
||||
url: '/system/role/' + roleId,
|
||||
method: 'delete'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function allocatedUserList(query: UserQuery) {
|
||||
return request<R<PageResult<UserVO>>>({
|
||||
/**
|
||||
* 查询角色已授权用户列表
|
||||
*/
|
||||
export const allocatedUserList = (query: UserQuery): AxiosPromise<PageResult<UserVO>> => {
|
||||
return request({
|
||||
url: '/system/role/authUser/allocatedList',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function unallocatedUserList(query: UserQuery) {
|
||||
return request<R<PageResult<UserVO>>>({
|
||||
/**
|
||||
* 查询角色未授权用户列表
|
||||
*/
|
||||
export const unallocatedUserList = (query: UserQuery): AxiosPromise<PageResult<UserVO>> => {
|
||||
return request({
|
||||
url: '/system/role/authUser/unallocatedList',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function authUserCancel(data: { roleId: string | number; userId: string | number }) {
|
||||
return request<R>({
|
||||
/**
|
||||
* 取消用户授权角色
|
||||
*/
|
||||
export const authUserCancel = (data: any) => {
|
||||
return request({
|
||||
url: '/system/role/authUser/cancel',
|
||||
method: 'put',
|
||||
data
|
||||
data: data
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function authUserCancelAll(data: { roleId: string | number; userIds: string }) {
|
||||
return request<R>({
|
||||
/**
|
||||
* 批量取消用户授权角色
|
||||
*/
|
||||
export const authUserCancelAll = (data: any) => {
|
||||
return request({
|
||||
url: '/system/role/authUser/cancelAll',
|
||||
method: 'put',
|
||||
params: data
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function authUserSelectAll(data: { roleId: string | number; userIds: string }) {
|
||||
return request<R>({
|
||||
/**
|
||||
* 授权用户选择
|
||||
*/
|
||||
export const authUserSelectAll = (data: any) => {
|
||||
return request({
|
||||
url: '/system/role/authUser/selectAll',
|
||||
method: 'put',
|
||||
params: data
|
||||
});
|
||||
}
|
||||
|
||||
export function deptTreeSelect(roleId: string | number) {
|
||||
return request<R<RoleDeptTree>>({
|
||||
url: `/system/role/deptTree/${roleId}`,
|
||||
};
|
||||
// 根据角色ID查询部门树结构
|
||||
export const deptTreeSelect = (roleId: string | number): AxiosPromise<RoleDeptTree> => {
|
||||
return request({
|
||||
url: '/system/role/deptTree/' + roleId,
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export default {
|
||||
optionSelect,
|
||||
listRole
|
||||
};
|
||||
|
||||
@ -1,35 +1,17 @@
|
||||
import type { DeptTreeVO } from '@/api/system/dept/types';
|
||||
import type { MenuTreeOption } from '@/api/system/menu/types';
|
||||
import type { BaseEntity, PageQuery } from '@/api/types';
|
||||
/**
|
||||
* 菜单树形结构类型
|
||||
*/
|
||||
export interface DeptTreeOption {
|
||||
id: string;
|
||||
label: string;
|
||||
parentId: string;
|
||||
weight: number;
|
||||
children?: DeptTreeOption[];
|
||||
}
|
||||
|
||||
export interface RoleDeptTree {
|
||||
checkedKeys: Array<string | number>;
|
||||
depts: DeptTreeVO[];
|
||||
}
|
||||
|
||||
export interface RoleForm {
|
||||
roleId?: string | number;
|
||||
roleName?: string;
|
||||
roleKey?: string;
|
||||
roleSort?: number;
|
||||
status?: string;
|
||||
menuCheckStrictly?: boolean;
|
||||
deptCheckStrictly?: boolean;
|
||||
remark?: string;
|
||||
dataScope?: string;
|
||||
menuIds?: Array<string | number>;
|
||||
deptIds?: Array<string | number>;
|
||||
}
|
||||
|
||||
export interface RoleMenuTree {
|
||||
menus: MenuTreeOption[];
|
||||
checkedKeys: Array<string | number>;
|
||||
}
|
||||
|
||||
export interface RoleQuery extends PageQuery {
|
||||
roleName?: string;
|
||||
roleKey?: string;
|
||||
status?: string;
|
||||
checkedKeys: string[];
|
||||
depts: DeptTreeOption[];
|
||||
}
|
||||
|
||||
export interface RoleVO extends BaseEntity {
|
||||
@ -38,11 +20,33 @@ export interface RoleVO extends BaseEntity {
|
||||
roleKey: string;
|
||||
roleSort: number;
|
||||
dataScope: string;
|
||||
menuCheckStrictly?: boolean;
|
||||
deptCheckStrictly?: boolean;
|
||||
menuCheckStrictly: boolean;
|
||||
deptCheckStrictly: boolean;
|
||||
status: string;
|
||||
delFlag: string;
|
||||
remark?: any;
|
||||
flag: boolean;
|
||||
admin: boolean;
|
||||
menuIds?: Array<string | number>;
|
||||
deptIds?: Array<string | number>;
|
||||
admin: boolean;
|
||||
}
|
||||
|
||||
export interface RoleQuery extends PageQuery {
|
||||
roleName: string;
|
||||
roleKey: string;
|
||||
status: string;
|
||||
}
|
||||
|
||||
export interface RoleForm {
|
||||
roleName: string;
|
||||
roleKey: string;
|
||||
roleSort: number;
|
||||
status: string;
|
||||
menuCheckStrictly: boolean;
|
||||
deptCheckStrictly: boolean;
|
||||
remark: string;
|
||||
dataScope?: string;
|
||||
roleId: string | undefined;
|
||||
menuIds: Array<string | number>;
|
||||
deptIds: Array<string | number>;
|
||||
}
|
||||
|
||||
@ -1,23 +1,23 @@
|
||||
import type { R } from '@/api/types';
|
||||
import request from '@/api/request';
|
||||
import type { SocialAuthVO } from './types';
|
||||
import request from '@/utils/request';
|
||||
|
||||
// 获取跳转URL
|
||||
export function authRouterUrl(source: string) {
|
||||
return request<R<string>>({
|
||||
url: `/auth/binding/${source}`,
|
||||
return request({
|
||||
url: '/auth/binding/' + source,
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
|
||||
// 解绑账号
|
||||
export function authUnlock(authId: string) {
|
||||
return request<R>({
|
||||
url: `/auth/unlock/${authId}`,
|
||||
return request({
|
||||
url: '/auth/unlock/' + authId,
|
||||
method: 'delete'
|
||||
});
|
||||
}
|
||||
|
||||
//获取授权列表
|
||||
export function getAuthList() {
|
||||
return request<R<SocialAuthVO[]>>({
|
||||
return request({
|
||||
url: '/system/social/list',
|
||||
method: 'get'
|
||||
});
|
||||
|
||||
@ -1,7 +0,0 @@
|
||||
export interface SocialAuthVO {
|
||||
id: string;
|
||||
source?: string;
|
||||
avatar?: string;
|
||||
userName?: string;
|
||||
createTime?: string;
|
||||
}
|
||||
@ -1,154 +1,229 @@
|
||||
import type { DeptTreeVO } from '@/api/system/dept/types';
|
||||
import type { RoleVO } from '@/api/system/role/types';
|
||||
import type { PageResult, R } from '@/api/types';
|
||||
import request from '@/api/request';
|
||||
import type { PageResult } from '@/api/types';
|
||||
import type { AxiosPromise } from '@/utils/api-types';
|
||||
import request from '@/utils/request';
|
||||
import { parseStrEmpty } from '@/utils/ruoyi';
|
||||
import type { DeptTreeVO } from './../dept/types';
|
||||
import type { UserForm, UserInfoVO, UserProfileForm, UserQuery, UserVO } from './types';
|
||||
|
||||
export function listUser(query: UserQuery) {
|
||||
return request<R<PageResult<UserVO>>>({
|
||||
/**
|
||||
* 查询用户列表
|
||||
* @param query
|
||||
*/
|
||||
export const listUser = (query: UserQuery): AxiosPromise<PageResult<UserVO>> => {
|
||||
return request({
|
||||
url: '/system/user/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function optionSelect(userIds: Array<number | string>) {
|
||||
return request<R<UserVO[]>>({
|
||||
url: `/system/user/optionselect?userIds=${userIds}`,
|
||||
/**
|
||||
* 通过用户ids查询用户
|
||||
* @param userIds
|
||||
*/
|
||||
export const optionSelect = (userIds: (number | string)[]): AxiosPromise<UserVO[]> => {
|
||||
return request({
|
||||
url: '/system/user/optionselect?userIds=' + userIds,
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function getUser(userId?: string | number) {
|
||||
return request<R<UserInfoVO>>({
|
||||
url: `/system/user/${parseStrEmpty(userId)}`,
|
||||
/**
|
||||
* 获取用户详情
|
||||
* @param userId
|
||||
*/
|
||||
export const getUser = (userId?: string | number): AxiosPromise<UserInfoVO> => {
|
||||
return request({
|
||||
url: '/system/user/' + parseStrEmpty(userId),
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function addUser(data: UserForm) {
|
||||
return request<R>({
|
||||
/**
|
||||
* 新增用户
|
||||
*/
|
||||
export const addUser = (data: UserForm) => {
|
||||
return request({
|
||||
url: '/system/user',
|
||||
method: 'post',
|
||||
data
|
||||
data: data
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function updateUser(data: UserForm) {
|
||||
return request<R>({
|
||||
/**
|
||||
* 修改用户
|
||||
*/
|
||||
export const updateUser = (data: UserForm) => {
|
||||
return request({
|
||||
url: '/system/user',
|
||||
method: 'put',
|
||||
data
|
||||
data: data
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function delUser(userId: Array<string | number> | string | number) {
|
||||
return request<R>({
|
||||
url: `/system/user/${userId}`,
|
||||
/**
|
||||
* 删除用户
|
||||
* @param userId 用户ID
|
||||
*/
|
||||
export const delUser = (userId: Array<string | number> | string | number) => {
|
||||
return request({
|
||||
url: '/system/user/' + userId,
|
||||
method: 'delete'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function resetUserPwd(userId: string | number, password: string) {
|
||||
return request<R>({
|
||||
/**
|
||||
* 用户密码重置
|
||||
* @param userId 用户ID
|
||||
* @param password 密码
|
||||
*/
|
||||
export const resetUserPwd = (userId: string | number, password: string) => {
|
||||
const data = {
|
||||
userId,
|
||||
password
|
||||
};
|
||||
return request({
|
||||
url: '/system/user/resetPwd',
|
||||
method: 'put',
|
||||
headers: {
|
||||
isEncrypt: true,
|
||||
repeatSubmit: false
|
||||
},
|
||||
data: {
|
||||
userId,
|
||||
password
|
||||
}
|
||||
data: data
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function changeUserStatus(userId: number | string, status: string) {
|
||||
return request<R>({
|
||||
/**
|
||||
* 用户状态修改
|
||||
* @param userId 用户ID
|
||||
* @param status 用户状态
|
||||
*/
|
||||
export const changeUserStatus = (userId: number | string, status: string) => {
|
||||
const data = {
|
||||
userId,
|
||||
status
|
||||
};
|
||||
return request({
|
||||
url: '/system/user/changeStatus',
|
||||
method: 'put',
|
||||
data: {
|
||||
userId,
|
||||
status
|
||||
}
|
||||
data: data
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function unlockUser(userId: number | string) {
|
||||
return request<R>({
|
||||
url: `/system/user/unlock/${userId}`,
|
||||
/**
|
||||
* 解锁用户
|
||||
* @param userId 用户ID
|
||||
*/
|
||||
export const unlockUser = (userId: number | string) => {
|
||||
return request({
|
||||
url: '/system/user/unlock/' + userId,
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function getUserProfile() {
|
||||
return request<R<UserInfoVO>>({
|
||||
/**
|
||||
* 查询用户个人信息
|
||||
*/
|
||||
export const getUserProfile = (): AxiosPromise<UserInfoVO> => {
|
||||
return request({
|
||||
url: '/system/user/profile',
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function updateUserProfile(data: UserProfileForm) {
|
||||
return request<R>({
|
||||
/**
|
||||
* 修改用户个人信息
|
||||
* @param data 用户信息
|
||||
*/
|
||||
export const updateUserProfile = (data: UserProfileForm) => {
|
||||
return request({
|
||||
url: '/system/user/profile',
|
||||
method: 'put',
|
||||
data
|
||||
data: data
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function updateUserPwd(oldPassword: string, newPassword: string) {
|
||||
return request<R>({
|
||||
/**
|
||||
* 用户密码重置
|
||||
* @param oldPassword 旧密码
|
||||
* @param newPassword 新密码
|
||||
*/
|
||||
export const updateUserPwd = (oldPassword: string, newPassword: string) => {
|
||||
const data = {
|
||||
oldPassword,
|
||||
newPassword
|
||||
};
|
||||
return request({
|
||||
url: '/system/user/profile/updatePwd',
|
||||
method: 'put',
|
||||
headers: {
|
||||
isEncrypt: true,
|
||||
repeatSubmit: false
|
||||
},
|
||||
data: {
|
||||
oldPassword,
|
||||
newPassword
|
||||
}
|
||||
data: data
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function importUser(data: FormData, updateSupport: boolean) {
|
||||
return request<R>({
|
||||
url: '/system/user/importData',
|
||||
method: 'post',
|
||||
params: {
|
||||
updateSupport: updateSupport ? 1 : 0
|
||||
},
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
export function getAuthRole(userId: string | number) {
|
||||
return request<R<{ user: UserVO; roles: RoleVO[] }>>({
|
||||
url: `/system/user/authRole/${userId}`,
|
||||
/**
|
||||
* 查询授权角色
|
||||
* @param userId 用户ID
|
||||
*/
|
||||
export const getAuthRole = (userId: string | number): AxiosPromise<{ user: UserVO; roles: RoleVO[] }> => {
|
||||
return request({
|
||||
url: '/system/user/authRole/' + userId,
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function updateAuthRole(data: { userId: string; roleIds: string }) {
|
||||
return request<R>({
|
||||
/**
|
||||
* 保存授权角色
|
||||
* @param data 用户ID
|
||||
*/
|
||||
export const updateAuthRole = (data: { userId: string; roleIds: string }) => {
|
||||
return request({
|
||||
url: '/system/user/authRole',
|
||||
method: 'put',
|
||||
params: data
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function deptTreeSelect() {
|
||||
return request<R<DeptTreeVO[]>>({
|
||||
/**
|
||||
* 查询当前部门的所有用户信息
|
||||
* @param deptId
|
||||
*/
|
||||
export const listUserByDeptId = (deptId: string | number): AxiosPromise<UserVO[]> => {
|
||||
return request({
|
||||
url: '/system/user/list/dept/' + deptId,
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 查询部门下拉树结构
|
||||
*/
|
||||
export const deptTreeSelect = (): AxiosPromise<DeptTreeVO[]> => {
|
||||
return request({
|
||||
url: '/system/user/deptTree',
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function listUserByDeptId(deptId: string | number) {
|
||||
return request<R<UserVO[]>>({
|
||||
url: `/system/user/list/dept/${deptId}`,
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
export default {
|
||||
listUser,
|
||||
getUser,
|
||||
optionSelect,
|
||||
addUser,
|
||||
updateUser,
|
||||
delUser,
|
||||
resetUserPwd,
|
||||
changeUserStatus,
|
||||
unlockUser,
|
||||
getUserProfile,
|
||||
updateUserProfile,
|
||||
updateUserPwd,
|
||||
getAuthRole,
|
||||
updateAuthRole,
|
||||
deptTreeSelect,
|
||||
listUserByDeptId
|
||||
};
|
||||
|
||||
@ -1,37 +1,81 @@
|
||||
import type { DeptVO } from '@/api/system/dept/types';
|
||||
import type { PostVO } from '@/api/system/post/types';
|
||||
import type { RoleVO } from '@/api/system/role/types';
|
||||
import type { BaseEntity, PageQuery } from '@/api/types';
|
||||
|
||||
/**
|
||||
* 用户信息
|
||||
*/
|
||||
export interface UserInfo {
|
||||
user: {
|
||||
userId: string | number;
|
||||
userName: string;
|
||||
nickName?: string;
|
||||
avatar?: string | number;
|
||||
avatarUrl?: string;
|
||||
deptName?: string;
|
||||
};
|
||||
user: UserVO;
|
||||
roles: string[];
|
||||
permissions: string[];
|
||||
}
|
||||
|
||||
export interface UserForm {
|
||||
userId?: string | number;
|
||||
deptId?: number | string;
|
||||
/**
|
||||
* 用户查询对象类型
|
||||
*/
|
||||
export interface UserQuery extends PageQuery {
|
||||
userName?: string;
|
||||
nickName?: string;
|
||||
password?: string;
|
||||
phoneNumber?: string;
|
||||
status?: string;
|
||||
deptId?: string | number;
|
||||
roleId?: string | number;
|
||||
userIds?: string | number | (string | number)[] | undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户返回对象
|
||||
*/
|
||||
export interface UserVO extends BaseEntity {
|
||||
userId: string | number;
|
||||
tenantId: string;
|
||||
deptId: number;
|
||||
userName: string;
|
||||
nickName: string;
|
||||
userType: string;
|
||||
email: string;
|
||||
phoneNumber: string;
|
||||
gender: string;
|
||||
avatar?: string | number;
|
||||
avatarUrl?: string;
|
||||
status: string;
|
||||
delFlag: string;
|
||||
loginIp: string;
|
||||
loginDate: string;
|
||||
remark: string;
|
||||
deptName: string;
|
||||
/** 详情接口可能返回嵌套部门 */
|
||||
dept?: { deptName?: string };
|
||||
roles: RoleVO[];
|
||||
roleIds: any;
|
||||
postIds: any;
|
||||
roleId: any;
|
||||
admin: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户表单类型
|
||||
*/
|
||||
export interface UserForm {
|
||||
id?: string;
|
||||
userId?: string;
|
||||
deptId?: number;
|
||||
userName: string;
|
||||
nickName?: string;
|
||||
password: string;
|
||||
phoneNumber?: string;
|
||||
email?: string;
|
||||
gender?: string;
|
||||
status?: string;
|
||||
status: string;
|
||||
remark?: string;
|
||||
avatar?: string | number;
|
||||
postIds?: Array<string | number>;
|
||||
roleIds?: Array<string | number>;
|
||||
postIds: string[];
|
||||
roleIds: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* 个人资料表单类型
|
||||
*/
|
||||
export interface UserProfileForm {
|
||||
nickName?: string;
|
||||
phoneNumber?: string;
|
||||
@ -41,42 +85,17 @@ export interface UserProfileForm {
|
||||
}
|
||||
|
||||
export interface UserInfoVO {
|
||||
user?: UserVO;
|
||||
user: UserVO;
|
||||
roles: RoleVO[];
|
||||
roleIds: Array<string | number>;
|
||||
roleIds: string[];
|
||||
posts: PostVO[];
|
||||
postIds: Array<string | number>;
|
||||
roleGroup?: string;
|
||||
postGroup?: string;
|
||||
postIds: string[];
|
||||
roleGroup: string;
|
||||
postGroup: string;
|
||||
}
|
||||
|
||||
export interface UserQuery extends PageQuery {
|
||||
userName?: string;
|
||||
nickName?: string;
|
||||
phoneNumber?: string;
|
||||
status?: string;
|
||||
deptId?: string | number;
|
||||
roleId?: string | number;
|
||||
userIds?: string | number;
|
||||
}
|
||||
|
||||
export interface UserVO extends BaseEntity {
|
||||
userId?: string | number;
|
||||
tenantId?: string;
|
||||
deptId?: number | string;
|
||||
userName?: string;
|
||||
nickName?: string;
|
||||
userType?: string;
|
||||
email?: string;
|
||||
phoneNumber?: string;
|
||||
gender?: string;
|
||||
avatar?: string | number;
|
||||
avatarUrl?: string;
|
||||
status?: string;
|
||||
deptName?: string;
|
||||
roles?: RoleVO[];
|
||||
admin?: boolean;
|
||||
loginIp?: string;
|
||||
loginDate?: string;
|
||||
dept?: DeptVO;
|
||||
export interface ResetPwdForm {
|
||||
oldPassword: string;
|
||||
newPassword: string;
|
||||
confirmPassword: string;
|
||||
}
|
||||
|
||||
@ -1,78 +1,81 @@
|
||||
import type { PageResult, R } from '@/api/types';
|
||||
import request from '@/api/request';
|
||||
import type { DbTableQuery, DbTableVO, GenTableDetailPayload, TableQuery, TableVO } from './types';
|
||||
import type { PageResult } from '@/api/types';
|
||||
import type { AxiosPromise } from '@/utils/api-types';
|
||||
import request from '@/utils/request';
|
||||
import type { DbTableForm, DbTableQuery, DbTableVO, GenTableDetailPayload, TableQuery, TableVO } from './types';
|
||||
|
||||
export function listTable(query: TableQuery) {
|
||||
return request<R<PageResult<TableVO>>>({
|
||||
export type { GenTableDetailPayload } from './types';
|
||||
|
||||
// 查询生成表数据
|
||||
export const listTable = (query: TableQuery): AxiosPromise<PageResult<TableVO>> => {
|
||||
return request({
|
||||
url: '/tool/gen/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
}
|
||||
|
||||
export function listDbTable(query: DbTableQuery) {
|
||||
return request<R<PageResult<DbTableVO>>>({
|
||||
};
|
||||
// 查询db数据库列表
|
||||
export const listDbTable = (query: DbTableQuery): AxiosPromise<PageResult<DbTableVO>> => {
|
||||
return request({
|
||||
url: '/tool/gen/db/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function getGenTable(tableId: string | number) {
|
||||
return request<R<GenTableDetailPayload>>({
|
||||
url: `/tool/gen/${tableId}`,
|
||||
// 查询表详细信息
|
||||
export const getGenTable = (tableId: string | number): AxiosPromise<GenTableDetailPayload> => {
|
||||
return request({
|
||||
url: '/tool/gen/' + tableId,
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function updateGenTable(data: DbTableVO & { columns?: unknown[]; params?: Record<string, unknown> }) {
|
||||
return request<R>({
|
||||
// 修改代码生成信息
|
||||
export const updateGenTable = (data: DbTableForm): AxiosPromise<unknown> => {
|
||||
return request({
|
||||
url: '/tool/gen',
|
||||
method: 'put',
|
||||
data
|
||||
data: data
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function importTable(data: { tables: string; dataName: string }) {
|
||||
return request<R>({
|
||||
// 导入表
|
||||
export const importTable = (data: { tables: string; dataName: string }): AxiosPromise<unknown> => {
|
||||
return request({
|
||||
url: '/tool/gen/importTable',
|
||||
method: 'post',
|
||||
params: data
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function previewTable(tableId: string | number) {
|
||||
return request<R<Record<string, string>>>({
|
||||
url: `/tool/gen/preview/${tableId}`,
|
||||
// 预览生成代码
|
||||
export const previewTable = (tableId: string | number) => {
|
||||
return request({
|
||||
url: '/tool/gen/preview/' + tableId,
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function delTable(tableId: string | number | Array<string | number>) {
|
||||
return request<R>({
|
||||
url: `/tool/gen/${tableId}`,
|
||||
// 删除表数据
|
||||
export const delTable = (tableId: string | number | Array<string | number>) => {
|
||||
return request({
|
||||
url: '/tool/gen/' + tableId,
|
||||
method: 'delete'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function synchDb(tableId: string | number) {
|
||||
return request<R>({
|
||||
url: `/tool/gen/synchDb/${tableId}`,
|
||||
// 同步数据库
|
||||
export const synchDb = (tableId: string | number) => {
|
||||
return request({
|
||||
url: '/tool/gen/synchDb/' + tableId,
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function getDataNames() {
|
||||
return request<R<string[]>>({
|
||||
// 获取数据源名称
|
||||
export const getDataNames = () => {
|
||||
return request({
|
||||
url: '/tool/gen/getDataNames',
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
|
||||
export function batchGenCode(tableIdStr: string) {
|
||||
return request<Blob>({
|
||||
url: `/tool/gen/batchGenCode?tableIdStr=${tableIdStr}`,
|
||||
method: 'get',
|
||||
responseType: 'blob'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@ -1,53 +1,27 @@
|
||||
import type { BaseEntity, PageQuery } from '@/api/types';
|
||||
|
||||
export interface DbColumnVO extends BaseEntity {
|
||||
columnId?: string | number;
|
||||
tableId?: string | number;
|
||||
columnName?: string;
|
||||
columnComment?: string;
|
||||
columnType?: string;
|
||||
javaType?: string;
|
||||
javaField?: string;
|
||||
isPk?: string;
|
||||
isIncrement?: string;
|
||||
isRequired?: string;
|
||||
isInsert?: string;
|
||||
isEdit?: string;
|
||||
isList?: string;
|
||||
isQuery?: string;
|
||||
queryType?: string;
|
||||
htmlType?: string;
|
||||
dictType?: string;
|
||||
sort?: number;
|
||||
increment?: boolean;
|
||||
capJavaField?: string;
|
||||
usableColumn?: boolean;
|
||||
superColumn?: boolean;
|
||||
list?: boolean;
|
||||
pk?: boolean;
|
||||
insert?: boolean;
|
||||
edit?: boolean;
|
||||
query?: boolean;
|
||||
required?: boolean;
|
||||
}
|
||||
|
||||
export interface DbTableQuery extends PageQuery {
|
||||
dataName?: string;
|
||||
tableName?: string;
|
||||
tableComment?: string;
|
||||
}
|
||||
|
||||
export interface DbTableVO extends Omit<TableVO, 'tableId'> {
|
||||
tableId?: string | number;
|
||||
columns?: DbColumnVO[];
|
||||
options?: string;
|
||||
remark?: string;
|
||||
treeCode?: string;
|
||||
treeParentCode?: string;
|
||||
treeName?: string;
|
||||
menuIds?: Array<string | number>;
|
||||
parentMenuId?: string | number;
|
||||
parentMenuName?: string;
|
||||
export interface TableVO extends BaseEntity {
|
||||
createDept: number | string;
|
||||
tableId: string | number;
|
||||
dataName: string;
|
||||
tableName: string;
|
||||
tableComment: string;
|
||||
className: string;
|
||||
tplCategory: string;
|
||||
frontendType: string;
|
||||
packageName: string;
|
||||
moduleName: string;
|
||||
businessName: string;
|
||||
functionName: string;
|
||||
functionAuthor: string;
|
||||
pkColumn?: any;
|
||||
columns?: any;
|
||||
options?: any;
|
||||
remark?: any;
|
||||
treeCode?: any;
|
||||
treeParentCode?: any;
|
||||
treeName?: any;
|
||||
menuIds?: any;
|
||||
parentMenuId?: any;
|
||||
parentMenuName?: any;
|
||||
enableExport?: boolean;
|
||||
enableStatus?: boolean;
|
||||
statusField?: string;
|
||||
@ -55,37 +29,187 @@ export interface DbTableVO extends Omit<TableVO, 'tableId'> {
|
||||
uniqueFields?: string[];
|
||||
enableSort?: boolean;
|
||||
sortField?: string;
|
||||
frontendType?: string;
|
||||
treeRootValue?: string;
|
||||
treeAncestorsField?: string;
|
||||
treeOrderField?: string;
|
||||
tree: boolean;
|
||||
crud: boolean;
|
||||
}
|
||||
|
||||
export interface TableQuery extends PageQuery {
|
||||
tableName: string;
|
||||
tableComment: string;
|
||||
dataName: string;
|
||||
}
|
||||
|
||||
export interface DbColumnVO extends BaseEntity {
|
||||
createDept?: any;
|
||||
columnId?: any;
|
||||
tableId?: any;
|
||||
columnName?: any;
|
||||
columnComment?: any;
|
||||
columnType?: any;
|
||||
javaType?: any;
|
||||
javaField?: any;
|
||||
isPk?: any;
|
||||
isIncrement?: any;
|
||||
isRequired?: any;
|
||||
isInsert?: any;
|
||||
isEdit?: any;
|
||||
isList?: any;
|
||||
isQuery?: any;
|
||||
queryType?: any;
|
||||
htmlType?: any;
|
||||
dictType?: any;
|
||||
sort?: any;
|
||||
increment: boolean;
|
||||
capJavaField?: any;
|
||||
usableColumn: boolean;
|
||||
superColumn: boolean;
|
||||
list: boolean;
|
||||
pk: boolean;
|
||||
insert: boolean;
|
||||
edit: boolean;
|
||||
query: boolean;
|
||||
required: boolean;
|
||||
}
|
||||
|
||||
export interface DbTableVO {
|
||||
createDept?: any;
|
||||
tableId?: any;
|
||||
tableName: string;
|
||||
tableComment: string;
|
||||
className?: any;
|
||||
tplCategory?: any;
|
||||
frontendType?: string;
|
||||
packageName?: any;
|
||||
moduleName?: any;
|
||||
businessName?: any;
|
||||
functionName?: any;
|
||||
functionAuthor?: any;
|
||||
pkColumn?: any;
|
||||
columns: DbColumnVO[];
|
||||
options?: any;
|
||||
remark?: any;
|
||||
treeCode?: any;
|
||||
treeParentCode?: any;
|
||||
treeName?: any;
|
||||
menuIds?: any;
|
||||
parentMenuId?: any;
|
||||
parentMenuName?: any;
|
||||
enableExport?: boolean;
|
||||
enableStatus?: boolean;
|
||||
statusField?: string;
|
||||
enableUnique?: boolean;
|
||||
uniqueFields?: string[];
|
||||
enableSort?: boolean;
|
||||
sortField?: string;
|
||||
treeRootValue?: string;
|
||||
treeAncestorsField?: string;
|
||||
treeOrderField?: string;
|
||||
tree: boolean;
|
||||
crud: boolean;
|
||||
}
|
||||
|
||||
export interface DbTableQuery extends PageQuery {
|
||||
dataName: string;
|
||||
tableName: string;
|
||||
tableComment: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 代码生成表详情接口 data 结构
|
||||
* - info:当前表 GenTable
|
||||
* - rows:字段列表 GenTableColumn[]
|
||||
*/
|
||||
export interface GenTableDetailPayload {
|
||||
info: DbTableVO;
|
||||
rows: DbColumnVO[];
|
||||
}
|
||||
|
||||
export interface TableQuery extends PageQuery {
|
||||
tableName?: string;
|
||||
tableComment?: string;
|
||||
dataName?: string;
|
||||
export interface DbColumnForm extends BaseEntity {
|
||||
createDept: number;
|
||||
columnId: string;
|
||||
tableId: string;
|
||||
columnName: string;
|
||||
columnComment: string;
|
||||
columnType: string;
|
||||
javaType: string;
|
||||
javaField: string;
|
||||
isPk: string;
|
||||
isIncrement: string;
|
||||
isRequired: string;
|
||||
isInsert?: any;
|
||||
isEdit: string;
|
||||
isList: string;
|
||||
isQuery?: any;
|
||||
queryType: string;
|
||||
htmlType: string;
|
||||
dictType: string;
|
||||
sort: number;
|
||||
increment: boolean;
|
||||
capJavaField: string;
|
||||
usableColumn: boolean;
|
||||
superColumn: boolean;
|
||||
list: boolean;
|
||||
pk: boolean;
|
||||
insert: boolean;
|
||||
edit: boolean;
|
||||
query: boolean;
|
||||
required: boolean;
|
||||
}
|
||||
|
||||
export interface TableVO extends BaseEntity {
|
||||
createDept?: number | string;
|
||||
tableId: string | number;
|
||||
dataName?: string;
|
||||
tableName: string;
|
||||
tableComment?: string;
|
||||
className?: string;
|
||||
tplCategory?: string;
|
||||
frontendType?: string;
|
||||
packageName?: string;
|
||||
moduleName?: string;
|
||||
businessName?: string;
|
||||
functionName?: string;
|
||||
functionAuthor?: string;
|
||||
tree?: boolean;
|
||||
crud?: boolean;
|
||||
export interface DbParamForm {
|
||||
treeCode?: any;
|
||||
treeName?: any;
|
||||
treeParentCode?: any;
|
||||
parentMenuId: string;
|
||||
enableExport?: boolean;
|
||||
enableStatus?: boolean;
|
||||
statusField?: string;
|
||||
enableUnique?: boolean;
|
||||
uniqueFields?: string[];
|
||||
enableSort?: boolean;
|
||||
sortField?: string;
|
||||
treeRootValue?: string;
|
||||
treeAncestorsField?: string;
|
||||
treeOrderField?: string;
|
||||
}
|
||||
|
||||
export interface DbTableForm extends BaseEntity {
|
||||
createDept?: any;
|
||||
tableId: string | string;
|
||||
tableName: string;
|
||||
tableComment: string;
|
||||
className: string;
|
||||
tplCategory: string;
|
||||
frontendType: string;
|
||||
packageName: string;
|
||||
moduleName: string;
|
||||
businessName: string;
|
||||
functionName: string;
|
||||
functionAuthor: string;
|
||||
pkColumn?: any;
|
||||
columns: DbColumnForm[];
|
||||
options: string;
|
||||
remark?: any;
|
||||
treeCode?: any;
|
||||
treeParentCode?: any;
|
||||
treeName?: any;
|
||||
menuIds?: any;
|
||||
parentMenuId: string;
|
||||
parentMenuName?: any;
|
||||
enableExport?: boolean;
|
||||
enableStatus?: boolean;
|
||||
statusField?: string;
|
||||
enableUnique?: boolean;
|
||||
uniqueFields?: string[];
|
||||
enableSort?: boolean;
|
||||
sortField?: string;
|
||||
treeRootValue?: string;
|
||||
treeAncestorsField?: string;
|
||||
treeOrderField?: string;
|
||||
tree: boolean;
|
||||
crud: boolean;
|
||||
params: DbParamForm;
|
||||
}
|
||||
|
||||
113
src/api/types.ts
113
src/api/types.ts
@ -1,88 +1,51 @@
|
||||
import type { MenuDataItem } from '@ant-design/pro-components';
|
||||
|
||||
export interface BaseEntity {
|
||||
createBy?: string;
|
||||
createTime?: string;
|
||||
updateBy?: string;
|
||||
updateTime?: string;
|
||||
remark?: string;
|
||||
}
|
||||
|
||||
export interface LoginParams {
|
||||
username?: string;
|
||||
password?: string;
|
||||
code?: string;
|
||||
uuid?: string;
|
||||
rememberMe?: boolean;
|
||||
socialCode?: string;
|
||||
socialState?: string;
|
||||
source?: string;
|
||||
clientId?: string;
|
||||
grantType?: string;
|
||||
}
|
||||
|
||||
export interface LoginResult {
|
||||
access_token: string;
|
||||
}
|
||||
|
||||
export interface PageQuery {
|
||||
pageNum?: number;
|
||||
pageSize?: number;
|
||||
orderByColumn?: string;
|
||||
isAsc?: 'ascending' | 'descending';
|
||||
params?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
export interface PageResult<T = unknown> {
|
||||
total: number;
|
||||
rows: T[];
|
||||
}
|
||||
|
||||
export interface R<T = unknown> {
|
||||
code: number;
|
||||
msg: string;
|
||||
data: T;
|
||||
}
|
||||
|
||||
export interface RegisterParams {
|
||||
username?: string;
|
||||
password?: string;
|
||||
/**
|
||||
* 注册
|
||||
*/
|
||||
export type RegisterForm = {
|
||||
username: string;
|
||||
password: string;
|
||||
confirmPassword?: string;
|
||||
code?: string;
|
||||
uuid?: string;
|
||||
userType?: string;
|
||||
clientId?: string;
|
||||
grantType?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* 登录请求
|
||||
*/
|
||||
export interface LoginData {
|
||||
username?: string;
|
||||
password?: string;
|
||||
rememberMe?: boolean;
|
||||
socialCode?: string;
|
||||
socialState?: string;
|
||||
source?: string;
|
||||
code?: string;
|
||||
uuid?: string;
|
||||
clientId: string;
|
||||
grantType: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 登录响应
|
||||
*/
|
||||
export interface LoginResult {
|
||||
access_token: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证码返回
|
||||
*/
|
||||
export interface VerifyCodeResult {
|
||||
captchaEnabled: boolean;
|
||||
uuid?: string;
|
||||
img?: string;
|
||||
}
|
||||
|
||||
export interface RouteMeta {
|
||||
title?: string;
|
||||
icon?: string | null;
|
||||
noCache?: boolean;
|
||||
link?: string;
|
||||
hidden?: boolean;
|
||||
activeMenu?: string;
|
||||
/**
|
||||
* 分页返回结果
|
||||
*/
|
||||
export interface PageResult<T = any> {
|
||||
total: number;
|
||||
rows: T[];
|
||||
}
|
||||
|
||||
export interface BackendRoute {
|
||||
name?: string;
|
||||
path: string;
|
||||
component?: string;
|
||||
redirect?: string;
|
||||
query?: string;
|
||||
hidden?: boolean;
|
||||
alwaysShow?: boolean;
|
||||
meta?: RouteMeta;
|
||||
children?: BackendRoute[];
|
||||
}
|
||||
|
||||
export type RuntimeMenuItem = Omit<MenuDataItem, 'routes'> & {
|
||||
backendRoute?: BackendRoute;
|
||||
routes?: RuntimeMenuItem[];
|
||||
};
|
||||
|
||||
@ -1,49 +1,76 @@
|
||||
import type { R } from '@/api/types';
|
||||
import request from '@/api/request';
|
||||
import type { CategoryForm, CategoryQuery, CategoryTreeVO, CategoryVO } from './types';
|
||||
import type { CategoryForm, CategoryQuery, CategoryTreeVO, CategoryVO } from '@/api/workflow/category/types';
|
||||
import type { AxiosPromise } from '@/utils/api-types';
|
||||
import request from '@/utils/request';
|
||||
|
||||
export function listCategory(query?: CategoryQuery) {
|
||||
return request<R<CategoryVO[]>>({
|
||||
/**
|
||||
* 查询流程分类列表
|
||||
* @param query
|
||||
* @returns {*}
|
||||
*/
|
||||
|
||||
export const listCategory = (query?: CategoryQuery): AxiosPromise<CategoryVO[]> => {
|
||||
return request({
|
||||
url: '/workflow/category/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function getCategory(categoryId: string | number) {
|
||||
return request<R<CategoryVO>>({
|
||||
url: `/workflow/category/${categoryId}`,
|
||||
/**
|
||||
* 查询流程分类详细
|
||||
* @param categoryId
|
||||
*/
|
||||
export const getCategory = (categoryId: string | number): AxiosPromise<CategoryVO> => {
|
||||
return request({
|
||||
url: '/workflow/category/' + categoryId,
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function addCategory(data: CategoryForm) {
|
||||
return request<R>({
|
||||
/**
|
||||
* 新增流程分类
|
||||
* @param data
|
||||
*/
|
||||
export const addCategory = (data: CategoryForm) => {
|
||||
return request({
|
||||
url: '/workflow/category',
|
||||
method: 'post',
|
||||
data
|
||||
data: data
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function updateCategory(data: CategoryForm) {
|
||||
return request<R>({
|
||||
/**
|
||||
* 修改流程分类
|
||||
* @param data
|
||||
*/
|
||||
export const updateCategory = (data: CategoryForm) => {
|
||||
return request({
|
||||
url: '/workflow/category',
|
||||
method: 'put',
|
||||
data
|
||||
data: data
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function delCategory(categoryId: string | number | Array<string | number>) {
|
||||
return request<R>({
|
||||
url: `/workflow/category/${categoryId}`,
|
||||
/**
|
||||
* 删除流程分类
|
||||
* @param categoryId
|
||||
*/
|
||||
export const delCategory = (categoryId: string | number | Array<string | number>) => {
|
||||
return request({
|
||||
url: '/workflow/category/' + categoryId,
|
||||
method: 'delete'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function categoryTree(query?: CategoryForm) {
|
||||
return request<R<CategoryTreeVO[]>>({
|
||||
url: '/workflow/category/categoryTree',
|
||||
/**
|
||||
* 获取流程分类树列表
|
||||
* @param query 流程实例id
|
||||
* @returns
|
||||
*/
|
||||
export const categoryTree = (query?: CategoryForm): AxiosPromise<CategoryTreeVO[]> => {
|
||||
return request({
|
||||
url: `/workflow/category/categoryTree`,
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@ -1,29 +1,67 @@
|
||||
import type { BaseEntity } from '@/api/types';
|
||||
|
||||
export interface CategoryForm extends BaseEntity {
|
||||
categoryId?: string | number;
|
||||
categoryName?: string;
|
||||
parentId?: string | number;
|
||||
orderNum?: number;
|
||||
}
|
||||
|
||||
export interface CategoryQuery {
|
||||
categoryName?: string;
|
||||
}
|
||||
|
||||
export interface CategoryTreeVO {
|
||||
id: number | string;
|
||||
label: string;
|
||||
parentId: number | string;
|
||||
weight: number;
|
||||
children?: CategoryTreeVO[];
|
||||
children: CategoryTreeVO[];
|
||||
}
|
||||
export interface CategoryVO {
|
||||
/**
|
||||
* 流程分类ID
|
||||
*/
|
||||
categoryId: string | number;
|
||||
|
||||
/**
|
||||
* 父级id
|
||||
*/
|
||||
parentId: string | number;
|
||||
|
||||
/**
|
||||
* 流程分类名称
|
||||
*/
|
||||
categoryName: string;
|
||||
|
||||
/**
|
||||
* 显示顺序
|
||||
*/
|
||||
orderNum: number;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
createTime: string;
|
||||
|
||||
/**
|
||||
* 子对象
|
||||
*/
|
||||
children: CategoryVO[];
|
||||
}
|
||||
|
||||
export interface CategoryVO {
|
||||
categoryId: string | number;
|
||||
export interface CategoryForm extends BaseEntity {
|
||||
/**
|
||||
* 流程分类ID
|
||||
*/
|
||||
categoryId?: string | number;
|
||||
|
||||
/**
|
||||
* 流程分类名称
|
||||
*/
|
||||
categoryName?: string;
|
||||
|
||||
/**
|
||||
* 父流程分类id
|
||||
*/
|
||||
parentId?: string | number;
|
||||
categoryName: string;
|
||||
orderNum: number;
|
||||
createTime?: string;
|
||||
children?: CategoryVO[];
|
||||
|
||||
/**
|
||||
* 显示顺序
|
||||
*/
|
||||
orderNum?: number;
|
||||
}
|
||||
|
||||
export interface CategoryQuery {
|
||||
/**
|
||||
* 流程分类名称
|
||||
*/
|
||||
categoryName?: string;
|
||||
}
|
||||
|
||||
@ -1,137 +1,176 @@
|
||||
import type { PageResult, R } from '@/api/types';
|
||||
import request from '@/api/request';
|
||||
import type { definitionXmlVO, FlowDefinitionForm, FlowDefinitionQuery, FlowDefinitionVO } from './types';
|
||||
import type { PageResult } from '@/api/types';
|
||||
import type {
|
||||
definitionXmlVO,
|
||||
FlowDefinitionForm,
|
||||
FlowDefinitionQuery,
|
||||
FlowDefinitionVo
|
||||
} from '@/api/workflow/definition/types';
|
||||
import type { AxiosPromise } from '@/utils/api-types';
|
||||
import request from '@/utils/request';
|
||||
|
||||
export function listDefinition(query: FlowDefinitionQuery) {
|
||||
return request<R<PageResult<FlowDefinitionVO>>>({
|
||||
url: '/workflow/definition/list',
|
||||
/**
|
||||
* 获取流程定义列表
|
||||
* @param query 流程实例id
|
||||
* @returns
|
||||
*/
|
||||
export const listDefinition = (query: FlowDefinitionQuery): AxiosPromise<PageResult<FlowDefinitionVo>> => {
|
||||
return request({
|
||||
url: `/workflow/definition/list`,
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function unPublishList(query: FlowDefinitionQuery) {
|
||||
return request<R<PageResult<FlowDefinitionVO>>>({
|
||||
url: '/workflow/definition/unPublishList',
|
||||
/**
|
||||
* 查询未发布的流程定义列表
|
||||
* @param query 流程实例id
|
||||
* @returns
|
||||
*/
|
||||
export const unPublishList = (query: FlowDefinitionQuery): AxiosPromise<PageResult<FlowDefinitionVo>> => {
|
||||
return request({
|
||||
url: `/workflow/definition/unPublishList`,
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function definitionXml(definitionId: string) {
|
||||
return request<R<definitionXmlVO>>({
|
||||
/**
|
||||
* 通过流程定义id获取xml
|
||||
* @param definitionId 流程定义id
|
||||
* @returns
|
||||
*/
|
||||
export const definitionXml = (definitionId: string): AxiosPromise<definitionXmlVO> => {
|
||||
return request({
|
||||
url: `/workflow/definition/definitionXml/${definitionId}`,
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function deleteDefinition(id: string | string[]) {
|
||||
return request<R>({
|
||||
/**
|
||||
* 删除流程定义
|
||||
* @param id 流程定义id
|
||||
* @returns
|
||||
*/
|
||||
export const deleteDefinition = (id: string | string[]) => {
|
||||
return request({
|
||||
url: `/workflow/definition/${id}`,
|
||||
method: 'delete'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function activeDefinition(definitionId: string, activityStatus: boolean) {
|
||||
return request<R>({
|
||||
/**
|
||||
* 挂起/激活
|
||||
* @param definitionId 流程定义id
|
||||
* @param activityStatus 状态
|
||||
* @returns
|
||||
*/
|
||||
export const active = (definitionId: string, activityStatus: boolean) => {
|
||||
return request({
|
||||
url: `/workflow/definition/active/${definitionId}`,
|
||||
method: 'put',
|
||||
params: { active: activityStatus }
|
||||
params: {
|
||||
active: activityStatus
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function active(definitionId: string, activityStatus: boolean) {
|
||||
return activeDefinition(definitionId, activityStatus);
|
||||
}
|
||||
|
||||
export function importDefinition(data: FormData) {
|
||||
return request<R>({
|
||||
/**
|
||||
* 通过zip或xml部署流程定义
|
||||
* @returns
|
||||
*/
|
||||
export function importDef(data: any) {
|
||||
return request({
|
||||
url: '/workflow/definition/importDef',
|
||||
method: 'post',
|
||||
data,
|
||||
headers: { repeatSubmit: false }
|
||||
data: data,
|
||||
headers: {
|
||||
repeatSubmit: false
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function importDef(data: FormData) {
|
||||
return importDefinition(data);
|
||||
}
|
||||
|
||||
export function publishDefinition(id: string) {
|
||||
return request<R>({
|
||||
/**
|
||||
* 发布流程定义
|
||||
* @param id 流程定义id
|
||||
* @returns
|
||||
*/
|
||||
export const publish = (id: string) => {
|
||||
return request({
|
||||
url: `/workflow/definition/publish/${id}`,
|
||||
method: 'put'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function publish(id: string) {
|
||||
return publishDefinition(id);
|
||||
}
|
||||
|
||||
export function unPublish(id: string) {
|
||||
return request<R>({
|
||||
/**
|
||||
* 取消发布流程定义
|
||||
* @param id 流程定义id
|
||||
* @returns
|
||||
*/
|
||||
export const unPublish = (id: string) => {
|
||||
return request({
|
||||
url: `/workflow/definition/unPublish/${id}`,
|
||||
method: 'put'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function xmlString(id: string) {
|
||||
return request<R<string>>({
|
||||
/**
|
||||
* 获取流程定义xml字符串
|
||||
* @param id 流程定义id
|
||||
* @returns
|
||||
*/
|
||||
export const xmlString = (id: string) => {
|
||||
return request({
|
||||
url: `/workflow/definition/xmlString/${id}`,
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function addDefinition(data: FlowDefinitionForm) {
|
||||
return request<R>({
|
||||
url: '/workflow/definition',
|
||||
/**
|
||||
* 新增
|
||||
* @param data 参数
|
||||
* @returns
|
||||
*/
|
||||
export const add = (data: FlowDefinitionForm) => {
|
||||
return request({
|
||||
url: `/workflow/definition`,
|
||||
method: 'post',
|
||||
data
|
||||
data: data
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function add(data: FlowDefinitionForm) {
|
||||
return addDefinition(data);
|
||||
}
|
||||
|
||||
export function editDefinition(data: FlowDefinitionForm) {
|
||||
return request<R>({
|
||||
url: '/workflow/definition',
|
||||
/**
|
||||
* 修改
|
||||
* @param data 参数
|
||||
* @returns
|
||||
*/
|
||||
export const edit = (data: FlowDefinitionForm) => {
|
||||
return request({
|
||||
url: `/workflow/definition`,
|
||||
method: 'put',
|
||||
data
|
||||
data: data
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function edit(data: FlowDefinitionForm) {
|
||||
return editDefinition(data);
|
||||
}
|
||||
|
||||
export function getDefinition(id: string) {
|
||||
return request<R<FlowDefinitionVO>>({
|
||||
/**
|
||||
* 查询详情
|
||||
* @param id 参数
|
||||
* @returns
|
||||
*/
|
||||
export const getInfo = (id: number | string) => {
|
||||
return request({
|
||||
url: `/workflow/definition/${id}`,
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function getInfo(id: string | number) {
|
||||
return getDefinition(String(id));
|
||||
}
|
||||
|
||||
export function copyDefinition(id: string) {
|
||||
return request<R>({
|
||||
/**
|
||||
* 复制流程定义
|
||||
* @param id 流程定义id
|
||||
* @returns
|
||||
*/
|
||||
export const copy = (id: string) => {
|
||||
return request({
|
||||
url: `/workflow/definition/copy/${id}`,
|
||||
method: 'post'
|
||||
});
|
||||
}
|
||||
|
||||
export function copy(id: string) {
|
||||
return copyDefinition(id);
|
||||
}
|
||||
|
||||
export function exportDefinition(id: string) {
|
||||
return request<Blob>({
|
||||
url: `/workflow/definition/exportDef/${id}`,
|
||||
method: 'post',
|
||||
responseType: 'blob'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@ -1,41 +1,32 @@
|
||||
import type { PageQuery } from '@/api/types';
|
||||
|
||||
export interface FlowDefinitionForm {
|
||||
id?: string;
|
||||
flowName?: string;
|
||||
flowCode?: string;
|
||||
category?: string | number;
|
||||
ext?: string;
|
||||
formPath?: string;
|
||||
formCustom?: string;
|
||||
modelValue?: string;
|
||||
}
|
||||
|
||||
export interface FlowDefinitionQuery extends PageQuery {
|
||||
flowCode?: string;
|
||||
flowName?: string;
|
||||
category?: string | number;
|
||||
category: string | number;
|
||||
isPublish?: number;
|
||||
}
|
||||
|
||||
export interface FlowDefinitionVO {
|
||||
export interface FlowDefinitionVo {
|
||||
id: string;
|
||||
flowName: string;
|
||||
flowCode: string;
|
||||
category?: string | number;
|
||||
categoryName?: string;
|
||||
formPath?: string;
|
||||
formCustom?: string;
|
||||
modelValue?: string;
|
||||
version?: string | number;
|
||||
isPublish?: number;
|
||||
activityStatus?: number;
|
||||
ext?: string;
|
||||
createTime?: string | Date;
|
||||
updateTime?: string | Date;
|
||||
formPath: string;
|
||||
version: string;
|
||||
isPublish: number;
|
||||
activityStatus: number;
|
||||
createTime: Date;
|
||||
updateTime: Date;
|
||||
}
|
||||
|
||||
export type FlowDefinitionVo = FlowDefinitionVO;
|
||||
export interface FlowDefinitionForm {
|
||||
id: string;
|
||||
flowName: string;
|
||||
flowCode: string;
|
||||
category: string;
|
||||
ext: string;
|
||||
formPath: string;
|
||||
formCustom: string;
|
||||
modelValue: string;
|
||||
}
|
||||
|
||||
export interface definitionXmlVO {
|
||||
xml: string[];
|
||||
|
||||
@ -1,84 +1,126 @@
|
||||
import type { PageResult, R } from '@/api/types';
|
||||
import request from '@/api/request';
|
||||
import type { FlowHisTaskResult, FlowInstanceQuery, FlowInstanceVO } from './types';
|
||||
import type { PageResult } from '@/api/types';
|
||||
import type { FlowInstanceQuery, FlowInstanceVO } from '@/api/workflow/instance/types';
|
||||
import type { AxiosPromise } from '@/utils/api-types';
|
||||
import request from '@/utils/request';
|
||||
|
||||
export function pageByRunning(query: FlowInstanceQuery) {
|
||||
return request<R<PageResult<FlowInstanceVO>>>({
|
||||
/**
|
||||
* 查询运行中实例列表
|
||||
* @param query
|
||||
* @returns {*}
|
||||
*/
|
||||
export const pageByRunning = (query: FlowInstanceQuery): AxiosPromise<PageResult<FlowInstanceVO>> => {
|
||||
return request({
|
||||
url: '/workflow/instance/pageByRunning',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function pageByFinish(query: FlowInstanceQuery) {
|
||||
return request<R<PageResult<FlowInstanceVO>>>({
|
||||
/**
|
||||
* 查询已完成实例列表
|
||||
* @param query
|
||||
* @returns {*}
|
||||
*/
|
||||
export const pageByFinish = (query: FlowInstanceQuery): AxiosPromise<PageResult<FlowInstanceVO>> => {
|
||||
return request({
|
||||
url: '/workflow/instance/pageByFinish',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function pageByCurrent(query: FlowInstanceQuery) {
|
||||
return request<R<PageResult<FlowInstanceVO>>>({
|
||||
/**
|
||||
* 通过业务id获取历史流程图
|
||||
*/
|
||||
export const flowHisTaskList = (businessId: string | number) => {
|
||||
return request({
|
||||
url: `/workflow/instance/flowHisTaskList/${businessId}?t=${Math.random()}`,
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 分页查询当前登录人单据
|
||||
* @param query
|
||||
* @returns {*}
|
||||
*/
|
||||
export const pageByCurrent = (query: FlowInstanceQuery): AxiosPromise<PageResult<FlowInstanceVO>> => {
|
||||
return request({
|
||||
url: '/workflow/instance/pageByCurrent',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function cancelProcessApply(data: {
|
||||
id?: string | number;
|
||||
businessId?: string | number;
|
||||
comment?: string;
|
||||
message?: string;
|
||||
}) {
|
||||
return request<R>({
|
||||
url: '/workflow/instance/cancelProcessApply',
|
||||
/**
|
||||
* 撤销流程
|
||||
* @param data 参数
|
||||
* @returns
|
||||
*/
|
||||
export const cancelProcessApply = (data: any) => {
|
||||
return request({
|
||||
url: `/workflow/instance/cancelProcessApply`,
|
||||
method: 'put',
|
||||
data
|
||||
data: data
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function instanceVariable(instanceId: string | number) {
|
||||
return request<R<{ variable: string }>>({
|
||||
/**
|
||||
* 获取流程变量
|
||||
* @param instanceId 实例id
|
||||
* @returns
|
||||
*/
|
||||
export const instanceVariable = (instanceId: string | number) => {
|
||||
return request({
|
||||
url: `/workflow/instance/instanceVariable/${instanceId}`,
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function deleteByInstanceIds(instanceIds: Array<string | number> | string | number) {
|
||||
return request<R>({
|
||||
/**
|
||||
* 删除
|
||||
* @param instanceIds 流程实例id
|
||||
* @returns
|
||||
*/
|
||||
export const deleteByInstanceIds = (instanceIds: Array<string | number> | string | number) => {
|
||||
return request({
|
||||
url: `/workflow/instance/deleteByInstanceIds/${instanceIds}`,
|
||||
method: 'delete'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function deleteHisByInstanceIds(instanceIds: Array<string | number> | string | number) {
|
||||
return request<R>({
|
||||
/**
|
||||
* 删除历史流程实例
|
||||
* @param instanceIds
|
||||
*/
|
||||
export const deleteHisByInstanceIds = (instanceIds: Array<string | number> | string | number) => {
|
||||
return request({
|
||||
url: `/workflow/instance/deleteHisByInstanceIds/${instanceIds}`,
|
||||
method: 'delete'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function invalid(data: { id: string | number; comment?: string }) {
|
||||
return request<R>({
|
||||
url: '/workflow/instance/invalid',
|
||||
/**
|
||||
* 作废流程
|
||||
* @param data 参数
|
||||
* @returns
|
||||
*/
|
||||
export const invalid = (data: any) => {
|
||||
return request({
|
||||
url: `/workflow/instance/invalid`,
|
||||
method: 'post',
|
||||
data
|
||||
data: data
|
||||
});
|
||||
}
|
||||
|
||||
export function updateVariable(data: { instanceId?: string | number; key?: string; value?: string }) {
|
||||
return request<R>({
|
||||
url: '/workflow/instance/updateVariable',
|
||||
};
|
||||
/**
|
||||
* 修改流程变量
|
||||
* @param data 参数
|
||||
* @returns
|
||||
*/
|
||||
export const updateVariable = (data: any) => {
|
||||
return request({
|
||||
url: `/workflow/instance/updateVariable`,
|
||||
method: 'put',
|
||||
data
|
||||
data: data
|
||||
});
|
||||
}
|
||||
|
||||
export function flowHisTaskList(businessId: string | number) {
|
||||
return request<R<FlowHisTaskResult>>({
|
||||
url: `/workflow/instance/flowHisTaskList/${businessId}?t=${Math.random()}`,
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@ -1,10 +1,4 @@
|
||||
import type { BaseEntity, PageQuery } from '@/api/types';
|
||||
import type { FlowHistoryVO, FlowTaskVO } from '@/api/workflow/task/types';
|
||||
|
||||
export interface FlowHisTaskResult {
|
||||
list: FlowHistoryVO[];
|
||||
instanceId?: string | number;
|
||||
}
|
||||
import type { FlowTaskVO } from '@/api/workflow/task/types';
|
||||
|
||||
export interface FlowInstanceQuery extends PageQuery {
|
||||
category?: string | number;
|
||||
@ -17,25 +11,18 @@ export interface FlowInstanceQuery extends PageQuery {
|
||||
|
||||
export interface FlowInstanceVO extends BaseEntity {
|
||||
id: string | number;
|
||||
definitionId?: string;
|
||||
flowName?: string;
|
||||
flowCode?: string;
|
||||
version?: string | number;
|
||||
definitionId: string;
|
||||
flowName: string;
|
||||
flowCode: string;
|
||||
version: string;
|
||||
businessId: string;
|
||||
activityStatus?: number;
|
||||
tenantId?: string;
|
||||
createTime?: string;
|
||||
updateTime?: string;
|
||||
createBy?: string;
|
||||
createByName?: string;
|
||||
flowStatus?: string;
|
||||
flowStatusName?: string;
|
||||
flowTaskList?: FlowTaskVO[];
|
||||
businessCode?: string;
|
||||
businessTitle?: string;
|
||||
nodeName?: string;
|
||||
categoryName?: string;
|
||||
isSuspended?: boolean;
|
||||
formCustom?: string;
|
||||
formPath?: string;
|
||||
activityStatus: number;
|
||||
tenantId: string;
|
||||
createTime: string;
|
||||
createBy: string;
|
||||
flowStatus: string;
|
||||
flowStatusName: string;
|
||||
flowTaskList: FlowTaskVO[];
|
||||
businessCode: string;
|
||||
businessTitle: string;
|
||||
}
|
||||
|
||||
@ -1,49 +1,76 @@
|
||||
import type { PageResult, R } from '@/api/types';
|
||||
import request from '@/api/request';
|
||||
import type { LeaveForm, LeaveQuery, LeaveVO } from './types';
|
||||
import type { PageResult } from '@/api/types';
|
||||
import type { LeaveForm, LeaveQuery, LeaveVO } from '@/api/workflow/leave/types';
|
||||
import type { AxiosPromise } from '@/utils/api-types';
|
||||
import request from '@/utils/request';
|
||||
|
||||
export function listLeave(query?: LeaveQuery) {
|
||||
return request<R<PageResult<LeaveVO>>>({
|
||||
/**
|
||||
* 查询请假列表
|
||||
* @param query
|
||||
* @returns {*}
|
||||
*/
|
||||
|
||||
export const listLeave = (query?: LeaveQuery): AxiosPromise<PageResult<LeaveVO>> => {
|
||||
return request({
|
||||
url: '/workflow/leave/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function getLeave(id: string | number) {
|
||||
return request<R<LeaveVO>>({
|
||||
url: `/workflow/leave/${id}`,
|
||||
/**
|
||||
* 查询请假详细
|
||||
* @param id
|
||||
*/
|
||||
export const getLeave = (id: string | number): AxiosPromise<LeaveVO> => {
|
||||
return request({
|
||||
url: '/workflow/leave/' + id,
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function addLeave(data: LeaveForm) {
|
||||
return request<R<LeaveVO>>({
|
||||
/**
|
||||
* 新增请假
|
||||
* @param data
|
||||
*/
|
||||
export const addLeave = (data: LeaveForm): AxiosPromise<LeaveVO> => {
|
||||
return request({
|
||||
url: '/workflow/leave',
|
||||
method: 'post',
|
||||
data
|
||||
data: data
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function submitAndFlowStart(data: LeaveForm) {
|
||||
return request<R<LeaveVO>>({
|
||||
/**
|
||||
* 提交请假并发起流程
|
||||
* @param data
|
||||
*/
|
||||
export const submitAndFlowStart = (data: LeaveForm): AxiosPromise<LeaveVO> => {
|
||||
return request({
|
||||
url: '/workflow/leave/submitAndFlowStart',
|
||||
method: 'post',
|
||||
data
|
||||
data: data
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function updateLeave(data: LeaveForm) {
|
||||
return request<R<LeaveVO>>({
|
||||
/**
|
||||
* 修改请假
|
||||
* @param data
|
||||
*/
|
||||
export const updateLeave = (data: LeaveForm): AxiosPromise<LeaveVO> => {
|
||||
return request({
|
||||
url: '/workflow/leave',
|
||||
method: 'put',
|
||||
data
|
||||
data: data
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function delLeave(id: string | number | Array<string | number>) {
|
||||
return request<R>({
|
||||
url: `/workflow/leave/${id}`,
|
||||
/**
|
||||
* 删除请假
|
||||
* @param id
|
||||
*/
|
||||
export const delLeave = (id: string | number | Array<string | number>) => {
|
||||
return request({
|
||||
url: '/workflow/leave/' + id,
|
||||
method: 'delete'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@ -1,4 +1,13 @@
|
||||
import type { BaseEntity, PageQuery } from '@/api/types';
|
||||
export interface LeaveVO {
|
||||
id: string | number;
|
||||
applyCode?: string;
|
||||
leaveType: string;
|
||||
startDate: string;
|
||||
endDate: string;
|
||||
leaveDays: number;
|
||||
remark: string;
|
||||
status?: string;
|
||||
}
|
||||
|
||||
export interface LeaveForm extends BaseEntity {
|
||||
id?: string | number;
|
||||
@ -15,14 +24,3 @@ export interface LeaveQuery extends PageQuery {
|
||||
startLeaveDays?: number;
|
||||
endLeaveDays?: number;
|
||||
}
|
||||
|
||||
export interface LeaveVO {
|
||||
id: string | number;
|
||||
applyCode?: string;
|
||||
leaveType?: string;
|
||||
startDate?: string;
|
||||
endDate?: string;
|
||||
leaveDays?: number;
|
||||
remark?: string;
|
||||
status?: string;
|
||||
}
|
||||
|
||||
@ -1,41 +1,64 @@
|
||||
import type { PageResult, R } from '@/api/types';
|
||||
import request from '@/api/request';
|
||||
import type { SpelForm, SpelQuery, SpelVO } from './types';
|
||||
import type { PageResult } from '@/api/types';
|
||||
import type { SpelForm, SpelQuery, SpelVO } from '@/api/workflow/spel/types';
|
||||
import type { AxiosPromise } from '@/utils/api-types';
|
||||
import request from '@/utils/request';
|
||||
|
||||
export function listSpel(query?: SpelQuery) {
|
||||
return request<R<PageResult<SpelVO>>>({
|
||||
/**
|
||||
* 查询流程spel表达式定义列表
|
||||
* @param query
|
||||
* @returns {*}
|
||||
*/
|
||||
|
||||
export const listSpel = (query?: SpelQuery): AxiosPromise<PageResult<SpelVO>> => {
|
||||
return request({
|
||||
url: '/workflow/spel/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function getSpel(id: string | number) {
|
||||
return request<R<SpelVO>>({
|
||||
url: `/workflow/spel/${id}`,
|
||||
/**
|
||||
* 查询流程spel表达式定义详细
|
||||
* @param id
|
||||
*/
|
||||
export const getSpel = (id: string | number): AxiosPromise<SpelVO> => {
|
||||
return request({
|
||||
url: '/workflow/spel/' + id,
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function addSpel(data: SpelForm) {
|
||||
return request<R>({
|
||||
/**
|
||||
* 新增流程spel表达式定义
|
||||
* @param data
|
||||
*/
|
||||
export const addSpel = (data: SpelForm) => {
|
||||
return request({
|
||||
url: '/workflow/spel',
|
||||
method: 'post',
|
||||
data
|
||||
data: data
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function updateSpel(data: SpelForm) {
|
||||
return request<R>({
|
||||
/**
|
||||
* 修改流程spel表达式定义
|
||||
* @param data
|
||||
*/
|
||||
export const updateSpel = (data: SpelForm) => {
|
||||
return request({
|
||||
url: '/workflow/spel',
|
||||
method: 'put',
|
||||
data
|
||||
data: data
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function delSpel(id: string | number | Array<string | number>) {
|
||||
return request<R>({
|
||||
url: `/workflow/spel/${id}`,
|
||||
/**
|
||||
* 删除流程spel表达式定义
|
||||
* @param id
|
||||
*/
|
||||
export const delSpel = (id: string | number | Array<string | number>) => {
|
||||
return request({
|
||||
url: '/workflow/spel/' + id,
|
||||
method: 'delete'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@ -1,29 +1,105 @@
|
||||
import type { BaseEntity, PageQuery } from '@/api/types';
|
||||
export interface SpelVO {
|
||||
/**
|
||||
* 主键id
|
||||
*/
|
||||
id: string | number;
|
||||
|
||||
/**
|
||||
* 组件名称
|
||||
*/
|
||||
componentName: string;
|
||||
|
||||
/**
|
||||
* 方法名
|
||||
*/
|
||||
methodName: string;
|
||||
|
||||
/**
|
||||
* 参数
|
||||
*/
|
||||
methodParams: string;
|
||||
|
||||
/**
|
||||
* 预览spel值
|
||||
*/
|
||||
viewSpel: string;
|
||||
|
||||
/**
|
||||
* 状态(0正常 1停用)
|
||||
*/
|
||||
status: string;
|
||||
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
remark?: string;
|
||||
}
|
||||
|
||||
export interface SpelForm extends BaseEntity {
|
||||
/**
|
||||
* 主键id
|
||||
*/
|
||||
id?: string | number;
|
||||
|
||||
/**
|
||||
* 组件名称
|
||||
*/
|
||||
componentName?: string;
|
||||
|
||||
/**
|
||||
* 方法名
|
||||
*/
|
||||
methodName?: string;
|
||||
|
||||
/**
|
||||
* 参数
|
||||
*/
|
||||
methodParams?: string;
|
||||
|
||||
/**
|
||||
* 预览spel值
|
||||
*/
|
||||
viewSpel?: string;
|
||||
|
||||
/**
|
||||
* 状态(0正常 1停用)
|
||||
*/
|
||||
status?: string;
|
||||
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
remark?: string;
|
||||
}
|
||||
|
||||
export interface SpelQuery extends PageQuery {
|
||||
/**
|
||||
* 组件名称
|
||||
*/
|
||||
componentName?: string;
|
||||
methodName?: string;
|
||||
methodParams?: string;
|
||||
viewSpel?: string;
|
||||
status?: string;
|
||||
}
|
||||
|
||||
export interface SpelVO {
|
||||
id: string | number;
|
||||
componentName?: string;
|
||||
/**
|
||||
* 方法名
|
||||
*/
|
||||
methodName?: string;
|
||||
|
||||
/**
|
||||
* 参数
|
||||
*/
|
||||
methodParams?: string;
|
||||
|
||||
/**
|
||||
* 预览spel值
|
||||
*/
|
||||
viewSpel?: string;
|
||||
|
||||
/**
|
||||
* 状态(0正常 1停用)
|
||||
*/
|
||||
status?: string;
|
||||
remark?: string;
|
||||
|
||||
/**
|
||||
* 日期范围参数
|
||||
*/
|
||||
params?: any;
|
||||
}
|
||||
|
||||
@ -1,129 +1,207 @@
|
||||
import type { PageResult, R } from '@/api/types';
|
||||
import type { StartProcessBo } from '@/api/workflow/workflowCommon/types';
|
||||
import request from '@/api/request';
|
||||
import type { FlowNextNodeVO, FlowTaskVO, StartWorkflowResult, TaskOperationBo, TaskQuery } from './types';
|
||||
import type { PageResult } from '@/api/types';
|
||||
import type { FlowTaskVO, TaskOperationBo, TaskQuery } from '@/api/workflow/task/types';
|
||||
import type { AxiosPromise } from '@/utils/api-types';
|
||||
import request from '@/utils/request';
|
||||
|
||||
export function pageByTaskWait(query: TaskQuery) {
|
||||
return request<R<PageResult<FlowTaskVO>>>({
|
||||
/**
|
||||
* 查询待办列表
|
||||
* @param query
|
||||
* @returns {*}
|
||||
*/
|
||||
export const pageByTaskWait = (query: TaskQuery): AxiosPromise<PageResult<FlowTaskVO>> => {
|
||||
return request({
|
||||
url: '/workflow/task/pageByTaskWait',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function pageByTaskFinish(query: TaskQuery) {
|
||||
return request<R<PageResult<FlowTaskVO>>>({
|
||||
/**
|
||||
* 查询已办列表
|
||||
* @param query
|
||||
* @returns {*}
|
||||
*/
|
||||
export const pageByTaskFinish = (query: TaskQuery): AxiosPromise<PageResult<FlowTaskVO>> => {
|
||||
return request({
|
||||
url: '/workflow/task/pageByTaskFinish',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function pageByTaskCopy(query: TaskQuery) {
|
||||
return request<R<PageResult<FlowTaskVO>>>({
|
||||
/**
|
||||
* 查询当前用户的抄送列表
|
||||
* @param query
|
||||
* @returns {*}
|
||||
*/
|
||||
export const pageByTaskCopy = (query: TaskQuery): AxiosPromise<PageResult<FlowTaskVO>> => {
|
||||
return request({
|
||||
url: '/workflow/task/pageByTaskCopy',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function pageByAllTaskWait(query: TaskQuery) {
|
||||
return request<R<PageResult<FlowTaskVO>>>({
|
||||
/**
|
||||
* 查询全部待办任务
|
||||
* @param query
|
||||
* @returns {*}
|
||||
*/
|
||||
export const pageByAllTaskWait = (query: TaskQuery): AxiosPromise<PageResult<FlowTaskVO>> => {
|
||||
return request({
|
||||
url: '/workflow/task/pageByAllTaskWait',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function pageByAllTaskFinish(query: TaskQuery) {
|
||||
return request<R<PageResult<FlowTaskVO>>>({
|
||||
/**
|
||||
* 查询全部已办任务
|
||||
* @param query
|
||||
* @returns {*}
|
||||
*/
|
||||
export const pageByAllTaskFinish = (query: TaskQuery): AxiosPromise<PageResult<FlowTaskVO>> => {
|
||||
return request({
|
||||
url: '/workflow/task/pageByAllTaskFinish',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function startWorkFlow(data: StartProcessBo) {
|
||||
return request<R<StartWorkflowResult>>({
|
||||
/**
|
||||
* 启动流程
|
||||
* @param data
|
||||
* @returns {*}
|
||||
*/
|
||||
export const startWorkFlow = (data: object): any => {
|
||||
return request({
|
||||
url: '/workflow/task/startWorkFlow',
|
||||
method: 'post',
|
||||
data
|
||||
data: data
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function updateAssignee(taskIdList: Array<string | number>, userId: string | number) {
|
||||
return request<R>({
|
||||
/**
|
||||
* 办理流程
|
||||
* @param data
|
||||
* @returns {*}
|
||||
*/
|
||||
export const completeTask = (data: object) => {
|
||||
return request({
|
||||
url: '/workflow/task/completeTask',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 任务驳回
|
||||
* @param data
|
||||
* @returns {*}
|
||||
*/
|
||||
export const backProcess = (data: any): any => {
|
||||
return request({
|
||||
url: '/workflow/task/backProcess',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取当前任务
|
||||
* @param taskId
|
||||
* @returns
|
||||
*/
|
||||
export const getTask = (taskId: string) => {
|
||||
return request({
|
||||
url: '/workflow/task/getTask/' + taskId,
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 修改任务办理人
|
||||
* @param taskIdList
|
||||
* @param userId
|
||||
* @returns
|
||||
*/
|
||||
export const updateAssignee = (taskIdList: Array<string>, userId: string) => {
|
||||
return request({
|
||||
url: `/workflow/task/updateAssignee/${userId}`,
|
||||
method: 'put',
|
||||
data: taskIdList
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function urgeTask(data: { taskIdList: Array<string | number>; message?: string; messageType?: string[] }) {
|
||||
return request<R>({
|
||||
url: '/workflow/task/urgeTask',
|
||||
/**
|
||||
* 终止任务
|
||||
* @returns
|
||||
*/
|
||||
export const terminationTask = (data: any) => {
|
||||
return request({
|
||||
url: `/workflow/task/terminationTask`,
|
||||
method: 'post',
|
||||
data
|
||||
data: data
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function completeTask(data: Record<string, unknown>) {
|
||||
return request<R>({
|
||||
url: '/workflow/task/completeTask',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
export function backProcess(data: Record<string, unknown>) {
|
||||
return request<R>({
|
||||
url: '/workflow/task/backProcess',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
export function getTask(taskId: string | number) {
|
||||
return request<R<FlowTaskVO>>({
|
||||
url: `/workflow/task/getTask/${taskId}`,
|
||||
/**
|
||||
* 获取可驳回得任务节点
|
||||
* @returns
|
||||
*/
|
||||
export const getBackTaskNode = (taskId: string | number, nodeCode: string) => {
|
||||
return request({
|
||||
url: `/workflow/task/getBackTaskNode/${taskId}/${nodeCode}`,
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function terminationTask(data: { taskId?: string | number; comment?: string }) {
|
||||
return request<R>({
|
||||
url: '/workflow/task/terminationTask',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
export function getBackTaskNode(taskId: string | number, nodeCode?: string) {
|
||||
return request<R<FlowNextNodeVO[]>>({
|
||||
url: `/workflow/task/getBackTaskNode/${taskId}/${nodeCode || ''}`,
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
|
||||
export function taskOperation(data: TaskOperationBo, operation: string) {
|
||||
return request<R>({
|
||||
/**
|
||||
* 任务操作 操作类型,委派 delegateTask、转办 transferTask、加签 addSignature、减签 reductionSignature
|
||||
* @returns
|
||||
*/
|
||||
export const taskOperation = (data: TaskOperationBo, operation: string) => {
|
||||
return request({
|
||||
url: `/workflow/task/taskOperation/${operation}`,
|
||||
method: 'post',
|
||||
data
|
||||
data: data
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function currentTaskAllUser(taskId: string | number) {
|
||||
return request<R<Array<{ userId: string | number; nickName: string; nodeName?: string }>>>({
|
||||
/**
|
||||
* 获取当前任务办理人
|
||||
* @param taskId 任务id
|
||||
* @returns
|
||||
*/
|
||||
export const currentTaskAllUser = (taskId: string | number) => {
|
||||
return request({
|
||||
url: `/workflow/task/currentTaskAllUser/${taskId}`,
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export function getNextNodeList(data: { taskId?: string | number; variables?: Record<string, unknown> }) {
|
||||
return request<R<FlowNextNodeVO[]>>({
|
||||
/**
|
||||
* 获取下一节点写
|
||||
* @param data参数
|
||||
* @returns
|
||||
*/
|
||||
export const getNextNodeList = (data: any): any => {
|
||||
return request({
|
||||
url: '/workflow/task/getNextNodeList',
|
||||
method: 'post',
|
||||
data
|
||||
data: data
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 催办任务
|
||||
* @param data参数
|
||||
* @returns
|
||||
*/
|
||||
export const urgeTask = (data: any): any => {
|
||||
return request({
|
||||
url: '/workflow/task/urgeTask',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
};
|
||||
|
||||
@ -1,82 +1,60 @@
|
||||
import type { OssVO } from '@/api/system/oss/types';
|
||||
import type { PageQuery } from '@/api/types';
|
||||
|
||||
export interface ButtonList {
|
||||
code: string;
|
||||
show: boolean;
|
||||
}
|
||||
|
||||
export interface FlowCopyVo {
|
||||
userId: string | number;
|
||||
nickName: string;
|
||||
}
|
||||
|
||||
export interface FlowHistoryVO {
|
||||
nodeName?: string;
|
||||
approverName?: string;
|
||||
flowStatus?: string;
|
||||
message?: string;
|
||||
createTime?: string;
|
||||
updateTime?: string;
|
||||
runDuration?: string;
|
||||
ext?: string | number;
|
||||
attachmentList?: OssVO[];
|
||||
}
|
||||
|
||||
export interface FlowNextNodeVO {
|
||||
nodeCode: string;
|
||||
nodeName: string;
|
||||
permissionFlag?: string | Array<string | number>;
|
||||
}
|
||||
|
||||
export interface FlowTaskVO {
|
||||
id: string | number;
|
||||
createTime?: string | Date;
|
||||
updateTime?: string | Date;
|
||||
tenantId?: string;
|
||||
definitionId?: string;
|
||||
instanceId?: string;
|
||||
flowName?: string;
|
||||
businessId: string;
|
||||
nodeCode?: string;
|
||||
nodeName?: string;
|
||||
flowCode?: string;
|
||||
flowStatus?: string;
|
||||
flowStatusName?: string;
|
||||
flowTaskStatus?: string;
|
||||
formCustom?: string;
|
||||
formPath?: string;
|
||||
nodeType?: number;
|
||||
nodeRatio?: string | number;
|
||||
version?: string;
|
||||
applyNode?: boolean;
|
||||
buttonList?: ButtonList[];
|
||||
copyList?: FlowCopyVo[];
|
||||
varList?: Record<string, string>;
|
||||
businessCode?: string;
|
||||
businessTitle?: string;
|
||||
categoryName?: string;
|
||||
createByName?: string;
|
||||
assigneeNames?: string;
|
||||
approverName?: string;
|
||||
}
|
||||
|
||||
export interface StartWorkflowResult {
|
||||
taskId?: string | number;
|
||||
}
|
||||
|
||||
export interface TaskOperationBo {
|
||||
taskId?: string | number;
|
||||
userId?: string | number;
|
||||
userIds?: Array<string | number>;
|
||||
message?: string;
|
||||
messageType?: string[];
|
||||
variables?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
export interface TaskQuery extends PageQuery {
|
||||
nodeName?: string;
|
||||
flowCode?: string;
|
||||
flowName?: string;
|
||||
createByIds?: Array<string | number>;
|
||||
}
|
||||
|
||||
export interface ParticipantVo {
|
||||
groupIds?: string[] | number[];
|
||||
candidate: string[] | number[];
|
||||
candidateName: string[];
|
||||
claim: boolean;
|
||||
}
|
||||
export interface FlowTaskVO {
|
||||
id: string | number;
|
||||
createTime?: Date;
|
||||
updateTime?: Date;
|
||||
tenantId?: string;
|
||||
definitionId?: string;
|
||||
instanceId: string;
|
||||
flowName: string;
|
||||
businessId: string;
|
||||
nodeCode: string;
|
||||
nodeName: string;
|
||||
flowCode: string;
|
||||
flowStatus: string;
|
||||
formCustom: string;
|
||||
formPath: string;
|
||||
nodeType: number;
|
||||
nodeRatio: string | number;
|
||||
version?: string;
|
||||
applyNode?: boolean;
|
||||
buttonList?: ButtonList[];
|
||||
copyList?: FlowCopyVo[];
|
||||
varList?: Map<string, string>;
|
||||
businessCode: string;
|
||||
businessTitle: string;
|
||||
}
|
||||
|
||||
export interface ButtonList {
|
||||
code: string;
|
||||
show: boolean;
|
||||
}
|
||||
export interface FlowCopyVo {
|
||||
userId: string | number;
|
||||
nickName: string;
|
||||
}
|
||||
|
||||
export interface TaskOperationBo {
|
||||
//委派/转办人的用户ID(必填,准对委派/转办人操作)
|
||||
userId?: string;
|
||||
//加签/减签人的用户ID列表(必填,针对加签/减签操作)
|
||||
userIds?: string[];
|
||||
//任务ID(必填)
|
||||
taskId: string | number;
|
||||
//消息类型
|
||||
messageType?: string[];
|
||||
//意见或备注信息(可选)
|
||||
message?: string;
|
||||
}
|
||||
|
||||
@ -1,17 +1,17 @@
|
||||
import { history } from '@umijs/max';
|
||||
import { normalizePath } from '@/utils/ruoyi';
|
||||
import type { RouterJumpVO } from './types';
|
||||
|
||||
export function routerJump(routerJump: RouterJumpVO) {
|
||||
history.push(
|
||||
`${normalizePath(routerJump.formPath || '/workflow/leaveEdit/index')}?${new URLSearchParams({
|
||||
id: routerJump.businessId,
|
||||
type: routerJump.type,
|
||||
taskId: String(routerJump.taskId)
|
||||
}).toString()}`
|
||||
);
|
||||
}
|
||||
import type { RouterJumpVo } from '@/api/workflow/workflowCommon/types';
|
||||
import tab from '@/plugins/tab';
|
||||
import router from '@/router';
|
||||
|
||||
export default {
|
||||
routerJump
|
||||
routerJump(routerJumpVo: RouterJumpVo) {
|
||||
tab.closePage(router.currentRoute.value);
|
||||
router.push({
|
||||
path: routerJumpVo.formPath,
|
||||
query: {
|
||||
id: routerJumpVo.businessId,
|
||||
type: routerJumpVo.type,
|
||||
taskId: routerJumpVo.taskId
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
export interface RouterJumpVO {
|
||||
export interface RouterJumpVo {
|
||||
businessId: string;
|
||||
taskId: string | number;
|
||||
type: string;
|
||||
@ -9,6 +9,6 @@ export interface RouterJumpVO {
|
||||
export interface StartProcessBo {
|
||||
businessId: string | number;
|
||||
flowCode: string;
|
||||
variables: Record<string, unknown>;
|
||||
bizExt: Record<string, unknown>;
|
||||
variables: any;
|
||||
bizExt: any;
|
||||
}
|
||||
|
||||
14
src/app.tsx
14
src/app.tsx
@ -1,14 +0,0 @@
|
||||
import type { ReactNode } from 'react';
|
||||
import { QueryClientProvider } from '@tanstack/react-query';
|
||||
import { appEnv } from '@/utils/env';
|
||||
import { queryClient } from '@/utils/queryClient';
|
||||
|
||||
export async function getInitialState() {
|
||||
return {
|
||||
name: appEnv.logoTitle
|
||||
};
|
||||
}
|
||||
|
||||
export function rootContainer(container: ReactNode) {
|
||||
return <QueryClientProvider client={queryClient}>{container}</QueryClientProvider>;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user