mirror of
https://gitee.com/JavaLionLi/plus-ui.git
synced 2026-06-18 12:28:01 +00:00
Compare commits
117 Commits
v5.6.2-v2.
...
future/6.X
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8716e1285d | ||
|
|
6030831f55 | ||
|
|
d3027b6927 | ||
|
|
612b54fabf | ||
|
|
3b876bdbc1 | ||
|
|
382c37e896 | ||
|
|
84cbada026 | ||
|
|
979bad0275 | ||
|
|
46fbc9db0b | ||
|
|
3dcddd0f2c | ||
|
|
d74c18e58e | ||
|
|
62694b1554 | ||
|
|
18b3e6af87 | ||
|
|
fb889c02db | ||
|
|
ee613d59c4 | ||
|
|
441fb733d8 | ||
|
|
6b039422b3 | ||
|
|
c004cbc86c | ||
|
|
77241676a4 | ||
|
|
f242ce050d | ||
|
|
97206612b7 | ||
|
|
ae2935a081 | ||
|
|
fcd1d6ae75 | ||
|
|
779b42c653 | ||
|
|
f2c09f33c9 | ||
|
|
5f0e2484b0 | ||
|
|
b5b248f547 | ||
|
|
2d41047330 | ||
|
|
04ed7bf7a7 | ||
|
|
a9b2d7745d | ||
|
|
5ebb9060ea | ||
|
|
bfd6903d25 | ||
|
|
4311a09056 | ||
|
|
44c1c6a91c | ||
|
|
145112dffa | ||
|
|
de67f38988 | ||
|
|
97e984afa5 | ||
|
|
6c1dae5b6a | ||
|
|
87fad7bc16 | ||
|
|
c7bcf52f03 | ||
|
|
6a3394bdc5 | ||
|
|
2ce3786ee0 | ||
|
|
2d13215304 | ||
|
|
f269c003da | ||
|
|
7aaa40b46c | ||
|
|
ffcb15ba96 | ||
|
|
0a5c72988a | ||
|
|
8a900fa94d | ||
|
|
f10e6dbc71 | ||
|
|
090e395dcf | ||
|
|
eab7f49553 | ||
|
|
cedf975ed3 | ||
|
|
d38329befb | ||
|
|
9acf52e8a7 | ||
|
|
eb62402423 | ||
|
|
8c730950a9 | ||
|
|
1133665122 | ||
|
|
4f713d66db | ||
|
|
02b5ca7c48 | ||
|
|
4f8bcbd525 | ||
|
|
fb5b1ed1bd | ||
|
|
a2d9a6d5f2 | ||
|
|
90926ddc7c | ||
|
|
b64357a595 | ||
|
|
81552f0cb9 | ||
|
|
029eb6636c | ||
|
|
38c67fba25 | ||
|
|
3f30bac6f3 | ||
|
|
0b65932eee | ||
|
|
16d9e1e7fe | ||
|
|
199771997d | ||
|
|
6c395bb65b | ||
|
|
57f18eece5 | ||
|
|
cac35ecf4c | ||
|
|
f38e5e7c4d | ||
|
|
125da80953 | ||
|
|
7f99bbf889 | ||
|
|
87c09d0917 | ||
|
|
87b56a3823 | ||
|
|
680c00bd06 | ||
|
|
ebc7760de2 | ||
|
|
5907e4778b | ||
|
|
b9e5220069 | ||
|
|
1daa291bb6 | ||
|
|
5be8fcf571 | ||
|
|
42f63fbe24 | ||
|
|
05527965e2 | ||
|
|
8469c254af | ||
|
|
2502de3081 | ||
|
|
fed0ac99e8 | ||
|
|
9f577be6ff | ||
|
|
1fb116f059 | ||
|
|
448f5f303e | ||
|
|
da5f369bb4 | ||
|
|
13a41679bc | ||
|
|
1442d539e8 | ||
|
|
a86557cc2a | ||
|
|
4fb60440f2 | ||
|
|
6ed02a2ddd | ||
|
|
3194ea4fe7 | ||
|
|
8286b5906e | ||
|
|
c69a6a2a45 | ||
|
|
f2de9af401 | ||
|
|
790abfac85 | ||
|
|
e138c314a8 | ||
|
|
b9ca2d3c67 | ||
|
|
c415e130b9 | ||
|
|
decbd4ac34 | ||
|
|
b8b0db2367 | ||
|
|
82d28a3188 | ||
|
|
8eac8987b1 | ||
|
|
d97c93cc98 | ||
|
|
766d9b2142 | ||
|
|
833dfa67fc | ||
|
|
b83755e626 | ||
|
|
e3227f5cc5 | ||
|
|
0dc58fa3c6 |
28
.claude/agents/frontend-api-types.md
Normal file
28
.claude/agents/frontend-api-types.md
Normal file
@@ -0,0 +1,28 @@
|
||||
---
|
||||
name: frontend-api-types
|
||||
description: 前端 API 与类型定义专家。用于当前项目中的 src/api 层、types.ts、返回结构、Query/Form/VO/InfoVO 定义,以及前后端接口映射任务。
|
||||
---
|
||||
|
||||
你负责当前前端项目中的 API 层和类型定义。
|
||||
|
||||
## 核心原则
|
||||
|
||||
1. 先看当前模块已有 `src/api/<module>/<business>`。
|
||||
2. API 路径、返回类型、命名风格与当前模块保持一致。
|
||||
3. 能明确写出类型时,不要偷懒用 `any`。
|
||||
4. 如果当前模块已有 `export default { ... }`,继续保持一致。
|
||||
|
||||
## 重点关注
|
||||
|
||||
- `Query`
|
||||
- `VO`
|
||||
- `Form`
|
||||
- `InfoVO`
|
||||
- `AxiosPromise<PageResult<T>>`
|
||||
- 详情接口与列表接口返回结构
|
||||
|
||||
## 自检
|
||||
|
||||
- API 路径是否与后端一致
|
||||
- 类型是否覆盖接口真实结构
|
||||
- 是否不必要地把类型写宽了
|
||||
18
.claude/agents/frontend-crud-coding.md
Normal file
18
.claude/agents/frontend-crud-coding.md
Normal file
@@ -0,0 +1,18 @@
|
||||
---
|
||||
name: frontend-crud-coding
|
||||
description: 前端总入口。用于当前前端项目中的标准 CRUD 页面、新增 API/types、复杂列表页增强、树筛选、导入导出、权限按钮与弹窗表单等任务,并根据任务类型选择合适的前端子 agent。
|
||||
---
|
||||
|
||||
你是当前前端项目的总入口 agent。
|
||||
|
||||
先判断任务类型,再按下面规则处理:
|
||||
|
||||
1. 如果是新增标准 CRUD 页面、补 `src/api`、`types.ts`、`index.vue`,优先使用 `frontend-crud-page.md`。
|
||||
2. 如果是修改已有列表页、增强导入导出、树筛选、更多菜单、状态切换,优先使用 `frontend-page-enhancement.md`。
|
||||
3. 如果只改接口层和类型定义,优先使用 `frontend-api-types.md`。
|
||||
|
||||
通用要求:
|
||||
|
||||
- 先读当前目录下最近似页面和 API,再动代码。
|
||||
- 冲突时优先相信当前项目真实页面,其次是公共组件和工具,再其次才是关联后端工程的 generator 模板。
|
||||
- 默认直接产出可落地代码,而不是只给抽象建议。
|
||||
37
.claude/agents/frontend-crud-page.md
Normal file
37
.claude/agents/frontend-crud-page.md
Normal file
@@ -0,0 +1,37 @@
|
||||
---
|
||||
name: frontend-crud-page
|
||||
description: 前端标准 CRUD 页面专家。用于当前项目中的新建列表页、弹窗表单页、标准 API/types/index.vue 骨架,以及 gen 模板到项目风格的落地任务。
|
||||
---
|
||||
|
||||
你负责当前前端项目中的标准 CRUD 页面实现。
|
||||
|
||||
## 核心原则
|
||||
|
||||
1. 先看当前模块最近似页面。
|
||||
2. 再参考关联后端工程中的 generator 模板。
|
||||
3. 默认同时维护:
|
||||
`src/api/<module>/<business>/index.ts`
|
||||
`src/api/<module>/<business>/types.ts`
|
||||
`src/views/<module>/<business>/index.vue`
|
||||
|
||||
## 页面规则
|
||||
|
||||
- 页面优先使用 `<script setup name="Xxx" lang="ts">`
|
||||
- 标准结构通常包含:
|
||||
搜索区、表格区、工具栏、分页、编辑弹窗
|
||||
- 常见状态:
|
||||
`loading`、`showSearch`、`ids`、`single`、`multiple`、`total`
|
||||
- 查询与表单优先使用 `reactive<PageData<Form, Query>>({...})`
|
||||
|
||||
## API / types 规则
|
||||
|
||||
- 请求统一通过 `@/utils/request`
|
||||
- 同目录维护 `index.ts` 与 `types.ts`
|
||||
- 标准 CRUD 通常包含:列表、详情、新增、修改、删除
|
||||
- 列表接口通常返回 `AxiosPromise<PageResult<XxxVO>>`
|
||||
|
||||
## 自检
|
||||
|
||||
- API 路径是否与后端一致
|
||||
- `index.ts` 与 `types.ts` 是否同步补齐
|
||||
- 页面是否只是模板裸输出,如果是要继续补强到当前项目风格
|
||||
27
.claude/agents/frontend-page-enhancement.md
Normal file
27
.claude/agents/frontend-page-enhancement.md
Normal file
@@ -0,0 +1,27 @@
|
||||
---
|
||||
name: frontend-page-enhancement
|
||||
description: 复杂前端页面增强专家。用于修改当前项目中已经存在的列表页、树筛选页、带导入导出和更多菜单的页面,强调增量修改和保留现有交互能力。
|
||||
---
|
||||
|
||||
你负责当前前端项目中已有页面的增强,不是重写页面。
|
||||
|
||||
## 核心原则
|
||||
|
||||
1. 优先阅读当前页面完整实现。
|
||||
2. 增量修改,不重写整页。
|
||||
3. 保留已有树筛选、导入导出、列显隐、更多菜单、状态切换、路由跳转、SCSS 页面壳。
|
||||
4. 不要把复杂页面退化成 generator 式基础列表页。
|
||||
|
||||
## 常见任务
|
||||
|
||||
- 调整工具栏和更多菜单
|
||||
- 增加筛选条件和日期范围
|
||||
- 增加导入导出能力
|
||||
- 增加状态切换、快捷操作、确认弹窗
|
||||
- 补复杂页面的小型子功能
|
||||
|
||||
## 自检
|
||||
|
||||
- 是否破坏了原页面结构和样式
|
||||
- 是否误删了已有权限控制或交互能力
|
||||
- 是否应该拆成子组件而不是继续堆主页面
|
||||
143
.codex/skills/frontend-crud-coding/SKILL.md
Normal file
143
.codex/skills/frontend-crud-coding/SKILL.md
Normal file
@@ -0,0 +1,143 @@
|
||||
---
|
||||
name: frontend-crud-coding
|
||||
description: 在当前 plus-ui-new 前端项目中按真实 Vue 3 + TypeScript + Element Plus + oxlint/oxfmt 代码风格生成或修改页面、API、types、hooks 接入和样式壳。用于新增或修改标准 CRUD 列表页、树表页、系统管理页、监控页、workflow 页面、demo 页面,补齐与后端接口对应的 src/api、types 和 src/views 代码;触发后应先读取适用 references,再阅读目标模块真实代码和项目内 gen 代码生成模板。
|
||||
---
|
||||
|
||||
# 前端编码规范
|
||||
|
||||
先对齐当前前端项目里的真实实现,再参考项目内 `gen` 目录下的代码生成模板。不要只套通用 Vue 模板,也不要把 generator 模板原样复制进来而忽略当前项目已经演进出的 hooks、页面壳、类型入口和下载方式。
|
||||
|
||||
## 执行流程
|
||||
|
||||
1. 判断任务类型:新增标准 CRUD、树表、已有页面增强、复杂业务页、只补 API/types。
|
||||
2. 按“文档读取规则”读取必要 reference,不一次性展开所有资料。
|
||||
3. 阅读目标目录下最近似的真实代码:
|
||||
- 标准单表优先看 `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)。
|
||||
- 不确定任务边界、需要标准用例或提问方式时,再读 [references/examples.md](references/examples.md)。
|
||||
- reference 只约束实现方式和自检范围;发生冲突时,以当前模块真实代码和实际调用点为准。
|
||||
|
||||
## 优先级规则
|
||||
|
||||
发生冲突时按下面顺序决策:
|
||||
|
||||
1. 目标目录下最近似页面、API、types 的真实实现。
|
||||
2. 当前项目公共 hooks、组件、工具和样式约定。
|
||||
3. 项目内 `gen` 代码生成模板。
|
||||
4. 通用 Vue 3 / Element Plus 习惯。
|
||||
|
||||
也就是说:
|
||||
|
||||
- 同模块已有页面怎么写,优先怎么写。
|
||||
- 没有现成页面时,使用项目内 `gen` 模板作为骨架,再改成当前项目风格。
|
||||
- 复杂模块不能为了“标准 CRUD”退化成裸模板页。
|
||||
|
||||
## 仓库通用规则
|
||||
|
||||
- 遵循 [`.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/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/views/<module>/<business>/index.vue`
|
||||
|
||||
按业务复杂度,可能继续补:
|
||||
|
||||
- 导入弹窗
|
||||
- 详情抽屉或详情页
|
||||
- 树筛选面板
|
||||
- 列显隐配置
|
||||
- 分配/授权子页面
|
||||
- 自定义 SCSS 样式
|
||||
|
||||
## 任务分型
|
||||
|
||||
### 1. 标准单表 CRUD
|
||||
|
||||
以 `gen/index.vue.ftl`、`gen/api.ts.ftl`、`gen/types.ts.ftl` 和 `src/views/demo/demo/index.vue` 为主要起点,补齐列表、搜索、分页、新增、编辑、删除、导出、权限、类型和验证。
|
||||
|
||||
### 2. 树表 CRUD
|
||||
|
||||
以 `src/views/demo/tree/index.vue`、`src/views/workflow/category/index.vue` 为主要起点。列表接口通常返回数组而不是 `PageResult`,页面使用 `handleTree`、`useTreeTableExpand`,`Query` 通常不继承 `PageQuery`。
|
||||
|
||||
### 3. 强业务页面
|
||||
|
||||
如果页面包含树筛选、导入导出、更多菜单、状态切换、角色分配、详情抽屉、复杂校验、联动选择或独立路由,优先增量修改现有页面。不要重写成简单 CRUD。
|
||||
|
||||
### 4. 工作流页面
|
||||
|
||||
workflow 目录优先参考 `src/views/workflow/*`。流程定义、流程实例、任务列表、请假申请等页面通常有业务按钮、弹窗和路由跳转,不要硬套 system 模块。
|
||||
|
||||
### 5. 只补 API 和 types
|
||||
|
||||
只维护 `src/api/<module>/<business>/index.ts` 与 `types.ts`,但仍要与后端路由、返回结构、当前模块导入方式和类型入口一致。
|
||||
|
||||
## 输出要求
|
||||
|
||||
使用本 skill 时,默认期望产出应满足:
|
||||
|
||||
- 类型完整,不把页面逻辑大量写成 `any`。
|
||||
- API 路径、函数名、权限标识与后端接口保持一致。
|
||||
- 标准页查询、重置、分页、弹窗、提交、删除、导出流程闭环完整。
|
||||
- 复杂页面保留原有交互能力和业务约束。
|
||||
- 代码体现当前项目 hooks、页面壳和下载方式,而不是 `gen` 模板裸输出。
|
||||
- 交付前说明运行过的验证命令;如果无法验证,说明原因。
|
||||
|
||||
## 快速检查清单
|
||||
|
||||
- `AxiosPromise` 是否来自 `@/utils/api-types`。
|
||||
- `PageResult` 是否来自 `@/api/types`。
|
||||
- API `params` 和 `data` 是否与后端方法一致。
|
||||
- 日期范围是否通过 `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`。
|
||||
|
||||
## 推荐提问方式
|
||||
|
||||
推荐把请求描述到下面粒度:
|
||||
|
||||
- 目标模块和业务名
|
||||
- 后端接口前缀
|
||||
- 是新增页面、修改页面,还是只补 API/types
|
||||
- 是否需要导入、导出、树筛选、树表、状态切换、字典、权限按钮
|
||||
- 希望参考哪个现有页面
|
||||
|
||||
例如:
|
||||
|
||||
- 使用 `$frontend-crud-coding` 为 `/system/client` 补一套标准 CRUD 页面,参考 `gen` 模板、`demo/demo` 和 `system/client`。
|
||||
- 使用 `$frontend-crud-coding` 修改 `workflow/category` 列表页,增加导出按钮和状态筛选,保持当前 workflow 风格。
|
||||
7
.codex/skills/frontend-crud-coding/agents/openai.yaml
Normal file
7
.codex/skills/frontend-crud-coding/agents/openai.yaml
Normal file
@@ -0,0 +1,7 @@
|
||||
interface:
|
||||
display_name: "前端编码"
|
||||
short_description: "按 plus-ui-new 真实代码和 gen 模板编写前端 CRUD"
|
||||
default_prompt: "使用 $frontend-crud-coding 先读取适用 reference,再按当前 plus-ui-new 真实页面、hooks、API/types 约定和项目内 gen 模板实现前端修改。"
|
||||
|
||||
policy:
|
||||
allow_implicit_invocation: true
|
||||
126
.codex/skills/frontend-crud-coding/references/examples.md
Normal file
126
.codex/skills/frontend-crud-coding/references/examples.md
Normal file
@@ -0,0 +1,126 @@
|
||||
# 使用案例
|
||||
|
||||
## 案例 1:新增标准 CRUD 页面
|
||||
|
||||
### 用户提问示例
|
||||
|
||||
```text
|
||||
使用 $frontend-crud-coding 为 system/client 补一套前端 CRUD 页面。
|
||||
后端接口已经有 /system/client/list、/system/client/{id}、POST /system/client、PUT /system/client、DELETE /system/client/{ids}。
|
||||
请参考项目内 gen 模板、src/views/demo/demo/index.vue 和现有 system/client 风格实现。
|
||||
```
|
||||
|
||||
### 期望执行方式
|
||||
|
||||
- 先看 `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:新增树表页面
|
||||
|
||||
### 用户提问示例
|
||||
|
||||
```text
|
||||
使用 $frontend-crud-coding 为 demo/tree2 新增树表 CRUD,接口返回数组,字段包含 id、parentId、name、orderNum。
|
||||
参考 src/views/demo/tree/index.vue 和 workflow/category。
|
||||
```
|
||||
|
||||
### 期望执行方式
|
||||
|
||||
- 优先判断这是树表,不生成分页 `PageResult` 页面。
|
||||
- API 列表返回 `AxiosPromise<Tree2VO[]>`。
|
||||
- `Query` 不继承 `PageQuery`。
|
||||
- 页面使用 `handleTree`、`row-key`、`tree-props`、`useTreeTableExpand`、`el-tree-select`。
|
||||
- 新增子节点时从当前行带入 `parentId`。
|
||||
|
||||
## 案例 3:修改已有复杂列表页
|
||||
|
||||
### 用户提问示例
|
||||
|
||||
```text
|
||||
使用 $frontend-crud-coding 修改 system/user 页面:
|
||||
1. 新增一个创建时间快捷筛选
|
||||
2. 导出按钮保留在更多菜单中
|
||||
3. 保持现有树筛选、导入、列显隐、详情抽屉和角色分配不变
|
||||
```
|
||||
|
||||
### 期望执行方式
|
||||
|
||||
- 判断这是“已有复杂页面增强”,不是重新生成 CRUD。
|
||||
- 优先阅读 `src/views/system/user/index.vue`。
|
||||
- 保留 `TreePanel`、导入弹窗、`right-toolbar` 列显隐、`UserViewDrawer`、角色分配路由、权限控制。
|
||||
- 只增量修改搜索和查询参数处理。
|
||||
|
||||
## 案例 4:修改 workflow 页面
|
||||
|
||||
### 用户提问示例
|
||||
|
||||
```text
|
||||
使用 $frontend-crud-coding 为 workflow/category 增加状态筛选和导出按钮,保持 workflow 模块自己的树表风格。
|
||||
```
|
||||
|
||||
### 期望执行方式
|
||||
|
||||
- 优先看 `src/views/workflow/category/index.vue` 和 `src/api/workflow/category/*`。
|
||||
- 判断是否需要后端新增导出接口;前端导出路径保持 `workflow/category/export`。
|
||||
- 不迁移 system/user 的用户专属逻辑。
|
||||
- 保留树表、`useTreeTableExpand`、`handleTree` 和分类弹窗逻辑。
|
||||
|
||||
## 案例 5:只补 API 和 types
|
||||
|
||||
### 用户提问示例
|
||||
|
||||
```text
|
||||
使用 $frontend-crud-coding 为 monitor/cache 补全前端 API 和 types,页面先不改。
|
||||
```
|
||||
|
||||
### 期望执行方式
|
||||
|
||||
- 只维护 `src/api/monitor/cache/index.ts` 和 `src/api/monitor/cache/types.ts`。
|
||||
- 仍然检查同目录 monitor API 的 `export function` / `export const` 风格。
|
||||
- 返回类型使用 `AxiosPromise` from `@/utils/api-types`。
|
||||
- 不创建页面,不改路由。
|
||||
|
||||
## 案例 6:接入后端新增状态切换接口
|
||||
|
||||
### 用户提问示例
|
||||
|
||||
```text
|
||||
使用 $frontend-crud-coding 给 system/client 页面接入 PUT /system/client/changeStatus,状态字段 status,参考 gen 模板。
|
||||
```
|
||||
|
||||
### 期望执行方式
|
||||
|
||||
- API 增加 `changeClientStatus(id, status)`。
|
||||
- types 确认 `status` 类型是 string、number 还是 boolean。
|
||||
- 表格列用 `el-switch`,active/inactive 值跟后端字段类型一致。
|
||||
- 切换失败时回滚原状态。
|
||||
- 权限使用 `system:client:edit` 或后端实际权限。
|
||||
|
||||
## 推荐的高质量任务描述
|
||||
|
||||
```text
|
||||
使用 $frontend-crud-coding 在当前前端项目中新增 `/system/notice` 列表页增强:
|
||||
1. 保留现有页面
|
||||
2. 新增状态筛选和导出
|
||||
3. API 路径沿用后端接口
|
||||
4. 参考 system/config 的工具栏与导出交互
|
||||
5. 参考 gen 模板补齐缺失 types
|
||||
```
|
||||
|
||||
## 不推荐的任务描述
|
||||
|
||||
```text
|
||||
帮我写个后台页面
|
||||
```
|
||||
|
||||
更好的写法至少补充:
|
||||
|
||||
- 模块名
|
||||
- 业务名
|
||||
- 后端接口前缀
|
||||
- 是新增还是修改
|
||||
- 是否需要分页、导出、树表、字典、权限
|
||||
- 想参考哪个现有页面
|
||||
153
.codex/skills/frontend-crud-coding/references/frontend.md
Normal file
153
.codex/skills/frontend-crud-coding/references/frontend.md
Normal file
@@ -0,0 +1,153 @@
|
||||
# 前端约定
|
||||
|
||||
## 优先参考的代码来源
|
||||
|
||||
- 当前目标目录下最近似页面、API、types。
|
||||
- 标准单表:`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`
|
||||
|
||||
## 基础栈与格式
|
||||
|
||||
- 技术栈是 Vue 3 + TypeScript + Element Plus + Vite + Pinia。
|
||||
- 包管理按仓库现状使用 pnpm。
|
||||
- `.editorconfig` 要求 UTF-8、LF、2 空格缩进。
|
||||
- 当前仓库没有 `.prettierrc`;格式化使用 `pnpm run fmt`,lint 使用 `pnpm lint`。
|
||||
- 不要在一个页面里混入与仓库不一致的格式和写法。
|
||||
|
||||
## API 文件规则
|
||||
|
||||
- 标准 API 文件放在 `src/api/<module>/<business>/index.ts`,同目录维护 `types.ts`。
|
||||
- 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}`
|
||||
`addXxx` -> `POST /<module>/<business>`
|
||||
`updateXxx` -> `PUT /<module>/<business>`
|
||||
`delXxx` -> `DELETE /<module>/<business>/{id or ids}`
|
||||
`changeXxxStatus` -> `PUT /<module>/<business>/changeStatus`
|
||||
- query string 用 `params`,请求体用 `data`。
|
||||
- 加密、防重复提交等 headers 直接写在请求配置里,例如用户重置密码中的 `isEncrypt`、`repeatSubmit`。
|
||||
- 当前仓库有些 API 使用 `export const`,有些使用 `export function`;新增标准 CRUD 优先跟随 `gen/api.ts.ftl` 和相邻模块。
|
||||
- 只有相邻模块已有 `export default { ... }` 聚合时才新增默认导出。
|
||||
|
||||
## 类型文件规则
|
||||
|
||||
- 标准类型定义 `VO`、`Form`、`Query`,必要时补 `InfoVO`、`TreeVO`、`ResetPwdForm` 等扩展类型。
|
||||
- `Form` 通常继承 `BaseEntity`。
|
||||
- 非树表 `Query` 通常继承 `PageQuery`。
|
||||
- 树表 `Query` 通常不继承 `PageQuery`。
|
||||
- ID 字段通常使用 `string | number`,批量删除参数使用 `string | number | Array<string | number>`。
|
||||
- Java 数值类型映射为 `number`,Boolean 映射为 `boolean`,日期/文本默认 `string`。
|
||||
- 日期范围查询保留 `params?: any`,不要因为它看起来宽松就删掉。
|
||||
- 列表对象、表单对象、查询对象职责分开;字段不一致时不要强行复用一个接口。
|
||||
- 能明确写出类型时不要用 `any`;组件库、字典或历史接口确实无法收窄时再保留。
|
||||
|
||||
## Vue 页面结构规则
|
||||
|
||||
- 页面优先使用 `<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)`。
|
||||
|
||||
## 页面行为规则
|
||||
|
||||
- `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` 或流程定义页面。
|
||||
|
||||
## 字典、权限与公共工具
|
||||
|
||||
- 字典使用 `useDict`:
|
||||
`const { sys_normal_disable } = toRefs<any>(useDict('sys_normal_disable'));`
|
||||
- 新增代码默认使用当前项目主流 `v-hasPermi`。
|
||||
- 如果正在修改的文件已经混用或使用 `v-has-permi`,保持同文件现状,不为统一写法而重排无关代码。
|
||||
- `el-dropdown-item` 延迟加载导致权限指令不可靠时,使用 `v-if="checkPermi([...])"`,参考 `system/user`。
|
||||
- 常用工具:
|
||||
`modal` from `@/plugins/modal`
|
||||
`download as requestDownload` from `@/utils/request`
|
||||
`useDict` from `@/utils/dict`
|
||||
`checkPermi` from `@/utils/permission`
|
||||
`handleTree`、`parseStrEmpty` from `@/utils/ruoyi`
|
||||
|
||||
## 组件与样式规则
|
||||
|
||||
- 优先复用公共组件:`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')` 组树。
|
||||
- 使用 `row-key`、`:tree-props="{ children: 'children', hasChildren: 'hasChildren' }"`。
|
||||
- 展开/折叠使用 `useTreeTableExpand`。
|
||||
- 表单中上级节点使用 `el-tree-select`。
|
||||
- 新增子节点时从当前行回填 `parentId`。
|
||||
- 删除确认文案优先使用业务名称,而不是批量 ID 文案。
|
||||
|
||||
## 与 gen 模板的关系
|
||||
|
||||
- `gen` 是当前前端项目内的生成模板,优先于外部后端工程拷贝的模板。
|
||||
- 新增标准单表页面时读取 `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:运行 skill 基础校验即可。
|
||||
- 改前端 TS/Vue/API/types:优先运行 `pnpm exec vue-tsc --noEmit`。
|
||||
- 改页面模板、import、权限或较多文件:再运行 `pnpm lint`。
|
||||
- 改公共 hooks、组件、构建相关或大范围页面:再运行 `pnpm build`。
|
||||
- 如果验证因为环境、依赖或权限失败,交付时说明失败命令和原因。
|
||||
|
||||
## 避免事项
|
||||
|
||||
- 不要从 `axios` 引入 `AxiosPromise`。
|
||||
- 不要绕开 `request` 或 `requestDownload` 自造请求/下载封装。
|
||||
- 不要跳过 `types.ts`,把类型全写在页面里。
|
||||
- 不要删除日期范围 `params`、权限指令、导出、导入、树筛选、列显隐等现有能力。
|
||||
- 不要为了“更整洁”重写复杂页面的大块业务逻辑。
|
||||
- 不要在新增标准页里使用与仓库不一致的 UI 壳或状态管理方式。
|
||||
@@ -1,5 +1,5 @@
|
||||
# 页面标题
|
||||
VITE_APP_TITLE = RuoYi-Vue-Plus多租户管理系统
|
||||
VITE_APP_TITLE = RuoYi-Vue-Plus后台管理系统
|
||||
VITE_APP_LOGO_TITLE = RuoYi-Vue-Plus
|
||||
|
||||
# 开发环境配置
|
||||
@@ -17,20 +17,26 @@ 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 = 'MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdHnzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ=='
|
||||
VITE_APP_RSA_PUBLIC_KEY = 'MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDvEDuRIOM3oZPWj9Ukoc5pQklR4PFH6/clnjeFqjDLIgDyQvjxhgqAZQA+E9eD6qu6FsXPmK8djcL+nh3cFHz4pX473jDvO3Sve+8yL3VRQ0n2pRgQ2a01MJsy+WwTZCBYWf0VnLRIvANUoWQgy9vz94q7Va44dg7A1/3ICf+xAwIDAQAB'
|
||||
# 接口响应解密 RSA 私钥与后端加密公钥对应 如更换需前后端一同更换
|
||||
VITE_APP_RSA_PRIVATE_KEY = 'MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAmc3CuPiGL/LcIIm7zryCEIbl1SPzBkr75E2VMtxegyZ1lYRD+7TZGAPkvIsBcaMs6Nsy0L78n2qh+lIZMpLH8wIDAQABAkEAk82Mhz0tlv6IVCyIcw/s3f0E+WLmtPFyR9/WtV3Y5aaejUkU60JpX4m5xNR2VaqOLTZAYjW8Wy0aXr3zYIhhQQIhAMfqR9oFdYw1J9SsNc+CrhugAvKTi0+BF6VoL6psWhvbAiEAxPPNTmrkmrXwdm/pQQu3UOQmc2vCZ5tiKpW10CgJi8kCIFGkL6utxw93Ncj4exE/gPLvKcT+1Emnoox+O9kRXss5AiAMtYLJDaLEzPrAWcZeeSgSIzbL+ecokmFKSDDcRske6QIgSMkHedwND1olF8vlKsJUGK3BcdtM8w4Xq7BpSBwsloE='
|
||||
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'
|
||||
|
||||
# websocket 开关 默认使用sse推送
|
||||
VITE_APP_WEBSOCKET = false
|
||||
# 统一消息推送开关
|
||||
VITE_APP_MESSAGE_ENABLED = true
|
||||
|
||||
# sse 开关
|
||||
VITE_APP_SSE = true
|
||||
# sse / websocket
|
||||
VITE_APP_MESSAGE_TRANSPORT = 'sse'
|
||||
|
||||
# 统一消息推送路径
|
||||
VITE_APP_MESSAGE_PATH = '/resource/message'
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# 页面标题
|
||||
VITE_APP_TITLE = RuoYi-Vue-Plus多租户管理系统
|
||||
VITE_APP_TITLE = RuoYi-Vue-Plus后台管理系统
|
||||
VITE_APP_LOGO_TITLE = RuoYi-Vue-Plus
|
||||
|
||||
# 生产环境配置
|
||||
@@ -14,6 +14,9 @@ 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'
|
||||
|
||||
@@ -25,15 +28,18 @@ VITE_APP_PORT = 80
|
||||
# 接口加密功能开关(如需关闭 后端也必须对应关闭)
|
||||
VITE_APP_ENCRYPT = true
|
||||
# 接口加密传输 RSA 公钥与后端解密私钥对应 如更换需前后端一同更换
|
||||
VITE_APP_RSA_PUBLIC_KEY = 'MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdHnzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ=='
|
||||
VITE_APP_RSA_PUBLIC_KEY = 'MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDvEDuRIOM3oZPWj9Ukoc5pQklR4PFH6/clnjeFqjDLIgDyQvjxhgqAZQA+E9eD6qu6FsXPmK8djcL+nh3cFHz4pX473jDvO3Sve+8yL3VRQ0n2pRgQ2a01MJsy+WwTZCBYWf0VnLRIvANUoWQgy9vz94q7Va44dg7A1/3ICf+xAwIDAQAB'
|
||||
# 接口响应解密 RSA 私钥与后端加密公钥对应 如更换需前后端一同更换
|
||||
VITE_APP_RSA_PRIVATE_KEY = 'MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAmc3CuPiGL/LcIIm7zryCEIbl1SPzBkr75E2VMtxegyZ1lYRD+7TZGAPkvIsBcaMs6Nsy0L78n2qh+lIZMpLH8wIDAQABAkEAk82Mhz0tlv6IVCyIcw/s3f0E+WLmtPFyR9/WtV3Y5aaejUkU60JpX4m5xNR2VaqOLTZAYjW8Wy0aXr3zYIhhQQIhAMfqR9oFdYw1J9SsNc+CrhugAvKTi0+BF6VoL6psWhvbAiEAxPPNTmrkmrXwdm/pQQu3UOQmc2vCZ5tiKpW10CgJi8kCIFGkL6utxw93Ncj4exE/gPLvKcT+1Emnoox+O9kRXss5AiAMtYLJDaLEzPrAWcZeeSgSIzbL+ecokmFKSDDcRske6QIgSMkHedwND1olF8vlKsJUGK3BcdtM8w4Xq7BpSBwsloE='
|
||||
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'
|
||||
|
||||
# websocket 开关 默认使用sse推送
|
||||
VITE_APP_WEBSOCKET = false
|
||||
# 统一消息推送开关
|
||||
VITE_APP_MESSAGE_ENABLED = true
|
||||
|
||||
# sse 开关
|
||||
VITE_APP_SSE = true
|
||||
# sse / websocket
|
||||
VITE_APP_MESSAGE_TRANSPORT = 'sse'
|
||||
|
||||
# 统一消息推送路径
|
||||
VITE_APP_MESSAGE_PATH = '/resource/message'
|
||||
|
||||
@@ -1,328 +0,0 @@
|
||||
{
|
||||
"globals": {
|
||||
"Component": true,
|
||||
"ComponentPublicInstance": true,
|
||||
"ComputedRef": true,
|
||||
"DirectiveBinding": true,
|
||||
"EffectScope": true,
|
||||
"ElLoading": true,
|
||||
"ElMessage": true,
|
||||
"ElMessageBox": true,
|
||||
"ElNotification": true,
|
||||
"ExtractDefaultPropTypes": true,
|
||||
"ExtractPropTypes": true,
|
||||
"ExtractPublicPropTypes": true,
|
||||
"InjectionKey": true,
|
||||
"MaybeRef": true,
|
||||
"MaybeRefOrGetter": true,
|
||||
"PropType": true,
|
||||
"Ref": true,
|
||||
"ShallowRef": true,
|
||||
"Slot": true,
|
||||
"Slots": true,
|
||||
"VNode": true,
|
||||
"WritableComputedRef": true,
|
||||
"acceptHMRUpdate": true,
|
||||
"asyncComputed": true,
|
||||
"autoResetRef": true,
|
||||
"computed": true,
|
||||
"computedAsync": true,
|
||||
"computedEager": true,
|
||||
"computedInject": true,
|
||||
"computedWithControl": true,
|
||||
"controlledComputed": true,
|
||||
"controlledRef": true,
|
||||
"createApp": true,
|
||||
"createEventHook": true,
|
||||
"createGlobalState": true,
|
||||
"createInjectionState": true,
|
||||
"createPinia": true,
|
||||
"createReactiveFn": true,
|
||||
"createRef": true,
|
||||
"createReusableTemplate": true,
|
||||
"createSharedComposable": true,
|
||||
"createTemplatePromise": true,
|
||||
"createUnrefFn": true,
|
||||
"customRef": true,
|
||||
"debouncedRef": true,
|
||||
"debouncedWatch": true,
|
||||
"defineAsyncComponent": true,
|
||||
"defineComponent": true,
|
||||
"defineStore": true,
|
||||
"eagerComputed": true,
|
||||
"effectScope": true,
|
||||
"extendRef": true,
|
||||
"getActivePinia": true,
|
||||
"getCurrentInstance": true,
|
||||
"getCurrentScope": true,
|
||||
"getCurrentWatcher": true,
|
||||
"h": true,
|
||||
"ignorableWatch": true,
|
||||
"inject": true,
|
||||
"injectLocal": true,
|
||||
"isDefined": true,
|
||||
"isProxy": true,
|
||||
"isReactive": true,
|
||||
"isReadonly": true,
|
||||
"isRef": true,
|
||||
"isShallow": true,
|
||||
"makeDestructurable": true,
|
||||
"mapActions": true,
|
||||
"mapGetters": true,
|
||||
"mapState": true,
|
||||
"mapStores": true,
|
||||
"mapWritableState": true,
|
||||
"markRaw": true,
|
||||
"nextTick": true,
|
||||
"onActivated": true,
|
||||
"onBeforeMount": true,
|
||||
"onBeforeRouteLeave": true,
|
||||
"onBeforeRouteUpdate": true,
|
||||
"onBeforeUnmount": true,
|
||||
"onBeforeUpdate": true,
|
||||
"onClickOutside": true,
|
||||
"onDeactivated": true,
|
||||
"onElementRemoval": true,
|
||||
"onErrorCaptured": true,
|
||||
"onKeyStroke": true,
|
||||
"onLongPress": true,
|
||||
"onMounted": true,
|
||||
"onRenderTracked": true,
|
||||
"onRenderTriggered": true,
|
||||
"onScopeDispose": true,
|
||||
"onServerPrefetch": true,
|
||||
"onStartTyping": true,
|
||||
"onUnmounted": true,
|
||||
"onUpdated": true,
|
||||
"onWatcherCleanup": true,
|
||||
"pausableWatch": true,
|
||||
"provide": true,
|
||||
"provideLocal": true,
|
||||
"reactify": true,
|
||||
"reactifyObject": true,
|
||||
"reactive": true,
|
||||
"reactiveComputed": true,
|
||||
"reactiveOmit": true,
|
||||
"reactivePick": true,
|
||||
"readonly": true,
|
||||
"ref": true,
|
||||
"refAutoReset": true,
|
||||
"refDebounced": true,
|
||||
"refDefault": true,
|
||||
"refManualReset": true,
|
||||
"refThrottled": true,
|
||||
"refWithControl": true,
|
||||
"resolveComponent": true,
|
||||
"resolveRef": true,
|
||||
"setActivePinia": true,
|
||||
"setMapStoreSuffix": true,
|
||||
"shallowReactive": true,
|
||||
"shallowReadonly": true,
|
||||
"shallowRef": true,
|
||||
"storeToRefs": true,
|
||||
"syncRef": true,
|
||||
"syncRefs": true,
|
||||
"templateRef": true,
|
||||
"throttledRef": true,
|
||||
"throttledWatch": true,
|
||||
"toRaw": true,
|
||||
"toReactive": true,
|
||||
"toRef": true,
|
||||
"toRefs": true,
|
||||
"toValue": true,
|
||||
"triggerRef": true,
|
||||
"tryOnBeforeMount": true,
|
||||
"tryOnBeforeUnmount": true,
|
||||
"tryOnMounted": true,
|
||||
"tryOnScopeDispose": true,
|
||||
"tryOnUnmounted": true,
|
||||
"unref": true,
|
||||
"unrefElement": true,
|
||||
"until": true,
|
||||
"useActiveElement": true,
|
||||
"useAnimate": true,
|
||||
"useArrayDifference": true,
|
||||
"useArrayEvery": true,
|
||||
"useArrayFilter": true,
|
||||
"useArrayFind": true,
|
||||
"useArrayFindIndex": true,
|
||||
"useArrayFindLast": true,
|
||||
"useArrayIncludes": true,
|
||||
"useArrayJoin": true,
|
||||
"useArrayMap": true,
|
||||
"useArrayReduce": true,
|
||||
"useArraySome": true,
|
||||
"useArrayUnique": true,
|
||||
"useAsyncQueue": true,
|
||||
"useAsyncState": true,
|
||||
"useAttrs": true,
|
||||
"useBase64": true,
|
||||
"useBattery": true,
|
||||
"useBluetooth": true,
|
||||
"useBreakpoints": true,
|
||||
"useBroadcastChannel": true,
|
||||
"useBrowserLocation": true,
|
||||
"useCached": true,
|
||||
"useClipboard": true,
|
||||
"useClipboardItems": true,
|
||||
"useCloned": true,
|
||||
"useColorMode": true,
|
||||
"useConfirmDialog": true,
|
||||
"useCountdown": true,
|
||||
"useCounter": true,
|
||||
"useCssModule": true,
|
||||
"useCssSupports": true,
|
||||
"useCssVar": true,
|
||||
"useCssVars": true,
|
||||
"useCurrentElement": true,
|
||||
"useCycleList": true,
|
||||
"useDark": true,
|
||||
"useDateFormat": true,
|
||||
"useDebounce": true,
|
||||
"useDebounceFn": true,
|
||||
"useDebouncedRefHistory": true,
|
||||
"useDeviceMotion": true,
|
||||
"useDeviceOrientation": true,
|
||||
"useDevicePixelRatio": true,
|
||||
"useDevicesList": true,
|
||||
"useDisplayMedia": true,
|
||||
"useDocumentVisibility": true,
|
||||
"useDraggable": true,
|
||||
"useDropZone": true,
|
||||
"useElementBounding": true,
|
||||
"useElementByPoint": true,
|
||||
"useElementHover": true,
|
||||
"useElementSize": true,
|
||||
"useElementVisibility": true,
|
||||
"useEventBus": true,
|
||||
"useEventListener": true,
|
||||
"useEventSource": true,
|
||||
"useEyeDropper": true,
|
||||
"useFavicon": true,
|
||||
"useFetch": true,
|
||||
"useFileDialog": true,
|
||||
"useFileSystemAccess": true,
|
||||
"useFocus": true,
|
||||
"useFocusWithin": true,
|
||||
"useFps": true,
|
||||
"useFullscreen": true,
|
||||
"useGamepad": true,
|
||||
"useGeolocation": true,
|
||||
"useId": true,
|
||||
"useIdle": true,
|
||||
"useImage": true,
|
||||
"useInfiniteScroll": true,
|
||||
"useIntersectionObserver": true,
|
||||
"useInterval": true,
|
||||
"useIntervalFn": true,
|
||||
"useKeyModifier": true,
|
||||
"useLastChanged": true,
|
||||
"useLink": true,
|
||||
"useLocalStorage": true,
|
||||
"useMagicKeys": true,
|
||||
"useManualRefHistory": true,
|
||||
"useMediaControls": true,
|
||||
"useMediaQuery": true,
|
||||
"useMemoize": true,
|
||||
"useMemory": true,
|
||||
"useModel": true,
|
||||
"useMounted": true,
|
||||
"useMouse": true,
|
||||
"useMouseInElement": true,
|
||||
"useMousePressed": true,
|
||||
"useMutationObserver": true,
|
||||
"useNavigatorLanguage": true,
|
||||
"useNetwork": true,
|
||||
"useNow": true,
|
||||
"useObjectUrl": true,
|
||||
"useOffsetPagination": true,
|
||||
"useOnline": true,
|
||||
"usePageLeave": true,
|
||||
"useParallax": true,
|
||||
"useParentElement": true,
|
||||
"usePerformanceObserver": true,
|
||||
"usePermission": true,
|
||||
"usePointer": true,
|
||||
"usePointerLock": true,
|
||||
"usePointerSwipe": true,
|
||||
"usePreferredColorScheme": true,
|
||||
"usePreferredContrast": true,
|
||||
"usePreferredDark": true,
|
||||
"usePreferredLanguages": true,
|
||||
"usePreferredReducedMotion": true,
|
||||
"usePreferredReducedTransparency": true,
|
||||
"usePrevious": true,
|
||||
"useRafFn": true,
|
||||
"useRefHistory": true,
|
||||
"useResizeObserver": true,
|
||||
"useRoute": true,
|
||||
"useRouter": true,
|
||||
"useSSRWidth": true,
|
||||
"useScreenOrientation": true,
|
||||
"useScreenSafeArea": true,
|
||||
"useScriptTag": true,
|
||||
"useScroll": true,
|
||||
"useScrollLock": true,
|
||||
"useSessionStorage": true,
|
||||
"useShare": true,
|
||||
"useSlots": true,
|
||||
"useSorted": true,
|
||||
"useSpeechRecognition": true,
|
||||
"useSpeechSynthesis": true,
|
||||
"useStepper": true,
|
||||
"useStorage": true,
|
||||
"useStorageAsync": true,
|
||||
"useStyleTag": true,
|
||||
"useSupported": true,
|
||||
"useSwipe": true,
|
||||
"useTemplateRef": true,
|
||||
"useTemplateRefsList": true,
|
||||
"useTextDirection": true,
|
||||
"useTextSelection": true,
|
||||
"useTextareaAutosize": true,
|
||||
"useThrottle": true,
|
||||
"useThrottleFn": true,
|
||||
"useThrottledRefHistory": true,
|
||||
"useTimeAgo": true,
|
||||
"useTimeAgoIntl": true,
|
||||
"useTimeout": true,
|
||||
"useTimeoutFn": true,
|
||||
"useTimeoutPoll": true,
|
||||
"useTimestamp": true,
|
||||
"useTitle": true,
|
||||
"useToNumber": true,
|
||||
"useToString": true,
|
||||
"useToggle": true,
|
||||
"useTransition": true,
|
||||
"useUrlSearchParams": true,
|
||||
"useUserMedia": true,
|
||||
"useVModel": true,
|
||||
"useVModels": true,
|
||||
"useVibrate": true,
|
||||
"useVirtualList": true,
|
||||
"useWakeLock": true,
|
||||
"useWebNotification": true,
|
||||
"useWebSocket": true,
|
||||
"useWebWorker": true,
|
||||
"useWebWorkerFn": true,
|
||||
"useWindowFocus": true,
|
||||
"useWindowScroll": true,
|
||||
"useWindowSize": true,
|
||||
"watch": true,
|
||||
"watchArray": true,
|
||||
"watchAtMost": true,
|
||||
"watchDebounced": true,
|
||||
"watchDeep": true,
|
||||
"watchEffect": true,
|
||||
"watchIgnorable": true,
|
||||
"watchImmediate": true,
|
||||
"watchOnce": true,
|
||||
"watchPausable": true,
|
||||
"watchPostEffect": true,
|
||||
"watchSyncEffect": true,
|
||||
"watchThrottled": true,
|
||||
"watchTriggerable": true,
|
||||
"watchWithFilter": true,
|
||||
"whenever": true
|
||||
}
|
||||
}
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -14,6 +14,7 @@ selenium-debug.log
|
||||
# Editor directories and files
|
||||
.idea
|
||||
.vscode
|
||||
*.iml
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
@@ -22,7 +23,6 @@ selenium-debug.log
|
||||
|
||||
package-lock.json
|
||||
yarn.lock
|
||||
pnpm-lock.yaml
|
||||
|
||||
# 编译生成的文件
|
||||
auto-imports.d.ts
|
||||
|
||||
31
.oxfmtrc.json
Normal file
31
.oxfmtrc.json
Normal file
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"$schema": "./node_modules/oxfmt/configuration_schema.json",
|
||||
"ignorePatterns": [
|
||||
"src/types/components.d.ts",
|
||||
"src/types/auto-imports.d.ts",
|
||||
".ai_state",
|
||||
".claude",
|
||||
".codex",
|
||||
"doc"
|
||||
],
|
||||
"printWidth": 120,
|
||||
"singleQuote": true,
|
||||
"trailingComma": "none",
|
||||
"arrowParens": "avoid",
|
||||
"htmlWhitespaceSensitivity": "ignore",
|
||||
"experimentalSortPackageJson": {
|
||||
"sortScripts": true
|
||||
},
|
||||
"sortImports": {
|
||||
"newlinesBetween": false,
|
||||
"groups": [
|
||||
"type-import",
|
||||
["value-builtin", "value-external"],
|
||||
"type-internal",
|
||||
"value-internal",
|
||||
["type-parent", "type-sibling", "type-index"],
|
||||
["value-parent", "value-sibling", "value-index"],
|
||||
"unknown"
|
||||
]
|
||||
}
|
||||
}
|
||||
25
.oxlintrc.json
Normal file
25
.oxlintrc.json
Normal file
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"$schema": "https://raw.githubusercontent.com/oxc-project/oxc/main/npm/oxlint/configuration_schema.json",
|
||||
"plugins": ["eslint", "typescript", "unicorn", "oxc", "import", "vue"],
|
||||
"rules": {
|
||||
"typescript/no-empty-function": "off",
|
||||
"typescript/no-explicit-any": "off",
|
||||
"typescript/no-unused-vars": "off",
|
||||
"typescript/no-this-alias": "off",
|
||||
"typescript/no-empty-object-type": "off",
|
||||
"typescript/no-unused-expressions": "off",
|
||||
"prefer-rest-params": "off",
|
||||
"import/no-unassigned-import": "off",
|
||||
"import/no-named-as-default-member": "off",
|
||||
"import/no-named-as-default": "off",
|
||||
"no-shadow": "off",
|
||||
"unicorn/prefer-add-event-listener": "off",
|
||||
"unicorn/consistent-function-scoping": "off",
|
||||
"unicorn/no-instanceof-builtins": "off"
|
||||
},
|
||||
"categories": {
|
||||
"correctness": "error",
|
||||
"suspicious": "error"
|
||||
},
|
||||
"ignorePatterns": [".ai_state", ".claude", ".codex", "doc", "dist/**", "dist-ssr/**", "coverage/**"]
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
/dist/*
|
||||
.local
|
||||
.output.js
|
||||
/node_modules/**
|
||||
|
||||
**/*.svg
|
||||
**/*.sh
|
||||
|
||||
/public/*
|
||||
20
.prettierrc
20
.prettierrc
@@ -1,20 +0,0 @@
|
||||
{
|
||||
"printWidth": 150,
|
||||
"tabWidth": 2,
|
||||
"useTabs": false,
|
||||
"semi": true,
|
||||
"singleQuote": true,
|
||||
"quoteProps": "preserve",
|
||||
"jsxSingleQuote": false,
|
||||
"bracketSameLine": false,
|
||||
"trailingComma": "none",
|
||||
"bracketSpacing": true,
|
||||
"embeddedLanguageFormatting": "auto",
|
||||
"arrowParens": "always",
|
||||
"requirePragma": false,
|
||||
"insertPragma": false,
|
||||
"proseWrap": "preserve",
|
||||
"htmlWhitespaceSensitivity": "css",
|
||||
"vueIndentScriptAndStyle": false,
|
||||
"endOfLine": "auto"
|
||||
}
|
||||
53
README.md
53
README.md
@@ -1,32 +1,33 @@
|
||||
## 平台简介
|
||||
|
||||
- 本仓库为前端技术栈 [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)<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) |
|
||||
| 🔥 微服务框架 | 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) |
|
||||
|
||||
## 分支说明
|
||||
|
||||
- ts分支(稳定发布主分支 生产可用)
|
||||
- 6.X分支(稳定发布主分支 生产可用)
|
||||
- dev分支(开发分支 开发过程中使用)
|
||||
|
||||
## 前端运行
|
||||
|
||||
```bash
|
||||
# 安装依赖
|
||||
npm install --registry=https://registry.npmmirror.com
|
||||
pnpm install --registry=https://registry.npmmirror.com
|
||||
|
||||
# 启动服务
|
||||
npm run dev
|
||||
pnpm dev
|
||||
|
||||
# 构建生产环境
|
||||
npm run build:prod
|
||||
pnpm build:prod
|
||||
|
||||
# 前端访问地址 http://localhost:80
|
||||
```
|
||||
@@ -35,8 +36,6 @@ npm run build:prod
|
||||
|
||||
| 业务 | 功能说明 | 本框架 | RuoYi |
|
||||
| ------------ | ------------------------------------------------------------- | ------ | ----------------------------- |
|
||||
| 租户管理 | 系统内租户的管理 如:租户套餐、过期时间、用户数量、企业信息等 | 支持 | 无 |
|
||||
| 租户套餐管理 | 系统内租户所能使用的套餐管理 如:套餐内所包含的菜单等 | 支持 | 无 |
|
||||
| 用户管理 | 用户的管理配置 如:新增用户、分配用户所属部门、角色、岗位等 | 支持 | 支持 |
|
||||
| 部门管理 | 配置系统组织机构(公司、部门、小组) 树结构展现支持数据权限 | 支持 | 支持 |
|
||||
| 岗位管理 | 配置系统用户所属担任职务 | 支持 | 支持 |
|
||||
@@ -60,24 +59,18 @@ npm run build:prod
|
||||
|
||||
## 演示图例
|
||||
|
||||
| | |
|
||||
| ---------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
| | |
|
||||
|--------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------|
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
@echo off
|
||||
echo.
|
||||
echo [<5B><>Ϣ] <20><><EFBFBD><EFBFBD>Web<65><62><EFBFBD>̣<EFBFBD><CCA3><EFBFBD><EFBFBD><EFBFBD>dist<73>ļ<EFBFBD><C4BC><EFBFBD>
|
||||
echo.
|
||||
|
||||
%~d0
|
||||
cd %~dp0
|
||||
|
||||
cd ..
|
||||
yarn build:prod
|
||||
|
||||
pause
|
||||
@@ -1,12 +0,0 @@
|
||||
@echo off
|
||||
echo.
|
||||
echo [<5B><>Ϣ] <20><>װWeb<65><62><EFBFBD>̣<EFBFBD><CCA3><EFBFBD><EFBFBD><EFBFBD>node_modules<65>ļ<EFBFBD><C4BC><EFBFBD>
|
||||
echo.
|
||||
|
||||
%~d0
|
||||
cd %~dp0
|
||||
|
||||
cd ..
|
||||
yarn --registry=https://registry.npmmirror.com
|
||||
|
||||
pause
|
||||
@@ -1,12 +0,0 @@
|
||||
@echo off
|
||||
echo.
|
||||
echo [<5B><>Ϣ] ʹ<><CAB9> Vite <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> Web <20><><EFBFBD>̡<EFBFBD>
|
||||
echo.
|
||||
|
||||
%~d0
|
||||
cd %~dp0
|
||||
|
||||
cd ..
|
||||
yarn dev
|
||||
|
||||
pause
|
||||
@@ -1,44 +0,0 @@
|
||||
import pluginVue from 'eslint-plugin-vue';
|
||||
import globals from 'globals';
|
||||
import prettier from 'eslint-plugin-prettier';
|
||||
import { defineConfigWithVueTs, vueTsConfigs } from '@vue/eslint-config-typescript';
|
||||
import skipFormatting from '@vue/eslint-config-prettier/skip-formatting';
|
||||
|
||||
export default defineConfigWithVueTs(
|
||||
{
|
||||
name: 'app/files-to-lint',
|
||||
files: ['**/*.{js,cjs,ts,mts,tsx,vue}']
|
||||
},
|
||||
|
||||
{
|
||||
name: 'app/files-to-ignore',
|
||||
ignores: ['**/dist/**', '**/dist-ssr/**', '**/coverage/**']
|
||||
},
|
||||
{
|
||||
languageOptions: {
|
||||
globals: globals.browser
|
||||
}
|
||||
},
|
||||
pluginVue.configs['flat/essential'],
|
||||
vueTsConfigs.recommended,
|
||||
skipFormatting,
|
||||
{
|
||||
plugins: { prettier },
|
||||
rules: {
|
||||
'@typescript-eslint/no-empty-function': 'off',
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
'@typescript-eslint/no-unused-vars': 'off',
|
||||
'@typescript-eslint/no-this-alias': 'off',
|
||||
// vue
|
||||
'vue/multi-word-component-names': 'off',
|
||||
'vue/valid-define-props': 'off',
|
||||
'vue/no-v-model-argument': 'off',
|
||||
'prefer-rest-params': 'off',
|
||||
// prettier
|
||||
'prettier/prettier': 'error',
|
||||
// 允许使用空Object类型 {}
|
||||
'@typescript-eslint/no-empty-object-type': 'off',
|
||||
'@typescript-eslint/no-unused-expressions': 'off'
|
||||
}
|
||||
}
|
||||
);
|
||||
103
gen/api.ts.ftl
Normal file
103
gen/api.ts.ftl
Normal file
@@ -0,0 +1,103 @@
|
||||
import type { ${BusinessName}Form, ${BusinessName}Query, ${BusinessName}VO } from '@/api/${moduleName}/${businessName}/types';
|
||||
import type { PageResult } from '@/api/types';
|
||||
import type { AxiosPromise } from '@/utils/api-types';
|
||||
import request from '@/utils/request';
|
||||
|
||||
/**
|
||||
* 查询${functionName}列表
|
||||
* @param query
|
||||
* @returns {*}
|
||||
*/
|
||||
|
||||
export const list${BusinessName} = (query?: ${BusinessName}Query): AxiosPromise<PageResult<${BusinessName}VO>> => {
|
||||
return request({
|
||||
url: '/${moduleName}/${businessName}/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 查询${functionName}详细
|
||||
* @param ${pkColumn.javaField}
|
||||
*/
|
||||
export const get${BusinessName} = (${pkColumn.javaField}: string | number): AxiosPromise<${BusinessName}VO> => {
|
||||
return request({
|
||||
url: '/${moduleName}/${businessName}/' + ${pkColumn.javaField},
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 新增${functionName}
|
||||
* @param data
|
||||
*/
|
||||
export const add${BusinessName} = (data: ${BusinessName}Form) => {
|
||||
return request({
|
||||
url: '/${moduleName}/${businessName}',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 修改${functionName}
|
||||
* @param data
|
||||
*/
|
||||
export const update${BusinessName} = (data: ${BusinessName}Form) => {
|
||||
return request({
|
||||
url: '/${moduleName}/${businessName}',
|
||||
method: 'put',
|
||||
data: data
|
||||
});
|
||||
};
|
||||
|
||||
<#if enableStatus>
|
||||
/**
|
||||
* 修改${functionName}状态
|
||||
* @param ${pkColumn.javaField}
|
||||
* @param status
|
||||
*/
|
||||
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}: status
|
||||
}
|
||||
});
|
||||
};
|
||||
</#if>
|
||||
|
||||
<#if enableSort>
|
||||
/**
|
||||
* 调整${functionName}排序
|
||||
* @param ${pkColumn.javaField}
|
||||
* @param sortValue
|
||||
*/
|
||||
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}: sortValue
|
||||
}
|
||||
});
|
||||
};
|
||||
</#if>
|
||||
|
||||
/**
|
||||
* 删除${functionName}
|
||||
* @param ${pkColumn.javaField}
|
||||
*/
|
||||
export const del${BusinessName} = (${pkColumn.javaField}: string | number | Array<string | number>) => {
|
||||
return request({
|
||||
url: '/${moduleName}/${businessName}/' + ${pkColumn.javaField},
|
||||
method: 'delete'
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
|
||||
692
gen/index-tree.vue.ftl
Normal file
692
gen/index-tree.vue.ftl
Normal file
@@ -0,0 +1,692 @@
|
||||
<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>
|
||||
|
||||
611
gen/index.vue.ftl
Normal file
611
gen/index.vue.ftl
Normal file
@@ -0,0 +1,611 @@
|
||||
<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="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>`n<#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>
|
||||
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">
|
||||
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">
|
||||
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">
|
||||
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>
|
||||
|
||||
50
gen/types.ts.ftl
Normal file
50
gen/types.ts.ftl
Normal file
@@ -0,0 +1,50 @@
|
||||
import type { BaseEntity<#if !table.tree>, PageQuery</#if> } from '@/api/types';
|
||||
|
||||
export interface ${BusinessName}VO {
|
||||
<#list columns as column>
|
||||
<#if column.list>
|
||||
/**
|
||||
* ${column.columnComment}
|
||||
*/
|
||||
${column.javaField}: ${column.tsType};
|
||||
<#if column.htmlType == "imageUpload">
|
||||
/**
|
||||
* ${column.columnComment}Url
|
||||
*/
|
||||
${column.javaField}Url: string;
|
||||
</#if>
|
||||
</#if>
|
||||
</#list>
|
||||
<#if table.tree>
|
||||
/**
|
||||
* 子对象
|
||||
*/
|
||||
children: ${BusinessName}VO[];
|
||||
</#if>
|
||||
}
|
||||
|
||||
export interface ${BusinessName}Form extends BaseEntity {
|
||||
<#list columns as column>
|
||||
<#if column.insert || column.edit>
|
||||
/**
|
||||
* ${column.columnComment}
|
||||
*/
|
||||
${column.javaField}?: ${column.tsType};
|
||||
</#if>
|
||||
</#list>
|
||||
}
|
||||
|
||||
export interface ${BusinessName}Query<#if !table.tree> extends PageQuery</#if> {
|
||||
<#list columns as column>
|
||||
<#if column.query>
|
||||
/**
|
||||
* ${column.columnComment}
|
||||
*/
|
||||
${column.javaField}?: ${column.tsType};
|
||||
</#if>
|
||||
</#list>
|
||||
/**
|
||||
* 日期范围参数
|
||||
*/
|
||||
params?: any;
|
||||
}
|
||||
34
html/ie.html
34
html/ie.html
@@ -144,7 +144,8 @@
|
||||
font-size: 14px;
|
||||
line-height: 24px;
|
||||
color: #454545;
|
||||
font-family: 'Microsoft YaHei UI', 'Microsoft YaHei', DengXian, SimSun, 'Segoe UI', Tahoma, Helvetica, sans-serif;
|
||||
font-family:
|
||||
'Microsoft YaHei UI', 'Microsoft YaHei', DengXian, SimSun, 'Segoe UI', Tahoma, Helvetica, sans-serif;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
h1 {
|
||||
@@ -208,15 +209,18 @@
|
||||
</head>
|
||||
<body style="margin-top: 50px">
|
||||
<h1>请升级您的浏览器,以便我们更好的为您提供服务!</h1>
|
||||
<p>您正在使用 Internet Explorer 的早期版本(IE11以下版本或使用该内核的浏览器)。这意味着在升级浏览器前,您将无法访问此网站。</p>
|
||||
<p>
|
||||
您正在使用 Internet Explorer
|
||||
的早期版本(IE11以下版本或使用该内核的浏览器)。这意味着在升级浏览器前,您将无法访问此网站。
|
||||
</p>
|
||||
<hr />
|
||||
<h2>请注意:微软公司对Windows XP 及 Internet Explorer 早期版本的支持已经结束</h2>
|
||||
<p>
|
||||
自 2016 年 1 月 12 日起,Microsoft 不再为 IE 11
|
||||
以下版本提供相应支持和更新。没有关键的浏览器安全更新,您的电脑可能易受有害病毒、间谍软件和其他恶意软件的攻击,它们可以窃取或损害您的业务数据和信息。请参阅
|
||||
<a href="https://www.microsoft.com/zh-cn/WindowsForBusiness/End-of-IE-support"
|
||||
>微软对 Internet Explorer 早期版本的支持将于 2016 年 1 月 12 日结束的说明</a
|
||||
>
|
||||
<a href="https://www.microsoft.com/zh-cn/WindowsForBusiness/End-of-IE-support">
|
||||
微软对 Internet Explorer 早期版本的支持将于 2016 年 1 月 12 日结束的说明
|
||||
</a>
|
||||
。
|
||||
</p>
|
||||
<hr />
|
||||
@@ -224,16 +228,28 @@
|
||||
<p>推荐使用以下浏览器的最新版本。如果您的电脑已有以下浏览器的最新版本则直接使用该浏览器访问即可。</p>
|
||||
<ul class="browser">
|
||||
<li class="browser-chrome">
|
||||
<a href="https://www.google.cn/chrome/browser/desktop/index.html?hl=zh-CN&standalone=1"> 谷歌浏览器<span>Google Chrome</span></a>
|
||||
<a href="https://www.google.cn/chrome/browser/desktop/index.html?hl=zh-CN&standalone=1">
|
||||
谷歌浏览器
|
||||
<span>Google Chrome</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="browser-firefox">
|
||||
<a href="https://www.mozilla.org/zh-CN/firefox/new/"> 火狐浏览器<span>Mozilla Firefox</span></a>
|
||||
<a href="https://www.mozilla.org/zh-CN/firefox/new/">
|
||||
火狐浏览器
|
||||
<span>Mozilla Firefox</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="browser-ie">
|
||||
<a href="https://windows.microsoft.com/zh-cn/internet-explorer/download-ie"> IE 11 浏览器<span>Internet Explorer</span></a>
|
||||
<a href="https://windows.microsoft.com/zh-cn/internet-explorer/download-ie">
|
||||
IE 11 浏览器
|
||||
<span>Internet Explorer</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="browser-360">
|
||||
<a href="http://se.360.cn/"> 360安全浏览器<span>360 Chrome</span></a>
|
||||
<a href="http://se.360.cn/">
|
||||
360安全浏览器
|
||||
<span>360 Chrome</span>
|
||||
</a>
|
||||
</li>
|
||||
<div class="clean"></div>
|
||||
</ul>
|
||||
|
||||
@@ -7,11 +7,11 @@
|
||||
<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>
|
||||
<!--[if lt IE 11]>
|
||||
<script>
|
||||
window.location.href = '/html/ie.html';
|
||||
</script><!
|
||||
[endif]-->
|
||||
</script>
|
||||
<![endif]-->
|
||||
<style>
|
||||
html,
|
||||
body,
|
||||
|
||||
113
package.json
113
package.json
@@ -2,95 +2,86 @@
|
||||
"$schema": "https://json.schemastore.org/package",
|
||||
"name": "ruoyi-vue-plus",
|
||||
"version": "5.5.3-2.5.3",
|
||||
"description": "RuoYi-Vue-Plus多租户管理系统",
|
||||
"author": "LionLi",
|
||||
"description": "RuoYi-Vue-Plus后台管理系统",
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite serve --mode development",
|
||||
"build:prod": "vite build --mode production",
|
||||
"build:dev": "vite build --mode development",
|
||||
"preview": "vite preview",
|
||||
"lint:eslint": "eslint",
|
||||
"lint:eslint:fix": "eslint --fix",
|
||||
"prettier": "prettier --write ."
|
||||
},
|
||||
"author": "LionLi",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://gitee.com/JavaLionLi/plus-ui.git"
|
||||
},
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build:dev": "vite build --mode development",
|
||||
"build:prod": "vite build --mode production",
|
||||
"dev": "vite serve --mode development",
|
||||
"fmt": "oxfmt .",
|
||||
"lint": "oxlint src",
|
||||
"lint:fix": "oxlint --fix src",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@element-plus/icons-vue": "2.3.2",
|
||||
"@highlightjs/vue-plugin": "2.1.2",
|
||||
"@vueup/vue-quill": "1.2.0",
|
||||
"@vueuse/core": "14.2.1",
|
||||
"@iconify/vue": "^5.0.1",
|
||||
"@vueuse/core": "14.3.0",
|
||||
"@wangeditor-next/editor": "5.7.10",
|
||||
"@wangeditor-next/editor-for-vue": "5.1.14",
|
||||
"animate.css": "4.1.1",
|
||||
"await-to-js": "3.0.0",
|
||||
"axios": "1.13.6",
|
||||
"axios": "1.17.0",
|
||||
"crypto-js": "4.2.0",
|
||||
"echarts": "6.0.0",
|
||||
"element-plus": "2.13.5",
|
||||
"file-saver": "2.0.5",
|
||||
"echarts": "6.1.0",
|
||||
"element-plus": "2.14.1",
|
||||
"highlight.js": "11.11.1",
|
||||
"image-conversion": "2.1.1",
|
||||
"js-cookie": "3.0.5",
|
||||
"jsencrypt": "3.5.4",
|
||||
"nprogress": "0.2.0",
|
||||
"pinia": "3.0.4",
|
||||
"screenfull": "6.0.2",
|
||||
"vue": "3.5.30",
|
||||
"vue": "3.5.35",
|
||||
"vue-cropper": "1.1.4",
|
||||
"vue-i18n": "11.3.0",
|
||||
"vue-i18n": "11.4.5",
|
||||
"vue-json-pretty": "2.6.0",
|
||||
"vue-router": "5.0.3",
|
||||
"vue-types": "6.0.0",
|
||||
"vxe-table": "4.18.1"
|
||||
"vue-router": "5.1.0",
|
||||
"vue-types": "7.0.0",
|
||||
"vxe-table": "4.19.10"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@iconify/json": "^2.2.448",
|
||||
"@types/crypto-js": "4.2.2",
|
||||
"@types/file-saver": "2.0.7",
|
||||
"@types/js-cookie": "3.0.6",
|
||||
"@types/node": "^25.4.0",
|
||||
"@types/node": "^25.9.2",
|
||||
"@types/nprogress": "0.2.3",
|
||||
"@unocss/preset-attributify": "66.6.6",
|
||||
"@unocss/preset-icons": "66.6.6",
|
||||
"@unocss/preset-uno": "66.6.6",
|
||||
"@vitejs/plugin-vue": "6.0.4",
|
||||
"@vue/compiler-sfc": "3.5.30",
|
||||
"@vue/eslint-config-prettier": "10.2.0",
|
||||
"@vue/eslint-config-typescript": "14.6.0",
|
||||
"autoprefixer": "10.4.27",
|
||||
"eslint": "9.39.1",
|
||||
"eslint-plugin-prettier": "5.5.5",
|
||||
"eslint-plugin-vue": "9.33.0",
|
||||
"globals": "17.4.0",
|
||||
"prettier": "3.8.1",
|
||||
"sass": "1.98.0",
|
||||
"typescript": "~5.9.3",
|
||||
"unocss": "66.6.6",
|
||||
"@unocss/preset-attributify": "66.7.0",
|
||||
"@unocss/preset-wind3": "66.7.0",
|
||||
"@vitejs/plugin-vue": "^6.0.7",
|
||||
"@vue/compiler-sfc": "3.5.35",
|
||||
"autoprefixer": "10.5.0",
|
||||
"oxfmt": "^0.53.0",
|
||||
"oxlint": "^1.68.0",
|
||||
"sass": "1.100.0",
|
||||
"typescript": "^6.0.3",
|
||||
"unocss": "66.7.0",
|
||||
"unplugin-auto-import": "21.0.0",
|
||||
"unplugin-icons": "23.0.1",
|
||||
"unplugin-vue-components": "31.0.0",
|
||||
"unplugin-vue-components": "32.1.0",
|
||||
"unplugin-vue-setup-extend-plus": "1.0.1",
|
||||
"vite": "7.3.1",
|
||||
"vite-plugin-compression": "0.5.1",
|
||||
"vite-plugin-svg-icons-ng": "^1.5.2",
|
||||
"vite-plugin-vue-devtools": "8.0.7",
|
||||
"vitest": "4.0.18",
|
||||
"vue-tsc": "^3.2.5"
|
||||
},
|
||||
"overrides": {
|
||||
"quill": "1.3.7"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.19.0",
|
||||
"npm": ">=8.19.0"
|
||||
"vite": "^8.0.16",
|
||||
"vite-plugin-svg-icons-ng": "^1.9.1",
|
||||
"vitest": "4.1.8",
|
||||
"vue-tsc": "^3.3.4"
|
||||
},
|
||||
"browserslist": [
|
||||
"Chrome >= 87",
|
||||
"Edge >= 88",
|
||||
"Safari >= 14",
|
||||
"Firefox >= 78"
|
||||
]
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=20.19.0",
|
||||
"pnpm": ">=10.0.0"
|
||||
},
|
||||
"pnpm": {
|
||||
"onlyBuiltDependencies": [
|
||||
"@parcel/watcher",
|
||||
"es5-ext",
|
||||
"esbuild"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
4578
pnpm-lock.yaml
generated
Normal file
4578
pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -5,9 +5,9 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useAppStore } from '@/store/modules/app';
|
||||
import { useSettingsStore } from '@/store/modules/settings';
|
||||
import { handleThemeStyle } from '@/utils/theme';
|
||||
import { useAppStore } from '@/store/modules/app';
|
||||
|
||||
const appStore = useAppStore();
|
||||
|
||||
|
||||
10
src/api/ai/agent/index.ts
Normal file
10
src/api/ai/agent/index.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import type { AxiosPromise } from '@/utils/api-types';
|
||||
import request from '@/utils/request';
|
||||
import type { SnailOpenApiUser } from './types';
|
||||
|
||||
export const registerCurrentSnailUser = (): AxiosPromise<SnailOpenApiUser> => {
|
||||
return request({
|
||||
url: '/snail-ai/user/register',
|
||||
method: 'post'
|
||||
});
|
||||
};
|
||||
6
src/api/ai/agent/types.ts
Normal file
6
src/api/ai/agent/types.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
export interface SnailOpenApiUser {
|
||||
openId: string;
|
||||
nickname?: string;
|
||||
externalId?: string;
|
||||
created?: boolean;
|
||||
}
|
||||
2
src/api/ai/index.ts
Normal file
2
src/api/ai/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export * from './agent';
|
||||
export * from './agent/types';
|
||||
@@ -1,13 +1,14 @@
|
||||
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';
|
||||
import { AxiosPromise } from 'axios';
|
||||
import { DemoVO, DemoForm, DemoQuery } from '@/api/demo/demo/types';
|
||||
|
||||
/**
|
||||
* 查询测试单列表
|
||||
* @param query
|
||||
* @returns {*}
|
||||
*/
|
||||
export const listDemo = (query?: DemoQuery): AxiosPromise<DemoVO[]> => {
|
||||
export const listDemo = (query?: DemoQuery): AxiosPromise<PageResult<DemoVO>> => {
|
||||
return request({
|
||||
url: '/demo/demo/list',
|
||||
method: 'get',
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { TreeForm, TreeQuery, TreeVO } from '@/api/demo/tree/types';
|
||||
import type { AxiosPromise } from '@/utils/api-types';
|
||||
import request from '@/utils/request';
|
||||
import { AxiosPromise } from 'axios';
|
||||
import { TreeVO, TreeForm, TreeQuery } from '@/api/demo/tree/types';
|
||||
|
||||
/**
|
||||
* 查询测试树列表
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import type { UserInfo } from '@/api/system/user/types';
|
||||
import type { AxiosPromise } from '@/utils/api-types';
|
||||
import { closePush } from '@/utils/push';
|
||||
import request from '@/utils/request';
|
||||
import { AxiosPromise } from 'axios';
|
||||
import { LoginData, LoginResult, VerifyCodeResult, TenantInfo } from './types';
|
||||
import { UserInfo } from '@/api/system/user/types';
|
||||
import type { LoginData, LoginResult, VerifyCodeResult } from './types';
|
||||
|
||||
// pc端固定客户端授权id
|
||||
const clientId = import.meta.env.VITE_APP_CLIENT_ID;
|
||||
@@ -51,9 +52,13 @@ export function register(data: any) {
|
||||
* 注销
|
||||
*/
|
||||
export function logout() {
|
||||
if (import.meta.env.VITE_APP_SSE === 'true') {
|
||||
closePush();
|
||||
if (
|
||||
import.meta.env.VITE_APP_MESSAGE_ENABLED === 'true' &&
|
||||
import.meta.env.VITE_APP_MESSAGE_TRANSPORT.toLowerCase() === 'sse'
|
||||
) {
|
||||
request({
|
||||
url: '/resource/sse/close',
|
||||
url: import.meta.env.VITE_APP_MESSAGE_PATH + '/close',
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
@@ -100,14 +105,3 @@ export function getInfo(): AxiosPromise<UserInfo> {
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
|
||||
// 获取租户列表
|
||||
export function getTenantList(isToken: boolean): AxiosPromise<TenantInfo> {
|
||||
return request({
|
||||
url: '/auth/tenant/list',
|
||||
headers: {
|
||||
isToken: isToken
|
||||
},
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { RouteRecordRaw } from 'vue-router';
|
||||
import type { AxiosPromise } from '@/utils/api-types';
|
||||
import request from '@/utils/request';
|
||||
import { AxiosPromise } from 'axios';
|
||||
import { RouteRecordRaw } from 'vue-router';
|
||||
|
||||
// 获取路由
|
||||
export function getRouters(): AxiosPromise<RouteRecordRaw[]> {
|
||||
|
||||
51
src/api/monitor/cache/index.ts
vendored
51
src/api/monitor/cache/index.ts
vendored
@@ -1,6 +1,6 @@
|
||||
import type { AxiosPromise } from '@/utils/api-types';
|
||||
import request from '@/utils/request';
|
||||
import { AxiosPromise } from 'axios';
|
||||
import { CacheVO } from './types';
|
||||
import type { CacheVO } from './types';
|
||||
|
||||
// 查询缓存详细
|
||||
export function getCache(): AxiosPromise<CacheVO> {
|
||||
@@ -10,50 +10,3 @@ export function getCache(): AxiosPromise<CacheVO> {
|
||||
});
|
||||
}
|
||||
|
||||
// 查询缓存名称列表
|
||||
export function listCacheName() {
|
||||
return request({
|
||||
url: '/monitor/cache/getNames',
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
|
||||
// 查询缓存键名列表
|
||||
export function listCacheKey(cacheName: string) {
|
||||
return request({
|
||||
url: '/monitor/cache/getKeys/' + cacheName,
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
|
||||
// 查询缓存内容
|
||||
export function getCacheValue(cacheName: string, cacheKey: string) {
|
||||
return request({
|
||||
url: '/monitor/cache/getValue/' + cacheName + '/' + cacheKey,
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
|
||||
// 清理指定名称缓存
|
||||
export function clearCacheName(cacheName: string) {
|
||||
return request({
|
||||
url: '/monitor/cache/clearCacheName/' + cacheName,
|
||||
method: 'delete'
|
||||
});
|
||||
}
|
||||
|
||||
// 清理指定键名缓存
|
||||
export function clearCacheKey(cacheName: string, cacheKey: string) {
|
||||
return request({
|
||||
url: '/monitor/cache/clearCacheKey/' + cacheName + '/' + cacheKey,
|
||||
method: 'delete'
|
||||
});
|
||||
}
|
||||
|
||||
// 清理全部缓存
|
||||
export function clearCacheAll() {
|
||||
return request({
|
||||
url: '/monitor/cache/clearCacheAll',
|
||||
method: 'delete'
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import type { PageResult } from '@/api/types';
|
||||
import type { AxiosPromise } from '@/utils/api-types';
|
||||
import request from '@/utils/request';
|
||||
import { LoginInfoQuery, LoginInfoVO } from './types';
|
||||
import { AxiosPromise } from 'axios';
|
||||
import type { LoginInfoQuery, LoginInfoVO } from './types';
|
||||
|
||||
// 查询登录日志列表
|
||||
export function list(query: LoginInfoQuery): AxiosPromise<LoginInfoVO[]> {
|
||||
export function list(query: LoginInfoQuery): AxiosPromise<PageResult<LoginInfoVO>> {
|
||||
return request({
|
||||
url: '/monitor/logininfor/list',
|
||||
url: '/monitor/loginInfo/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
@@ -14,7 +15,7 @@ export function list(query: LoginInfoQuery): AxiosPromise<LoginInfoVO[]> {
|
||||
// 删除登录日志
|
||||
export function delLoginInfo(infoId: string | number | Array<string | number>) {
|
||||
return request({
|
||||
url: '/monitor/logininfor/' + infoId,
|
||||
url: '/monitor/loginInfo/' + infoId,
|
||||
method: 'delete'
|
||||
});
|
||||
}
|
||||
@@ -22,7 +23,7 @@ export function delLoginInfo(infoId: string | number | Array<string | number>) {
|
||||
// 解锁用户登录状态
|
||||
export function unlockLoginInfo(userName: string | Array<string>) {
|
||||
return request({
|
||||
url: '/monitor/logininfor/unlock/' + userName,
|
||||
url: '/monitor/loginInfo/unlock/' + userName,
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
@@ -30,7 +31,7 @@ export function unlockLoginInfo(userName: string | Array<string>) {
|
||||
// 清空登录日志
|
||||
export function cleanLoginInfo() {
|
||||
return request({
|
||||
url: '/monitor/logininfor/clean',
|
||||
url: '/monitor/loginInfo/clean',
|
||||
method: 'delete'
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import type { PageResult } from '@/api/types';
|
||||
import type { AxiosPromise } from '@/utils/api-types';
|
||||
import request from '@/utils/request';
|
||||
import { OnlineQuery, OnlineVO } from './types';
|
||||
import { AxiosPromise } from 'axios';
|
||||
import type { OnlineQuery, OnlineVO } from './types';
|
||||
|
||||
// 查询在线用户列表
|
||||
export function list(query: OnlineQuery): AxiosPromise<OnlineVO[]> {
|
||||
export function list(query: OnlineQuery): AxiosPromise<PageResult<OnlineVO>> {
|
||||
return request({
|
||||
url: '/monitor/online/list',
|
||||
method: 'get',
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import type { PageResult } from '@/api/types';
|
||||
import type { AxiosPromise } from '@/utils/api-types';
|
||||
import request from '@/utils/request';
|
||||
import { OperLogQuery, OperLogVO } from './types';
|
||||
import { AxiosPromise } from 'axios';
|
||||
import type { OperLogQuery, OperLogVO } from './types';
|
||||
|
||||
// 查询操作日志列表
|
||||
export function list(query: OperLogQuery): AxiosPromise<OperLogVO[]> {
|
||||
export function list(query: OperLogQuery): AxiosPromise<PageResult<OperLogVO>> {
|
||||
return request({
|
||||
url: '/monitor/operlog/list',
|
||||
method: 'get',
|
||||
|
||||
@@ -2,6 +2,12 @@ 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;
|
||||
@@ -18,7 +24,13 @@ export interface OperLogVO extends BaseEntity {
|
||||
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;
|
||||
@@ -40,7 +52,13 @@ export interface OperLogForm {
|
||||
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;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
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';
|
||||
import { AxiosPromise } from 'axios';
|
||||
import { ClientVO, ClientForm, ClientQuery } from '@/api/system/client/types';
|
||||
|
||||
/**
|
||||
* 查询客户端管理列表
|
||||
@@ -8,7 +9,7 @@ import { ClientVO, ClientForm, ClientQuery } from '@/api/system/client/types';
|
||||
* @returns {*}
|
||||
*/
|
||||
|
||||
export const listClient = (query?: ClientQuery): AxiosPromise<ClientVO[]> => {
|
||||
export const listClient = (query?: ClientQuery): AxiosPromise<PageResult<ClientVO>> => {
|
||||
return request({
|
||||
url: '/system/client/list',
|
||||
method: 'get',
|
||||
|
||||
@@ -29,6 +29,26 @@ export interface ClientVO {
|
||||
*/
|
||||
deviceType: string;
|
||||
|
||||
/**
|
||||
* 允许访问路径
|
||||
*/
|
||||
accessPath?: string;
|
||||
|
||||
/**
|
||||
* 允许访问路径列表
|
||||
*/
|
||||
accessPathList?: string[];
|
||||
|
||||
/**
|
||||
* IP白名单
|
||||
*/
|
||||
ipWhitelist?: string;
|
||||
|
||||
/**
|
||||
* IP白名单列表
|
||||
*/
|
||||
ipWhitelistList?: string[];
|
||||
|
||||
/**
|
||||
* token活跃超时时间
|
||||
*/
|
||||
@@ -76,6 +96,26 @@ export interface ClientForm extends BaseEntity {
|
||||
*/
|
||||
deviceType?: string;
|
||||
|
||||
/**
|
||||
* 允许访问路径
|
||||
*/
|
||||
accessPath?: string;
|
||||
|
||||
/**
|
||||
* 允许访问路径列表
|
||||
*/
|
||||
accessPathList?: string[];
|
||||
|
||||
/**
|
||||
* IP白名单
|
||||
*/
|
||||
ipWhitelist?: string;
|
||||
|
||||
/**
|
||||
* IP白名单列表
|
||||
*/
|
||||
ipWhitelistList?: string[];
|
||||
|
||||
/**
|
||||
* token活跃超时时间
|
||||
*/
|
||||
@@ -118,6 +158,16 @@ export interface ClientQuery extends PageQuery {
|
||||
*/
|
||||
deviceType?: string;
|
||||
|
||||
/**
|
||||
* 允许访问路径
|
||||
*/
|
||||
accessPath?: string;
|
||||
|
||||
/**
|
||||
* IP白名单
|
||||
*/
|
||||
ipWhitelist?: string;
|
||||
|
||||
/**
|
||||
* token活跃超时时间
|
||||
*/
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import type { PageResult } from '@/api/types';
|
||||
import type { AxiosPromise } from '@/utils/api-types';
|
||||
import request from '@/utils/request';
|
||||
import { ConfigForm, ConfigQuery, ConfigVO } from './types';
|
||||
import { AxiosPromise } from 'axios';
|
||||
import type { ConfigForm, ConfigQuery, ConfigVO } from './types';
|
||||
|
||||
// 查询参数列表
|
||||
export function listConfig(query: ConfigQuery): AxiosPromise<ConfigVO[]> {
|
||||
export function listConfig(query: ConfigQuery): AxiosPromise<PageResult<ConfigVO>> {
|
||||
return request({
|
||||
url: '/system/config/list',
|
||||
method: 'get',
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { AxiosPromise } from '@/utils/api-types';
|
||||
import request from '@/utils/request';
|
||||
import { AxiosPromise } from 'axios';
|
||||
import { DeptForm, DeptQuery, DeptTreeVO, DeptVO } from './types';
|
||||
import { type DeptForm, type DeptQuery, type DeptVO } from './types';
|
||||
|
||||
// 查询部门列表
|
||||
export const listDept = (query?: DeptQuery) => {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { PageResult } from '@/api/types';
|
||||
import type { AxiosPromise } from '@/utils/api-types';
|
||||
import request from '@/utils/request';
|
||||
import { AxiosPromise } from 'axios';
|
||||
import { DictDataForm, DictDataQuery, DictDataVO } from './types';
|
||||
import type { DictDataForm, DictDataQuery, DictDataVO } from './types';
|
||||
// 根据字典类型查询字典数据信息
|
||||
export function getDicts(dictType: string): AxiosPromise<DictDataVO[]> {
|
||||
return request({
|
||||
@@ -10,7 +11,7 @@ export function getDicts(dictType: string): AxiosPromise<DictDataVO[]> {
|
||||
}
|
||||
|
||||
// 查询字典数据列表
|
||||
export function listData(query: DictDataQuery): AxiosPromise<DictDataVO[]> {
|
||||
export function listData(query: DictDataQuery): AxiosPromise<PageResult<DictDataVO>> {
|
||||
return request({
|
||||
url: '/system/dict/data/list',
|
||||
method: 'get',
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import type { PageResult } from '@/api/types';
|
||||
import type { AxiosPromise } from '@/utils/api-types';
|
||||
import request from '@/utils/request';
|
||||
import { DictTypeForm, DictTypeVO, DictTypeQuery } from './types';
|
||||
import { AxiosPromise } from 'axios';
|
||||
import type { DictTypeForm, DictTypeQuery, DictTypeVO } from './types';
|
||||
|
||||
// 查询字典类型列表
|
||||
export function listType(query: DictTypeQuery): AxiosPromise<DictTypeVO[]> {
|
||||
export function listType(query: DictTypeQuery): AxiosPromise<PageResult<DictTypeVO>> {
|
||||
return request({
|
||||
url: '/system/dict/type/list',
|
||||
method: 'get',
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { AxiosPromise } from '@/utils/api-types';
|
||||
import request from '@/utils/request';
|
||||
import { AxiosPromise } from 'axios';
|
||||
import { MenuQuery, MenuVO, MenuForm, MenuTreeOption, RoleMenuTree } from './types';
|
||||
import type { MenuForm, MenuQuery, MenuTreeOption, MenuVO, RoleMenuTree } from './types';
|
||||
|
||||
// 查询菜单列表
|
||||
export const listMenu = (query?: MenuQuery): AxiosPromise<MenuVO[]> => {
|
||||
@@ -35,14 +35,6 @@ export const roleMenuTreeselect = (roleId: string | number): AxiosPromise<RoleMe
|
||||
});
|
||||
};
|
||||
|
||||
// 根据角色ID查询菜单下拉树结构
|
||||
export const tenantPackageMenuTreeselect = (packageId: string | number): AxiosPromise<RoleMenuTree> => {
|
||||
return request({
|
||||
url: '/system/menu/tenantPackageMenuTreeselect/' + packageId,
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
// 新增菜单
|
||||
export const addMenu = (data: MenuForm) => {
|
||||
return request({
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { MenuTypeEnum } from '@/enums/MenuTypeEnum';
|
||||
import type { MenuTypeEnum } from '@/enums/MenuTypeEnum';
|
||||
|
||||
/**
|
||||
* 菜单树形结构类型
|
||||
@@ -8,12 +8,28 @@ export interface MenuTreeOption {
|
||||
label: string;
|
||||
parentId: string | number;
|
||||
weight: number;
|
||||
menuType?: MenuTypeEnum | string;
|
||||
visible?: string;
|
||||
status?: string;
|
||||
disabled?: boolean;
|
||||
children?: MenuTreeOption[];
|
||||
}
|
||||
|
||||
export interface RoleMenuTree {
|
||||
menus: MenuTreeOption[];
|
||||
checkedKeys: string[];
|
||||
checkedKeys: Array<string | number>;
|
||||
}
|
||||
|
||||
/**
|
||||
* 角色菜单分配中的按钮节点类型
|
||||
*/
|
||||
export interface RoleMenuButtonOption {
|
||||
menuId: string | number;
|
||||
menuName: string;
|
||||
parentId: string | number;
|
||||
perms?: string;
|
||||
status?: string;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -44,6 +60,8 @@ export interface MenuVO extends BaseEntity {
|
||||
visible: string;
|
||||
status: string;
|
||||
icon: string;
|
||||
activeMenu: string;
|
||||
ext: string;
|
||||
remark: string;
|
||||
}
|
||||
|
||||
@@ -63,6 +81,8 @@ export interface MenuForm {
|
||||
visible?: string;
|
||||
status?: string;
|
||||
icon?: string;
|
||||
activeMenu?: string;
|
||||
ext?: string;
|
||||
remark?: string;
|
||||
query?: string;
|
||||
perms?: string;
|
||||
|
||||
10
src/api/system/message/index.ts
Normal file
10
src/api/system/message/index.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import type { AxiosPromise } from '@/utils/api-types';
|
||||
import request from '@/utils/request';
|
||||
import type { MessageBoxVO } from './types';
|
||||
|
||||
export function getMessageBox(): AxiosPromise<MessageBoxVO> {
|
||||
return request({
|
||||
url: '/resource/message/box',
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
17
src/api/system/message/types.ts
Normal file
17
src/api/system/message/types.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
export interface MessageVO extends BaseEntity {
|
||||
messageId: number | string;
|
||||
category: string;
|
||||
type: string;
|
||||
source: string;
|
||||
title: string;
|
||||
message: string;
|
||||
content?: string;
|
||||
data?: Record<string, any> | null;
|
||||
path?: string;
|
||||
}
|
||||
|
||||
export interface MessageBoxVO {
|
||||
systemList: MessageVO[];
|
||||
noticeList: MessageVO[];
|
||||
workflowList: MessageVO[];
|
||||
}
|
||||
@@ -1,8 +1,9 @@
|
||||
import type { PageResult } from '@/api/types';
|
||||
import type { AxiosPromise } from '@/utils/api-types';
|
||||
import request from '@/utils/request';
|
||||
import { NoticeForm, NoticeQuery, NoticeVO } from './types';
|
||||
import { AxiosPromise } from 'axios';
|
||||
import type { NoticeForm, NoticeQuery, NoticeVO } from './types';
|
||||
// 查询公告列表
|
||||
export function listNotice(query: NoticeQuery): AxiosPromise<NoticeVO[]> {
|
||||
export function listNotice(query: NoticeQuery): AxiosPromise<PageResult<NoticeVO>> {
|
||||
return request({
|
||||
url: '/system/notice/list',
|
||||
method: 'get',
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import type { PageResult } from '@/api/types';
|
||||
import type { AxiosPromise } from '@/utils/api-types';
|
||||
import request from '@/utils/request';
|
||||
import { OssQuery, OssVO } from './types';
|
||||
import { AxiosPromise } from 'axios';
|
||||
import type { OssQuery, OssUploadVO, OssVO } from './types';
|
||||
|
||||
// 查询OSS对象存储列表
|
||||
export function listOss(query: OssQuery): AxiosPromise<OssVO[]> {
|
||||
export function listOss(query: OssQuery): AxiosPromise<PageResult<OssVO>> {
|
||||
return request({
|
||||
url: '/resource/oss/list',
|
||||
method: 'get',
|
||||
@@ -19,6 +20,15 @@ export function listByIds(ossId: string | number): AxiosPromise<OssVO[]> {
|
||||
});
|
||||
}
|
||||
|
||||
// 上传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({
|
||||
|
||||
@@ -8,6 +8,12 @@ export interface OssVO extends BaseEntity {
|
||||
service: string;
|
||||
}
|
||||
|
||||
export interface OssUploadVO {
|
||||
url: string;
|
||||
fileName: string;
|
||||
ossId: string;
|
||||
}
|
||||
|
||||
export interface OssQuery extends PageQuery {
|
||||
fileName: string;
|
||||
originalName: string;
|
||||
@@ -20,3 +26,17 @@ export interface OssQuery extends PageQuery {
|
||||
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,9 +1,10 @@
|
||||
import type { PageResult } from '@/api/types';
|
||||
import type { AxiosPromise } from '@/utils/api-types';
|
||||
import request from '@/utils/request';
|
||||
import { OssConfigForm, OssConfigQuery, OssConfigVO } from './types';
|
||||
import { AxiosPromise } from 'axios';
|
||||
import type { OssConfigForm, OssConfigQuery, OssConfigVO } from './types';
|
||||
|
||||
// 查询对象存储配置列表
|
||||
export function listOssConfig(query: OssConfigQuery): AxiosPromise<OssConfigVO[]> {
|
||||
export function listOssConfig(query: OssConfigQuery): AxiosPromise<PageResult<OssConfigVO>> {
|
||||
return request({
|
||||
url: '/resource/oss/config/list',
|
||||
method: 'get',
|
||||
|
||||
@@ -6,7 +6,7 @@ export interface OssConfigVO extends BaseEntity {
|
||||
bucketName: string;
|
||||
prefix: string;
|
||||
endpoint: string;
|
||||
domain: string;
|
||||
domainUrl: string;
|
||||
isHttps: string;
|
||||
region: string;
|
||||
status: string;
|
||||
@@ -29,7 +29,7 @@ export interface OssConfigForm {
|
||||
bucketName: string;
|
||||
prefix: string;
|
||||
endpoint: string;
|
||||
domain: string;
|
||||
domainUrl: string;
|
||||
isHttps: string;
|
||||
accessPolicy: string;
|
||||
region: string;
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import type { PageResult } from '@/api/types';
|
||||
import type { AxiosPromise } from '@/utils/api-types';
|
||||
import request from '@/utils/request';
|
||||
import { PostForm, PostQuery, PostVO } from './types';
|
||||
import { AxiosPromise } from 'axios';
|
||||
import { DeptTreeVO } from '../dept/types';
|
||||
import type { DeptTreeVO } from '../dept/types';
|
||||
import type { PostForm, PostQuery, PostVO } from './types';
|
||||
|
||||
// 查询岗位列表
|
||||
export function listPost(query: PostQuery): AxiosPromise<PostVO[]> {
|
||||
export function listPost(query: PostQuery): AxiosPromise<PageResult<PostVO>> {
|
||||
return request({
|
||||
url: '/system/post/list',
|
||||
method: 'get',
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { UserVO } from '@/api/system/user/types';
|
||||
import { UserQuery } from '@/api/system/user/types';
|
||||
import { AxiosPromise } from 'axios';
|
||||
import { RoleQuery, RoleVO, RoleDeptTree } 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 const listRole = (query: RoleQuery): AxiosPromise<RoleVO[]> => {
|
||||
export const listRole = (query: RoleQuery): AxiosPromise<PageResult<RoleVO>> => {
|
||||
return request({
|
||||
url: '/system/role/list',
|
||||
method: 'get',
|
||||
@@ -45,7 +45,7 @@ export const addRole = (data: any) => {
|
||||
};
|
||||
|
||||
/**
|
||||
* 修改角色
|
||||
* 修改角色基础信息
|
||||
* @param data
|
||||
*/
|
||||
export const updateRole = (data: any) => {
|
||||
@@ -57,11 +57,11 @@ export const updateRole = (data: any) => {
|
||||
};
|
||||
|
||||
/**
|
||||
* 角色数据权限
|
||||
* 修改角色权限(菜单权限 + 数据权限)
|
||||
*/
|
||||
export const dataScope = (data: any) => {
|
||||
export const updateRolePermission = (data: any) => {
|
||||
return request({
|
||||
url: '/system/role/dataScope',
|
||||
url: '/system/role/permission',
|
||||
method: 'put',
|
||||
data: data
|
||||
});
|
||||
@@ -95,7 +95,7 @@ export const delRole = (roleId: Array<string | number> | string | number) => {
|
||||
/**
|
||||
* 查询角色已授权用户列表
|
||||
*/
|
||||
export const allocatedUserList = (query: UserQuery): AxiosPromise<UserVO[]> => {
|
||||
export const allocatedUserList = (query: UserQuery): AxiosPromise<PageResult<UserVO>> => {
|
||||
return request({
|
||||
url: '/system/role/authUser/allocatedList',
|
||||
method: 'get',
|
||||
@@ -106,7 +106,7 @@ export const allocatedUserList = (query: UserQuery): AxiosPromise<UserVO[]> => {
|
||||
/**
|
||||
* 查询角色未授权用户列表
|
||||
*/
|
||||
export const unallocatedUserList = (query: UserQuery): AxiosPromise<UserVO[]> => {
|
||||
export const unallocatedUserList = (query: UserQuery): AxiosPromise<PageResult<UserVO>> => {
|
||||
return request({
|
||||
url: '/system/role/authUser/unallocatedList',
|
||||
method: 'get',
|
||||
|
||||
@@ -1,14 +1,10 @@
|
||||
import request from '@/utils/request';
|
||||
|
||||
// 获取跳转URL
|
||||
export function authRouterUrl(source: string, tenantId: string) {
|
||||
export function authRouterUrl(source: string) {
|
||||
return request({
|
||||
url: '/auth/binding/' + source,
|
||||
method: 'get',
|
||||
params: {
|
||||
tenantId: tenantId,
|
||||
domain: window.location.host
|
||||
}
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,109 +0,0 @@
|
||||
import request from '@/utils/request';
|
||||
import { TenantForm, TenantQuery, TenantVO } from './types';
|
||||
import { AxiosPromise } from 'axios';
|
||||
|
||||
// 查询租户列表
|
||||
export function listTenant(query: TenantQuery): AxiosPromise<TenantVO[]> {
|
||||
return request({
|
||||
url: '/system/tenant/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
}
|
||||
|
||||
// 查询租户详细
|
||||
export function getTenant(id: string | number): AxiosPromise<TenantVO> {
|
||||
return request({
|
||||
url: '/system/tenant/' + id,
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
|
||||
// 新增租户
|
||||
export function addTenant(data: TenantForm) {
|
||||
return request({
|
||||
url: '/system/tenant',
|
||||
method: 'post',
|
||||
headers: {
|
||||
isEncrypt: true,
|
||||
repeatSubmit: false
|
||||
},
|
||||
data: data
|
||||
});
|
||||
}
|
||||
|
||||
// 修改租户
|
||||
export function updateTenant(data: TenantForm) {
|
||||
return request({
|
||||
url: '/system/tenant',
|
||||
method: 'put',
|
||||
data: data
|
||||
});
|
||||
}
|
||||
|
||||
// 租户状态修改
|
||||
export function changeTenantStatus(id: string | number, tenantId: string | number, status: string) {
|
||||
const data = {
|
||||
id,
|
||||
tenantId,
|
||||
status
|
||||
};
|
||||
return request({
|
||||
url: '/system/tenant/changeStatus',
|
||||
method: 'put',
|
||||
data: data
|
||||
});
|
||||
}
|
||||
|
||||
// 删除租户
|
||||
export function delTenant(id: string | number | Array<string | number>) {
|
||||
return request({
|
||||
url: '/system/tenant/' + id,
|
||||
method: 'delete'
|
||||
});
|
||||
}
|
||||
|
||||
// 动态切换租户
|
||||
export function dynamicTenant(tenantId: string | number) {
|
||||
return request({
|
||||
url: '/system/tenant/dynamic/' + tenantId,
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
|
||||
// 清除动态租户
|
||||
export function dynamicClear() {
|
||||
return request({
|
||||
url: '/system/tenant/dynamic/clear',
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
|
||||
// 同步租户套餐
|
||||
export function syncTenantPackage(tenantId: string | number, packageId: string | number) {
|
||||
const data = {
|
||||
tenantId,
|
||||
packageId
|
||||
};
|
||||
return request({
|
||||
url: '/system/tenant/syncTenantPackage',
|
||||
method: 'get',
|
||||
params: data
|
||||
});
|
||||
}
|
||||
|
||||
// 同步租户字典
|
||||
export function syncTenantDict() {
|
||||
return request({
|
||||
url: '/system/tenant/syncTenantDict',
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
|
||||
// 同步租户字典
|
||||
export function syncTenantConfig() {
|
||||
return request({
|
||||
url: '/system/tenant/syncTenantConfig',
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
export interface TenantVO extends BaseEntity {
|
||||
id: number | string;
|
||||
tenantId: number | string;
|
||||
username: string;
|
||||
contactUserName: string;
|
||||
contactPhone: string;
|
||||
companyName: string;
|
||||
licenseNumber: string;
|
||||
address: string;
|
||||
domain: string;
|
||||
intro: string;
|
||||
remark: string;
|
||||
packageId: string | number;
|
||||
expireTime: string;
|
||||
accountCount: number;
|
||||
status: string;
|
||||
}
|
||||
|
||||
export interface TenantQuery extends PageQuery {
|
||||
tenantId: string | number;
|
||||
|
||||
contactUserName: string;
|
||||
|
||||
contactPhone: string;
|
||||
|
||||
companyName: string;
|
||||
}
|
||||
|
||||
export interface TenantForm {
|
||||
id: number | string | undefined;
|
||||
tenantId: number | string | undefined;
|
||||
username: string;
|
||||
password: string;
|
||||
contactUserName: string;
|
||||
contactPhone: string;
|
||||
companyName: string;
|
||||
licenseNumber: string;
|
||||
domain: string;
|
||||
address: string;
|
||||
intro: string;
|
||||
remark: string;
|
||||
packageId: string | number;
|
||||
expireTime: string;
|
||||
accountCount: number;
|
||||
status: string;
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
import request from '@/utils/request';
|
||||
import { TenantPkgForm, TenantPkgQuery, TenantPkgVO } from './types';
|
||||
import { AxiosPromise } from 'axios';
|
||||
|
||||
// 查询租户套餐列表
|
||||
export function listTenantPackage(query?: TenantPkgQuery): AxiosPromise<TenantPkgVO[]> {
|
||||
return request({
|
||||
url: '/system/tenant/package/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
}
|
||||
|
||||
// 查询租户套餐下拉选列表
|
||||
export function selectTenantPackage(): AxiosPromise<TenantPkgVO[]> {
|
||||
return request({
|
||||
url: '/system/tenant/package/selectList',
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
|
||||
// 查询租户套餐详细
|
||||
export function getTenantPackage(packageId: string | number): AxiosPromise<TenantPkgVO> {
|
||||
return request({
|
||||
url: '/system/tenant/package/' + packageId,
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
|
||||
// 新增租户套餐
|
||||
export function addTenantPackage(data: TenantPkgForm) {
|
||||
return request({
|
||||
url: '/system/tenant/package',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
}
|
||||
|
||||
// 修改租户套餐
|
||||
export function updateTenantPackage(data: TenantPkgForm) {
|
||||
return request({
|
||||
url: '/system/tenant/package',
|
||||
method: 'put',
|
||||
data: data
|
||||
});
|
||||
}
|
||||
|
||||
// 租户套餐状态修改
|
||||
export function changePackageStatus(packageId: number | string, status: string) {
|
||||
const data = {
|
||||
packageId,
|
||||
status
|
||||
};
|
||||
return request({
|
||||
url: '/system/tenant/package/changeStatus',
|
||||
method: 'put',
|
||||
data: data
|
||||
});
|
||||
}
|
||||
|
||||
// 删除租户套餐
|
||||
export function delTenantPackage(packageId: string | number | Array<string | number>) {
|
||||
return request({
|
||||
url: '/system/tenant/package/' + packageId,
|
||||
method: 'delete'
|
||||
});
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
export interface TenantPkgVO extends BaseEntity {
|
||||
packageId: string | number;
|
||||
packageName: string;
|
||||
menuIds: string;
|
||||
remark: string;
|
||||
menuCheckStrictly: boolean;
|
||||
status: string;
|
||||
}
|
||||
|
||||
export interface TenantPkgQuery extends PageQuery {
|
||||
packageName: string;
|
||||
}
|
||||
|
||||
export interface TenantPkgForm {
|
||||
packageId: string | number | undefined;
|
||||
packageName: string;
|
||||
menuIds: string;
|
||||
remark: string;
|
||||
menuCheckStrictly: boolean;
|
||||
}
|
||||
@@ -1,15 +1,16 @@
|
||||
import { DeptTreeVO } from './../dept/types';
|
||||
import { RoleVO } from '@/api/system/role/types';
|
||||
import type { RoleVO } from '@/api/system/role/types';
|
||||
import type { PageResult } from '@/api/types';
|
||||
import type { AxiosPromise } from '@/utils/api-types';
|
||||
import request from '@/utils/request';
|
||||
import { AxiosPromise } from 'axios';
|
||||
import { UserForm, UserQuery, UserVO, UserInfoVO } from './types';
|
||||
import { parseStrEmpty } from '@/utils/ruoyi';
|
||||
import type { DeptTreeVO } from './../dept/types';
|
||||
import type { UserForm, UserInfoVO, UserProfileForm, UserQuery, UserVO } from './types';
|
||||
|
||||
/**
|
||||
* 查询用户列表
|
||||
* @param query
|
||||
*/
|
||||
export const listUser = (query: UserQuery): AxiosPromise<UserVO[]> => {
|
||||
export const listUser = (query: UserQuery): AxiosPromise<PageResult<UserVO>> => {
|
||||
return request({
|
||||
url: '/system/user/list',
|
||||
method: 'get',
|
||||
@@ -110,6 +111,17 @@ export const changeUserStatus = (userId: number | string, status: string) => {
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 解锁用户
|
||||
* @param userId 用户ID
|
||||
*/
|
||||
export const unlockUser = (userId: number | string) => {
|
||||
return request({
|
||||
url: '/system/user/unlock/' + userId,
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 查询用户个人信息
|
||||
*/
|
||||
@@ -124,7 +136,7 @@ export const getUserProfile = (): AxiosPromise<UserInfoVO> => {
|
||||
* 修改用户个人信息
|
||||
* @param data 用户信息
|
||||
*/
|
||||
export const updateUserProfile = (data: UserForm) => {
|
||||
export const updateUserProfile = (data: UserProfileForm) => {
|
||||
return request({
|
||||
url: '/system/user/profile',
|
||||
method: 'put',
|
||||
@@ -153,18 +165,6 @@ export const updateUserPwd = (oldPassword: string, newPassword: string) => {
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 用户头像上传
|
||||
* @param data 头像文件
|
||||
*/
|
||||
export const uploadAvatar = (data: FormData) => {
|
||||
return request({
|
||||
url: '/system/user/profile/avatar',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 查询授权角色
|
||||
* @param userId 用户ID
|
||||
@@ -218,10 +218,10 @@ export default {
|
||||
delUser,
|
||||
resetUserPwd,
|
||||
changeUserStatus,
|
||||
unlockUser,
|
||||
getUserProfile,
|
||||
updateUserProfile,
|
||||
updateUserPwd,
|
||||
uploadAvatar,
|
||||
getAuthRole,
|
||||
updateAuthRole,
|
||||
deptTreeSelect,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { RoleVO } from '@/api/system/role/types';
|
||||
import { PostVO } from '@/api/system/post/types';
|
||||
import type { PostVO } from '@/api/system/post/types';
|
||||
import type { RoleVO } from '@/api/system/role/types';
|
||||
|
||||
/**
|
||||
* 用户信息
|
||||
@@ -16,11 +16,11 @@ export interface UserInfo {
|
||||
export interface UserQuery extends PageQuery {
|
||||
userName?: string;
|
||||
nickName?: string;
|
||||
phonenumber?: string;
|
||||
phoneNumber?: string;
|
||||
status?: string;
|
||||
deptId?: string | number;
|
||||
roleId?: string | number;
|
||||
userIds?: string | number | (string | number)[] | undefined;
|
||||
userIds?: string | number | (string | number)[] | undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -34,8 +34,8 @@ export interface UserVO extends BaseEntity {
|
||||
nickName: string;
|
||||
userType: string;
|
||||
email: string;
|
||||
phonenumber: string;
|
||||
sex: string;
|
||||
phoneNumber: string;
|
||||
gender: string;
|
||||
avatar: string;
|
||||
status: string;
|
||||
delFlag: string;
|
||||
@@ -43,6 +43,8 @@ export interface UserVO extends BaseEntity {
|
||||
loginDate: string;
|
||||
remark: string;
|
||||
deptName: string;
|
||||
/** 详情接口可能返回嵌套部门 */
|
||||
dept?: { deptName?: string };
|
||||
roles: RoleVO[];
|
||||
roleIds: any;
|
||||
postIds: any;
|
||||
@@ -60,15 +62,26 @@ export interface UserForm {
|
||||
userName: string;
|
||||
nickName?: string;
|
||||
password: string;
|
||||
phonenumber?: string;
|
||||
phoneNumber?: string;
|
||||
email?: string;
|
||||
sex?: string;
|
||||
gender?: string;
|
||||
status: string;
|
||||
remark?: string;
|
||||
postIds: string[];
|
||||
roleIds: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* 个人资料表单类型
|
||||
*/
|
||||
export interface UserProfileForm {
|
||||
nickName?: string;
|
||||
phoneNumber?: string;
|
||||
email?: string;
|
||||
gender?: string;
|
||||
avatar?: string | number;
|
||||
}
|
||||
|
||||
export interface UserInfoVO {
|
||||
user: UserVO;
|
||||
roles: RoleVO[];
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
import type { PageResult } from '@/api/types';
|
||||
import type { AxiosPromise } from '@/utils/api-types';
|
||||
import request from '@/utils/request';
|
||||
import { DbTableQuery, DbTableVO, TableQuery, TableVO, GenTableVO, DbTableForm } from './types';
|
||||
import { AxiosPromise } from 'axios';
|
||||
import type { DbTableForm, DbTableQuery, DbTableVO, GenTableDetailPayload, TableQuery, TableVO } from './types';
|
||||
|
||||
export type { GenTableDetailPayload } from './types';
|
||||
|
||||
// 查询生成表数据
|
||||
export const listTable = (query: TableQuery): AxiosPromise<TableVO[]> => {
|
||||
export const listTable = (query: TableQuery): AxiosPromise<PageResult<TableVO>> => {
|
||||
return request({
|
||||
url: '/tool/gen/list',
|
||||
method: 'get',
|
||||
@@ -11,7 +14,7 @@ export const listTable = (query: TableQuery): AxiosPromise<TableVO[]> => {
|
||||
});
|
||||
};
|
||||
// 查询db数据库列表
|
||||
export const listDbTable = (query: DbTableQuery): AxiosPromise<DbTableVO[]> => {
|
||||
export const listDbTable = (query: DbTableQuery): AxiosPromise<PageResult<DbTableVO>> => {
|
||||
return request({
|
||||
url: '/tool/gen/db/list',
|
||||
method: 'get',
|
||||
@@ -20,7 +23,7 @@ export const listDbTable = (query: DbTableQuery): AxiosPromise<DbTableVO[]> => {
|
||||
};
|
||||
|
||||
// 查询表详细信息
|
||||
export const getGenTable = (tableId: string | number): AxiosPromise<GenTableVO> => {
|
||||
export const getGenTable = (tableId: string | number): AxiosPromise<GenTableDetailPayload> => {
|
||||
return request({
|
||||
url: '/tool/gen/' + tableId,
|
||||
method: 'get'
|
||||
@@ -28,7 +31,7 @@ export const getGenTable = (tableId: string | number): AxiosPromise<GenTableVO>
|
||||
};
|
||||
|
||||
// 修改代码生成信息
|
||||
export const updateGenTable = (data: DbTableForm): AxiosPromise<GenTableVO> => {
|
||||
export const updateGenTable = (data: DbTableForm): AxiosPromise<unknown> => {
|
||||
return request({
|
||||
url: '/tool/gen',
|
||||
method: 'put',
|
||||
@@ -37,7 +40,7 @@ export const updateGenTable = (data: DbTableForm): AxiosPromise<GenTableVO> => {
|
||||
};
|
||||
|
||||
// 导入表
|
||||
export const importTable = (data: { tables: string; dataName: string }): AxiosPromise<GenTableVO> => {
|
||||
export const importTable = (data: { tables: string; dataName: string }): AxiosPromise<unknown> => {
|
||||
return request({
|
||||
url: '/tool/gen/importTable',
|
||||
method: 'post',
|
||||
@@ -61,14 +64,6 @@ export const delTable = (tableId: string | number | Array<string | number>) => {
|
||||
});
|
||||
};
|
||||
|
||||
// 生成代码(自定义路径)
|
||||
export const genCode = (tableId: string | number) => {
|
||||
return request({
|
||||
url: '/tool/gen/genCode/' + tableId,
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
// 同步数据库
|
||||
export const synchDb = (tableId: string | number) => {
|
||||
return request({
|
||||
|
||||
@@ -4,17 +4,14 @@ export interface TableVO extends BaseEntity {
|
||||
dataName: string;
|
||||
tableName: string;
|
||||
tableComment: string;
|
||||
subTableName?: any;
|
||||
subTableFkName?: any;
|
||||
className: string;
|
||||
tplCategory: string;
|
||||
frontendType: string;
|
||||
packageName: string;
|
||||
moduleName: string;
|
||||
businessName: string;
|
||||
functionName: string;
|
||||
functionAuthor: string;
|
||||
genType: string;
|
||||
genPath: string;
|
||||
pkColumn?: any;
|
||||
columns?: any;
|
||||
options?: any;
|
||||
@@ -25,6 +22,16 @@ export interface TableVO extends BaseEntity {
|
||||
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;
|
||||
}
|
||||
@@ -72,17 +79,14 @@ export interface DbTableVO {
|
||||
tableId?: any;
|
||||
tableName: string;
|
||||
tableComment: string;
|
||||
subTableName?: any;
|
||||
subTableFkName?: any;
|
||||
className?: any;
|
||||
tplCategory?: any;
|
||||
frontendType?: string;
|
||||
packageName?: any;
|
||||
moduleName?: any;
|
||||
businessName?: any;
|
||||
functionName?: any;
|
||||
functionAuthor?: any;
|
||||
genType?: any;
|
||||
genPath?: any;
|
||||
pkColumn?: any;
|
||||
columns: DbColumnVO[];
|
||||
options?: any;
|
||||
@@ -93,6 +97,16 @@ export interface DbTableVO {
|
||||
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;
|
||||
}
|
||||
@@ -103,10 +117,14 @@ export interface DbTableQuery extends PageQuery {
|
||||
tableComment: string;
|
||||
}
|
||||
|
||||
export interface GenTableVO {
|
||||
/**
|
||||
* 代码生成表详情接口 data 结构
|
||||
* - info:当前表 GenTable
|
||||
* - rows:字段列表 GenTableColumn[]
|
||||
*/
|
||||
export interface GenTableDetailPayload {
|
||||
info: DbTableVO;
|
||||
rows: DbColumnVO[];
|
||||
tables: DbTableVO[];
|
||||
}
|
||||
|
||||
export interface DbColumnForm extends BaseEntity {
|
||||
@@ -146,6 +164,16 @@ export interface DbParamForm {
|
||||
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 {
|
||||
@@ -153,17 +181,14 @@ export interface DbTableForm extends BaseEntity {
|
||||
tableId: string | string;
|
||||
tableName: string;
|
||||
tableComment: string;
|
||||
subTableName?: any;
|
||||
subTableFkName?: any;
|
||||
className: string;
|
||||
tplCategory: string;
|
||||
frontendType: string;
|
||||
packageName: string;
|
||||
moduleName: string;
|
||||
businessName: string;
|
||||
functionName: string;
|
||||
functionAuthor: string;
|
||||
genType: string;
|
||||
genPath: string;
|
||||
pkColumn?: any;
|
||||
columns: DbColumnForm[];
|
||||
options: string;
|
||||
@@ -174,6 +199,16 @@ export interface DbTableForm extends BaseEntity {
|
||||
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;
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
* 注册
|
||||
*/
|
||||
export type RegisterForm = {
|
||||
tenantId: string;
|
||||
username: string;
|
||||
password: string;
|
||||
confirmPassword?: string;
|
||||
@@ -15,7 +14,6 @@ export type RegisterForm = {
|
||||
* 登录请求
|
||||
*/
|
||||
export interface LoginData {
|
||||
tenantId?: string;
|
||||
username?: string;
|
||||
password?: string;
|
||||
rememberMe?: boolean;
|
||||
@@ -45,15 +43,9 @@ export interface VerifyCodeResult {
|
||||
}
|
||||
|
||||
/**
|
||||
* 租户
|
||||
* 分页返回结果
|
||||
*/
|
||||
export interface TenantVO {
|
||||
companyName: string;
|
||||
domain: any;
|
||||
tenantId: string;
|
||||
}
|
||||
|
||||
export interface TenantInfo {
|
||||
tenantEnabled: boolean;
|
||||
voList: TenantVO[];
|
||||
export interface PageResult<T = any> {
|
||||
total: number;
|
||||
rows: T[];
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { CategoryForm, CategoryQuery, CategoryTreeVO, CategoryVO } from '@/api/workflow/category/types';
|
||||
import type { AxiosPromise } from '@/utils/api-types';
|
||||
import request from '@/utils/request';
|
||||
import { AxiosPromise } from 'axios';
|
||||
import { CategoryVO, CategoryForm, CategoryQuery, CategoryTreeVO } from '@/api/workflow/category/types';
|
||||
|
||||
/**
|
||||
* 查询流程分类列表
|
||||
|
||||
@@ -1,13 +1,19 @@
|
||||
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';
|
||||
import { FlowDefinitionQuery, definitionXmlVO, FlowDefinitionForm, FlowDefinitionVo } from '@/api/workflow/definition/types';
|
||||
import { AxiosPromise } from 'axios';
|
||||
|
||||
/**
|
||||
* 获取流程定义列表
|
||||
* @param query 流程实例id
|
||||
* @returns
|
||||
*/
|
||||
export const listDefinition = (query: FlowDefinitionQuery): AxiosPromise<FlowDefinitionVo[]> => {
|
||||
export const listDefinition = (query: FlowDefinitionQuery): AxiosPromise<PageResult<FlowDefinitionVo>> => {
|
||||
return request({
|
||||
url: `/workflow/definition/list`,
|
||||
method: 'get',
|
||||
@@ -20,7 +26,7 @@ export const listDefinition = (query: FlowDefinitionQuery): AxiosPromise<FlowDef
|
||||
* @param query 流程实例id
|
||||
* @returns
|
||||
*/
|
||||
export const unPublishList = (query: FlowDefinitionQuery): AxiosPromise<FlowDefinitionVo[]> => {
|
||||
export const unPublishList = (query: FlowDefinitionQuery): AxiosPromise<PageResult<FlowDefinitionVo>> => {
|
||||
return request({
|
||||
url: `/workflow/definition/unPublishList`,
|
||||
method: 'get',
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
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';
|
||||
import { FlowInstanceQuery, FlowInstanceVO } from '@/api/workflow/instance/types';
|
||||
import { AxiosPromise } from 'axios';
|
||||
|
||||
/**
|
||||
* 查询运行中实例列表
|
||||
* @param query
|
||||
* @returns {*}
|
||||
*/
|
||||
export const pageByRunning = (query: FlowInstanceQuery): AxiosPromise<FlowInstanceVO[]> => {
|
||||
export const pageByRunning = (query: FlowInstanceQuery): AxiosPromise<PageResult<FlowInstanceVO>> => {
|
||||
return request({
|
||||
url: '/workflow/instance/pageByRunning',
|
||||
method: 'get',
|
||||
@@ -20,7 +21,7 @@ export const pageByRunning = (query: FlowInstanceQuery): AxiosPromise<FlowInstan
|
||||
* @param query
|
||||
* @returns {*}
|
||||
*/
|
||||
export const pageByFinish = (query: FlowInstanceQuery): AxiosPromise<FlowInstanceVO[]> => {
|
||||
export const pageByFinish = (query: FlowInstanceQuery): AxiosPromise<PageResult<FlowInstanceVO>> => {
|
||||
return request({
|
||||
url: '/workflow/instance/pageByFinish',
|
||||
method: 'get',
|
||||
@@ -33,7 +34,7 @@ export const pageByFinish = (query: FlowInstanceQuery): AxiosPromise<FlowInstanc
|
||||
*/
|
||||
export const flowHisTaskList = (businessId: string | number) => {
|
||||
return request({
|
||||
url: `/workflow/instance/flowHisTaskList/${businessId}` + '?t' + Math.random(),
|
||||
url: `/workflow/instance/flowHisTaskList/${businessId}?t=${Math.random()}`,
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
@@ -43,7 +44,7 @@ export const flowHisTaskList = (businessId: string | number) => {
|
||||
* @param query
|
||||
* @returns {*}
|
||||
*/
|
||||
export const pageByCurrent = (query: FlowInstanceQuery): AxiosPromise<FlowInstanceVO[]> => {
|
||||
export const pageByCurrent = (query: FlowInstanceQuery): AxiosPromise<PageResult<FlowInstanceVO>> => {
|
||||
return request({
|
||||
url: '/workflow/instance/pageByCurrent',
|
||||
method: 'get',
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { FlowTaskVO } from '@/api/workflow/task/types';
|
||||
import type { FlowTaskVO } from '@/api/workflow/task/types';
|
||||
|
||||
export interface FlowInstanceQuery extends PageQuery {
|
||||
category?: string | number;
|
||||
nodeName?: string;
|
||||
flowCode?: string;
|
||||
flowName?: string;
|
||||
createByIds?: string[] | number[];
|
||||
createByIds?: Array<string | number>;
|
||||
businessId?: string;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
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';
|
||||
import { AxiosPromise } from 'axios';
|
||||
import { LeaveVO, LeaveQuery, LeaveForm } from '@/api/workflow/leave/types';
|
||||
|
||||
/**
|
||||
* 查询请假列表
|
||||
@@ -8,7 +9,7 @@ import { LeaveVO, LeaveQuery, LeaveForm } from '@/api/workflow/leave/types';
|
||||
* @returns {*}
|
||||
*/
|
||||
|
||||
export const listLeave = (query?: LeaveQuery): AxiosPromise<LeaveVO[]> => {
|
||||
export const listLeave = (query?: LeaveQuery): AxiosPromise<PageResult<LeaveVO>> => {
|
||||
return request({
|
||||
url: '/workflow/leave/list',
|
||||
method: 'get',
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
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';
|
||||
import { AxiosPromise } from 'axios';
|
||||
import { SpelVO, SpelForm, SpelQuery } from '@/api/workflow/spel/types';
|
||||
|
||||
/**
|
||||
* 查询流程spel表达式定义列表
|
||||
@@ -8,7 +9,7 @@ import { SpelVO, SpelForm, SpelQuery } from '@/api/workflow/spel/types';
|
||||
* @returns {*}
|
||||
*/
|
||||
|
||||
export const listSpel = (query?: SpelQuery): AxiosPromise<SpelVO[]> => {
|
||||
export const listSpel = (query?: SpelQuery): AxiosPromise<PageResult<SpelVO>> => {
|
||||
return request({
|
||||
url: '/workflow/spel/list',
|
||||
method: 'get',
|
||||
|
||||
@@ -33,7 +33,6 @@ export interface SpelVO {
|
||||
* 备注
|
||||
*/
|
||||
remark?: string;
|
||||
|
||||
}
|
||||
|
||||
export interface SpelForm extends BaseEntity {
|
||||
@@ -71,11 +70,9 @@ export interface SpelForm extends BaseEntity {
|
||||
* 备注
|
||||
*/
|
||||
remark?: string;
|
||||
|
||||
}
|
||||
|
||||
export interface SpelQuery extends PageQuery {
|
||||
|
||||
/**
|
||||
* 组件名称
|
||||
*/
|
||||
@@ -101,11 +98,8 @@ export interface SpelQuery extends PageQuery {
|
||||
*/
|
||||
status?: string;
|
||||
|
||||
/**
|
||||
* 日期范围参数
|
||||
*/
|
||||
params?: any;
|
||||
/**
|
||||
* 日期范围参数
|
||||
*/
|
||||
params?: any;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
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';
|
||||
import { AxiosPromise } from 'axios';
|
||||
import { TaskQuery, FlowTaskVO, TaskOperationBo } from '@/api/workflow/task/types';
|
||||
|
||||
/**
|
||||
* 查询待办列表
|
||||
* @param query
|
||||
* @returns {*}
|
||||
*/
|
||||
export const pageByTaskWait = (query: TaskQuery): AxiosPromise<FlowTaskVO[]> => {
|
||||
export const pageByTaskWait = (query: TaskQuery): AxiosPromise<PageResult<FlowTaskVO>> => {
|
||||
return request({
|
||||
url: '/workflow/task/pageByTaskWait',
|
||||
method: 'get',
|
||||
@@ -20,7 +21,7 @@ export const pageByTaskWait = (query: TaskQuery): AxiosPromise<FlowTaskVO[]> =>
|
||||
* @param query
|
||||
* @returns {*}
|
||||
*/
|
||||
export const pageByTaskFinish = (query: TaskQuery): AxiosPromise<FlowTaskVO[]> => {
|
||||
export const pageByTaskFinish = (query: TaskQuery): AxiosPromise<PageResult<FlowTaskVO>> => {
|
||||
return request({
|
||||
url: '/workflow/task/pageByTaskFinish',
|
||||
method: 'get',
|
||||
@@ -33,7 +34,7 @@ export const pageByTaskFinish = (query: TaskQuery): AxiosPromise<FlowTaskVO[]> =
|
||||
* @param query
|
||||
* @returns {*}
|
||||
*/
|
||||
export const pageByTaskCopy = (query: TaskQuery): AxiosPromise<FlowTaskVO[]> => {
|
||||
export const pageByTaskCopy = (query: TaskQuery): AxiosPromise<PageResult<FlowTaskVO>> => {
|
||||
return request({
|
||||
url: '/workflow/task/pageByTaskCopy',
|
||||
method: 'get',
|
||||
@@ -42,11 +43,11 @@ export const pageByTaskCopy = (query: TaskQuery): AxiosPromise<FlowTaskVO[]> =>
|
||||
};
|
||||
|
||||
/**
|
||||
* 当前租户所有待办任务
|
||||
* 查询全部待办任务
|
||||
* @param query
|
||||
* @returns {*}
|
||||
*/
|
||||
export const pageByAllTaskWait = (query: TaskQuery): AxiosPromise<FlowTaskVO[]> => {
|
||||
export const pageByAllTaskWait = (query: TaskQuery): AxiosPromise<PageResult<FlowTaskVO>> => {
|
||||
return request({
|
||||
url: '/workflow/task/pageByAllTaskWait',
|
||||
method: 'get',
|
||||
@@ -55,11 +56,11 @@ export const pageByAllTaskWait = (query: TaskQuery): AxiosPromise<FlowTaskVO[]>
|
||||
};
|
||||
|
||||
/**
|
||||
* 当前租户所有已办任务
|
||||
* 查询全部已办任务
|
||||
* @param query
|
||||
* @returns {*}
|
||||
*/
|
||||
export const pageByAllTaskFinish = (query: TaskQuery): AxiosPromise<FlowTaskVO[]> => {
|
||||
export const pageByAllTaskFinish = (query: TaskQuery): AxiosPromise<PageResult<FlowTaskVO>> => {
|
||||
return request({
|
||||
url: '/workflow/task/pageByAllTaskFinish',
|
||||
method: 'get',
|
||||
|
||||
@@ -2,7 +2,7 @@ export interface TaskQuery extends PageQuery {
|
||||
nodeName?: string;
|
||||
flowCode?: string;
|
||||
flowName?: string;
|
||||
createByIds?: string[] | number[];
|
||||
createByIds?: Array<string | number>;
|
||||
}
|
||||
|
||||
export interface ParticipantVo {
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import { RouterJumpVo } from '@/api/workflow/workflowCommon/types';
|
||||
import type { RouterJumpVo } from '@/api/workflow/workflowCommon/types';
|
||||
import tab from '@/plugins/tab';
|
||||
import router from '@/router';
|
||||
|
||||
export default {
|
||||
routerJump(routerJumpVo: RouterJumpVo, proxy) {
|
||||
proxy.$tab.closePage(proxy.$route);
|
||||
proxy.$router.push({
|
||||
routerJump(routerJumpVo: RouterJumpVo) {
|
||||
tab.closePage(router.currentRoute.value);
|
||||
router.push({
|
||||
path: routerJumpVo.formPath,
|
||||
query: {
|
||||
id: routerJumpVo.businessId,
|
||||
|
||||
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
90
src/assets/styles/base/_document.scss
Normal file
90
src/assets/styles/base/_document.scss
Normal file
@@ -0,0 +1,90 @@
|
||||
/* Global document styles and lightweight utility helpers. */
|
||||
|
||||
// --- 视口与排版根 ---
|
||||
body {
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
text-rendering: optimizeLegibility;
|
||||
background: var(--app-shell-bg);
|
||||
font-family:
|
||||
'MiSans', 'HarmonyOS Sans SC', 'PingFang SC', 'Source Han Sans SC', 'Noto Sans SC', 'Hiragino Sans GB',
|
||||
'Microsoft YaHei', sans-serif;
|
||||
}
|
||||
|
||||
label {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
html {
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html.dark .svg-icon,
|
||||
html.dark svg {
|
||||
fill: var(--el-text-color-regular);
|
||||
}
|
||||
|
||||
#app {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
html,
|
||||
body,
|
||||
#app {
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
*,
|
||||
*:before,
|
||||
*:after {
|
||||
box-sizing: inherit;
|
||||
}
|
||||
|
||||
a:focus,
|
||||
a:active {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
a,
|
||||
a:focus,
|
||||
a:hover {
|
||||
cursor: pointer;
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
div:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.clearfix {
|
||||
&:after {
|
||||
visibility: hidden;
|
||||
display: block;
|
||||
font-size: 0;
|
||||
content: ' ';
|
||||
clear: both;
|
||||
height: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.h1,
|
||||
.h2,
|
||||
.h3,
|
||||
.h4,
|
||||
.h5,
|
||||
.h6,
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
font-family: inherit;
|
||||
font-weight: 500;
|
||||
line-height: 1.1;
|
||||
color: inherit;
|
||||
}
|
||||
@@ -1,99 +0,0 @@
|
||||
@use './variables.module.scss' as *;
|
||||
|
||||
@mixin colorBtn($color) {
|
||||
background: $color;
|
||||
|
||||
&:hover {
|
||||
color: $color;
|
||||
|
||||
&:before,
|
||||
&:after {
|
||||
background: $color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.blue-btn {
|
||||
@include colorBtn($blue);
|
||||
}
|
||||
|
||||
.light-blue-btn {
|
||||
@include colorBtn($light-blue);
|
||||
}
|
||||
|
||||
.red-btn {
|
||||
@include colorBtn($red);
|
||||
}
|
||||
|
||||
.pink-btn {
|
||||
@include colorBtn($pink);
|
||||
}
|
||||
|
||||
.green-btn {
|
||||
@include colorBtn($green);
|
||||
}
|
||||
|
||||
.tiffany-btn {
|
||||
@include colorBtn($tiffany);
|
||||
}
|
||||
|
||||
.yellow-btn {
|
||||
@include colorBtn($yellow);
|
||||
}
|
||||
|
||||
.pan-btn {
|
||||
font-size: 14px;
|
||||
color: #fff;
|
||||
padding: 14px 36px;
|
||||
border-radius: var(--app-radius-md);
|
||||
border: none;
|
||||
outline: none;
|
||||
transition: 600ms ease all;
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
|
||||
&:hover {
|
||||
background: #fff;
|
||||
|
||||
&:before,
|
||||
&:after {
|
||||
width: 100%;
|
||||
transition: 600ms ease all;
|
||||
}
|
||||
}
|
||||
|
||||
&:before,
|
||||
&:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
height: 2px;
|
||||
width: 0;
|
||||
transition: 400ms ease all;
|
||||
}
|
||||
|
||||
&::after {
|
||||
right: inherit;
|
||||
top: inherit;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.custom-button {
|
||||
display: inline-block;
|
||||
line-height: 1;
|
||||
white-space: nowrap;
|
||||
cursor: pointer;
|
||||
background: #fff;
|
||||
color: #fff;
|
||||
-webkit-appearance: none;
|
||||
text-align: center;
|
||||
box-sizing: border-box;
|
||||
outline: 0;
|
||||
margin: 0;
|
||||
padding: 10px 15px;
|
||||
font-size: 14px;
|
||||
border-radius: var(--app-radius-sm);
|
||||
}
|
||||
67
src/assets/styles/components/_card-shell.scss
Normal file
67
src/assets/styles/components/_card-shell.scss
Normal file
@@ -0,0 +1,67 @@
|
||||
/* 业务列表/卡片页:分页、树、列表、el-card 等(与 vendors/element-plus 通用覆盖叠加,入口顺序见 index.scss)。
|
||||
* 属「页面模式」而非 EP 公共主题,故放在 components。 */
|
||||
|
||||
// --- 历史「表单分区标题」样式 ---
|
||||
.form-header {
|
||||
font-size: 15px;
|
||||
color: var(--el-color-primary);
|
||||
border-bottom: 1px solid var(--app-surface-border);
|
||||
margin: 8px 10px 25px 10px;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
|
||||
// --- 树形容器描边(暗色覆盖在 _search-panel) ---
|
||||
.tree-border {
|
||||
margin-top: 8px;
|
||||
border: 1px solid var(--app-surface-border);
|
||||
background: var(--app-surface-bg);
|
||||
border-radius: 22px;
|
||||
width: 100%;
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.pagination-container .el-pagination > .el-pagination__jump {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.pagination-container .el-pagination > .el-pagination__sizes {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
.el-tree-node__content > .el-checkbox {
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
// --- el-card 本业务页外观(与 EP vendors 叠加) ---
|
||||
.el-card__header {
|
||||
padding: 14px 16px 10px !important;
|
||||
min-height: auto;
|
||||
background: transparent;
|
||||
border-bottom: 1px solid var(--app-surface-border);
|
||||
}
|
||||
|
||||
.el-card__body {
|
||||
padding: 16px !important;
|
||||
}
|
||||
|
||||
.el-card {
|
||||
border-radius: var(--app-radius-base);
|
||||
box-shadow: var(--app-shadow-sm);
|
||||
border-color: var(--app-surface-border);
|
||||
overflow: hidden;
|
||||
transition:
|
||||
box-shadow 0.2s ease,
|
||||
border-color 0.2s ease;
|
||||
background: var(--app-surface-bg);
|
||||
}
|
||||
|
||||
.el-card:hover {
|
||||
box-shadow: var(--app-shadow-md);
|
||||
border-color: var(--app-accent-soft);
|
||||
}
|
||||
|
||||
.card-box {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
76
src/assets/styles/components/_legacy-utilities.scss
Normal file
76
src/assets/styles/components/_legacy-utilities.scss
Normal file
@@ -0,0 +1,76 @@
|
||||
/* 工具类 */
|
||||
|
||||
.pt5 {
|
||||
padding-top: 5px;
|
||||
}
|
||||
|
||||
.pr5 {
|
||||
padding-right: 5px;
|
||||
}
|
||||
|
||||
.pl5 {
|
||||
padding-left: 5px;
|
||||
}
|
||||
|
||||
.pb5 {
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
|
||||
.mt5 {
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.mr5 {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.mb5 {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.mb8 {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.ml5 {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.mt10 {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.mr10 {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.mb10 {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.ml10 {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.mt20 {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.mr20 {
|
||||
margin-right: 20px;
|
||||
}
|
||||
|
||||
.mb20 {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.ml20 {
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
.el-dialog.scrollbar .el-dialog__body {
|
||||
overflow: auto;
|
||||
overflow-x: hidden;
|
||||
max-height: 70vh;
|
||||
padding: 14px 22px 4px;
|
||||
}
|
||||
9
src/assets/styles/components/_misc.scss
Normal file
9
src/assets/styles/components/_misc.scss
Normal file
@@ -0,0 +1,9 @@
|
||||
.link-type,
|
||||
.link-type:focus {
|
||||
color: var(--el-color-primary);
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
color: var(--app-accent-strong);
|
||||
}
|
||||
}
|
||||
200
src/assets/styles/components/_page-shell.scss
Normal file
200
src/assets/styles/components/_page-shell.scss
Normal file
@@ -0,0 +1,200 @@
|
||||
/* Shared vertical page shell spacing helpers. */
|
||||
|
||||
.p-2 {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.app-main > .p-2 {
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
.p-2 > .el-row,
|
||||
.p-2 > .el-card,
|
||||
.p-2 > .search-wrap,
|
||||
.p-2 > div,
|
||||
.p-2 > section {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
|
||||
// --- CRUD / 树表页用混入(在 .vue 内 @use 后 @include) ---
|
||||
|
||||
@mixin action-link-buttons($background: rgba(53, 109, 255, 0.08)) {
|
||||
.data-table {
|
||||
:deep(.el-button.is-link) {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 10px;
|
||||
background: $background;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@mixin toolbar-responsive($mobile-breakpoint: 900px) {
|
||||
@media (max-width: $mobile-breakpoint) {
|
||||
.toolbar-shell {
|
||||
align-items: flex-start;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@mixin content-stack($gap: 12px) {
|
||||
.content-main {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: $gap;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin dept-tree-panel($padding-top: 6px) {
|
||||
.dept-tree {
|
||||
padding-top: $padding-top;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin collapsible-tree-layout($mobile-breakpoint: 900px) {
|
||||
.content-grid,
|
||||
.selector-layout {
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.tree-panel-col,
|
||||
.tree-content-col {
|
||||
min-width: 0;
|
||||
transition:
|
||||
max-width 0.24s ease,
|
||||
flex-basis 0.24s ease;
|
||||
}
|
||||
|
||||
.tree-panel-col.is-collapsed {
|
||||
max-width: 56px;
|
||||
flex: 0 0 56px;
|
||||
}
|
||||
|
||||
.tree-content-col.is-tree-collapsed {
|
||||
max-width: calc(100% - 56px);
|
||||
flex: 0 0 calc(100% - 56px);
|
||||
}
|
||||
|
||||
.tree-panel-shell,
|
||||
.side-panel {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.tree-panel-shell {
|
||||
--tree-panel-max-height: 620px;
|
||||
}
|
||||
|
||||
.tree-panel-shell :deep(.el-card__header) {
|
||||
display: block;
|
||||
padding: 12px 16px !important;
|
||||
}
|
||||
|
||||
.tree-panel-shell :deep(.el-card__body) {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: var(--tree-panel-max-height);
|
||||
min-height: 0;
|
||||
max-height: var(--tree-panel-max-height);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.tree-panel-header {
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.tree-panel-header::after {
|
||||
display: block;
|
||||
width: 9px;
|
||||
min-width: 9px;
|
||||
height: 9px;
|
||||
margin-left: auto;
|
||||
flex-shrink: 0;
|
||||
transform-origin: center;
|
||||
transform: rotate(135deg);
|
||||
}
|
||||
|
||||
.tree-panel-header.is-collapsed {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.side-panel.is-collapsed :deep(.el-card__body) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.dept-tree,
|
||||
.selector-tree {
|
||||
flex: 1 1 auto;
|
||||
min-height: 180px;
|
||||
max-height: 100%;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
padding-right: 4px;
|
||||
scrollbar-width: thin;
|
||||
}
|
||||
|
||||
.dept-tree::-webkit-scrollbar,
|
||||
.selector-tree::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
|
||||
.dept-tree::-webkit-scrollbar-thumb,
|
||||
.selector-tree::-webkit-scrollbar-thumb {
|
||||
border-radius: 999px;
|
||||
background: var(--app-text-muted);
|
||||
opacity: 0.55;
|
||||
}
|
||||
|
||||
.dept-tree::-webkit-scrollbar-track,
|
||||
.selector-tree::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.tree-panel-shell.is-collapsed :deep(.el-card__header) {
|
||||
padding: 12px 0 !important;
|
||||
}
|
||||
|
||||
.tree-panel-shell.is-collapsed .tree-panel-header::after {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.tree-panel-shell.is-collapsed .tree-panel-header::after {
|
||||
transform: rotate(-45deg);
|
||||
}
|
||||
|
||||
@media (max-width: $mobile-breakpoint) {
|
||||
.tree-panel-col,
|
||||
.tree-content-col {
|
||||
max-width: 100%;
|
||||
flex: 0 0 100%;
|
||||
}
|
||||
|
||||
.tree-panel-shell {
|
||||
--tree-panel-max-height: 420px;
|
||||
}
|
||||
|
||||
.side-panel.is-collapsed {
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@mixin table-crud-page($mobile-breakpoint: 900px, $background: rgba(53, 109, 255, 0.08)) {
|
||||
@include action-link-buttons($background);
|
||||
@include toolbar-responsive($mobile-breakpoint);
|
||||
}
|
||||
|
||||
@mixin tree-table-crud-page(
|
||||
$mobile-breakpoint: 900px,
|
||||
$gap: 12px,
|
||||
$tree-padding-top: 6px,
|
||||
$background: rgba(53, 109, 255, 0.08)
|
||||
) {
|
||||
@include content-stack($gap);
|
||||
@include dept-tree-panel($tree-padding-top);
|
||||
@include collapsible-tree-layout($mobile-breakpoint);
|
||||
@include table-crud-page($mobile-breakpoint, $background);
|
||||
}
|
||||
136
src/assets/styles/components/_query-form.scss
Normal file
136
src/assets/styles/components/_query-form.scss
Normal file
@@ -0,0 +1,136 @@
|
||||
/* Shared search and filter form layout. */
|
||||
|
||||
.query-form {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px 14px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.query-form .el-form-item {
|
||||
flex: 0 0 auto;
|
||||
margin-bottom: 0;
|
||||
margin-right: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.query-form .el-form-item__content {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.query-form.el-form--inline .el-form-item__label {
|
||||
flex: 0 0 auto;
|
||||
width: auto;
|
||||
min-width: 68px;
|
||||
overflow: visible;
|
||||
white-space: nowrap;
|
||||
word-break: keep-all;
|
||||
line-break: strict;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.query-form .el-form-item:last-child {
|
||||
margin-left: auto;
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
.query-form .el-form-item:last-child .el-form-item__content {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
gap: 6px;
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
|
||||
.query-form .el-form-item:last-child .el-button {
|
||||
margin-left: 0 !important;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.query-form .el-form-item:last-child .el-button + .el-button {
|
||||
margin-left: 0 !important;
|
||||
}
|
||||
|
||||
.query-form .el-form-item[style*='width: 308px'] {
|
||||
width: auto !important;
|
||||
min-width: 376px;
|
||||
}
|
||||
|
||||
.query-form .el-form-item[style*='width: 308px'] .el-form-item__content {
|
||||
flex: 0 0 308px;
|
||||
min-width: 308px;
|
||||
}
|
||||
|
||||
.query-form .el-form-item[style*='width: 308px'] .el-date-editor.el-range-editor {
|
||||
width: 308px;
|
||||
min-width: 308px;
|
||||
}
|
||||
|
||||
.query-form .el-input__wrapper,
|
||||
.query-form .el-select__wrapper,
|
||||
.query-form .el-textarea__inner,
|
||||
.query-form .el-date-editor,
|
||||
.query-form .el-range-editor,
|
||||
.query-form .el-cascader .el-input__wrapper,
|
||||
.query-form .el-input-number,
|
||||
.query-form .el-input-number__decrease,
|
||||
.query-form .el-input-number__increase {
|
||||
border-radius: 12px !important;
|
||||
}
|
||||
|
||||
.query-form .el-button {
|
||||
border-radius: 10px !important;
|
||||
}
|
||||
|
||||
html.dark .query-form .el-button--primary {
|
||||
--el-button-bg-color: rgba(37, 99, 235, 0.28);
|
||||
--el-button-border-color: rgba(96, 165, 250, 0.62);
|
||||
--el-button-text-color: #dbeafe;
|
||||
--el-button-hover-bg-color: rgba(59, 130, 246, 0.36);
|
||||
--el-button-hover-border-color: rgba(147, 197, 253, 0.78);
|
||||
--el-button-hover-text-color: #eff6ff;
|
||||
--el-button-active-bg-color: rgba(29, 78, 216, 0.44);
|
||||
--el-button-active-border-color: rgba(96, 165, 250, 0.86);
|
||||
--el-button-active-text-color: #eff6ff;
|
||||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.query-form {
|
||||
gap: 10px 12px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.query-form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.query-form .el-form-item {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.query-form .el-form-item[style*='width: 308px'] {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.query-form .el-form-item[style*='width: 308px'] .el-form-item__content {
|
||||
flex: 1 1 auto;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.query-form .el-input,
|
||||
.query-form .el-select,
|
||||
.query-form .el-date-editor,
|
||||
.query-form .el-cascader,
|
||||
.query-form .el-input-number {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
.query-form .el-form-item:last-child {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
91
src/assets/styles/components/_search-panel.scss
Normal file
91
src/assets/styles/components/_search-panel.scss
Normal file
@@ -0,0 +1,91 @@
|
||||
/* Search panel, table actions, and responsive panel shells. */
|
||||
|
||||
// --- 表格工具列链接按钮(全局) ---
|
||||
.data-table .el-button.is-link {
|
||||
width: 28px !important;
|
||||
height: 28px !important;
|
||||
min-width: 28px !important;
|
||||
padding: 0 !important;
|
||||
border-radius: 10px !important;
|
||||
background: var(--app-accent-soft) !important;
|
||||
}
|
||||
|
||||
.data-table .el-button.is-link + .el-button.is-link {
|
||||
margin-left: 4px !important;
|
||||
}
|
||||
|
||||
.search-wrap {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
|
||||
.search-panel,
|
||||
.table-panel {
|
||||
transform: translateZ(0);
|
||||
backface-visibility: hidden;
|
||||
}
|
||||
|
||||
.search-panel {
|
||||
isolation: isolate;
|
||||
|
||||
.el-card__header {
|
||||
display: block;
|
||||
padding: 12px 16px !important;
|
||||
}
|
||||
|
||||
.el-card__body {
|
||||
padding-top: 16px !important;
|
||||
overflow: hidden;
|
||||
max-height: 560px;
|
||||
opacity: 1;
|
||||
transition:
|
||||
max-height 0.32s cubic-bezier(0.22, 1, 0.36, 1),
|
||||
opacity 0.24s ease,
|
||||
padding-top 0.24s ease,
|
||||
padding-bottom 0.24s ease;
|
||||
}
|
||||
}
|
||||
|
||||
.search-panel.is-collapsed {
|
||||
.el-card__body {
|
||||
max-height: 0;
|
||||
opacity: 0;
|
||||
padding-top: 0 !important;
|
||||
padding-bottom: 0 !important;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
.search-panel-toggle {
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
transition: color 0.24s ease;
|
||||
|
||||
&:hover {
|
||||
color: var(--app-accent-strong);
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
margin-left: auto;
|
||||
width: 9px;
|
||||
height: 9px;
|
||||
border-right: 2px solid currentColor;
|
||||
border-bottom: 2px solid currentColor;
|
||||
color: var(--app-text-muted);
|
||||
transform: rotate(-135deg);
|
||||
transition:
|
||||
transform 0.24s ease,
|
||||
color 0.24s ease;
|
||||
}
|
||||
}
|
||||
|
||||
.search-panel.is-collapsed .search-panel-toggle::after {
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
|
||||
html.dark {
|
||||
.tree-border {
|
||||
background: var(--app-surface-bg);
|
||||
border-color: var(--app-surface-border);
|
||||
}
|
||||
}
|
||||
79
src/assets/styles/components/_selector-dialog.scss
Normal file
79
src/assets/styles/components/_selector-dialog.scss
Normal file
@@ -0,0 +1,79 @@
|
||||
/* Reusable scoped mixins for selector dialogs backed by cards and vxe tables. */
|
||||
|
||||
/* 为多个根选择器统一设置 gap */
|
||||
@mixin shell-gap($selectors...) {
|
||||
@each $selector in $selectors {
|
||||
#{$selector} {
|
||||
gap: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 卡片区域撑满弹窗 */
|
||||
@mixin card-shell {
|
||||
.selector-card {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
/* 按弹窗根 class 收紧 body 顶内边距 */
|
||||
@mixin dialog-body-padding($dialog-class) {
|
||||
.#{$dialog-class} :deep(.el-dialog__body) {
|
||||
padding-top: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 头部标题与已选 tag 换行、窄屏左对齐 */
|
||||
@mixin selector-header-tags($mobile-breakpoint: 768px) {
|
||||
.selector-header {
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.selector-tags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: flex-end;
|
||||
gap: 6px;
|
||||
max-width: min(100%, 520px);
|
||||
}
|
||||
|
||||
.selector-tags :deep(.el-tag) {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
@media (max-width: $mobile-breakpoint) {
|
||||
.selector-tags {
|
||||
justify-content: flex-start;
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* vxe 表格圆角与表头色,与全局表格 token 一致 */
|
||||
@mixin selector-table {
|
||||
.selector-table {
|
||||
border-radius: 10px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.selector-table :deep(.vxe-table--render-default) {
|
||||
border-radius: 10px;
|
||||
color: var(--app-text-title);
|
||||
}
|
||||
|
||||
.selector-table :deep(.vxe-header--column) {
|
||||
background: var(--tableHeaderBg);
|
||||
color: var(--tableHeaderTextColor);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.selector-table :deep(.vxe-body--column),
|
||||
.selector-table :deep(.vxe-header--column) {
|
||||
border-color: var(--app-surface-border);
|
||||
}
|
||||
|
||||
.selector-table :deep(.vxe-body--row.row--hover),
|
||||
.selector-table :deep(.vxe-body--row:hover) {
|
||||
background-color: rgba(53, 109, 255, 0.05);
|
||||
}
|
||||
}
|
||||
84
src/assets/styles/components/_table-toolbar.scss
Normal file
84
src/assets/styles/components/_table-toolbar.scss
Normal file
@@ -0,0 +1,84 @@
|
||||
/* Table headers and toolbar action layouts. */
|
||||
|
||||
.top-right-btn {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.panel-heading {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.panel-heading h3,
|
||||
.table-heading h3 {
|
||||
margin: 0;
|
||||
font-size: 15px;
|
||||
letter-spacing: 0;
|
||||
color: var(--app-text-title);
|
||||
}
|
||||
|
||||
.panel-kicker {
|
||||
display: none;
|
||||
color: var(--app-accent-strong);
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.04em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.toolbar-shell {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 10px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.table-heading p {
|
||||
margin: 4px 0 0;
|
||||
font-size: 13px;
|
||||
color: var(--app-text-muted);
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.toolbar-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: nowrap;
|
||||
gap: 8px;
|
||||
margin-left: auto;
|
||||
white-space: nowrap;
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
padding-bottom: 2px;
|
||||
}
|
||||
|
||||
.toolbar-actions::-webkit-scrollbar {
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.toolbar-actions > * {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.toolbar-actions .top-right-btn {
|
||||
margin-left: 0;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.toolbar-actions .el-button + .el-button,
|
||||
.toolbar-actions .el-dropdown + .el-button,
|
||||
.toolbar-actions .el-button + .el-dropdown,
|
||||
.toolbar-actions .el-dropdown + .el-dropdown,
|
||||
.toolbar-actions .top-right-btn + .el-button,
|
||||
.toolbar-actions .el-button + .top-right-btn,
|
||||
.toolbar-actions .top-right-btn + .el-dropdown,
|
||||
.toolbar-actions .el-dropdown + .top-right-btn {
|
||||
margin-left: 0 !important;
|
||||
}
|
||||
|
||||
.table-panel .toolbar-actions .el-button:not(.is-circle):not(.is-link) {
|
||||
border-radius: 10px !important;
|
||||
}
|
||||
49
src/assets/styles/components/_theme-helpers.scss
Normal file
49
src/assets/styles/components/_theme-helpers.scss
Normal file
@@ -0,0 +1,49 @@
|
||||
/* Legacy theme helper classes and color accents. */
|
||||
|
||||
.el-button--cyan.is-active,
|
||||
.el-button--cyan:active {
|
||||
background: #20b2aa;
|
||||
border-color: #20b2aa;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.el-button--cyan:focus,
|
||||
.el-button--cyan:hover {
|
||||
background: #48d1cc;
|
||||
border-color: #48d1cc;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.el-button--cyan {
|
||||
background-color: #20b2aa;
|
||||
border-color: #20b2aa;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.text-navy {
|
||||
color: #1ab394;
|
||||
}
|
||||
|
||||
.text-primary {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.text-success {
|
||||
color: #1c84c6;
|
||||
}
|
||||
|
||||
.text-info {
|
||||
color: #23c6c8;
|
||||
}
|
||||
|
||||
.text-warning {
|
||||
color: #f8ac59;
|
||||
}
|
||||
|
||||
.text-danger {
|
||||
color: #ed5565;
|
||||
}
|
||||
|
||||
.text-muted {
|
||||
color: #888888;
|
||||
}
|
||||
@@ -1,277 +0,0 @@
|
||||
.el-collapse {
|
||||
.collapse__title {
|
||||
font-weight: 600;
|
||||
padding: 0 8px;
|
||||
font-size: 1.2em;
|
||||
line-height: 1.1em;
|
||||
}
|
||||
.el-collapse-item__content {
|
||||
padding: 0 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.el-divider--horizontal {
|
||||
margin-bottom: 10px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.el-breadcrumb__inner,
|
||||
.el-breadcrumb__inner a {
|
||||
font-weight: 400 !important;
|
||||
}
|
||||
|
||||
.el-upload {
|
||||
input[type='file'] {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
.el-upload__input {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.cell {
|
||||
.el-tag {
|
||||
margin-right: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
.small-padding {
|
||||
.cell {
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.fixed-width {
|
||||
.el-button--mini {
|
||||
padding: 7px 10px;
|
||||
width: 60px;
|
||||
}
|
||||
}
|
||||
|
||||
.status-col {
|
||||
.cell {
|
||||
padding: 0 10px;
|
||||
text-align: center;
|
||||
|
||||
.el-tag {
|
||||
margin-right: 0px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*-------------Dialog-------------**/
|
||||
.el-overlay {
|
||||
overflow: hidden;
|
||||
|
||||
.el-overlay-dialog {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
.el-dialog {
|
||||
margin: 0 auto !important;
|
||||
border-radius: var(--app-radius-lg);
|
||||
box-shadow: var(--app-shadow-md);
|
||||
overflow: hidden;
|
||||
|
||||
.el-dialog__body {
|
||||
padding: 15px !important;
|
||||
}
|
||||
.el-dialog__header {
|
||||
padding: 16px 16px 8px 16px;
|
||||
box-sizing: border-box;
|
||||
border-bottom: 1px solid var(--brder-color);
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.el-dialog__body {
|
||||
max-height: calc(90vh - 111px) !important;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
// refine element ui upload
|
||||
.upload-container {
|
||||
.el-upload {
|
||||
width: 100%;
|
||||
|
||||
.el-upload-dragger {
|
||||
width: 100%;
|
||||
height: 200px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// dropdown
|
||||
.el-dropdown-menu {
|
||||
border-radius: var(--app-radius-md);
|
||||
box-shadow: var(--app-shadow-sm);
|
||||
a {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
// fix date-picker ui bug in filter-item
|
||||
.el-range-editor.el-input__inner {
|
||||
display: inline-flex !important;
|
||||
}
|
||||
|
||||
// to fix el-date-picker css style
|
||||
.el-range-separator {
|
||||
box-sizing: content-box;
|
||||
}
|
||||
|
||||
.el-menu--collapse > div > .el-submenu > .el-submenu__title .el-submenu__icon-arrow {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.el-dropdown .el-dropdown-link {
|
||||
color: var(--el-color-primary) !important;
|
||||
}
|
||||
|
||||
/* 当 el-form 的 inline 属性为 true 时 */
|
||||
/* 设置 label 的宽度默认为 68px */
|
||||
.el-form--inline .el-form-item__label {
|
||||
width: 68px;
|
||||
}
|
||||
|
||||
/* 设置 el-select 的宽度默认为 240px */
|
||||
.el-form--inline .el-select {
|
||||
width: 240px;
|
||||
}
|
||||
|
||||
/* 设置 el-input 的宽度默认为 240px */
|
||||
.el-form--inline .el-input {
|
||||
width: 240px;
|
||||
}
|
||||
|
||||
/* 设置 el-message-box 消息弹框内容强制换行 */
|
||||
.el-message-box .el-message-box__message {
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.el-message-box {
|
||||
border-radius: var(--app-radius-lg);
|
||||
box-shadow: var(--app-shadow-md);
|
||||
}
|
||||
|
||||
.el-message,
|
||||
.el-notification,
|
||||
.el-alert {
|
||||
border-radius: var(--app-radius-md);
|
||||
box-shadow: var(--app-shadow-sm);
|
||||
}
|
||||
|
||||
// Modern rounded inputs
|
||||
.el-input__wrapper,
|
||||
.el-textarea__inner,
|
||||
.el-select__wrapper,
|
||||
.el-date-editor,
|
||||
.el-range-editor,
|
||||
.el-input-number,
|
||||
.el-input-number__decrease,
|
||||
.el-input-number__increase {
|
||||
border-radius: var(--app-radius-md);
|
||||
transition: box-shadow 0.2s ease, border-color 0.2s ease, background-color 0.2s ease;
|
||||
}
|
||||
|
||||
.el-input__wrapper.is-focus,
|
||||
.el-textarea__inner:focus,
|
||||
.el-select__wrapper.is-focus,
|
||||
.el-date-editor.is-focus,
|
||||
.el-range-editor.is-focus {
|
||||
box-shadow: 0 0 0 2px var(--el-color-primary-light-8);
|
||||
}
|
||||
|
||||
// Buttons
|
||||
.el-button {
|
||||
border-radius: var(--app-radius-md);
|
||||
transition: transform 0.15s ease, box-shadow 0.2s ease, background-color 0.2s ease, border-color 0.2s ease;
|
||||
}
|
||||
|
||||
.el-button:not(.is-text):not(.is-link):hover {
|
||||
transform: translateY(-1px);
|
||||
box-shadow: var(--app-shadow-sm);
|
||||
}
|
||||
|
||||
// Tags and badges
|
||||
.el-tag {
|
||||
border-radius: var(--app-radius-md);
|
||||
}
|
||||
|
||||
// Cards, popovers, drawers
|
||||
.el-popover,
|
||||
.el-tooltip__popper,
|
||||
.el-popper {
|
||||
border-radius: var(--app-radius-md);
|
||||
box-shadow: var(--app-shadow-sm);
|
||||
}
|
||||
|
||||
.el-drawer {
|
||||
border-radius: var(--app-radius-lg);
|
||||
box-shadow: var(--app-shadow-md);
|
||||
}
|
||||
|
||||
.el-drawer__header {
|
||||
margin-bottom: 0;
|
||||
padding: 16px 20px;
|
||||
border-bottom: 1px solid var(--el-border-color-light);
|
||||
}
|
||||
|
||||
// Table polish
|
||||
.el-table {
|
||||
border-radius: var(--app-radius-lg);
|
||||
overflow: hidden;
|
||||
box-shadow: var(--app-shadow-sm);
|
||||
border: 1px solid var(--el-border-color-lighter);
|
||||
}
|
||||
|
||||
.el-table__header-wrapper,
|
||||
.el-table__body-wrapper {
|
||||
background: var(--el-bg-color);
|
||||
}
|
||||
|
||||
.el-table__row:hover td.el-table__cell {
|
||||
background-color: var(--el-fill-color-light) !important;
|
||||
}
|
||||
|
||||
// Tabs
|
||||
.el-tabs__header {
|
||||
margin: 0 0 12px 0;
|
||||
}
|
||||
|
||||
.el-tabs__item {
|
||||
border-radius: var(--app-radius-md);
|
||||
margin: 0 2px;
|
||||
}
|
||||
|
||||
.el-tabs__nav-wrap::after {
|
||||
background-color: var(--el-border-color-lighter);
|
||||
}
|
||||
|
||||
// Pagination
|
||||
.el-pagination .btn-prev,
|
||||
.el-pagination .btn-next,
|
||||
.el-pagination .el-pager li {
|
||||
border-radius: var(--app-radius-sm);
|
||||
transition: background-color 0.2s ease, color 0.2s ease, box-shadow 0.2s ease;
|
||||
}
|
||||
|
||||
.el-pagination .el-pager li.is-active {
|
||||
box-shadow: var(--app-shadow-sm);
|
||||
}
|
||||
|
||||
// Breadcrumb
|
||||
.el-breadcrumb {
|
||||
padding: 0;
|
||||
background: transparent;
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
}
|
||||
@@ -1,226 +1,26 @@
|
||||
@use './variables.module.scss' as *;
|
||||
// =============================================================================
|
||||
// 全局样式入口(加载顺序即最终 CSS 层叠顺序,后载入的同优先级规则会覆盖先载入的)
|
||||
// =============================================================================
|
||||
@use './tokens/sass-vars' as *;
|
||||
@use './tokens/theme';
|
||||
@use './tokens/theme-dark';
|
||||
@use './mixin.scss';
|
||||
@use './transition.scss';
|
||||
@use './element-ui.scss';
|
||||
@use './sidebar.scss';
|
||||
@use './btn.scss';
|
||||
@use './ruoyi.scss';
|
||||
@use './base/document';
|
||||
@use './layout/surface';
|
||||
@use './layout/sidebar/shell' as sidebar-shell;
|
||||
@use './layout/sidebar/menu';
|
||||
@use './layout/sidebar/collapsed';
|
||||
@use './layout/sidebar/responsive';
|
||||
@use './layout/sidebar/popper' as sidebar-popper;
|
||||
@use './components/legacy-utilities';
|
||||
@use './components/misc';
|
||||
@use './components/page-shell';
|
||||
@use './components/card-shell';
|
||||
@use './components/theme-helpers';
|
||||
@use './components/table-toolbar';
|
||||
@use './components/query-form';
|
||||
@use './components/search-panel';
|
||||
|
||||
@use './vendors/element-plus/index' as element-custom;
|
||||
@use 'animate.css';
|
||||
@use 'element-plus/dist/index.css';
|
||||
|
||||
body {
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
text-rendering: optimizeLegibility;
|
||||
background: var(--el-bg-color-page);
|
||||
font-family:
|
||||
'MiSans',
|
||||
'HarmonyOS Sans SC',
|
||||
'PingFang SC',
|
||||
'Source Han Sans SC',
|
||||
'Noto Sans SC',
|
||||
'Hiragino Sans GB',
|
||||
'Microsoft YaHei',
|
||||
sans-serif;
|
||||
}
|
||||
|
||||
label {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
html {
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html.dark .svg-icon,
|
||||
html.dark svg {
|
||||
fill: var(--el-text-color-regular);
|
||||
}
|
||||
|
||||
#app {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
*,
|
||||
*:before,
|
||||
*:after {
|
||||
box-sizing: inherit;
|
||||
}
|
||||
|
||||
.no-padding {
|
||||
padding: 0px !important;
|
||||
}
|
||||
|
||||
.padding-content {
|
||||
padding: 4px 0;
|
||||
}
|
||||
|
||||
a:focus,
|
||||
a:active {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
a,
|
||||
a:focus,
|
||||
a:hover {
|
||||
cursor: pointer;
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
div:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.fr {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.fl {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.pr-5 {
|
||||
padding-right: 5px;
|
||||
}
|
||||
|
||||
.pl-5 {
|
||||
padding-left: 5px;
|
||||
}
|
||||
|
||||
.block {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.pointer {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.inlineBlock {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.clearfix {
|
||||
&:after {
|
||||
visibility: hidden;
|
||||
display: block;
|
||||
font-size: 0;
|
||||
content: ' ';
|
||||
clear: both;
|
||||
height: 0;
|
||||
}
|
||||
}
|
||||
|
||||
aside {
|
||||
background: #eef1f6;
|
||||
padding: 8px 24px;
|
||||
margin-bottom: 20px;
|
||||
border-radius: var(--app-radius-md);
|
||||
display: block;
|
||||
line-height: 32px;
|
||||
font-size: 16px;
|
||||
font-family:
|
||||
'MiSans',
|
||||
'HarmonyOS Sans SC',
|
||||
'PingFang SC',
|
||||
'Source Han Sans SC',
|
||||
'Noto Sans SC',
|
||||
'Hiragino Sans GB',
|
||||
'Microsoft YaHei',
|
||||
sans-serif;
|
||||
color: #2c3e50;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
|
||||
a {
|
||||
color: #337ab7;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
color: rgb(32, 160, 255);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//main-container全局样式
|
||||
.app-container {
|
||||
padding: 20px;
|
||||
background: var(--app-surface-bg);
|
||||
border: 1px solid var(--app-surface-border);
|
||||
border-radius: var(--app-radius-lg);
|
||||
box-shadow: var(--app-shadow-sm);
|
||||
}
|
||||
|
||||
// search面板样式
|
||||
.panel,
|
||||
.search {
|
||||
margin-bottom: 0.75rem;
|
||||
border-radius: var(--app-radius-lg);
|
||||
border: 1px solid var(--el-border-color-light);
|
||||
background-color: var(--el-bg-color-overlay);
|
||||
padding: 0.75rem;
|
||||
box-shadow: 0 0 0 rgba(0, 0, 0, 0);
|
||||
transition: box-shadow 0.2s ease, transform 0.2s ease, border-color 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
box-shadow: var(--app-shadow-sm);
|
||||
border-color: var(--el-border-color);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
}
|
||||
|
||||
.components-container {
|
||||
margin: 30px 50px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.text-center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.sub-navbar {
|
||||
height: 50px;
|
||||
line-height: 50px;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
text-align: right;
|
||||
padding-right: 20px;
|
||||
transition: 600ms ease position;
|
||||
background: linear-gradient(90deg, rgba(32, 182, 249, 1) 0%, rgba(32, 182, 249, 1) 0%, rgba(33, 120, 241, 1) 100%, rgba(33, 120, 241, 1) 100%);
|
||||
|
||||
.subtitle {
|
||||
font-size: 20px;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
&.draft {
|
||||
background: #d0d0d0;
|
||||
}
|
||||
|
||||
&.deleted {
|
||||
background: #d0d0d0;
|
||||
}
|
||||
}
|
||||
|
||||
.link-type,
|
||||
.link-type:focus {
|
||||
color: #337ab7;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
color: rgb(32, 160, 255);
|
||||
}
|
||||
}
|
||||
|
||||
.filter-container {
|
||||
padding-bottom: 10px;
|
||||
|
||||
.filter-item {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
24
src/assets/styles/layout/_surface.scss
Normal file
24
src/assets/styles/layout/_surface.scss
Normal file
@@ -0,0 +1,24 @@
|
||||
/* 主内容区面板(.app-container、.panel、.search):不依赖 Element 类名,与 components 中 EP 类补丁区分。 */
|
||||
|
||||
// --- 整块内容容器(常见于路由视图外包一层) ---
|
||||
.app-container {
|
||||
}
|
||||
|
||||
// --- 可复用面板条(hover 抬升阴影,非 el-card) ---
|
||||
.panel,
|
||||
.search {
|
||||
margin-bottom: 0.75rem;
|
||||
border-radius: var(--app-radius-base);
|
||||
border: 1px solid var(--app-surface-border);
|
||||
background-color: var(--app-surface-bg);
|
||||
padding: 0.75rem;
|
||||
box-shadow: 0 0 0 rgba(0, 0, 0, 0);
|
||||
transition:
|
||||
box-shadow 0.2s ease,
|
||||
border-color 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
box-shadow: var(--app-shadow-sm);
|
||||
border-color: rgba(64, 158, 255, 0.2);
|
||||
}
|
||||
}
|
||||
141
src/assets/styles/layout/sidebar/_collapsed.scss
Normal file
141
src/assets/styles/layout/sidebar/_collapsed.scss
Normal file
@@ -0,0 +1,141 @@
|
||||
/* 桌面端折叠侧栏:窄宽、仅图标、子菜单箭头隐藏与 tooltip 触发区域(类名 .hideSidebar)。 */
|
||||
|
||||
#app {
|
||||
.hideSidebar {
|
||||
.sidebar-container {
|
||||
width: 58px !important;
|
||||
}
|
||||
|
||||
.main-container {
|
||||
margin-left: 70px;
|
||||
}
|
||||
|
||||
.sidebar-shell {
|
||||
padding-left: 4px;
|
||||
padding-right: 4px;
|
||||
}
|
||||
|
||||
.submenu-title-noDropdown {
|
||||
padding: 0 !important;
|
||||
position: relative;
|
||||
height: 40px;
|
||||
width: 40px !important;
|
||||
min-width: 40px !important;
|
||||
margin: 3px auto !important;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center !important;
|
||||
box-sizing: border-box;
|
||||
|
||||
&.is-active {
|
||||
background-color: var(--side-menu-active-bg) !important;
|
||||
color: var(--side-menu-active-text) !important;
|
||||
box-shadow: inset 3px 0 0 var(--side-menu-active-line);
|
||||
}
|
||||
|
||||
.el-tooltip {
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
.el-menu-tooltip__trigger {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: inline-flex !important;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.svg-icon {
|
||||
margin-right: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
& .el-sub-menu {
|
||||
overflow: hidden;
|
||||
border-radius: var(--app-radius-md);
|
||||
|
||||
.el-sub-menu__title,
|
||||
.el-sub-menu__title.el-tooltip__trigger {
|
||||
border-radius: var(--app-radius-md);
|
||||
height: 40px;
|
||||
padding: 0 !important;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center !important;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
&.is-active .el-sub-menu__title,
|
||||
&.is-active .el-sub-menu__title.el-tooltip__trigger {
|
||||
background-color: var(--side-menu-active-bg) !important;
|
||||
box-shadow: inset 3px 0 0 var(--side-menu-active-line);
|
||||
}
|
||||
|
||||
& > .el-sub-menu__title {
|
||||
padding: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.el-menu--collapse {
|
||||
> div > .el-sub-menu {
|
||||
width: 40px !important;
|
||||
min-width: 40px !important;
|
||||
margin: 3px auto !important;
|
||||
}
|
||||
|
||||
> div > .el-sub-menu > .el-sub-menu__title,
|
||||
> div > .el-sub-menu > .el-sub-menu__title.el-tooltip__trigger {
|
||||
width: 100% !important;
|
||||
min-width: 100% !important;
|
||||
margin: 0 !important;
|
||||
box-sizing: border-box;
|
||||
transform: none;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.is-active .svg-icon {
|
||||
fill: currentColor;
|
||||
}
|
||||
|
||||
> div > .el-sub-menu > .el-sub-menu__title .svg-icon {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
transform: translate(-50%, -50%);
|
||||
display: block;
|
||||
margin: 0 !important;
|
||||
margin-right: 0 !important;
|
||||
}
|
||||
|
||||
> div > .el-sub-menu > .el-sub-menu__title {
|
||||
justify-content: center !important;
|
||||
|
||||
.el-sub-menu__icon-arrow {
|
||||
display: none !important;
|
||||
width: 0 !important;
|
||||
margin: 0 !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
|
||||
& > span {
|
||||
height: 0;
|
||||
width: 0;
|
||||
overflow: hidden;
|
||||
visibility: hidden;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
& > i {
|
||||
height: 0;
|
||||
width: 0;
|
||||
overflow: hidden;
|
||||
visibility: hidden;
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
113
src/assets/styles/layout/sidebar/_menu.scss
Normal file
113
src/assets/styles/layout/sidebar/_menu.scss
Normal file
@@ -0,0 +1,113 @@
|
||||
/* 侧栏内 el-menu:一级/嵌套项的 hover、active、浅色与 theme-dark 分支(与 _shell 中变量定义配合)。 */
|
||||
|
||||
@use '../../tokens/sass-vars' as *;
|
||||
|
||||
#app {
|
||||
.sidebar-container {
|
||||
.theme-dark .submenu-title-noDropdown,
|
||||
.theme-dark .el-sub-menu__title {
|
||||
border-radius: var(--app-radius-md);
|
||||
margin: 3px 8px;
|
||||
height: 40px;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--side-menu-hover-bg) !important;
|
||||
color: var(--side-menu-hover-text) !important;
|
||||
}
|
||||
}
|
||||
|
||||
.submenu-title-noDropdown,
|
||||
.el-sub-menu__title {
|
||||
border-radius: var(--app-radius-md);
|
||||
margin: 3px 8px;
|
||||
height: 40px;
|
||||
color: var(--side-menu-text) !important;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--side-menu-hover-bg) !important;
|
||||
color: var(--side-menu-hover-text) !important;
|
||||
}
|
||||
}
|
||||
|
||||
& .theme-dark .is-active > .el-sub-menu__title {
|
||||
color: var(--side-menu-active-text) !important;
|
||||
}
|
||||
|
||||
& .nest-menu .el-sub-menu > .el-sub-menu__title,
|
||||
& .el-sub-menu .el-menu-item {
|
||||
min-width: calc($base-sidebar-width - 16px) !important;
|
||||
border-radius: var(--app-radius-md);
|
||||
height: 40px;
|
||||
margin: 3px 8px;
|
||||
background: transparent !important;
|
||||
|
||||
&:not(.is-active):hover {
|
||||
background-color: var(--side-menu-hover-bg) !important;
|
||||
color: var(--side-menu-hover-text) !important;
|
||||
}
|
||||
}
|
||||
|
||||
& .theme-dark .nest-menu .el-sub-menu > .el-sub-menu__title,
|
||||
& .theme-dark .el-sub-menu .el-menu-item {
|
||||
border-radius: var(--app-radius-md);
|
||||
height: 40px;
|
||||
margin: 3px 8px;
|
||||
background: transparent !important;
|
||||
|
||||
&.is-active {
|
||||
background: var(--side-menu-active-bg) !important;
|
||||
color: var(--side-menu-active-text) !important;
|
||||
box-shadow:
|
||||
inset 3px 0 0 var(--side-menu-active-line),
|
||||
inset 0 0 0 1px var(--side-menu-active-border);
|
||||
}
|
||||
|
||||
&:not(.is-active):hover {
|
||||
background-color: var(--side-menu-hover-bg) !important;
|
||||
color: var(--side-menu-hover-text) !important;
|
||||
}
|
||||
}
|
||||
|
||||
& .theme-dark .nest-menu .el-sub-menu > .el-sub-menu__title,
|
||||
& .theme-dark .el-menu-item {
|
||||
border-radius: var(--app-radius-md);
|
||||
height: 40px;
|
||||
margin: 3px 8px;
|
||||
background: transparent !important;
|
||||
|
||||
&.is-active {
|
||||
background: var(--side-menu-active-bg) !important;
|
||||
color: var(--side-menu-active-text) !important;
|
||||
box-shadow:
|
||||
inset 3px 0 0 var(--side-menu-active-line),
|
||||
inset 0 0 0 1px var(--side-menu-active-border);
|
||||
}
|
||||
|
||||
&:not(.is-active):hover {
|
||||
background-color: var(--side-menu-hover-bg) !important;
|
||||
color: var(--side-menu-hover-text) !important;
|
||||
}
|
||||
}
|
||||
|
||||
& .nest-menu .el-sub-menu > .el-sub-menu__title,
|
||||
& .el-menu-item {
|
||||
border-radius: var(--app-radius-md);
|
||||
height: 40px;
|
||||
margin: 3px 8px;
|
||||
background: transparent !important;
|
||||
|
||||
&.is-active {
|
||||
background: var(--side-menu-active-bg) !important;
|
||||
color: var(--side-menu-active-text) !important;
|
||||
box-shadow:
|
||||
inset 3px 0 0 var(--side-menu-active-line),
|
||||
inset 0 0 0 1px var(--side-menu-active-border);
|
||||
}
|
||||
|
||||
&:not(.is-active):hover {
|
||||
background-color: var(--side-menu-hover-bg) !important;
|
||||
color: var(--side-menu-hover-text) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
22
src/assets/styles/layout/sidebar/_popper.scss
Normal file
22
src/assets/styles/layout/sidebar/_popper.scss
Normal file
@@ -0,0 +1,22 @@
|
||||
/* 折叠后弹出子菜单(el-popper.is-pure):圆角与弹出层内 menu 项样式。 */
|
||||
|
||||
.el-menu--vertical {
|
||||
& > .el-menu {
|
||||
.svg-icon {
|
||||
margin-right: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.el-popper.is-pure {
|
||||
border-radius: var(--app-radius-md);
|
||||
box-shadow: var(--app-shadow-md);
|
||||
|
||||
.el-menu--popup {
|
||||
border-radius: var(--app-radius-md);
|
||||
}
|
||||
|
||||
.el-menu-item {
|
||||
border-radius: var(--app-radius-sm);
|
||||
}
|
||||
}
|
||||
31
src/assets/styles/layout/sidebar/_responsive.scss
Normal file
31
src/assets/styles/layout/sidebar/_responsive.scss
Normal file
@@ -0,0 +1,31 @@
|
||||
/* 移动端侧栏:抽屉式滑出、隐藏时 translate;.withoutAnimation 关闭过渡(与布局组件 class 约定一致)。 */
|
||||
|
||||
@use '../../tokens/sass-vars' as *;
|
||||
|
||||
#app {
|
||||
.mobile {
|
||||
.main-container {
|
||||
margin-left: 0px;
|
||||
}
|
||||
|
||||
.sidebar-container {
|
||||
transition: transform 0.28s;
|
||||
width: $base-sidebar-width !important;
|
||||
}
|
||||
|
||||
&.hideSidebar {
|
||||
.sidebar-container {
|
||||
pointer-events: none;
|
||||
transition-duration: 0.3s;
|
||||
transform: translate3d(calc(-#{$base-sidebar-width} - 12px), 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.withoutAnimation {
|
||||
.main-container,
|
||||
.sidebar-container {
|
||||
transition: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
132
src/assets/styles/layout/sidebar/_shell.scss
Normal file
132
src/assets/styles/layout/sidebar/_shell.scss
Normal file
@@ -0,0 +1,132 @@
|
||||
/* 侧栏外壳:固定定位宽度、主区左边距、滚动条与 el-menu 根级样式;与 _menu/_collapsed/_responsive 分工。 */
|
||||
|
||||
@use '../../tokens/sass-vars' as *;
|
||||
|
||||
#app {
|
||||
// --- 主内容区相对侧栏的偏移 ---
|
||||
.main-container {
|
||||
height: 100%;
|
||||
transition: margin-left 0.28s;
|
||||
margin-left: calc(#{$base-sidebar-width} + 12px);
|
||||
position: relative;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.sidebarHide {
|
||||
margin-left: 0 !important;
|
||||
}
|
||||
|
||||
// --- 侧栏固定列:宽度过渡、主题下滚动条内菜单色变量 ---
|
||||
.sidebar-container {
|
||||
-webkit-transition: width 0.28s;
|
||||
transition: width 0.28s;
|
||||
width: $base-sidebar-width !important;
|
||||
background: transparent;
|
||||
height: calc(100vh - 24px);
|
||||
position: fixed;
|
||||
font-size: 0;
|
||||
top: 12px;
|
||||
left: 12px;
|
||||
z-index: 1001;
|
||||
overflow: hidden;
|
||||
box-sizing: border-box;
|
||||
padding: 0;
|
||||
border-right: none;
|
||||
-webkit-box-shadow: none;
|
||||
box-shadow: none;
|
||||
|
||||
.horizontal-collapse-transition {
|
||||
transition:
|
||||
0s width ease-in-out,
|
||||
0s padding-left ease-in-out,
|
||||
0s padding-right ease-in-out;
|
||||
}
|
||||
|
||||
.scrollbar-wrapper {
|
||||
overflow-x: hidden !important;
|
||||
}
|
||||
|
||||
.el-scrollbar__bar.is-vertical {
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.el-scrollbar {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.el-scrollbar.theme-light {
|
||||
--side-menu-text: #1f2937;
|
||||
--side-menu-hover-bg: rgba(64, 158, 255, 0.08);
|
||||
--side-menu-hover-text: #111827;
|
||||
--side-menu-active-bg: rgba(64, 158, 255, 0.12);
|
||||
--side-menu-active-text: #409eff;
|
||||
--side-menu-active-border: rgba(64, 158, 255, 0.16);
|
||||
--side-menu-active-line: #409eff;
|
||||
}
|
||||
|
||||
.el-scrollbar.theme-dark {
|
||||
--side-menu-text: #e5edf8;
|
||||
--side-menu-hover-bg: rgba(255, 255, 255, 0.08);
|
||||
--side-menu-hover-text: #ffffff;
|
||||
--side-menu-active-bg: rgba(64, 158, 255, 0.22);
|
||||
--side-menu-active-text: #ffffff;
|
||||
--side-menu-active-border: rgba(96, 165, 250, 0.24);
|
||||
--side-menu-active-line: #60a5fa;
|
||||
}
|
||||
|
||||
.is-horizontal {
|
||||
display: none;
|
||||
}
|
||||
|
||||
a {
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.svg-icon {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.el-menu {
|
||||
border: none;
|
||||
height: 100%;
|
||||
width: 100% !important;
|
||||
background: transparent !important;
|
||||
}
|
||||
|
||||
.el-menu--inline {
|
||||
background: transparent !important;
|
||||
}
|
||||
|
||||
.el-menu-item,
|
||||
.menu-title {
|
||||
overflow: hidden !important;
|
||||
text-overflow: ellipsis !important;
|
||||
white-space: nowrap !important;
|
||||
}
|
||||
|
||||
.el-menu-item,
|
||||
.el-sub-menu__title,
|
||||
.el-menu-item .svg-icon,
|
||||
.el-sub-menu__title .svg-icon,
|
||||
.el-sub-menu__icon-arrow {
|
||||
color: var(--side-menu-text) !important;
|
||||
fill: currentColor;
|
||||
}
|
||||
|
||||
.el-menu-item,
|
||||
.el-sub-menu__title {
|
||||
position: relative;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.el-menu-item .el-menu-tooltip__trigger {
|
||||
display: inline-block !important;
|
||||
}
|
||||
}
|
||||
|
||||
.el-menu--collapse .el-menu .el-sub-menu {
|
||||
min-width: $base-sidebar-width !important;
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
// 清除浮动(嵌套用)
|
||||
@mixin clearfix {
|
||||
&:after {
|
||||
content: '';
|
||||
@@ -6,9 +7,10 @@
|
||||
}
|
||||
}
|
||||
|
||||
// WebKit 滚动条配色(用于可滚动容器)
|
||||
@mixin scrollBar {
|
||||
&::-webkit-scrollbar-track-piece {
|
||||
background: #d3dce6;
|
||||
background: var(--app-surface-border);
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
@@ -16,23 +18,26 @@
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background: #99a9bf;
|
||||
background: var(--app-text-muted);
|
||||
border-radius: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
// 铺满父级的相对定位层
|
||||
@mixin relative {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
// 百分比宽度水平居中块
|
||||
@mixin pct($pct) {
|
||||
width: #{$pct};
|
||||
position: relative;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
// CSS 三角箭头(极少用,保留兼容)
|
||||
@mixin triangle($width, $height, $color, $direction) {
|
||||
$width: $width/2;
|
||||
$color-border-style: $height solid $color;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user