24 Commits

Author SHA1 Message Date
疯狂的狮子Li
9fd2b6f137 !263 发布 v5.6.1-v2.6.1 版本 依赖升级漏洞修复
Merge pull request !263 from 疯狂的狮子Li/dev
2026-04-24 01:50:59 +00:00
疯狂的狮子Li
2a6c440593 🦁🦁🦁发布 v5.6.1-v2.6.1 版本 依赖升级漏洞修复 2026-04-24 09:45:41 +08:00
疯狂的狮子Li
0ddf391de3 fix 修复 租户选择框样式问题 2026-04-14 10:32:46 +08:00
疯狂的狮子Li
f599aba890 update 优化 调整页面画风 不要过于生硬圆滑 2026-04-14 10:11:43 +08:00
疯狂的狮子Li
38a15e6cd3 update 页面圆角大小 默认调整为10 2026-04-14 09:19:51 +08:00
疯狂的狮子Li
e77fe0e618 update 布局设置增加 页面圆角大小 控制器 可以根据喜好自行设置页面圆角样式 2026-04-10 11:32:16 +08:00
疯狂的狮子Li
493d1131bf update vite 7.3.2 修复漏洞问题 2026-04-10 09:49:15 +08:00
疯狂的狮子Li
f37af6f48c update 将富文本编辑器重新改为oss存储(大家还是希望用oss) 2026-04-09 11:34:03 +08:00
疯狂的狮子Li
a79f2fb6c8 fix 修复 插件冲突导致样式一直重复加载问题 2026-03-31 09:24:53 +08:00
疯狂的狮子Li
eb6827765c fix 修复 搜索弹窗样式错误 2026-03-27 16:28:14 +08:00
疯狂的狮子Li
6bc3c618fe !262 发布 v5.6.0-v2.6.0 版本 新年第一版
Merge pull request !262 from 疯狂的狮子Li/dev
2026-03-24 03:49:37 +00:00
疯狂的狮子Li
0076f5f6f7 🦁🦁🦁发布 v5.6.0-v2.6.0 版本 新年第一版 2026-03-24 11:47:41 +08:00
疯狂的狮子Li
ef5ea98a03 update 替代有问题的插件 2026-03-19 15:55:39 +08:00
gssong
73f2374c72 add 增加流程实例权限 2026-03-19 10:27:06 +08:00
gssong
9dcb392220 Merge remote-tracking branch 'origin/dev' into dev 2026-03-19 09:58:02 +08:00
gssong
54636ac14f add 补充流程定义权限 2026-03-19 09:57:56 +08:00
Lau
51a852caea update 修改vben5前端仓库地址 2026-03-13 14:25:48 +00:00
疯狂的狮子Li
5c9c940588 !257 发布 5.5.3-2.5.3 版本 提前祝大家新年快乐
Merge pull request !257 from 疯狂的狮子Li/dev
2026-01-23 06:04:29 +00:00
疯狂的狮子Li
dfd1dc29d1 !256 fix 修复 代码漏改问题
Merge pull request !256 from 疯狂的狮子Li/dev
2025-12-23 05:56:54 +00:00
疯狂的狮子Li
b411505b19 !255 发布 5.5.2-2.5.2 版本 2025年最后一版
Merge pull request !255 from 疯狂的狮子Li/dev
2025-12-23 01:41:45 +00:00
疯狂的狮子Li
52ea8895d6 !246 发布 5.5.1-2.5.1 日常依赖升级bug修复
Merge pull request !246 from 疯狂的狮子Li/dev
2025-10-28 03:22:49 +00:00
疯狂的狮子Li
1b46739799 !241 发布 5.5.0-2.5.0 喜迎国庆
Merge pull request !241 from 疯狂的狮子Li/dev
2025-09-22 03:18:41 +00:00
疯狂的狮子Li
b000788785 !222 发布 5.4.1-2.4.1 小步迭代修复问题
Merge pull request !222 from 疯狂的狮子Li/dev
2025-07-01 01:13:49 +00:00
疯狂的狮子Li
2dc094c1db !212 发布 5.4.0-2.4.0 正式版
Merge pull request !212 from 疯狂的狮子Li/dev
2025-05-29 03:18:30 +00:00
271 changed files with 9284 additions and 17521 deletions

View File

@@ -1,28 +0,0 @@
---
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 路径是否与后端一致
- 类型是否覆盖接口真实结构
- 是否不必要地把类型写宽了

View File

@@ -1,18 +0,0 @@
---
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 模板。
- 默认直接产出可落地代码,而不是只给抽象建议。

View File

@@ -1,37 +0,0 @@
---
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` 是否同步补齐
- 页面是否只是模板裸输出,如果是要继续补强到当前项目风格

View File

@@ -1,27 +0,0 @@
---
name: frontend-page-enhancement
description: 复杂前端页面增强专家。用于修改当前项目中已经存在的列表页、树筛选页、带导入导出和更多菜单的页面,强调增量修改和保留现有交互能力。
---
你负责当前前端项目中已有页面的增强,不是重写页面。
## 核心原则
1. 优先阅读当前页面完整实现。
2. 增量修改,不重写整页。
3. 保留已有树筛选、导入导出、列显隐、更多菜单、状态切换、路由跳转、SCSS 页面壳。
4. 不要把复杂页面退化成 generator 式基础列表页。
## 常见任务
- 调整工具栏和更多菜单
- 增加筛选条件和日期范围
- 增加导入导出能力
- 增加状态切换、快捷操作、确认弹窗
- 补复杂页面的小型子功能
## 自检
- 是否破坏了原页面结构和样式
- 是否误删了已有权限控制或交互能力
- 是否应该拆成子组件而不是继续堆主页面

View File

@@ -1,136 +0,0 @@
---
name: frontend-crud-coding
description: 在当前前端项目中按现有 Vue 3 + TypeScript + Element Plus 代码风格生成或修改页面、API、types、组件接入和样式。用于新增列表页、表单弹窗页、树表页、系统管理页、workflow 页面,以及补全与后端接口对应的 src/api 和 src/views 代码。
---
# 前端编码规范
先对齐当前前端项目里的真实实现,再参考关联后端工程中代码生成器产出的前端模板。不要只套通用 Vue 模板,也不要把生成器模板原样照搬而忽略当前前端项目的实际演进。
## 适用场景
在下面这些任务里优先使用此 skill
- 新增标准 CRUD 列表页、弹窗表单页、树表页。
- 补齐后端新增接口对应的 `src/api``src/views``types.ts`
- 按系统管理、监控、工作流、demo 模块现有方式扩展页面功能。
- 调整已有列表页的搜索、导出、导入、树筛选、列显隐、权限按钮、样式壳。
- 把关联后端工程中的 generator 模板转换为符合当前前端项目风格的实际代码。
## 不适用场景
下面这些任务不要机械套用本 skill 的 CRUD 规则:
- 纯展示型落地页、营销页、可视化大屏。
- 完全独立的低代码设计器或第三方嵌入页。
- 全局框架升级、Vite 配置改造、构建链路迁移。
- 与当前项目目录结构明显不同的实验性页面。
## 执行流程
1. 先定位目标模块,并阅读 `src/api/<module>/<business>``src/views/<module>/<business>` 下最近似页面。
2. 再参考关联后端工程 `ruoyi-modules/ruoyi-gen/src/main/resources/vm/ts``vm/vue` 下的生成器模板,确认标准 CRUD 的基础骨架。
3. 新增代码时同时维护 `api/index.ts``api/types.ts``views/.../index.vue`,必要时补相关子页面或弹窗页。
4. 页面结构、样式组织、状态管理、权限指令、下载导出、字典使用都以仓库现有模式为准。
5. 如果后端接口与生成器套路一致,可以用 generator 模板作为起点;如果当前前端项目已有更强约定,以当前项目约定覆盖模板默认行为。
## 优先级规则
发生冲突时按下面顺序决策:
1. 当前目录下最近似页面的真实实现。
2. 当前项目公共组件、公共工具、公共样式约定。
3. 关联后端工程中的 generator 模板。
4. 通用 Vue / Element Plus 习惯。
也就是说:
- 同一模块已有页面怎么写,优先怎么写。
- 没有现成页面时,再退回到 generator 模板骨架。
- 没有现成模式时,才使用通用框架默认写法。
## 主要规则
详细规则见 [references/frontend.md](references/frontend.md)。
使用案例见 [references/examples.md](references/examples.md)。
## 仓库通用规则
- 遵循 [`.editorconfig`](../../../.editorconfig)UTF-8、LF、默认 2 空格缩进。
- 遵循 [`.prettierrc`](../../../.prettierrc):单引号、分号、`printWidth: 150``trailingComma: none`
- 页面优先使用 `<script setup name="Xxx" lang="ts">`
- 优先复用仓库已有基础设施,例如 `request``proxy?.$modal``proxy?.download``proxy?.useDict``pagination``right-toolbar`
- 对于标准 CRUD 页,允许先按后端生成器模板组织 `api/types/index.vue` 骨架,再补齐当前前端项目自己的页面壳、样式和交互。
- 新页面不要无故引入另一套状态管理、另一套请求封装或另一套 UI 风格。
## 目录映射规则
通常按下面的对应关系组织代码:
- 后端路由 `/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
目标是快速补齐 `api + types + index.vue`,优先参考 generator 模板,再贴近 demo 或系统模块现有页。
### 2. 强业务页面
如果页面包含树筛选、导入导出、更多操作、状态切换、角色分配、复杂校验、联动选择,则优先参考 `src/views/system/user/index.vue` 一类更完整页面。
### 3. 工作流页面
如果页面属于流程定义、分类、任务、实例等 workflow 目录,优先参考 `src/views/workflow/*`,不要硬套系统管理模块的页面骨架。
## 输出要求
使用本 skill 时,默认期望产出应满足:
- 类型完整,不把大量 `any` 塞进页面逻辑里。
- 查询、重置、分页、弹窗、删除、导出流程闭环完整。
- 权限指令、字典、公共组件接入到位。
- 样式尽量贴合现有页面壳,而不是只保证“功能能跑”。
- 如果是从 generator 模板演化而来,要体现出当前前端项目已有增强,而不是模板裸输出。
## 快速检查清单
- API 路径与后端路由完全对应。
- `src/api` 中同时维护 `index.ts``types.ts`
- 列表页查询、重置、导出、删除、弹窗提交流程与现有页一致。
- 继续使用项目内权限指令与公共组件。
- 表单、查询、弹窗、表格样式优先复用现有布局类和 SCSS 片段。
- 缩进、引号、分号与仓库格式一致。
## 推荐提问方式
推荐把请求描述到下面这个粒度:
- 目标模块和业务名
- 后端接口前缀
- 是新增页面还是修改页面
- 是否需要导入、导出、树筛选、状态切换、字典、权限按钮
- 希望参考哪个现有页面
例如:
- 使用 `$frontend-crud-coding``/system/client` 补一套标准 CRUD 页面,参考 `system/user` 和 generator 模板。
- 使用 `$frontend-crud-coding` 修改 `workflow/category` 列表页,增加导出按钮和状态筛选,保持当前项目风格。

View File

@@ -1,7 +0,0 @@
interface:
display_name: "前端编码"
short_description: "按当前前端项目约定编写页面与 API"
default_prompt: "使用 $frontend-crud-coding 在当前前端项目里按现有约定实现页面和 API 修改。"
policy:
allow_implicit_invocation: true

View File

@@ -1,112 +0,0 @@
# 使用案例
## 案例 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}。
请参考 generator 模板和现有的 system/user、system/config 页面风格实现。
```
### 期望执行方式
- 先看 `src/api/system/client/*` 是否已存在。
- 再看 `src/views/system/client/index.vue` 是否为空或缺失。
- 参考同目录系统模块页面,确定是否需要搜索卡片、表格卡片、弹窗、导出按钮。
- 再参考关联后端工程中的 generator 模板,补齐基础骨架。
### 期望产物
- `src/api/system/client/index.ts`
- `src/api/system/client/types.ts`
- `src/views/system/client/index.vue`
## 案例 2把 generator 模板落成当前项目风格
### 用户提问示例
```text
使用 $frontend-crud-coding 按 generator 模板为 demo/order 生成一个标准页面,但不要直接复制模板,要改成当前前端项目现有样式壳和工具链写法。
```
### 期望执行方式
- 先看 generator 的 `ts/types/index.vue` 模板。
- 再看 `src/views/demo/demo/index.vue``src/views/system/user/index.vue` 的实际风格差异。
- 生成的页面要使用当前项目里的 `right-toolbar``pagination``proxy?.$modal``proxy?.download` 等。
## 案例 3修改已有列表页
### 用户提问示例
```text
使用 $frontend-crud-coding 修改 system/user 页面:
1. 新增一个创建时间快捷筛选
2. 导出按钮放到更多菜单中
3. 保持现有样式和交互不变
```
### 期望执行方式
- 优先阅读现有 `src/views/system/user/index.vue`
- 判断这是“已有页面增强”,不是“重新生成页面”。
- 保留树筛选、导入导出、列显隐、角色分配等现有能力。
- 增量修改,而不是重写整个页面。
## 案例 4补齐复杂业务页面
### 用户提问示例
```text
使用 $frontend-crud-coding 为 workflow/category 增加导入、导出和状态切换功能,参考 system/user 的完整页面能力,但保持 workflow 模块自己的风格。
```
### 期望执行方式
- 优先看 `src/views/workflow/category/index.vue`
- 再看 `src/views/system/user/index.vue` 里复杂列表页的做法。
- 只迁移需要的能力,不把用户模块专属逻辑照搬到 workflow 页面。
## 案例 5只补 API 和 types
### 用户提问示例
```text
使用 $frontend-crud-coding 为 monitor/cache 补全前端 API 和 types页面先不改。
```
### 期望执行方式
- 只维护 `src/api/monitor/cache/index.ts``src/api/monitor/cache/types.ts`
- 仍然要与后端路由、现有 API 风格、返回类型保持一致。
## 案例 6推荐的高质量任务描述
下面这种描述最容易得到稳定结果:
```text
使用 $frontend-crud-coding 在当前前端项目中新增一个 `/system/notice` 列表页增强:
1. 保留现有页面
2. 新增状态筛选和导出
3. API 路径沿用后端现有接口
4. 参考 system/user 的工具栏与导出交互
5. 参考 generator 模板补齐缺失的 types 定义
```
## 不推荐的任务描述
下面这种描述太模糊,容易让产物偏离项目:
```text
帮我写个后台页面
```
更好的写法至少要补充:
- 模块名
- 业务名
- 后端接口前缀
- 是新增还是修改
- 想参考哪个现有页面

View File

@@ -1,241 +0,0 @@
# 前端约定
## 优先参考的代码来源
- 关联后端工程中的生成器模板:
`ruoyi-modules/ruoyi-gen/src/main/resources/vm/ts/*.vm`
`ruoyi-modules/ruoyi-gen/src/main/resources/vm/vue/*.vm`
- `src/api/system/user/index.ts`
- `src/api/system/user/types.ts`
- `src/views/system/user/index.vue`
- `src/views/demo/demo/index.vue`
- `src/views/system/*`
- `src/views/workflow/*`
- `src/components/*`
- `src/assets/styles/components/*`
## 基础栈与格式
- 技术栈是 Vue 3 + TypeScript + Element Plus + Vite。
- 请求统一通过 `@/utils/request`
- API 返回值类型常用 `AxiosPromise<T>`
- 项目默认 2 空格缩进。
- 使用单引号和分号。
- 不要在一个页面里混入与仓库不一致的格式和写法。
## 决策顺序
写代码时按下面顺序取样:
1. 当前业务目录下最近似页面。
2. 当前模块下最近似 API/types 文件。
3. 当前项目的公共组件、公共工具、公共样式。
4. 关联后端工程的 generator 模板。
5. 通用 Vue 3 / Element Plus 默认写法。
如果上述规则冲突,优先相信当前项目真实代码。
## API 文件规则
- 标准 CRUD 的 API、types、列表页骨架可以先参考后端生成器模板再根据当前前端项目风格落地。
- API 文件通常放在 `src/api/<module>/<business>/index.ts`
- 同目录维护 `types.ts`
- 常见 import 形式:
`import request from '@/utils/request';`
`import { AxiosPromise } from 'axios';`
`import { XxxForm, XxxQuery, XxxVO } from './types';`
`import { PageResult } from '@/api/types';`
- 列表接口通常返回 `AxiosPromise<PageResult<XxxVO>>`
- 详情接口返回 `AxiosPromise<XxxVO>` 或更复杂的 `InfoVO`
- 特殊请求参数沿用现有实现,例如:
`parseStrEmpty(userId)`
`headers: { isEncrypt: true, repeatSubmit: false }`
`params` 用于 query string`data` 用于 body。
- 当前仓库部分模块会在文件底部 `export default { ... }`,已有模块使用这种形式时继续保持一致。
### API 文件建议结构
标准 CRUD 一般按这个顺序组织:
1. import 区
2. 列表接口
3. 详情接口
4. 新增接口
5. 修改接口
6. 删除接口
7. 特殊接口
8. 可选的 `export default`
### API 常见判断
- 如果后端是列表分页接口,前端通常返回 `AxiosPromise<PageResult<XxxVO>>`
- 如果后端返回复合结构,例如 `user + roles + posts`,单独定义 `InfoVO`
- 如果接口需要加密或关闭重复提交,直接在 `headers` 里表达,不要另起封装。
## 类型文件规则
- 类型文件通常定义 `Query``VO``Form`,必要时补 `InfoVO``ResetPwdForm` 等扩展类型。
- `Query` 一般继承 `PageQuery`
- `VO` 常继承 `BaseEntity`
- ID 字段通常使用 `string | number`
- 列表页多选 ID 常用 `Array<string | number>`
- 数组字段在表单里常直接用 `string[]``number[]` 或宽松类型,优先跟随现有模块。
### 类型拆分建议
- `VO` 面向列表和详情展示。
- `Form` 面向新增和编辑。
- `Query` 面向列表筛选。
- `InfoVO` 面向详情页、编辑页、弹窗预加载等复合返回结构。
### 类型字段策略
- 能明确写出类型时,不要偷懒用 `any`
- 只有在当前模块已有宽松写法或后端返回非常不稳定时,才保留 `any`
- 如果列表和表单字段明显不同,不要强行复用一个接口类型。
## Vue 页面结构规则
- 标准 CRUD 页可先参考生成器的 `index.vue.vm` 骨架,再按本仓库现有页面补强。
- 页面优先使用 `<script setup name="Xxx" lang="ts">`
- 常见列表页结构:
搜索区卡片、表格区卡片、工具栏、分页、编辑弹窗。
- 常见页面状态包括:
`loading``showSearch``ids``single``multiple``total`
- 表单和查询对象通常通过 `reactive<PageData<Form, Query>>({...})` 管理。
- 弹窗状态通常使用:
`const dialog = reactive<DialogOption>({ visible: false, title: '' });`
- 表单 ref 通常命名为 `queryFormRef``xxxFormRef`
- 复杂页面可补充树面板、导入弹窗、子弹窗、路由跳转逻辑。
### 标准页面骨架
标准页面通常包含这些区域:
1. 搜索区
2. 表格区
3. 工具栏
4. 分页
5. 编辑弹窗
复杂页面可以额外增加:
- 左侧树筛选
- 导入弹窗
- 二级对话框
- 独立详情页
- 路由跳转按钮
- 列显隐控制
### 页面命名建议
- 页面组件名通常为业务名,例如 `name="User"``name="Demo"`
- 页面根类名尽量带模块语义,例如:
`system-user-page`
`demo-demo-page`
`workflow-category-page`
## 页面行为规则
- `getList` 负责发起列表请求、处理 loading、回填 `rows``total`
- `handleQuery` 先把 `pageNum` 置为 `1`,再重新查询。
- `resetQuery` 负责清空查询表单、日期范围、树节点选择,然后重新加载。
- `handleSelectionChange` 更新 `ids``single``multiple`
- `handleAdd` 重置表单并打开新增弹窗。
- `handleUpdate` 查详情后回填表单并打开编辑弹窗。
- `submitForm` 使用表单校验,通过后调用新增或修改接口,再提示成功并刷新列表。
- `handleDelete` 通常使用 `proxy?.$modal.confirm(...)` 二次确认。
- `handleExport` 使用 `proxy?.download(...)`
- 日期范围查询沿用 `proxy?.addDateRange(queryParams.value, dateRange.value)`
- 需要更稳妥地处理确认框或异步异常时,可沿用 `await-to-js``to(...)` 风格。
### 页面逻辑建议
- 新增和编辑优先共用一套弹窗和表单。
- `reset()``cancel()` 分开写,避免关闭弹窗时状态残留。
- `handleUpdate()` 先查详情再 `Object.assign(form.value, res.data)`
- 删除、状态切换、解锁、重置密码这类危险操作优先保留确认提示。
- 列表页只做列表页职责,复杂复合逻辑优先拆到子组件或独立页面。
## 字典、权限与公共工具
- 字典通常通过:
`const { xxx_dict } = toRefs<any>(proxy?.useDict('xxx_dict'));`
- 权限指令以仓库现状为准,存在 `v-hasPermi``v-has-permi` 两种写法;新增代码优先跟随所在目录附近文件,不要在同一文件里混用新的变体。
- 常用公共能力:
`proxy?.$modal`
`proxy?.download`
`proxy?.useDict`
`proxy?.getConfigKey`
`checkPermi`
`useUserStore`
### 权限规则
- 所有增删改导入导出按钮都先看附近页面是否有权限控制。
- 新按钮默认补权限指令,除非它是纯展示行为。
- 如果同目录页面使用 `v-hasPermi`,新代码优先继续用 `v-hasPermi`
- 如果同目录页面使用 `v-has-permi`,新代码优先继续用 `v-has-permi`
## 组件与样式规则
- 优先复用公共组件:
`right-toolbar`
`pagination`
`ImageUpload`
`ImagePreview`
`FileUpload`
`Editor`
`DictTag`
- 页面样式不要堆大量内联样式,优先沿用仓库里的布局类和组件样式。
- 已有页面使用 SCSS 模块片段时,继续沿用:
`@use '@/assets/styles/components/page-shell' as pageShell;`
`@include pageShell.xxx;`
- 类名命名保持模块化,例如:
`system-user-page`
`demo-demo-page`
`table-panel`
`search-panel`
`toolbar-shell`
### 样式落点建议
- 页面只需要轻量调整时,优先复用已有通用类。
- 页面结构明显复杂时,优先在 `<style lang="scss" scoped>` 中通过 `@use` 复用组件样式片段。
- 不要为了单页需求破坏全局组件样式。
## 与生成器模板的关系
- 关联后端工程生成器给出的前端结构可以作为起点,但真实页面通常更完整,包含:
树筛选、列显隐、导入导出、更多操作、SCSS 页面壳、复杂表单校验、独立子页面。
- 因此新增页面时,不要只满足“能跑”,要先看所在模块已有页面的复杂度和 UI 组织方式。
### 什么时候优先看 generator
- 新增一个标准单表 CRUD 页面时。
- 当前项目里还没有这个业务对应页面时。
- 你只拿到了后端路由和字段信息时。
### 什么时候优先看现有页面
- 当前模块已经有同类页面时。
- 页面包含树筛选、导入导出、联动弹窗、路由跳转时。
- 任务是“修改已有页面”而不是“新建页面”时。
## 避免事项
- 不要直接把后端仓库里的前端模板原样复制进来。
- 不要跳过 `types.ts`,把类型全堆在页面里。
- 不要绕开 `request` 自己再包一层请求工具。
- 不要引入与仓库现状不一致的 CSS 组织方式。
- 不要为了省事删掉权限控制、导出、导入、树筛选、日期范围等现有交互能力。
## 交付前自检
交付前至少检查这些点:
- 页面能否完整走通查询、新增、编辑、删除、导出流程。
- 类型是否与接口返回结构一致。
- 是否保留了原页面已有的权限和交互能力。
- 是否沿用了当前模块已有的组件和样式壳。
- 是否只是“生成器裸页”,如果是,需要继续补齐到当前项目风格。

View File

@@ -1,5 +1,5 @@
# 页面标题
VITE_APP_TITLE = RuoYi-Vue-Plus后台管理系统
VITE_APP_TITLE = RuoYi-Vue-Plus多租户管理系统
VITE_APP_LOGO_TITLE = RuoYi-Vue-Plus
# 开发环境配置
@@ -22,18 +22,15 @@ VITE_APP_PORT = 80
# 接口加密功能开关(如需关闭 后端也必须对应关闭)
VITE_APP_ENCRYPT = true
# 接口加密传输 RSA 公钥与后端解密私钥对应 如更换需前后端一同更换
VITE_APP_RSA_PUBLIC_KEY = 'MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDvEDuRIOM3oZPWj9Ukoc5pQklR4PFH6/clnjeFqjDLIgDyQvjxhgqAZQA+E9eD6qu6FsXPmK8djcL+nh3cFHz4pX473jDvO3Sve+8yL3VRQ0n2pRgQ2a01MJsy+WwTZCBYWf0VnLRIvANUoWQgy9vz94q7Va44dg7A1/3ICf+xAwIDAQAB'
VITE_APP_RSA_PUBLIC_KEY = 'MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdHnzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ=='
# 接口响应解密 RSA 私钥与后端加密公钥对应 如更换需前后端一同更换
VITE_APP_RSA_PRIVATE_KEY = 'MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAObq7yrxfvyieZtTjAYyrdvi59tYTXxjO5ajmPCRSXBY9M9wQ1tli297JN6mnY53UJMNyOFNSZVi8WSFoIXjpR87FmvChJlzeN/dZdd3SEs48Ee66XKeSePYqxa8oO5GKDsnajgpsOHKXSeeVSIysiIPS2/WsEqk0In9P4w3RsRFAgMBAAECgYBiMEWwce24SPICnRzuScBpvmsudrbEDIH7BOd0a6LZlcnLJwZNJ7mJlshPsHNQb+WgEf135+BBGEhioPtn0yuTdEuKP4kB9UdYUKiayWCoWhJpesv7sAD4RDClV7dhuV+gcd1AXD+YzyRIPbGm0VC2U+4q8/+UPRpVjqskbLVTgQJBAPRpou7g3S8n4XB527kq0D8I3+ZYwMxZhszwhrCDpJU319+ucmpLVwYIzDmZVeID2QQdUaDfIEViFHu95xDrGiUCQQDx3YOKn3yaEctk/ERVn7hDAyAXUbd8/pv2b24/M/l1ZevlsFem8U4Jk5Mu64t3z3YGJoymEjQmbucwT01iKhehAkEAxlnccsRmfFh/KkqauKE4M4++NTAd9zlInpUsmZ+cN8UEGnF2RTEzRKBrLOt1uWCqBB7PGiE6DVTVjr7FAQPrSQJAT5yeY87DcONSk9cFlzmPqV8p/QME5rvYEnHzVBKDlkUKNPyqnWToTvaoh9U4fyNmsfeWbEOprszqhFhWHG3GgQJAK8+ynmyFhaw63+Hx2KU5zR4hVuQso2IzrEurGxCxybV6mR7VBerb4502+EPx3PmOgxQL+niUFhcMWxcvBFP9+A=='
VITE_APP_RSA_PRIVATE_KEY = 'MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAmc3CuPiGL/LcIIm7zryCEIbl1SPzBkr75E2VMtxegyZ1lYRD+7TZGAPkvIsBcaMs6Nsy0L78n2qh+lIZMpLH8wIDAQABAkEAk82Mhz0tlv6IVCyIcw/s3f0E+WLmtPFyR9/WtV3Y5aaejUkU60JpX4m5xNR2VaqOLTZAYjW8Wy0aXr3zYIhhQQIhAMfqR9oFdYw1J9SsNc+CrhugAvKTi0+BF6VoL6psWhvbAiEAxPPNTmrkmrXwdm/pQQu3UOQmc2vCZ5tiKpW10CgJi8kCIFGkL6utxw93Ncj4exE/gPLvKcT+1Emnoox+O9kRXss5AiAMtYLJDaLEzPrAWcZeeSgSIzbL+ecokmFKSDDcRske6QIgSMkHedwND1olF8vlKsJUGK3BcdtM8w4Xq7BpSBwsloE='
# 客户端id
VITE_APP_CLIENT_ID = 'e5cd7e4891bf95d1d19206ce24a7b32e'
# 统一消息推送开关
VITE_APP_MESSAGE_ENABLED = true
# websocket 开关 默认使用sse推送
VITE_APP_WEBSOCKET = false
# sse / websocket
VITE_APP_MESSAGE_TRANSPORT = 'sse'
# 统一消息推送路径
VITE_APP_MESSAGE_PATH = '/resource/message'
# sse 开关
VITE_APP_SSE = true

View File

@@ -1,5 +1,5 @@
# 页面标题
VITE_APP_TITLE = RuoYi-Vue-Plus后台管理系统
VITE_APP_TITLE = RuoYi-Vue-Plus多租户管理系统
VITE_APP_LOGO_TITLE = RuoYi-Vue-Plus
# 生产环境配置
@@ -25,18 +25,15 @@ VITE_APP_PORT = 80
# 接口加密功能开关(如需关闭 后端也必须对应关闭)
VITE_APP_ENCRYPT = true
# 接口加密传输 RSA 公钥与后端解密私钥对应 如更换需前后端一同更换
VITE_APP_RSA_PUBLIC_KEY = 'MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDvEDuRIOM3oZPWj9Ukoc5pQklR4PFH6/clnjeFqjDLIgDyQvjxhgqAZQA+E9eD6qu6FsXPmK8djcL+nh3cFHz4pX473jDvO3Sve+8yL3VRQ0n2pRgQ2a01MJsy+WwTZCBYWf0VnLRIvANUoWQgy9vz94q7Va44dg7A1/3ICf+xAwIDAQAB'
VITE_APP_RSA_PUBLIC_KEY = 'MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdHnzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ=='
# 接口响应解密 RSA 私钥与后端加密公钥对应 如更换需前后端一同更换
VITE_APP_RSA_PRIVATE_KEY = 'MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAObq7yrxfvyieZtTjAYyrdvi59tYTXxjO5ajmPCRSXBY9M9wQ1tli297JN6mnY53UJMNyOFNSZVi8WSFoIXjpR87FmvChJlzeN/dZdd3SEs48Ee66XKeSePYqxa8oO5GKDsnajgpsOHKXSeeVSIysiIPS2/WsEqk0In9P4w3RsRFAgMBAAECgYBiMEWwce24SPICnRzuScBpvmsudrbEDIH7BOd0a6LZlcnLJwZNJ7mJlshPsHNQb+WgEf135+BBGEhioPtn0yuTdEuKP4kB9UdYUKiayWCoWhJpesv7sAD4RDClV7dhuV+gcd1AXD+YzyRIPbGm0VC2U+4q8/+UPRpVjqskbLVTgQJBAPRpou7g3S8n4XB527kq0D8I3+ZYwMxZhszwhrCDpJU319+ucmpLVwYIzDmZVeID2QQdUaDfIEViFHu95xDrGiUCQQDx3YOKn3yaEctk/ERVn7hDAyAXUbd8/pv2b24/M/l1ZevlsFem8U4Jk5Mu64t3z3YGJoymEjQmbucwT01iKhehAkEAxlnccsRmfFh/KkqauKE4M4++NTAd9zlInpUsmZ+cN8UEGnF2RTEzRKBrLOt1uWCqBB7PGiE6DVTVjr7FAQPrSQJAT5yeY87DcONSk9cFlzmPqV8p/QME5rvYEnHzVBKDlkUKNPyqnWToTvaoh9U4fyNmsfeWbEOprszqhFhWHG3GgQJAK8+ynmyFhaw63+Hx2KU5zR4hVuQso2IzrEurGxCxybV6mR7VBerb4502+EPx3PmOgxQL+niUFhcMWxcvBFP9+A=='
VITE_APP_RSA_PRIVATE_KEY = 'MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAmc3CuPiGL/LcIIm7zryCEIbl1SPzBkr75E2VMtxegyZ1lYRD+7TZGAPkvIsBcaMs6Nsy0L78n2qh+lIZMpLH8wIDAQABAkEAk82Mhz0tlv6IVCyIcw/s3f0E+WLmtPFyR9/WtV3Y5aaejUkU60JpX4m5xNR2VaqOLTZAYjW8Wy0aXr3zYIhhQQIhAMfqR9oFdYw1J9SsNc+CrhugAvKTi0+BF6VoL6psWhvbAiEAxPPNTmrkmrXwdm/pQQu3UOQmc2vCZ5tiKpW10CgJi8kCIFGkL6utxw93Ncj4exE/gPLvKcT+1Emnoox+O9kRXss5AiAMtYLJDaLEzPrAWcZeeSgSIzbL+ecokmFKSDDcRske6QIgSMkHedwND1olF8vlKsJUGK3BcdtM8w4Xq7BpSBwsloE='
# 客户端id
VITE_APP_CLIENT_ID = 'e5cd7e4891bf95d1d19206ce24a7b32e'
# 统一消息推送开关
VITE_APP_MESSAGE_ENABLED = true
# websocket 开关 默认使用sse推送
VITE_APP_WEBSOCKET = false
# sse / websocket
VITE_APP_MESSAGE_TRANSPORT = 'sse'
# 统一消息推送路径
VITE_APP_MESSAGE_PATH = '/resource/message'
# sse 开关
VITE_APP_SSE = true

328
.eslintrc-auto-import.json Normal file
View File

@@ -0,0 +1,328 @@
{
"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
}
}

View File

@@ -1,31 +0,0 @@
{
"$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"
]
}
}

View File

@@ -1,25 +0,0 @@
{
"$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/**"]
}

9
.prettierignore Normal file
View File

@@ -0,0 +1,9 @@
/dist/*
.local
.output.js
/node_modules/**
**/*.svg
**/*.sh
/public/*

20
.prettierrc Normal file
View File

@@ -0,0 +1,20 @@
{
"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"
}

View File

@@ -1,15 +1,15 @@
## 平台简介
- 本仓库为前端技术栈 [Vue3](https://v3.cn.vuejs.org) + [TS](https://www.typescriptlang.org/) + [Element Plus](https://element-plus.org/zh-CN) + [Vite](https://cn.vitejs.dev) 版本。
- 成员项目: 基于 vben5(ant-design-vue) 的前端项目 [ruoyi-plus-vben5](https://gitee.com/dapppp/ruoyi-plus-vben5)
- 成员项目: 基于 vben5(ant-design-vue) 的前端项目 [ruoyi-plus-vben5](https://github.com/imdap/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) |
## 分支说明
@@ -20,13 +20,13 @@
```bash
# 安装依赖
pnpm install --registry=https://registry.npmmirror.com
npm install --registry=https://registry.npmmirror.com
# 启动服务
pnpm dev
npm run dev
# 构建生产环境
pnpm build:prod
npm run build:prod
# 前端访问地址 http://localhost:80
```
@@ -35,6 +35,8 @@ pnpm build:prod
| 业务 | 功能说明 | 本框架 | RuoYi |
| ------------ | ------------------------------------------------------------- | ------ | ----------------------------- |
| 租户管理 | 系统内租户的管理 如:租户套餐、过期时间、用户数量、企业信息等 | 支持 | 无 |
| 租户套餐管理 | 系统内租户所能使用的套餐管理 如:套餐内所包含的菜单等 | 支持 | 无 |
| 用户管理 | 用户的管理配置 如:新增用户、分配用户所属部门、角色、岗位等 | 支持 | 支持 |
| 部门管理 | 配置系统组织机构(公司、部门、小组) 树结构展现支持数据权限 | 支持 | 支持 |
| 岗位管理 | 配置系统用户所属担任职务 | 支持 | 支持 |
@@ -78,4 +80,4 @@ pnpm build:prod
| ![输入图片说明](https://foruda.gitee.com/images/1680078941718318363/b68a0f72_1766278.png '屏幕截图') | ![输入图片说明](https://foruda.gitee.com/images/1680078963175518631/3bb769a1_1766278.png '屏幕截图') |
| ![输入图片说明](https://foruda.gitee.com/images/1680078982294090567/b31c343d_1766278.png '屏幕截图') | ![输入图片说明](https://foruda.gitee.com/images/1680079000642440444/77ca82a9_1766278.png '屏幕截图') |
| ![输入图片说明](https://foruda.gitee.com/images/1680079020995074177/03b7d52e_1766278.png '屏幕截图') | ![输入图片说明](https://foruda.gitee.com/images/1680079039367822173/76811806_1766278.png '屏幕截图') |
| ![输入图片说明](https://foruda.gitee.com/images/1680079274333484664/4dfdc7c0_1766278.png '屏幕截图') | ![输入图片说明](https://foruda.gitee.com/images/1680079290467458224/d6715fcf_1766278.png '屏幕截图') |
| ![输入图片说明](https://foruda.gitee.com/images/1680079274333484664/4dfdc7c0_1766278.png '屏幕截图') | ![输入图片说明](https://foruda.gitee.com/images/1680079290467458224/d6715fcf_1766278.png '屏幕截图') |

12
bin/build.bat Normal file
View File

@@ -0,0 +1,12 @@
@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

12
bin/package.bat Normal file
View File

@@ -0,0 +1,12 @@
@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

12
bin/run-web.bat Normal file
View File

@@ -0,0 +1,12 @@
@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

44
eslint.config.ts Normal file
View File

@@ -0,0 +1,44 @@
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'
}
}
);

View File

@@ -144,8 +144,7 @@
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 {
@@ -209,18 +208,15 @@
</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 />
@@ -228,28 +224,16 @@
<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>

View File

@@ -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,

View File

@@ -1,87 +1,95 @@
{
"$schema": "https://json.schemastore.org/package",
"name": "ruoyi-vue-plus",
"version": "5.5.3-2.5.3",
"description": "RuoYi-Vue-Plus后台管理系统",
"license": "MIT",
"version": "5.6.1-2.6.1",
"description": "RuoYi-Vue-Plus多租户管理系统",
"author": "LionLi",
"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 ."
},
"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",
"@iconify/vue": "^5.0.0",
"@vueup/vue-quill": "1.2.0",
"@vueuse/core": "14.2.1",
"@wangeditor-next/editor": "5.7.0",
"@wangeditor-next/editor-for-vue": "5.1.14",
"animate.css": "4.1.1",
"await-to-js": "3.0.0",
"axios": "1.15.2",
"axios": "1.13.6",
"crypto-js": "4.2.0",
"echarts": "6.0.0",
"element-plus": "2.13.7",
"element-plus": "2.13.5",
"file-saver": "2.0.5",
"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",
"vue": "3.5.33",
"screenfull": "6.0.2",
"vue": "3.5.30",
"vue-cropper": "1.1.4",
"vue-i18n": "11.4.0",
"vue-i18n": "11.3.0",
"vue-json-pretty": "2.6.0",
"vue-router": "5.0.6",
"vue-router": "5.0.3",
"vue-types": "6.0.0",
"vxe-table": "4.18.13"
"vxe-table": "4.18.1"
},
"devDependencies": {
"@iconify/json": "^2.2.448",
"@types/crypto-js": "4.2.2",
"@types/node": "^25.6.0",
"@types/file-saver": "2.0.7",
"@types/js-cookie": "3.0.6",
"@types/node": "^25.4.0",
"@types/nprogress": "0.2.3",
"@unocss/preset-attributify": "66.6.8",
"@unocss/preset-wind3": "66.6.8",
"@vitejs/plugin-vue": "^6.0.6",
"@vue/compiler-sfc": "3.5.33",
"autoprefixer": "10.5.0",
"oxfmt": "^0.46.0",
"oxlint": "^1.61.0",
"sass": "1.99.0",
"typescript": "^6.0.3",
"unocss": "66.6.8",
"@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",
"unplugin-auto-import": "21.0.0",
"unplugin-vue-components": "32.0.0",
"unplugin-icons": "23.0.1",
"unplugin-vue-components": "31.0.0",
"unplugin-vue-setup-extend-plus": "1.0.1",
"vite": "^8.0.10",
"vite-plugin-svg-icons-ng": "^1.8.0",
"vitest": "4.1.5",
"vue-tsc": "^3.2.7"
"vite": "7.3.2",
"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"
},
"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"
]
}
]
}

View File

@@ -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();

View File

@@ -1,14 +1,13 @@
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<PageResult<DemoVO>> => {
export const listDemo = (query?: DemoQuery): AxiosPromise<DemoVO[]> => {
return request({
url: '/demo/demo/list',
method: 'get',

View File

@@ -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';
/**
* 查询测试树列表

View File

@@ -1,8 +1,7 @@
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 type { LoginData, LoginResult, VerifyCodeResult } from './types';
import { AxiosPromise } from 'axios';
import { LoginData, LoginResult, VerifyCodeResult, TenantInfo } from './types';
import { UserInfo } from '@/api/system/user/types';
// pc端固定客户端授权id
const clientId = import.meta.env.VITE_APP_CLIENT_ID;
@@ -52,13 +51,9 @@ export function register(data: any) {
* 注销
*/
export function logout() {
closePush();
if (
import.meta.env.VITE_APP_MESSAGE_ENABLED === 'true' &&
import.meta.env.VITE_APP_MESSAGE_TRANSPORT.toLowerCase() === 'sse'
) {
if (import.meta.env.VITE_APP_SSE === 'true') {
request({
url: import.meta.env.VITE_APP_MESSAGE_PATH + '/close',
url: '/resource/sse/close',
method: 'get'
});
}
@@ -105,3 +100,14 @@ 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'
});
}

View File

@@ -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[]> {

View File

@@ -1,6 +1,6 @@
import type { AxiosPromise } from '@/utils/api-types';
import request from '@/utils/request';
import type { CacheVO } from './types';
import { AxiosPromise } from 'axios';
import { CacheVO } from './types';
// 查询缓存详细
export function getCache(): AxiosPromise<CacheVO> {

View File

@@ -1,12 +1,11 @@
import type { PageResult } from '@/api/types';
import type { AxiosPromise } from '@/utils/api-types';
import request from '@/utils/request';
import type { LoginInfoQuery, LoginInfoVO } from './types';
import { LoginInfoQuery, LoginInfoVO } from './types';
import { AxiosPromise } from 'axios';
// 查询登录日志列表
export function list(query: LoginInfoQuery): AxiosPromise<PageResult<LoginInfoVO>> {
export function list(query: LoginInfoQuery): AxiosPromise<LoginInfoVO[]> {
return request({
url: '/monitor/loginInfo/list',
url: '/monitor/logininfor/list',
method: 'get',
params: query
});
@@ -15,7 +14,7 @@ export function list(query: LoginInfoQuery): AxiosPromise<PageResult<LoginInfoVO
// 删除登录日志
export function delLoginInfo(infoId: string | number | Array<string | number>) {
return request({
url: '/monitor/loginInfo/' + infoId,
url: '/monitor/logininfor/' + infoId,
method: 'delete'
});
}
@@ -23,7 +22,7 @@ export function delLoginInfo(infoId: string | number | Array<string | number>) {
// 解锁用户登录状态
export function unlockLoginInfo(userName: string | Array<string>) {
return request({
url: '/monitor/loginInfo/unlock/' + userName,
url: '/monitor/logininfor/unlock/' + userName,
method: 'get'
});
}
@@ -31,7 +30,7 @@ export function unlockLoginInfo(userName: string | Array<string>) {
// 清空登录日志
export function cleanLoginInfo() {
return request({
url: '/monitor/loginInfo/clean',
url: '/monitor/logininfor/clean',
method: 'delete'
});
}

View File

@@ -1,10 +1,9 @@
import type { PageResult } from '@/api/types';
import type { AxiosPromise } from '@/utils/api-types';
import request from '@/utils/request';
import type { OnlineQuery, OnlineVO } from './types';
import { OnlineQuery, OnlineVO } from './types';
import { AxiosPromise } from 'axios';
// 查询在线用户列表
export function list(query: OnlineQuery): AxiosPromise<PageResult<OnlineVO>> {
export function list(query: OnlineQuery): AxiosPromise<OnlineVO[]> {
return request({
url: '/monitor/online/list',
method: 'get',

View File

@@ -1,10 +1,9 @@
import type { PageResult } from '@/api/types';
import type { AxiosPromise } from '@/utils/api-types';
import request from '@/utils/request';
import type { OperLogQuery, OperLogVO } from './types';
import { OperLogQuery, OperLogVO } from './types';
import { AxiosPromise } from 'axios';
// 查询操作日志列表
export function list(query: OperLogQuery): AxiosPromise<PageResult<OperLogVO>> {
export function list(query: OperLogQuery): AxiosPromise<OperLogVO[]> {
return request({
url: '/monitor/operlog/list',
method: 'get',

View File

@@ -2,12 +2,6 @@ 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;
@@ -24,13 +18,7 @@ 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;
@@ -52,13 +40,7 @@ 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;

View File

@@ -1,7 +1,6 @@
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';
/**
* 查询客户端管理列表
@@ -9,7 +8,7 @@ import request from '@/utils/request';
* @returns {*}
*/
export const listClient = (query?: ClientQuery): AxiosPromise<PageResult<ClientVO>> => {
export const listClient = (query?: ClientQuery): AxiosPromise<ClientVO[]> => {
return request({
url: '/system/client/list',
method: 'get',

View File

@@ -29,26 +29,6 @@ export interface ClientVO {
*/
deviceType: string;
/**
* 允许访问路径
*/
accessPath?: string;
/**
* 允许访问路径列表
*/
accessPathList?: string[];
/**
* IP白名单
*/
ipWhitelist?: string;
/**
* IP白名单列表
*/
ipWhitelistList?: string[];
/**
* token活跃超时时间
*/
@@ -96,26 +76,6 @@ export interface ClientForm extends BaseEntity {
*/
deviceType?: string;
/**
* 允许访问路径
*/
accessPath?: string;
/**
* 允许访问路径列表
*/
accessPathList?: string[];
/**
* IP白名单
*/
ipWhitelist?: string;
/**
* IP白名单列表
*/
ipWhitelistList?: string[];
/**
* token活跃超时时间
*/
@@ -158,16 +118,6 @@ export interface ClientQuery extends PageQuery {
*/
deviceType?: string;
/**
* 允许访问路径
*/
accessPath?: string;
/**
* IP白名单
*/
ipWhitelist?: string;
/**
* token活跃超时时间
*/

View File

@@ -1,10 +1,9 @@
import type { PageResult } from '@/api/types';
import type { AxiosPromise } from '@/utils/api-types';
import request from '@/utils/request';
import type { ConfigForm, ConfigQuery, ConfigVO } from './types';
import { ConfigForm, ConfigQuery, ConfigVO } from './types';
import { AxiosPromise } from 'axios';
// 查询参数列表
export function listConfig(query: ConfigQuery): AxiosPromise<PageResult<ConfigVO>> {
export function listConfig(query: ConfigQuery): AxiosPromise<ConfigVO[]> {
return request({
url: '/system/config/list',
method: 'get',

View File

@@ -1,6 +1,6 @@
import type { AxiosPromise } from '@/utils/api-types';
import request from '@/utils/request';
import { type DeptForm, type DeptQuery, type DeptVO } from './types';
import { AxiosPromise } from 'axios';
import { DeptForm, DeptQuery, DeptTreeVO, DeptVO } from './types';
// 查询部门列表
export const listDept = (query?: DeptQuery) => {

View File

@@ -1,7 +1,6 @@
import type { PageResult } from '@/api/types';
import type { AxiosPromise } from '@/utils/api-types';
import request from '@/utils/request';
import type { DictDataForm, DictDataQuery, DictDataVO } from './types';
import { AxiosPromise } from 'axios';
import { DictDataForm, DictDataQuery, DictDataVO } from './types';
// 根据字典类型查询字典数据信息
export function getDicts(dictType: string): AxiosPromise<DictDataVO[]> {
return request({
@@ -11,7 +10,7 @@ export function getDicts(dictType: string): AxiosPromise<DictDataVO[]> {
}
// 查询字典数据列表
export function listData(query: DictDataQuery): AxiosPromise<PageResult<DictDataVO>> {
export function listData(query: DictDataQuery): AxiosPromise<DictDataVO[]> {
return request({
url: '/system/dict/data/list',
method: 'get',

View File

@@ -1,10 +1,9 @@
import type { PageResult } from '@/api/types';
import type { AxiosPromise } from '@/utils/api-types';
import request from '@/utils/request';
import type { DictTypeForm, DictTypeQuery, DictTypeVO } from './types';
import { DictTypeForm, DictTypeVO, DictTypeQuery } from './types';
import { AxiosPromise } from 'axios';
// 查询字典类型列表
export function listType(query: DictTypeQuery): AxiosPromise<PageResult<DictTypeVO>> {
export function listType(query: DictTypeQuery): AxiosPromise<DictTypeVO[]> {
return request({
url: '/system/dict/type/list',
method: 'get',

View File

@@ -1,6 +1,6 @@
import type { AxiosPromise } from '@/utils/api-types';
import request from '@/utils/request';
import type { MenuForm, MenuQuery, MenuTreeOption, MenuVO, RoleMenuTree } from './types';
import { AxiosPromise } from 'axios';
import { MenuQuery, MenuVO, MenuForm, MenuTreeOption, RoleMenuTree } from './types';
// 查询菜单列表
export const listMenu = (query?: MenuQuery): AxiosPromise<MenuVO[]> => {
@@ -35,6 +35,14 @@ 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({

View File

@@ -1,4 +1,4 @@
import type { MenuTypeEnum } from '@/enums/MenuTypeEnum';
import { MenuTypeEnum } from '@/enums/MenuTypeEnum';
/**
* 菜单树形结构类型
@@ -8,28 +8,12 @@ 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: Array<string | number>;
}
/**
* 角色菜单分配中的按钮节点类型
*/
export interface RoleMenuButtonOption {
menuId: string | number;
menuName: string;
parentId: string | number;
perms?: string;
status?: string;
disabled?: boolean;
checkedKeys: string[];
}
/**
@@ -60,8 +44,6 @@ export interface MenuVO extends BaseEntity {
visible: string;
status: string;
icon: string;
activeMenu: string;
ext: string;
remark: string;
}
@@ -81,8 +63,6 @@ export interface MenuForm {
visible?: string;
status?: string;
icon?: string;
activeMenu?: string;
ext?: string;
remark?: string;
query?: string;
perms?: string;

View File

@@ -1,10 +0,0 @@
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'
});
}

View File

@@ -1,17 +0,0 @@
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[];
}

View File

@@ -1,9 +1,8 @@
import type { PageResult } from '@/api/types';
import type { AxiosPromise } from '@/utils/api-types';
import request from '@/utils/request';
import type { NoticeForm, NoticeQuery, NoticeVO } from './types';
import { NoticeForm, NoticeQuery, NoticeVO } from './types';
import { AxiosPromise } from 'axios';
// 查询公告列表
export function listNotice(query: NoticeQuery): AxiosPromise<PageResult<NoticeVO>> {
export function listNotice(query: NoticeQuery): AxiosPromise<NoticeVO[]> {
return request({
url: '/system/notice/list',
method: 'get',

View File

@@ -1,10 +1,9 @@
import type { PageResult } from '@/api/types';
import type { AxiosPromise } from '@/utils/api-types';
import request from '@/utils/request';
import type { OssQuery, OssVO } from './types';
import { OssQuery, OssVO } from './types';
import { AxiosPromise } from 'axios';
// 查询OSS对象存储列表
export function listOss(query: OssQuery): AxiosPromise<PageResult<OssVO>> {
export function listOss(query: OssQuery): AxiosPromise<OssVO[]> {
return request({
url: '/resource/oss/list',
method: 'get',

View File

@@ -1,10 +1,9 @@
import type { PageResult } from '@/api/types';
import type { AxiosPromise } from '@/utils/api-types';
import request from '@/utils/request';
import type { OssConfigForm, OssConfigQuery, OssConfigVO } from './types';
import { OssConfigForm, OssConfigQuery, OssConfigVO } from './types';
import { AxiosPromise } from 'axios';
// 查询对象存储配置列表
export function listOssConfig(query: OssConfigQuery): AxiosPromise<PageResult<OssConfigVO>> {
export function listOssConfig(query: OssConfigQuery): AxiosPromise<OssConfigVO[]> {
return request({
url: '/resource/oss/config/list',
method: 'get',

View File

@@ -6,7 +6,7 @@ export interface OssConfigVO extends BaseEntity {
bucketName: string;
prefix: string;
endpoint: string;
domainUrl: string;
domain: string;
isHttps: string;
region: string;
status: string;
@@ -29,7 +29,7 @@ export interface OssConfigForm {
bucketName: string;
prefix: string;
endpoint: string;
domainUrl: string;
domain: string;
isHttps: string;
accessPolicy: string;
region: string;

View File

@@ -1,11 +1,10 @@
import type { PageResult } from '@/api/types';
import type { AxiosPromise } from '@/utils/api-types';
import request from '@/utils/request';
import type { DeptTreeVO } from '../dept/types';
import type { PostForm, PostQuery, PostVO } from './types';
import { PostForm, PostQuery, PostVO } from './types';
import { AxiosPromise } from 'axios';
import { DeptTreeVO } from '../dept/types';
// 查询岗位列表
export function listPost(query: PostQuery): AxiosPromise<PageResult<PostVO>> {
export function listPost(query: PostQuery): AxiosPromise<PostVO[]> {
return request({
url: '/system/post/list',
method: 'get',

View File

@@ -1,10 +1,10 @@
import type { UserQuery, UserVO } from '@/api/system/user/types';
import type { PageResult } from '@/api/types';
import type { AxiosPromise } from '@/utils/api-types';
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 request from '@/utils/request';
import type { RoleDeptTree, RoleQuery, RoleVO } from './types';
export const listRole = (query: RoleQuery): AxiosPromise<PageResult<RoleVO>> => {
export const listRole = (query: RoleQuery): AxiosPromise<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 updateRolePermission = (data: any) => {
export const dataScope = (data: any) => {
return request({
url: '/system/role/permission',
url: '/system/role/dataScope',
method: 'put',
data: data
});
@@ -95,7 +95,7 @@ export const delRole = (roleId: Array<string | number> | string | number) => {
/**
* 查询角色已授权用户列表
*/
export const allocatedUserList = (query: UserQuery): AxiosPromise<PageResult<UserVO>> => {
export const allocatedUserList = (query: UserQuery): AxiosPromise<UserVO[]> => {
return request({
url: '/system/role/authUser/allocatedList',
method: 'get',
@@ -106,7 +106,7 @@ export const allocatedUserList = (query: UserQuery): AxiosPromise<PageResult<Use
/**
* 查询角色未授权用户列表
*/
export const unallocatedUserList = (query: UserQuery): AxiosPromise<PageResult<UserVO>> => {
export const unallocatedUserList = (query: UserQuery): AxiosPromise<UserVO[]> => {
return request({
url: '/system/role/authUser/unallocatedList',
method: 'get',

View File

@@ -1,10 +1,14 @@
import request from '@/utils/request';
// 获取跳转URL
export function authRouterUrl(source: string) {
export function authRouterUrl(source: string, tenantId: string) {
return request({
url: '/auth/binding/' + source,
method: 'get'
method: 'get',
params: {
tenantId: tenantId,
domain: window.location.host
}
});
}

View File

@@ -0,0 +1,109 @@
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'
});
}

View File

@@ -0,0 +1,46 @@
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;
}

View File

@@ -0,0 +1,67 @@
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'
});
}

View File

@@ -0,0 +1,20 @@
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;
}

View File

@@ -1,16 +1,15 @@
import type { RoleVO } from '@/api/system/role/types';
import type { PageResult } from '@/api/types';
import type { AxiosPromise } from '@/utils/api-types';
import { DeptTreeVO } from './../dept/types';
import { RoleVO } from '@/api/system/role/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, UserQuery, UserVO } from './types';
/**
* 查询用户列表
* @param query
*/
export const listUser = (query: UserQuery): AxiosPromise<PageResult<UserVO>> => {
export const listUser = (query: UserQuery): AxiosPromise<UserVO[]> => {
return request({
url: '/system/user/list',
method: 'get',
@@ -111,17 +110,6 @@ 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'
});
};
/**
* 查询用户个人信息
*/
@@ -230,7 +218,6 @@ export default {
delUser,
resetUserPwd,
changeUserStatus,
unlockUser,
getUserProfile,
updateUserProfile,
updateUserPwd,

View File

@@ -1,5 +1,5 @@
import type { PostVO } from '@/api/system/post/types';
import type { RoleVO } from '@/api/system/role/types';
import { RoleVO } from '@/api/system/role/types';
import { PostVO } from '@/api/system/post/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;
gender: string;
phonenumber: string;
sex: string;
avatar: string;
status: string;
delFlag: string;
@@ -43,8 +43,6 @@ export interface UserVO extends BaseEntity {
loginDate: string;
remark: string;
deptName: string;
/** 详情接口可能返回嵌套部门 */
dept?: { deptName?: string };
roles: RoleVO[];
roleIds: any;
postIds: any;
@@ -62,9 +60,9 @@ export interface UserForm {
userName: string;
nickName?: string;
password: string;
phoneNumber?: string;
phonenumber?: string;
email?: string;
gender?: string;
sex?: string;
status: string;
remark?: string;
postIds: string[];

View File

@@ -1,12 +1,9 @@
import type { PageResult } from '@/api/types';
import type { AxiosPromise } from '@/utils/api-types';
import request from '@/utils/request';
import type { DbTableForm, DbTableQuery, DbTableVO, GenTableDetailPayload, TableQuery, TableVO } from './types';
export type { GenTableDetailPayload } from './types';
import { DbTableQuery, DbTableVO, TableQuery, TableVO, GenTableVO, DbTableForm } from './types';
import { AxiosPromise } from 'axios';
// 查询生成表数据
export const listTable = (query: TableQuery): AxiosPromise<PageResult<TableVO>> => {
export const listTable = (query: TableQuery): AxiosPromise<TableVO[]> => {
return request({
url: '/tool/gen/list',
method: 'get',
@@ -14,7 +11,7 @@ export const listTable = (query: TableQuery): AxiosPromise<PageResult<TableVO>>
});
};
// 查询db数据库列表
export const listDbTable = (query: DbTableQuery): AxiosPromise<PageResult<DbTableVO>> => {
export const listDbTable = (query: DbTableQuery): AxiosPromise<DbTableVO[]> => {
return request({
url: '/tool/gen/db/list',
method: 'get',
@@ -23,7 +20,7 @@ export const listDbTable = (query: DbTableQuery): AxiosPromise<PageResult<DbTabl
};
// 查询表详细信息
export const getGenTable = (tableId: string | number): AxiosPromise<GenTableDetailPayload> => {
export const getGenTable = (tableId: string | number): AxiosPromise<GenTableVO> => {
return request({
url: '/tool/gen/' + tableId,
method: 'get'
@@ -31,7 +28,7 @@ export const getGenTable = (tableId: string | number): AxiosPromise<GenTableDeta
};
// 修改代码生成信息
export const updateGenTable = (data: DbTableForm): AxiosPromise<unknown> => {
export const updateGenTable = (data: DbTableForm): AxiosPromise<GenTableVO> => {
return request({
url: '/tool/gen',
method: 'put',
@@ -40,7 +37,7 @@ export const updateGenTable = (data: DbTableForm): AxiosPromise<unknown> => {
};
// 导入表
export const importTable = (data: { tables: string; dataName: string }): AxiosPromise<unknown> => {
export const importTable = (data: { tables: string; dataName: string }): AxiosPromise<GenTableVO> => {
return request({
url: '/tool/gen/importTable',
method: 'post',
@@ -64,6 +61,14 @@ 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({

View File

@@ -4,6 +4,8 @@ export interface TableVO extends BaseEntity {
dataName: string;
tableName: string;
tableComment: string;
subTableName?: any;
subTableFkName?: any;
className: string;
tplCategory: string;
packageName: string;
@@ -11,6 +13,8 @@ export interface TableVO extends BaseEntity {
businessName: string;
functionName: string;
functionAuthor: string;
genType: string;
genPath: string;
pkColumn?: any;
columns?: any;
options?: any;
@@ -21,16 +25,6 @@ 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;
}
@@ -78,6 +72,8 @@ export interface DbTableVO {
tableId?: any;
tableName: string;
tableComment: string;
subTableName?: any;
subTableFkName?: any;
className?: any;
tplCategory?: any;
packageName?: any;
@@ -85,6 +81,8 @@ export interface DbTableVO {
businessName?: any;
functionName?: any;
functionAuthor?: any;
genType?: any;
genPath?: any;
pkColumn?: any;
columns: DbColumnVO[];
options?: any;
@@ -95,16 +93,6 @@ 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;
}
@@ -115,14 +103,10 @@ export interface DbTableQuery extends PageQuery {
tableComment: string;
}
/**
* 代码生成表详情接口 data 结构
* - info当前表 GenTable
* - rows字段列表 GenTableColumn[]
*/
export interface GenTableDetailPayload {
export interface GenTableVO {
info: DbTableVO;
rows: DbColumnVO[];
tables: DbTableVO[];
}
export interface DbColumnForm extends BaseEntity {
@@ -162,16 +146,6 @@ 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 {
@@ -179,6 +153,8 @@ export interface DbTableForm extends BaseEntity {
tableId: string | string;
tableName: string;
tableComment: string;
subTableName?: any;
subTableFkName?: any;
className: string;
tplCategory: string;
packageName: string;
@@ -186,6 +162,8 @@ export interface DbTableForm extends BaseEntity {
businessName: string;
functionName: string;
functionAuthor: string;
genType: string;
genPath: string;
pkColumn?: any;
columns: DbColumnForm[];
options: string;
@@ -196,16 +174,6 @@ 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;

View File

@@ -2,6 +2,7 @@
* 注册
*/
export type RegisterForm = {
tenantId: string;
username: string;
password: string;
confirmPassword?: string;
@@ -14,6 +15,7 @@ export type RegisterForm = {
* 登录请求
*/
export interface LoginData {
tenantId?: string;
username?: string;
password?: string;
rememberMe?: boolean;
@@ -43,9 +45,15 @@ export interface VerifyCodeResult {
}
/**
* 分页返回结果
* 租户
*/
export interface PageResult<T = any> {
total: number;
rows: T[];
export interface TenantVO {
companyName: string;
domain: any;
tenantId: string;
}
export interface TenantInfo {
tenantEnabled: boolean;
voList: TenantVO[];
}

View File

@@ -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';
/**
* 查询流程分类列表

View File

@@ -1,19 +1,13 @@
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<PageResult<FlowDefinitionVo>> => {
export const listDefinition = (query: FlowDefinitionQuery): AxiosPromise<FlowDefinitionVo[]> => {
return request({
url: `/workflow/definition/list`,
method: 'get',
@@ -26,7 +20,7 @@ export const listDefinition = (query: FlowDefinitionQuery): AxiosPromise<PageRes
* @param query 流程实例id
* @returns
*/
export const unPublishList = (query: FlowDefinitionQuery): AxiosPromise<PageResult<FlowDefinitionVo>> => {
export const unPublishList = (query: FlowDefinitionQuery): AxiosPromise<FlowDefinitionVo[]> => {
return request({
url: `/workflow/definition/unPublishList`,
method: 'get',

View File

@@ -1,14 +1,13 @@
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<PageResult<FlowInstanceVO>> => {
export const pageByRunning = (query: FlowInstanceQuery): AxiosPromise<FlowInstanceVO[]> => {
return request({
url: '/workflow/instance/pageByRunning',
method: 'get',
@@ -21,7 +20,7 @@ export const pageByRunning = (query: FlowInstanceQuery): AxiosPromise<PageResult
* @param query
* @returns {*}
*/
export const pageByFinish = (query: FlowInstanceQuery): AxiosPromise<PageResult<FlowInstanceVO>> => {
export const pageByFinish = (query: FlowInstanceQuery): AxiosPromise<FlowInstanceVO[]> => {
return request({
url: '/workflow/instance/pageByFinish',
method: 'get',
@@ -34,7 +33,7 @@ export const pageByFinish = (query: FlowInstanceQuery): AxiosPromise<PageResult<
*/
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'
});
};
@@ -44,7 +43,7 @@ export const flowHisTaskList = (businessId: string | number) => {
* @param query
* @returns {*}
*/
export const pageByCurrent = (query: FlowInstanceQuery): AxiosPromise<PageResult<FlowInstanceVO>> => {
export const pageByCurrent = (query: FlowInstanceQuery): AxiosPromise<FlowInstanceVO[]> => {
return request({
url: '/workflow/instance/pageByCurrent',
method: 'get',

View File

@@ -1,11 +1,11 @@
import type { FlowTaskVO } from '@/api/workflow/task/types';
import { FlowTaskVO } from '@/api/workflow/task/types';
export interface FlowInstanceQuery extends PageQuery {
category?: string | number;
nodeName?: string;
flowCode?: string;
flowName?: string;
createByIds?: Array<string | number>;
createByIds?: string[] | number[];
businessId?: string;
}

View File

@@ -1,7 +1,6 @@
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';
/**
* 查询请假列表
@@ -9,7 +8,7 @@ import request from '@/utils/request';
* @returns {*}
*/
export const listLeave = (query?: LeaveQuery): AxiosPromise<PageResult<LeaveVO>> => {
export const listLeave = (query?: LeaveQuery): AxiosPromise<LeaveVO[]> => {
return request({
url: '/workflow/leave/list',
method: 'get',

View File

@@ -1,7 +1,6 @@
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表达式定义列表
@@ -9,7 +8,7 @@ import request from '@/utils/request';
* @returns {*}
*/
export const listSpel = (query?: SpelQuery): AxiosPromise<PageResult<SpelVO>> => {
export const listSpel = (query?: SpelQuery): AxiosPromise<SpelVO[]> => {
return request({
url: '/workflow/spel/list',
method: 'get',

View File

@@ -33,6 +33,7 @@ export interface SpelVO {
* 备注
*/
remark?: string;
}
export interface SpelForm extends BaseEntity {
@@ -70,9 +71,11 @@ export interface SpelForm extends BaseEntity {
* 备注
*/
remark?: string;
}
export interface SpelQuery extends PageQuery {
/**
* 组件名称
*/
@@ -98,8 +101,11 @@ export interface SpelQuery extends PageQuery {
*/
status?: string;
/**
* 日期范围参数
*/
params?: any;
/**
* 日期范围参数
*/
params?: any;
}

View File

@@ -1,14 +1,13 @@
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<PageResult<FlowTaskVO>> => {
export const pageByTaskWait = (query: TaskQuery): AxiosPromise<FlowTaskVO[]> => {
return request({
url: '/workflow/task/pageByTaskWait',
method: 'get',
@@ -21,7 +20,7 @@ export const pageByTaskWait = (query: TaskQuery): AxiosPromise<PageResult<FlowTa
* @param query
* @returns {*}
*/
export const pageByTaskFinish = (query: TaskQuery): AxiosPromise<PageResult<FlowTaskVO>> => {
export const pageByTaskFinish = (query: TaskQuery): AxiosPromise<FlowTaskVO[]> => {
return request({
url: '/workflow/task/pageByTaskFinish',
method: 'get',
@@ -34,7 +33,7 @@ export const pageByTaskFinish = (query: TaskQuery): AxiosPromise<PageResult<Flow
* @param query
* @returns {*}
*/
export const pageByTaskCopy = (query: TaskQuery): AxiosPromise<PageResult<FlowTaskVO>> => {
export const pageByTaskCopy = (query: TaskQuery): AxiosPromise<FlowTaskVO[]> => {
return request({
url: '/workflow/task/pageByTaskCopy',
method: 'get',
@@ -43,11 +42,11 @@ export const pageByTaskCopy = (query: TaskQuery): AxiosPromise<PageResult<FlowTa
};
/**
* 查询全部待办任务
* 当前租户所有待办任务
* @param query
* @returns {*}
*/
export const pageByAllTaskWait = (query: TaskQuery): AxiosPromise<PageResult<FlowTaskVO>> => {
export const pageByAllTaskWait = (query: TaskQuery): AxiosPromise<FlowTaskVO[]> => {
return request({
url: '/workflow/task/pageByAllTaskWait',
method: 'get',
@@ -56,11 +55,11 @@ export const pageByAllTaskWait = (query: TaskQuery): AxiosPromise<PageResult<Flo
};
/**
* 查询全部已办任务
* 当前租户所有已办任务
* @param query
* @returns {*}
*/
export const pageByAllTaskFinish = (query: TaskQuery): AxiosPromise<PageResult<FlowTaskVO>> => {
export const pageByAllTaskFinish = (query: TaskQuery): AxiosPromise<FlowTaskVO[]> => {
return request({
url: '/workflow/task/pageByAllTaskFinish',
method: 'get',

View File

@@ -2,7 +2,7 @@ export interface TaskQuery extends PageQuery {
nodeName?: string;
flowCode?: string;
flowName?: string;
createByIds?: Array<string | number>;
createByIds?: string[] | number[];
}
export interface ParticipantVo {

View File

@@ -1,11 +1,9 @@
import type { RouterJumpVo } from '@/api/workflow/workflowCommon/types';
import tab from '@/plugins/tab';
import router from '@/router';
import { RouterJumpVo } from '@/api/workflow/workflowCommon/types';
export default {
routerJump(routerJumpVo: RouterJumpVo) {
tab.closePage(router.currentRoute.value);
router.push({
routerJump(routerJumpVo: RouterJumpVo, proxy) {
proxy.$tab.closePage(proxy.$route);
proxy.$router.push({
path: routerJumpVo.formPath,
query: {
id: routerJumpVo.businessId,

View File

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@@ -1,90 +0,0 @@
/* 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;
}

View File

@@ -0,0 +1,99 @@
@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);
}

View File

@@ -1,67 +0,0 @@
/* 业务列表/卡片页分页、树、列表、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;
}

View File

@@ -1,76 +0,0 @@
/* 工具类 */
.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;
}

View File

@@ -1,9 +0,0 @@
.link-type,
.link-type:focus {
color: var(--el-color-primary);
cursor: pointer;
&:hover {
color: var(--app-accent-strong);
}
}

View File

@@ -1,200 +0,0 @@
/* 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);
}

View File

@@ -1,89 +0,0 @@
/* Shared search and filter form layout. */
.query-form {
display: flex;
flex-wrap: wrap;
gap: 10px 14px;
align-items: end;
}
.query-form .el-form-item {
flex: 0 0 auto;
margin-bottom: 0;
margin-right: 0;
display: flex;
align-items: flex-end;
min-width: 0;
}
.query-form .el-form-item__content {
min-width: 0;
}
.query-form .el-form-item:last-child {
margin-left: auto;
align-self: end;
}
.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-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;
}
@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-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;
}
}

View File

@@ -1,83 +0,0 @@
/* 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 {
.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);
}
:global(html.dark) {
.tree-border {
background: var(--app-surface-bg);
border-color: var(--app-surface-border);
}
}

View File

@@ -1,79 +0,0 @@
/* 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);
}
}

View File

@@ -1,84 +0,0 @@
/* 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;
}

View File

@@ -1,49 +0,0 @@
/* 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;
}

View File

@@ -0,0 +1,277 @@
.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-base);
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-base);
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(--el-border-radius-base);
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-base);
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-base);
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;
}

View File

@@ -1,24 +1,225 @@
// =============================================================================
// 全局样式入口(加载顺序即最终 CSS 层叠顺序,后载入的同优先级规则会覆盖先载入的)
// =============================================================================
@use './variables.module.scss' as *;
@use './mixin.scss';
@use './transition.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 './element-ui.scss';
@use './sidebar.scss';
@use './btn.scss';
@use './ruoyi.scss';
@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: var(--el-fill-color-light);
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: var(--el-text-color-primary);
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
a {
color: var(--el-color-primary);
cursor: pointer;
&:hover {
color: var(--el-color-primary-light-3);
}
}
}
//main-container全局样式
.app-container {
padding: 16px;
background: transparent;
border: none;
border-radius: 0;
box-shadow: none;
}
// search面板样式
.panel,
.search {
margin-bottom: 12px;
border-radius: var(--app-radius-base);
border: 1px solid var(--el-border-color-lighter);
padding: 16px;
background: var(--app-surface-bg);
box-shadow: var(--app-shadow-sm);
transition: box-shadow 0.2s ease, border-color 0.2s ease;
&:hover {
box-shadow: var(--app-shadow-md);
border-color: var(--el-border-color-light);
}
}
.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;
}
}

View File

@@ -1,24 +0,0 @@
/* 主内容区面板(.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);
}
}

View File

@@ -1,141 +0,0 @@
/* 桌面端折叠侧栏:窄宽、仅图标、子菜单箭头隐藏与 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;
}
}
}
}
}

View File

@@ -1,111 +0,0 @@
/* 侧栏内 el-menu一级/嵌套项的 hover、active、浅色与 theme-dark 分支(与 _shell 中变量定义配合)。 */
@use '../../variables.module.scss' as *;
#app {
.sidebar-container {
.theme-dark .submenu-title-noDropdown,
.theme-dark .el-sub-menu__title {
border-radius: var(--app-radius-md);
margin: 3px 8px;
&: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;
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;
}
}
}
}

View File

@@ -1,22 +0,0 @@
/* 折叠后弹出子菜单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);
}
}

View File

@@ -1,31 +0,0 @@
/* 移动端侧栏:抽屉式滑出、隐藏时 translate.withoutAnimation 关闭过渡(与布局组件 class 约定一致)。 */
@use '../../variables.module.scss' 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;
}
}
}

View File

@@ -1,132 +0,0 @@
/* 侧栏外壳:固定定位宽度、主区左边距、滚动条与 el-menu 根级样式;与 _menu/_collapsed/_responsive 分工。 */
@use '../../variables.module.scss' 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;
}
}

View File

@@ -1,4 +1,3 @@
// 清除浮动(嵌套用)
@mixin clearfix {
&:after {
content: '';
@@ -7,10 +6,9 @@
}
}
// WebKit 滚动条配色(用于可滚动容器)
@mixin scrollBar {
&::-webkit-scrollbar-track-piece {
background: var(--app-surface-border);
background: #d3dce6;
}
&::-webkit-scrollbar {
@@ -18,26 +16,23 @@
}
&::-webkit-scrollbar-thumb {
background: var(--app-text-muted);
background: #99a9bf;
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;

View File

@@ -0,0 +1,304 @@
@use './variables.module.scss' as *;
/**
* 通用css样式布局处理
* Copyright (c) 2019 ruoyi
*/
/** 基础通用 **/
.pt5 {
padding-top: 5px;
}
.pr5 {
padding-right: 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;
}
.h1,
.h2,
.h3,
.h4,
.h5,
.h6,
h1,
h2,
h3,
h4,
h5,
h6 {
font-family: inherit;
font-weight: 500;
line-height: 1.1;
color: inherit;
}
.el-form .el-form-item__label {
font-weight: 600;
}
.el-dialog:not(.is-fullscreen) {
margin-top: 0 !important;
}
.el-dialog.scrollbar .el-dialog__body {
overflow: auto;
overflow-x: hidden;
max-height: 70vh;
padding: 10px 20px 0;
}
.el-table {
.el-table__header-wrapper,
.el-table__fixed-header-wrapper {
th {
word-break: break-word;
background-color: $table-header-bg !important;
color: $table-header-text-color;
height: 40px !important;
font-size: 13px;
}
}
.el-table__body-wrapper {
.el-button [class*='el-icon-'] + span {
margin-left: 1px;
}
}
}
/** 表单布局 **/
.form-header {
font-size: 15px;
color: #6379bb;
border-bottom: 1px solid #ddd;
margin: 8px 10px 25px 10px;
padding-bottom: 5px;
}
/** 表格布局 **/
.pagination-container {
display: flex;
justify-content: flex-end;
margin-top: 20px;
padding: 10px 20px !important;
}
/* tree border */
.tree-border {
margin-top: 5px;
border: 1px solid var(--el-border-color-light);
background: var(--el-bg-color);
border-radius: var(--app-radius-md);
width: 100%;
}
@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-table .fixed-width .el-button--small {
padding-left: 0;
padding-right: 0;
width: inherit;
}
/** 表格更多操作下拉样式 */
.el-table .el-dropdown-link {
cursor: pointer;
color: #409eff;
margin-left: 10px;
}
.el-table .el-dropdown,
.el-icon-arrow-down {
font-size: 12px;
}
.el-tree-node__content > .el-checkbox {
margin-right: 8px;
}
.list-group-striped > .list-group-item {
border-left: 0;
border-right: 0;
border-radius: 0;
padding-left: 0;
padding-right: 0;
}
.list-group {
padding-left: 0px;
list-style: none;
}
.list-group-item {
border-bottom: 1px solid #e7eaec;
border-top: 1px solid #e7eaec;
margin-bottom: -1px;
padding: 11px 0px;
font-size: 13px;
}
.pull-right {
float: right !important;
}
.el-card__header {
padding: 14px 15px 7px !important;
min-height: 40px;
background: var(--el-fill-color-blank);
border-bottom: 1px solid var(--el-border-color-light);
}
.el-card__body {
padding: 15px 20px 20px 20px !important;
}
.el-card {
border-radius: var(--app-radius-lg);
box-shadow: var(--app-shadow-sm);
border-color: var(--el-border-color-lighter);
overflow: hidden;
transition: box-shadow 0.2s ease;
}
.el-card:hover {
box-shadow: var(--app-shadow-md);
}
.card-box {
margin-bottom: 10px;
}
/* button color */
.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 color */
.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;
}
/* image */
.img-circle {
border-radius: 50%;
}
.img-lg {
width: 120px;
height: 120px;
}
.avatar-upload-preview {
position: absolute;
top: 50%;
transform: translate(50%, -50%);
width: 200px;
height: 200px;
border-radius: 50%;
box-shadow: 0 0 4px #ccc;
overflow: hidden;
}
/* 拖拽列样式 */
.sortable-ghost {
opacity: 0.8;
color: #fff !important;
background: #42b983 !important;
}
/* 表格右侧工具栏样式 */
.top-right-btn {
margin-left: auto;
}
/* horizontal el menu */
.el-menu--horizontal .el-menu-item .svg-icon + span,
.el-menu--horizontal .el-sub-menu__title .svg-icon + span {
margin-left: 3px;
}

View File

@@ -0,0 +1,297 @@
@use './variables.module.scss' as *;
#app {
.main-container {
height: 100%;
transition: margin-left 0.28s;
margin-left: $base-sidebar-width;
position: relative;
background: var(--el-bg-color-page);
}
.sidebarHide {
margin-left: 0 !important;
}
.sidebar-container {
-webkit-transition: width 0.28s;
transition: width 0.28s;
width: $base-sidebar-width !important;
background-color: $base-menu-background;
height: 100%;
position: fixed;
font-size: 0;
top: 0;
bottom: 0;
left: 0;
z-index: 1001;
overflow: hidden;
-webkit-box-shadow: 2px 0 6px rgba(0, 21, 41, 0.35);
box-shadow: 0 0 8px 0 rgba(0, 0, 0, 0.1);
// reset element-ui css
.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%;
}
&.has-logo {
.el-scrollbar {
height: calc(100% - 50px);
}
}
.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;
}
.el-menu-item,
.menu-title {
overflow: hidden !important;
text-overflow: ellipsis !important;
white-space: nowrap !important;
}
.el-menu-item .el-menu-tooltip__trigger {
display: inline-block !important;
}
// menu hover
.theme-dark .sub-menu-title-noDropdown,
.theme-dark .el-sub-menu__title {
border-radius: var(--app-radius-md);
margin: 1px 5px 1px 5px;
&:hover {
background-color: $base-sub-menu-title-hover !important;
}
}
.sub-menu-title-noDropdown,
.el-sub-menu__title {
border-radius: var(--app-radius-md);
margin: 1px 5px 1px 5px;
&:hover {
background-color: rgba(0, 0, 0, 0.05) !important;
}
}
& .theme-dark .is-active > .el-sub-menu__title {
color: $base-menu-color-active !important;
}
& .nest-menu .el-sub-menu > .el-sub-menu__title,
& .el-sub-menu .el-menu-item {
min-width: calc($base-sidebar-width - 20px) !important;
border-radius: var(--app-radius-md);
height: 45px;
margin: 1px 5px 1px 5px;
&:not(.is-active):hover {
background-color: rgba(0, 0, 0, 0.1) !important;
}
}
& .theme-dark .nest-menu .el-sub-menu > .el-sub-menu__title,
& .theme-dark .el-sub-menu .el-menu-item {
background-color: $base-sub-menu-background !important;
border-radius: var(--app-radius-md);
height: 45px;
margin: 1px 5px 1px 5px;
&.is-active {
background-color: var(--el-menu-active-color) !important;
color: #fff;
}
&:not(.is-active):hover {
// you can use $sub-menuHover
background-color: $base-sub-menu-hover !important;
}
}
& .theme-dark .nest-menu .el-sub-menu > .el-sub-menu__title,
& .theme-dark .el-menu-item {
border-radius: var(--app-radius-md);
height: 45px;
margin: 1px 5px 1px 5px;
&.is-active {
background-color: var(--el-menu-active-color) !important;
color: #fff;
}
&:not(.is-active):hover {
// you can use $sub-menuHover
background-color: $base-menu-hover !important;
}
}
& .nest-menu .el-sub-menu > .el-sub-menu__title,
& .el-menu-item {
border-radius: var(--app-radius-md);
height: 45px;
margin: 1px 5px 1px 5px;
&.is-active {
background-color: var(--el-menu-active-color) !important;
color: #fff;
}
&:not(.is-active):hover {
// you can use $sub-menuHover
background-color: rgba(0, 0, 0, 0.04) !important;
}
}
}
// 收起菜单后的样式
.hideSidebar {
.sidebar-container {
width: 54px !important;
}
.main-container {
margin-left: 54px;
}
.sub-menu-title-noDropdown {
padding: 0 !important;
position: relative;
height: 45px;
// 选中状态的菜单
&.is-active {
background-color: var(--el-menu-active-color) !important;
color: #fff !important;
}
.el-tooltip {
padding: 0 !important;
}
}
& .el-sub-menu {
overflow: hidden;
border-radius: var(--app-radius-md);
.el-sub-menu__title.el-tooltip__trigger {
border-radius: var(--app-radius-md);
height: 45px;
}
// 选中状态的菜单
&.is-active .el-sub-menu__title.el-tooltip__trigger {
background-color: var(--el-menu-active-color) !important;
}
& > .el-sub-menu__title {
padding: 0 !important;
}
}
.el-menu--collapse {
.is-active .svg-icon {
fill: #fff;
}
.svg-icon {
display: flex;
margin: auto;
height: 100%;
// 这里设置width会跟随sidebar-container的transition 不符合预期
}
.el-sub-menu {
& > .el-sub-menu__title {
& > span {
height: 0;
width: 0;
overflow: hidden;
visibility: hidden;
display: inline-block;
}
& > i {
height: 0;
width: 0;
overflow: hidden;
visibility: hidden;
display: inline-block;
}
}
}
}
}
.el-menu--collapse .el-menu .el-sub-menu {
min-width: $base-sidebar-width !important;
}
// mobile responsive
.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(-$base-sidebar-width, 0, 0);
}
}
}
.withoutAnimation {
.main-container,
.sidebar-container {
transition: none;
}
}
}
// when menu collapsed
.el-menu--vertical {
& > .el-menu {
.svg-icon {
margin-right: 16px;
}
}
}
// 收起菜单后悬浮的菜单样式
.el-popper.is-pure{
border-radius: var(--app-radius-md);
.el-menu--popup{
border-radius: var(--app-radius-md);
}
.el-menu-item{
border-radius: var(--app-radius-sm);
}
}

View File

@@ -1,21 +0,0 @@
/* CSS module exports preserved for JS/Vue consumers. */
@use './sass-vars' as *;
:export {
menuColor: $base-menu-color;
menuLightColor: $base-menu-light-color;
menuColorActive: $base-menu-color-active;
menuBackground: $base-menu-background;
menuLightBackground: $base-menu-light-background;
subMenuBackground: $base-sub-menu-background;
subMenuHover: $base-sub-menu-hover;
sideBarWidth: $base-sidebar-width;
logoTitleColor: $base-logo-title-color;
logoLightTitleColor: $base-logo-light-title-color;
primaryColor: $color-primary;
successColor: $color-success;
dangerColor: $color-danger;
infoColor: $color-info;
warningColor: $color-warning;
}

View File

@@ -1,42 +0,0 @@
/* Sass variables consumed by legacy modules and CSS module exports. */
$blue: #324157;
$light-blue: #3a71a8;
$red: #c03639;
$pink: #e65d6e;
$green: #30b08f;
$tiffany: #4ab7bd;
$yellow: #fec171;
$panGreen: #30b08f;
$base-menu-color: var(--menuColor);
$base-menu-hover: var(--menuHover);
$base-menu-color-active: var(--menuActiveText);
$base-menu-background: var(--menuBg);
$base-logo-title-color: #ffffff;
$base-menu-light-color: rgba(0, 0, 0, 0.7);
$base-menu-light-background: #ffffff;
$base-logo-light-title-color: #001529;
$base-sub-menu-background: var(--subMenuBg);
$base-sub-menu-hover: var(--subMenuHover);
$base-sub-menu-title-hover: var(--subMenuTitleHover);
$fixed-header-bg: var(--fixedHeaderBg);
$table-header-bg: var(--tableHeaderBg);
$table-header-text-color: var(--tableHeaderTextColor);
$color-primary: #409eff;
$color-success: #67c23a;
$color-warning: #e6a23c;
$color-danger: #f56c6c;
$color-info: #909399;
$--color-primary: $color-primary;
$--color-success: $color-success;
$--color-warning: $color-warning;
$--color-danger: $color-danger;
$--color-info: $color-info;
$base-sidebar-width: 200px;

View File

@@ -1,205 +0,0 @@
/* 暗色主题:在 html.dark 上覆写与浅色同名的业务/app 变量 */
html.dark {
--menuBg: #111827;
--menuColor: #e5edf8;
--menuActiveText: #f8fbff;
--menuHover: rgba(85, 129, 255, 0.2);
--subMenuBg: #111827;
--subMenuActiveText: #f8fbff;
--subMenuHover: rgba(85, 129, 255, 0.18);
--subMenuTitleHover: rgba(148, 163, 184, 0.12);
--fixedHeaderBg: #0f172a;
--tableHeaderBg: var(--el-bg-color);
--tableHeaderTextColor: var(--el-text-color);
// --- 表格:暗色下选中行背景 ---
--el-table-current-row-bg-color: rgba(53, 109, 255, 0.14);
--el-table-row-hover-bg-color: rgba(148, 163, 184, 0.07);
// --- 全局:暗色下修正 primary 浅底色(表格行选中、树节点等) ---
--el-color-primary-light-9: rgba(53, 109, 255, 0.14);
.el-tree-node__content {
--el-color-primary-light-9: rgba(53, 109, 255, 0.14);
}
// --- 各类型按钮(含 plain在暗色下的 EP CSS 变量 ---
.el-button--primary {
--el-button-bg-color: #2b6bd3;
--el-button-border-color: #3a7be8;
--el-button-text-color: #eef4ff;
--el-button-hover-bg-color: #3a7be8;
--el-button-hover-border-color: #3a7be8;
--el-button-active-bg-color: #255fb8;
--el-button-active-border-color: #255fb8;
}
.el-button--primary.is-plain {
--el-button-bg-color: rgba(43, 107, 211, 0.12);
--el-button-border-color: rgba(58, 123, 232, 0.5);
--el-button-text-color: #dbe8ff;
--el-button-hover-bg-color: rgba(58, 123, 232, 0.2);
--el-button-hover-border-color: rgba(58, 123, 232, 0.7);
--el-button-active-bg-color: rgba(43, 107, 211, 0.28);
--el-button-active-border-color: rgba(43, 107, 211, 0.8);
}
.el-button--success {
--el-button-bg-color: #1f8a5a;
--el-button-border-color: #29a46d;
--el-button-text-color: #eefaf4;
--el-button-hover-bg-color: #29a46d;
--el-button-hover-border-color: #29a46d;
--el-button-active-bg-color: #1b784f;
--el-button-active-border-color: #1b784f;
}
.el-button--success.is-plain {
--el-button-bg-color: rgba(31, 138, 90, 0.12);
--el-button-border-color: rgba(41, 164, 109, 0.5);
--el-button-text-color: #dbf6e8;
--el-button-hover-bg-color: rgba(41, 164, 109, 0.2);
--el-button-hover-border-color: rgba(41, 164, 109, 0.7);
--el-button-active-bg-color: rgba(31, 138, 90, 0.28);
--el-button-active-border-color: rgba(31, 138, 90, 0.8);
}
.el-button--warning {
--el-button-bg-color: #b87922;
--el-button-border-color: #d6953b;
--el-button-text-color: #fff7e6;
--el-button-hover-bg-color: #d6953b;
--el-button-hover-border-color: #d6953b;
--el-button-active-bg-color: #a56c1d;
--el-button-active-border-color: #a56c1d;
}
.el-button--warning.is-plain {
--el-button-bg-color: rgba(184, 121, 34, 0.12);
--el-button-border-color: rgba(214, 149, 59, 0.5);
--el-button-text-color: #ffecc8;
--el-button-hover-bg-color: rgba(214, 149, 59, 0.2);
--el-button-hover-border-color: rgba(214, 149, 59, 0.7);
--el-button-active-bg-color: rgba(184, 121, 34, 0.28);
--el-button-active-border-color: rgba(184, 121, 34, 0.8);
}
.el-button--danger {
--el-button-bg-color: #b24a4a;
--el-button-border-color: #d16060;
--el-button-text-color: #ffecec;
--el-button-hover-bg-color: #d16060;
--el-button-hover-border-color: #d16060;
--el-button-active-bg-color: #9c3f3f;
--el-button-active-border-color: #9c3f3f;
}
.el-button--danger.is-plain {
--el-button-bg-color: rgba(178, 74, 74, 0.12);
--el-button-border-color: rgba(209, 96, 96, 0.5);
--el-button-text-color: #ffd6d6;
--el-button-hover-bg-color: rgba(209, 96, 96, 0.2);
--el-button-hover-border-color: rgba(209, 96, 96, 0.7);
--el-button-active-bg-color: rgba(178, 74, 74, 0.28);
--el-button-active-border-color: rgba(178, 74, 74, 0.8);
}
.el-button--info {
--el-button-bg-color: #4b5563;
--el-button-border-color: #667085;
--el-button-text-color: #f3f4f6;
--el-button-hover-bg-color: #667085;
--el-button-hover-border-color: #667085;
--el-button-active-bg-color: #3f4753;
--el-button-active-border-color: #3f4753;
}
.el-button--info.is-plain {
--el-button-bg-color: rgba(75, 85, 99, 0.16);
--el-button-border-color: rgba(102, 112, 133, 0.55);
--el-button-text-color: #e5e7eb;
--el-button-hover-bg-color: rgba(102, 112, 133, 0.22);
--el-button-hover-border-color: rgba(102, 112, 133, 0.75);
--el-button-active-bg-color: rgba(75, 85, 99, 0.3);
--el-button-active-border-color: rgba(75, 85, 99, 0.85);
}
// --- Switch / Tag 暗色适配 ---
.el-switch {
--el-switch-on-color: var(--el-color-primary-dark-6);
--el-switch-border-color: var(--el-color-primary-light-2);
}
.el-tag--primary {
--el-tag-bg-color: var(--el-color-primary-dark-6);
--el-tag-border-color: var(--el-color-primary-light-2);
}
.el-tag--success {
--el-tag-bg-color: rgba(31, 138, 90, 0.18);
--el-tag-border-color: rgba(41, 164, 109, 0.6);
--el-tag-text-color: #c7f2df;
}
.el-tag--warning {
--el-tag-bg-color: rgba(184, 121, 34, 0.18);
--el-tag-border-color: rgba(214, 149, 59, 0.6);
--el-tag-text-color: #ffe6bb;
}
.el-tag--danger {
--el-tag-bg-color: rgba(178, 74, 74, 0.18);
--el-tag-border-color: rgba(209, 96, 96, 0.6);
--el-tag-text-color: #ffd0d0;
}
.el-tag--info {
--el-tag-bg-color: rgba(75, 85, 99, 0.18);
--el-tag-border-color: rgba(102, 112, 133, 0.6);
--el-tag-text-color: #e5e7eb;
}
// --- tags-view 与 app 壳变量(暗色) ---
--tags-view-active-bg: var(--el-color-primary-dark-6);
--tags-view-active-border-color: var(--el-color-primary-light-2);
--app-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.28);
--app-shadow-md: 0 12px 28px rgba(0, 0, 0, 0.28);
--app-shadow-lg: 0 20px 44px rgba(0, 0, 0, 0.32);
--app-surface-bg: #111827;
--app-surface-border: rgba(71, 85, 105, 0.5);
--app-shell-bg: #060b16;
--app-shell-spot-1: rgba(69, 120, 255, 0.18);
--app-shell-spot-2: rgba(45, 212, 191, 0.14);
--app-sidebar-border: rgba(148, 163, 184, 0.1);
--app-navbar-bg: #111827;
--app-navbar-border: rgba(71, 85, 105, 0.48);
--app-navbar-shadow: 0 1px 2px rgba(0, 0, 0, 0.22);
--app-accent-soft: rgba(64, 158, 255, 0.14);
--app-accent-strong: #60a5fa;
--app-text-title: #f3f7ff;
--app-text-muted: #8ea0bd;
--app-overlay-mask: rgba(2, 6, 23, 0.58);
--app-elevated-soft-bg: rgba(148, 163, 184, 0.08);
--app-elevated-close-bg: rgba(148, 163, 184, 0.18);
--app-elevated-close-hover-bg: rgba(96, 165, 250, 0.18);
--el-bg-color-page: var(--app-shell-bg);
// --- vxe-table 暗色(与 vxe 业务表并存) ---
--vxe-font-color: #98989e;
--vxe-primary-color: #2c7ecf;
--vxe-icon-background-color: #98989e;
--vxe-table-font-color: #98989e;
--vxe-table-resizable-color: #95969a;
--vxe-table-header-background-color: #28282a;
--vxe-table-body-background-color: #151518;
--vxe-table-background-color: #4a5663;
--vxe-table-border-width: 1px;
--vxe-table-border-color: #37373a;
--vxe-toolbar-background-color: #37373a;
--brder-color: #37373a;
}

View File

@@ -1,67 +0,0 @@
/* 浅色主题:仅声明 CSS 自定义属性,无具体选择器样式。 */
:root {
// --- 侧栏与顶栏表格区域 ---
--menuBg: #ffffff;
--menuColor: #1f2937;
--menuActiveText: #111827;
--menuHover: rgba(64, 158, 255, 0.12);
--subMenuBg: #ffffff;
--subMenuActiveText: #111827;
--subMenuHover: rgba(64, 158, 255, 0.12);
--subMenuTitleHover: rgba(64, 158, 255, 0.08);
--el-menu-base-level-padding: 12px;
--el-menu-level-padding: 8px;
--el-menu-item-height: 50px;
--fixedHeaderBg: #ffffff;
--tableHeaderBg: #f8fafc;
--tableHeaderTextColor: #475569;
--brder-color: #e8e8e8;
--tags-view-active-bg: var(--el-color-primary);
--tags-view-active-border-color: var(--el-color-primary);
// --- 应用壳:圆角、阴影、表面、导航、强调色与文案色 ---
--app-radius-sm: calc(var(--app-radius-base) - 8px);
--app-radius-md: calc(var(--app-radius-base) - 4px);
--app-radius-lg: var(--app-radius-base);
--app-radius-base: 14px;
--app-space-base: 12px;
--app-shadow-sm: 0 1px 2px rgba(15, 23, 42, 0.05);
--app-shadow-md: 0 8px 24px rgba(15, 23, 42, 0.06);
--app-shadow-lg: 0 14px 36px rgba(15, 23, 42, 0.1);
--app-font-size-base: 14px;
--app-surface-bg: #ffffff;
--app-surface-border: #e5e7eb;
--app-shell-bg: #f5f7fa;
--app-shell-spot-1: rgba(64, 158, 255, 0.08);
--app-shell-spot-2: rgba(64, 158, 255, 0.04);
--app-sidebar-border: #e5e7eb;
--app-navbar-bg: #ffffff;
--app-navbar-border: #e5e7eb;
--app-navbar-shadow: 0 1px 2px rgba(15, 23, 42, 0.05);
--app-accent-soft: rgba(64, 158, 255, 0.08);
--app-accent-strong: #409eff;
--app-text-title: #1f2937;
--app-text-muted: #6b7280;
--app-overlay-mask: rgba(15, 23, 42, 0.22);
--app-elevated-soft-bg: #f8fafc;
--app-elevated-close-bg: rgba(148, 163, 184, 0.14);
--app-elevated-close-hover-bg: rgba(64, 158, 255, 0.14);
// --- 与 Element Plus 主题变量的桥接(页面背景、默认圆角/阴影) ---
--el-border-radius-base: var(--app-radius-md);
--el-border-radius-small: var(--app-radius-sm);
--el-border-radius-round: 999px;
--el-box-shadow-light: var(--app-shadow-sm);
--el-box-shadow: var(--app-shadow-md);
--el-bg-color-page: var(--app-shell-bg);
}

View File

@@ -1,6 +1,5 @@
// 全局 Vue transition 类名fade / fade-transform / breadcrumb与业务模块无关
// global transition css
// --- 淡入淡出 ---
/* fade */
.fade-enter-active,
.fade-leave-active {
@@ -12,7 +11,6 @@
opacity: 0;
}
// --- 带水平位移的淡入淡出(常用于主视图切换) ---
/* fade-transform */
.fade-transform--move,
.fade-transform-leave-active,
@@ -30,7 +28,6 @@
transform: translateX(30px);
}
// --- 面包屑列表项进出场 ---
/* breadcrumb transition */
.breadcrumb-enter-active,
.breadcrumb-leave-active {

View File

@@ -1,5 +1,287 @@
@forward './tokens/sass-vars';
// 全局SCSS变量
:root {
--menuBg: #1f2d3d;
--menuColor: #bfcbd9;
--menuActiveText: #f4f4f5;
--menuHover: #263445;
@use './tokens/theme';
@use './tokens/theme-dark';
@use './tokens/exports';
--subMenuBg: #1f2d3d;
--subMenuActiveText: #f4f4f5;
--subMenuHover: #001528;
--subMenuTitleHover: #293444;
// 菜单栏缩进
--el-menu-base-level-padding: 12px;
--el-menu-level-padding: 8px;
--el-menu-item-height: 50px;
--fixedHeaderBg: #ffffff;
--tableHeaderBg: #f8f8f9;
--tableHeaderTextColor: #515a6e;
// ele
--brder-color: #e8e8e8;
// 添加 tag 相关变量
--tags-view-active-bg: var(--el-color-primary);
--tags-view-active-border-color: var(--el-color-primary);
// Modern rounded style + soft shadows
--app-radius-base: 8px;
--app-radius-sm: calc(var(--app-radius-base) * 0.6);
--app-radius-md: var(--app-radius-base);
--app-radius-lg: calc(var(--app-radius-base) * 1.4);
--app-radius-lg: var(--app-radius-base);
--app-shadow-sm: 0 1px 2px rgba(15, 23, 42, 0.08), 0 6px 16px rgba(15, 23, 42, 0.08);
--app-shadow-md: 0 8px 24px rgba(15, 23, 42, 0.12);
--app-shadow-lg: 0 12px 32px rgba(15, 23, 42, 0.16);
--app-surface-bg: #ffffff;
--app-surface-border: var(--el-border-color-lighter);
// Element Plus tokens
--el-border-radius-base: var(--app-radius-md);
--el-border-radius-small: var(--app-radius-sm);
--el-border-radius-round: 999px;
--el-box-shadow-light: var(--app-shadow-sm);
--el-box-shadow: var(--app-shadow-md);
--el-bg-color-page: #f5f7fb;
}
html.dark {
--menuBg: #1d1e1f;
--menuColor: #bfcbd9;
--menuActiveText: #f4f4f5;
--menuHover: #171819;
--subMenuBg: #1d1e1f;
--subMenuActiveText: #f4f4f5;
--subMenuHover: #171819;
--subMenuTitleHover: #171819;
--fixedHeaderBg: #171819;
--tableHeaderBg: var(--el-bg-color);
--tableHeaderTextColor: var(--el-text-color);
// 覆盖ele 高亮当前行的标准暗色
.el-tree-node__content {
--el-color-primary-light-9: #262727;
}
.el-button--primary {
--el-button-bg-color: #2b6bd3;
--el-button-border-color: #3a7be8;
--el-button-text-color: #eef4ff;
--el-button-hover-bg-color: #3a7be8;
--el-button-hover-border-color: #3a7be8;
--el-button-active-bg-color: #255fb8;
--el-button-active-border-color: #255fb8;
}
.el-button--primary.is-plain {
--el-button-bg-color: rgba(43, 107, 211, 0.12);
--el-button-border-color: rgba(58, 123, 232, 0.5);
--el-button-text-color: #dbe8ff;
--el-button-hover-bg-color: rgba(58, 123, 232, 0.2);
--el-button-hover-border-color: rgba(58, 123, 232, 0.7);
--el-button-active-bg-color: rgba(43, 107, 211, 0.28);
--el-button-active-border-color: rgba(43, 107, 211, 0.8);
}
.el-button--success {
--el-button-bg-color: #1f8a5a;
--el-button-border-color: #29a46d;
--el-button-text-color: #eefaf4;
--el-button-hover-bg-color: #29a46d;
--el-button-hover-border-color: #29a46d;
--el-button-active-bg-color: #1b784f;
--el-button-active-border-color: #1b784f;
}
.el-button--success.is-plain {
--el-button-bg-color: rgba(31, 138, 90, 0.12);
--el-button-border-color: rgba(41, 164, 109, 0.5);
--el-button-text-color: #dbf6e8;
--el-button-hover-bg-color: rgba(41, 164, 109, 0.2);
--el-button-hover-border-color: rgba(41, 164, 109, 0.7);
--el-button-active-bg-color: rgba(31, 138, 90, 0.28);
--el-button-active-border-color: rgba(31, 138, 90, 0.8);
}
.el-button--warning {
--el-button-bg-color: #b87922;
--el-button-border-color: #d6953b;
--el-button-text-color: #fff7e6;
--el-button-hover-bg-color: #d6953b;
--el-button-hover-border-color: #d6953b;
--el-button-active-bg-color: #a56c1d;
--el-button-active-border-color: #a56c1d;
}
.el-button--warning.is-plain {
--el-button-bg-color: rgba(184, 121, 34, 0.12);
--el-button-border-color: rgba(214, 149, 59, 0.5);
--el-button-text-color: #ffecc8;
--el-button-hover-bg-color: rgba(214, 149, 59, 0.2);
--el-button-hover-border-color: rgba(214, 149, 59, 0.7);
--el-button-active-bg-color: rgba(184, 121, 34, 0.28);
--el-button-active-border-color: rgba(184, 121, 34, 0.8);
}
.el-button--danger {
--el-button-bg-color: #b24a4a;
--el-button-border-color: #d16060;
--el-button-text-color: #ffecec;
--el-button-hover-bg-color: #d16060;
--el-button-hover-border-color: #d16060;
--el-button-active-bg-color: #9c3f3f;
--el-button-active-border-color: #9c3f3f;
}
.el-button--danger.is-plain {
--el-button-bg-color: rgba(178, 74, 74, 0.12);
--el-button-border-color: rgba(209, 96, 96, 0.5);
--el-button-text-color: #ffd6d6;
--el-button-hover-bg-color: rgba(209, 96, 96, 0.2);
--el-button-hover-border-color: rgba(209, 96, 96, 0.7);
--el-button-active-bg-color: rgba(178, 74, 74, 0.28);
--el-button-active-border-color: rgba(178, 74, 74, 0.8);
}
.el-button--info {
--el-button-bg-color: #4b5563;
--el-button-border-color: #667085;
--el-button-text-color: #f3f4f6;
--el-button-hover-bg-color: #667085;
--el-button-hover-border-color: #667085;
--el-button-active-bg-color: #3f4753;
--el-button-active-border-color: #3f4753;
}
.el-button--info.is-plain {
--el-button-bg-color: rgba(75, 85, 99, 0.16);
--el-button-border-color: rgba(102, 112, 133, 0.55);
--el-button-text-color: #e5e7eb;
--el-button-hover-bg-color: rgba(102, 112, 133, 0.22);
--el-button-hover-border-color: rgba(102, 112, 133, 0.75);
--el-button-active-bg-color: rgba(75, 85, 99, 0.3);
--el-button-active-border-color: rgba(75, 85, 99, 0.85);
}
.el-switch {
--el-switch-on-color: var(--el-color-primary-dark-6);
--el-switch-border-color: var(--el-color-primary-light-2);
}
.el-tag--primary {
--el-tag-bg-color: var(--el-color-primary-dark-6);
--el-tag-border-color: var(--el-color-primary-light-2);
}
.el-tag--success {
--el-tag-bg-color: rgba(31, 138, 90, 0.18);
--el-tag-border-color: rgba(41, 164, 109, 0.6);
--el-tag-text-color: #c7f2df;
}
.el-tag--warning {
--el-tag-bg-color: rgba(184, 121, 34, 0.18);
--el-tag-border-color: rgba(214, 149, 59, 0.6);
--el-tag-text-color: #ffe6bb;
}
.el-tag--danger {
--el-tag-bg-color: rgba(178, 74, 74, 0.18);
--el-tag-border-color: rgba(209, 96, 96, 0.6);
--el-tag-text-color: #ffd0d0;
}
.el-tag--info {
--el-tag-bg-color: rgba(75, 85, 99, 0.18);
--el-tag-border-color: rgba(102, 112, 133, 0.6);
--el-tag-text-color: #e5e7eb;
}
// 在深色模式下使用更深的颜色
--tags-view-active-bg: var(--el-color-primary-dark-6);
--tags-view-active-border-color: var(--el-color-primary-light-2);
// Modern rounded style + soft shadows (dark)
--app-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.28), 0 8px 18px rgba(0, 0, 0, 0.25);
--app-shadow-md: 0 10px 26px rgba(0, 0, 0, 0.35);
--app-shadow-lg: 0 14px 34px rgba(0, 0, 0, 0.4);
--app-surface-bg: #151922;
--app-surface-border: var(--el-border-color);
--el-bg-color-page: #0f1115;
// vxe-table 主题
--vxe-font-color: #98989e;
--vxe-primary-color: #2c7ecf;
--vxe-icon-background-color: #98989e;
--vxe-table-font-color: #98989e;
--vxe-table-resizable-color: #95969a;
--vxe-table-header-background-color: #28282a;
--vxe-table-body-background-color: #151518;
--vxe-table-background-color: #4a5663;
--vxe-table-border-width: 1px;
--vxe-table-border-color: #37373a;
--vxe-toolbar-background-color: #37373a;
// ele
--brder-color: #37373a;
}
// base color
$blue: #324157;
$light-blue: #3a71a8;
$red: #c03639;
$pink: #e65d6e;
$green: #30b08f;
$tiffany: #4ab7bd;
$yellow: #fec171;
$panGreen: #30b08f;
// 默认菜单主题风格
$base-menu-color: var(--menuColor);
$base-menu-hover: var(--menuHover);
$base-menu-color-active: var(--menuActiveText);
$base-menu-background: var(--menuBg);
$base-logo-title-color: #ffffff;
$base-menu-light-color: rgba(0, 0, 0, 0.7);
$base-menu-light-background: #ffffff;
$base-logo-light-title-color: #001529;
$base-sub-menu-background: var(--subMenuBg);
$base-sub-menu-hover: var(--subMenuHover);
$base-sub-menu-title-hover: var(--subMenuTitleHover);
// 表单头背景色和标题颜色
$fixed-header-bg: var(--fixedHeaderBg);
$table-header-bg: var(--tableHeaderBg);
$table-header-text-color: var(--tableHeaderTextColor);
$--color-primary: #409eff;
$--color-success: #67c23a;
$--color-warning: #e6a23c;
$--color-danger: #f56c6c;
$--color-info: #909399;
$base-sidebar-width: 200px;
// the :export directive is the magic sauce for webpack
// https://www.bluematador.com/blog/how-to-share-variables-between-js-and-sass
:export {
menuColor: $base-menu-color;
menuLightColor: $base-menu-light-color;
menuColorActive: $base-menu-color-active;
menuBackground: $base-menu-background;
menuLightBackground: $base-menu-light-background;
subMenuBackground: $base-sub-menu-background;
subMenuHover: $base-sub-menu-hover;
sideBarWidth: $base-sidebar-width;
logoTitleColor: $base-logo-title-color;
logoLightTitleColor: $base-logo-light-title-color;
primaryColor: $--color-primary;
successColor: $--color-success;
dangerColor: $--color-danger;
infoColor: $--color-info;
warningColor: $--color-warning;
}

View File

@@ -1,13 +0,0 @@
.el-card {
overflow: hidden;
}
.el-card .el-card__header {
border-top-left-radius: var(--app-radius-base);
border-top-right-radius: var(--app-radius-base);
}
.el-card .el-card__body {
border-bottom-left-radius: var(--app-radius-base);
border-bottom-right-radius: var(--app-radius-base);
}

View File

@@ -1,109 +0,0 @@
/* 弹窗:遮罩模糊、居中 el-dialog 头身脚与关闭按钮;底部 .upload-container 为上传区宽度占位。 */
.el-overlay {
overflow: hidden;
background: var(--app-overlay-mask);
backdrop-filter: blur(10px);
.el-overlay-dialog {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
padding: 20px;
.el-dialog {
margin: 0 auto !important;
border-radius: var(--app-radius-base);
box-shadow: var(--app-shadow-md);
overflow: hidden;
border: 1px solid var(--app-surface-border);
background:
linear-gradient(180deg, rgba(255, 255, 255, 0.02), rgba(255, 255, 255, 0)) padding-box,
var(--app-surface-bg);
.el-dialog__header {
position: relative;
padding: 18px 20px 14px;
box-sizing: border-box;
border-bottom: 1px solid var(--app-surface-border);
margin-right: 0;
background: var(--app-elevated-soft-bg);
}
.el-dialog__title {
font-size: 16px;
font-weight: 600;
line-height: 1.3;
color: var(--app-text-title);
letter-spacing: 0;
}
.el-dialog__headerbtn {
position: absolute;
top: 50%;
right: 16px;
display: inline-flex;
align-items: center;
justify-content: center;
width: 32px;
height: 32px;
padding: 0;
margin: 0;
border: 0;
border-radius: 10px;
background: var(--app-elevated-close-bg);
transform: translateY(-50%);
transition:
background-color 0.2s ease,
transform 0.2s ease,
color 0.2s ease;
.el-dialog__close {
color: var(--app-text-muted);
font-size: 16px;
}
&:hover {
background: var(--app-elevated-close-hover-bg);
transform: translateY(calc(-50% - 1px));
.el-dialog__close {
color: var(--app-accent-strong);
}
}
}
.el-dialog__body {
padding: 18px 20px 12px !important;
}
.el-dialog__footer {
display: flex;
align-items: center;
justify-content: flex-end;
gap: 8px;
padding: 14px 20px 20px;
border-top: 1px solid var(--app-surface-border);
}
}
}
}
.el-dialog__body {
max-height: calc(90vh - 111px) !important;
overflow-y: auto;
overflow-x: hidden;
}
.el-dialog__footer .el-button {
margin-left: 0 !important;
}
.el-dialog__footer .dialog-footer {
display: flex;
width: 100%;
justify-content: flex-end;
gap: 12px;
}

Some files were not shown because too many files have changed in this diff Show More