81 Commits

Author SHA1 Message Date
疯狂的狮子Li
97e984afa5 update 依赖升级 2026-04-24 20:10:38 +08:00
疯狂的狮子Li
6c1dae5b6a update 重构 增强代码生成器各项功能
update 自动类型判断可根据不同数据库精确识别 对应的java类型
update 优化java字段名与数据库名不匹配则增加别名注解
update 增加 是否导出 是否状态切换 是否组合唯一校验 是否排序调整 和树结构相关字段等功能选择
update 优化自定义路径导出 导出全部代码
update 删除无用主子表相关代码
2026-04-17 18:24:05 +08:00
疯狂的狮子Li
87fad7bc16 fix 修复用户管理 部门树 不显示禁用部门问题 2026-04-17 16:08:05 +08:00
疯狂的狮子Li
c7bcf52f03 update 优化 重构抽出一些常用hooks组件简化页面编码 2026-04-17 15:46:41 +08:00
疯狂的狮子Li
6a3394bdc5 update 优化 重构抽出一些常用hooks组件简化页面编码 2026-04-17 15:09:17 +08:00
疯狂的狮子Li
2ce3786ee0 update 树组件支持禁用状态展示 2026-04-17 12:17:43 +08:00
疯狂的狮子Li
2d13215304 update 优化 菜单勾选栏改为左菜单右按钮结构 增加禁用与隐藏图标 2026-04-17 12:08:51 +08:00
lau
f269c003da update 角色菜单权限展示优化 2026-04-16 21:19:13 +08:00
疯狂的狮子Li
7aaa40b46c update 优化 页面上写死的常量使用字典 2026-04-16 16:10:30 +08:00
疯狂的狮子Li
ffcb15ba96 update 优化 白名单支持对通配符路径匹配 2026-04-16 16:10:16 +08:00
疯狂的狮子Li
0a5c72988a update 优化 工作流页面 封装办理人展示组件 支持展示三行 超过三行隐藏 鼠标悬停展示 2026-04-16 14:28:14 +08:00
疯狂的狮子Li
8a900fa94d update 优化 客户端管理 隐藏列表主键id 2026-04-16 14:16:34 +08:00
疯狂的狮子Li
f10e6dbc71 update 优化 客户端管理 增加白名单路径和白名单IP功能 可限制客户端能访问的具体路径与可访问的具体IP地址 2026-04-16 14:14:28 +08:00
疯狂的狮子Li
090e395dcf update 优化 权限选择框布局 2026-04-16 09:38:53 +08:00
lau
eab7f49553 update 角色基础信息修改接口地址改回原样 2026-04-15 20:43:26 +08:00
lau
cedf975ed3 update 角色管理菜单权限-数据权限放到一起 2026-04-15 20:38:20 +08:00
疯狂的狮子Li
d38329befb update 替换掉过时写法 2026-04-10 13:56:08 +08:00
疯狂的狮子Li
9acf52e8a7 update 重构 彻底删除proxy用法 改为vue3官方推荐写法 2026-04-10 13:56:08 +08:00
lau
eb62402423 update 默认圆角改为14 2026-04-10 13:07:29 +08:00
疯狂的狮子Li
8c730950a9 fix 修复 标题头与整体抽屉样式冲突问题 2026-04-09 16:41:51 +08:00
疯狂的狮子Li
1133665122 fix 修复 客户端管理 授权类型 标签歪斜问题 2026-04-09 15:23:55 +08:00
疯狂的狮子Li
4f713d66db update 优化 封装TreePanel树组件 简化页面代码 2026-04-09 13:58:30 +08:00
疯狂的狮子Li
02b5ca7c48 add 增加 用户管理 用户详情展示抽屉 2026-04-09 13:18:07 +08:00
疯狂的狮子Li
4f8bcbd525 update 富文本编辑器支持图片视频oss上传 支持私有库图片访问 增加ossContent oss富文本解析工具 2026-04-09 11:44:47 +08:00
疯狂的狮子Li
fb5b1ed1bd update 优化 loadView 预建 Map 查找表,路由组件解析从 O(n) 降为 O(1)
update 优化 generateRoutes 三次 JSON 序列化改为 structuredClone
fix 修复 layout watchEffect 改为 watch(width) 消除循环依赖
fix 修复 useDict 返回 reactive 对象保持响应式,增加并发请求去重
fix 修复 FileUpload/ImageUpload headers 改为 computed 避免 token 过期
update 优化 debounce 箭头函数改为 function 声明修复 this 绑定丢失
fix 修复 duplicateRouteChecker 修复 route.name 为空时的空指针崩溃
fix 修复 AppMain 动画随机选择 Math.round 改为 Math.floor 防止越界
fix 修复 TagsView 关闭页签时始终清除缓存,修复动态路由内存泄漏
2026-04-09 11:24:02 +08:00
疯狂的狮子Li
a2d9a6d5f2 update 统一框架所有主键id均使用雪花id 2026-04-08 14:55:15 +08:00
疯狂的狮子Li
90926ddc7c update vite 版本更新 2026-04-08 13:06:59 +08:00
疯狂的狮子Li
b64357a595 update 优化 操作日志 补齐一些必要的记录数据 2026-04-08 12:40:11 +08:00
疯狂的狮子Li
81552f0cb9 update 优化 菜单管理 增加激活菜单与扩展属性字段 2026-04-08 11:49:53 +08:00
lau
029eb6636c add 增加系统圆角配置,并将部分圆角样式改为使用变量 2026-04-01 23:42:48 +08:00
疯狂的狮子Li
38c67fba25 update 重构暗黑模式样式 将所有写死的颜色统一整理 2026-04-01 19:18:07 +08:00
疯狂的狮子Li
3f30bac6f3 fix 修复 误删样式 2026-04-01 18:00:34 +08:00
疯狂的狮子Li
0b65932eee fix 修复 表单滚动背景透明 数据重叠问题 2026-04-01 17:51:20 +08:00
疯狂的狮子Li
16d9e1e7fe update prettier 替换为 oxfmt 格式化整个项目的代码结构 2026-04-01 17:03:20 +08:00
疯狂的狮子Li
199771997d update npm 升级到 pnpm 更符合现在技术栈 2026-04-01 16:33:55 +08:00
疯狂的狮子Li
6c395bb65b update 升级 vite8
update 升级 ts6
update eslint 替换为 Oxlint
2026-04-01 16:15:03 +08:00
疯狂的狮子Li
57f18eece5 update 优化 支持表格列显隐状态记忆 2026-04-01 13:12:24 +08:00
疯狂的狮子Li
cac35ecf4c update 优化 代码小问题 2026-04-01 13:05:03 +08:00
疯狂的狮子Li
f38e5e7c4d update 优化 缩短代码生成模块 包名与模块名 2026-03-30 20:02:44 +08:00
疯狂的狮子Li
125da80953 add 增加 ai编程支持 支持codex与claude的skill 2026-03-30 17:31:32 +08:00
疯狂的狮子Li
7f99bbf889 update 修改接口路径与cloud版本保持一致 2026-03-27 18:26:37 +08:00
疯狂的狮子Li
87c09d0917 update 优化 富文本内容展示增加xss拦截过滤 2026-03-27 17:02:17 +08:00
疯狂的狮子Li
87b56a3823 update 优化 后端导入返回信息使用\n分割 避免前端出现xss问题 2026-03-27 16:43:31 +08:00
疯狂的狮子Li
680c00bd06 fix 修复 下载文件后端异常报错 被辞掉问题 2026-03-27 16:13:20 +08:00
疯狂的狮子Li
ebc7760de2 update 完成消息盒子功能前后端联动(已读未读在前端浏览器存储) 2026-03-27 14:37:21 +08:00
lau
5907e4778b fix 修复 头像选择器样式异常 2026-03-26 21:55:11 +08:00
疯狂的狮子Li
b9e5220069 update 优化 通知公告页面增加查看详情功能 支持预览已经编辑好的富文本内容
update 优化 消息盒子相关消息可直接跳转到详情页展示富文本内容
2026-03-26 18:06:00 +08:00
疯狂的狮子Li
1daa291bb6 update 消息盒子支持按类型分组切换展示 2026-03-26 17:34:51 +08:00
疯狂的狮子Li
5be8fcf571 update 重构 common-sse 与 common-websocket 合并为 ruoyi-common-push 推送模块 2026-03-26 17:25:36 +08:00
疯狂的狮子Li
42f63fbe24 update 消息推送增加 消息类型 消息来源 前端跳转路径等扩展参数 2026-03-26 15:34:02 +08:00
疯狂的狮子Li
05527965e2 update 优化 优化快速点击页签刷新出现404问题
update 优化 优化页签全屏显示展示形式
2026-03-26 11:06:50 +08:00
疯狂的狮子Li
8469c254af update 优化 左侧树结构增加可折叠按钮
update 优化 左侧树结构数据过多支持上下滑动
2026-03-26 10:09:46 +08:00
lau
2502de3081 update 优化 前端样式初步调整并删除无用样式 2026-03-25 22:33:41 +08:00
lau
fed0ac99e8 update 优化 表格内边框圆角适配 2026-03-25 13:13:34 +08:00
疯狂的狮子Li
9f577be6ff update 重构 将scss文件拆解 按具体功能细分文件 便于后续维护 2026-03-25 12:06:46 +08:00
lau
1fb116f059 update 优化 tags-view左右滚动按钮根据需要是否可滚动显示 2026-03-25 11:45:33 +08:00
疯狂的狮子Li
448f5f303e fix 修复 表单输入框边框丢失问题 2026-03-25 10:46:14 +08:00
疯狂的狮子Li
da5f369bb4 fix 修复 插件冲突导致样式一直重复加载问题 2026-03-25 10:36:18 +08:00
疯狂的狮子Li
13a41679bc update 优化 避免重复包装 2026-03-25 10:32:45 +08:00
疯狂的狮子Li
1442d539e8 fix 修复 oss配置页 状态未从01改为YN导致的问题 2026-03-25 10:25:51 +08:00
疯狂的狮子Li
a86557cc2a update 优化 页签区增加左右滚动、右侧下拉、单独“刷新”按钮
add 新增 TagsView 全屏功能
add 新增 设置面板增加了“持久化标签页”开关 支持标签页持久化
2026-03-24 17:58:07 +08:00
疯狂的狮子Li
4fb60440f2 update 优化 重新实现菜单搜索功能 2026-03-24 17:28:01 +08:00
疯狂的狮子Li
6ed02a2ddd update 优化 菜单管理列表新增类型显示 2026-03-24 17:20:06 +08:00
疯狂的狮子Li
3194ea4fe7 update 替代有问题的插件 2026-03-19 15:55:26 +08:00
疯狂的狮子Li
8286b5906e add 增加工作流权限标识符 2026-03-19 15:32:50 +08:00
gssong
c69a6a2a45 fix 修复两边布局没有和tab顶部对齐 2026-03-18 17:43:18 +08:00
疯狂的狮子Li
f2de9af401 fix 修复 新增流程定义样式错误 2026-03-18 17:17:25 +08:00
疯狂的狮子Li
790abfac85 update 用户管理 增加用户登录状态解锁功能 2026-03-18 13:45:37 +08:00
疯狂的狮子Li
e138c314a8 update 重构 修改框架内不正常命名与规范是和否的状态 2026-03-18 10:39:22 +08:00
疯狂的狮子Li
b9ca2d3c67 fix 修复 多级树下拉框样式问题 2026-03-17 21:25:24 +08:00
疯狂的狮子Li
c415e130b9 fix 修复 新增客户端 状态没有默认值问题 2026-03-17 21:08:28 +08:00
疯狂的狮子Li
decbd4ac34 update 优化 删除quill使用wangeditor-next替代 2026-03-17 21:01:55 +08:00
疯狂的狮子Li
b8b0db2367 !261 [mod]适配分页返回数据放入data中返回
Merge pull request !261 from YueYe/future/new
2026-03-17 11:16:09 +00:00
Carrierli
82d28a3188 [mod]适配分页返回数据放入data中返回 2026-03-17 16:18:12 +08:00
疯狂的狮子Li
8eac8987b1 update 优化 修改logo名称 2026-03-17 15:00:33 +08:00
疯狂的狮子Li
d97c93cc98 update 优化 首页内容 2026-03-17 13:05:37 +08:00
疯狂的狮子Li
766d9b2142 update 关闭 用户注册页面按钮 2026-03-17 12:53:56 +08:00
疯狂的狮子Li
833dfa67fc [重大更新] 整体主题样式重写 改为卡片式布局 样式更企业化 2026-03-17 12:35:56 +08:00
疯狂的狮子Li
b83755e626 update 优化 代码写法 删除无用依赖 2026-03-16 19:20:22 +08:00
疯狂的狮子Li
e3227f5cc5 update 删除多租户功能 2026-03-13 16:11:00 +08:00
疯狂的狮子Li
0dc58fa3c6 update 优化 将logininfor规范化为loginInfo 2026-03-13 15:06:22 +08:00
271 changed files with 17722 additions and 9297 deletions

View File

@@ -0,0 +1,28 @@
---
name: frontend-api-types
description: 前端 API 与类型定义专家。用于当前项目中的 src/api 层、types.ts、返回结构、Query/Form/VO/InfoVO 定义,以及前后端接口映射任务。
---
你负责当前前端项目中的 API 层和类型定义。
## 核心原则
1. 先看当前模块已有 `src/api/<module>/<business>`
2. API 路径、返回类型、命名风格与当前模块保持一致。
3. 能明确写出类型时,不要偷懒用 `any`
4. 如果当前模块已有 `export default { ... }`,继续保持一致。
## 重点关注
- `Query`
- `VO`
- `Form`
- `InfoVO`
- `AxiosPromise<PageResult<T>>`
- 详情接口与列表接口返回结构
## 自检
- API 路径是否与后端一致
- 类型是否覆盖接口真实结构
- 是否不必要地把类型写宽了

View File

@@ -0,0 +1,18 @@
---
name: frontend-crud-coding
description: 前端总入口。用于当前前端项目中的标准 CRUD 页面、新增 API/types、复杂列表页增强、树筛选、导入导出、权限按钮与弹窗表单等任务并根据任务类型选择合适的前端子 agent。
---
你是当前前端项目的总入口 agent。
先判断任务类型,再按下面规则处理:
1. 如果是新增标准 CRUD 页面、补 `src/api``types.ts``index.vue`,优先使用 `frontend-crud-page.md`
2. 如果是修改已有列表页、增强导入导出、树筛选、更多菜单、状态切换,优先使用 `frontend-page-enhancement.md`
3. 如果只改接口层和类型定义,优先使用 `frontend-api-types.md`
通用要求:
- 先读当前目录下最近似页面和 API再动代码。
- 冲突时优先相信当前项目真实页面,其次是公共组件和工具,再其次才是关联后端工程的 generator 模板。
- 默认直接产出可落地代码,而不是只给抽象建议。

View File

@@ -0,0 +1,37 @@
---
name: frontend-crud-page
description: 前端标准 CRUD 页面专家。用于当前项目中的新建列表页、弹窗表单页、标准 API/types/index.vue 骨架,以及 gen 模板到项目风格的落地任务。
---
你负责当前前端项目中的标准 CRUD 页面实现。
## 核心原则
1. 先看当前模块最近似页面。
2. 再参考关联后端工程中的 generator 模板。
3. 默认同时维护:
`src/api/<module>/<business>/index.ts`
`src/api/<module>/<business>/types.ts`
`src/views/<module>/<business>/index.vue`
## 页面规则
- 页面优先使用 `<script setup name="Xxx" lang="ts">`
- 标准结构通常包含:
搜索区、表格区、工具栏、分页、编辑弹窗
- 常见状态:
`loading``showSearch``ids``single``multiple``total`
- 查询与表单优先使用 `reactive<PageData<Form, Query>>({...})`
## API / types 规则
- 请求统一通过 `@/utils/request`
- 同目录维护 `index.ts``types.ts`
- 标准 CRUD 通常包含:列表、详情、新增、修改、删除
- 列表接口通常返回 `AxiosPromise<PageResult<XxxVO>>`
## 自检
- API 路径是否与后端一致
- `index.ts``types.ts` 是否同步补齐
- 页面是否只是模板裸输出,如果是要继续补强到当前项目风格

View File

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

View File

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

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

View File

@@ -0,0 +1,112 @@
# 使用案例
## 案例 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

@@ -0,0 +1,241 @@
# 前端约定
## 优先参考的代码来源
- 关联后端工程中的生成器模板:
`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
# 开发环境配置
@@ -29,8 +29,11 @@ VITE_APP_RSA_PRIVATE_KEY = 'MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAmc3C
# 客户端id
VITE_APP_CLIENT_ID = 'e5cd7e4891bf95d1d19206ce24a7b32e'
# websocket 开关 默认使用sse推送
VITE_APP_WEBSOCKET = false
# 统一消息推送开关
VITE_APP_MESSAGE_ENABLED = true
# sse 开关
VITE_APP_SSE = true
# sse / websocket
VITE_APP_MESSAGE_TRANSPORT = 'sse'
# 统一消息推送路径
VITE_APP_MESSAGE_PATH = '/resource/message'

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
# 生产环境配置
@@ -32,8 +32,11 @@ VITE_APP_RSA_PRIVATE_KEY = 'MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAmc3C
# 客户端id
VITE_APP_CLIENT_ID = 'e5cd7e4891bf95d1d19206ce24a7b32e'
# websocket 开关 默认使用sse推送
VITE_APP_WEBSOCKET = false
# 统一消息推送开关
VITE_APP_MESSAGE_ENABLED = true
# sse 开关
VITE_APP_SSE = true
# sse / websocket
VITE_APP_MESSAGE_TRANSPORT = 'sse'
# 统一消息推送路径
VITE_APP_MESSAGE_PATH = '/resource/message'

View File

@@ -1,328 +0,0 @@
{
"globals": {
"Component": true,
"ComponentPublicInstance": true,
"ComputedRef": true,
"DirectiveBinding": true,
"EffectScope": true,
"ElLoading": true,
"ElMessage": true,
"ElMessageBox": true,
"ElNotification": true,
"ExtractDefaultPropTypes": true,
"ExtractPropTypes": true,
"ExtractPublicPropTypes": true,
"InjectionKey": true,
"MaybeRef": true,
"MaybeRefOrGetter": true,
"PropType": true,
"Ref": true,
"ShallowRef": true,
"Slot": true,
"Slots": true,
"VNode": true,
"WritableComputedRef": true,
"acceptHMRUpdate": true,
"asyncComputed": true,
"autoResetRef": true,
"computed": true,
"computedAsync": true,
"computedEager": true,
"computedInject": true,
"computedWithControl": true,
"controlledComputed": true,
"controlledRef": true,
"createApp": true,
"createEventHook": true,
"createGlobalState": true,
"createInjectionState": true,
"createPinia": true,
"createReactiveFn": true,
"createRef": true,
"createReusableTemplate": true,
"createSharedComposable": true,
"createTemplatePromise": true,
"createUnrefFn": true,
"customRef": true,
"debouncedRef": true,
"debouncedWatch": true,
"defineAsyncComponent": true,
"defineComponent": true,
"defineStore": true,
"eagerComputed": true,
"effectScope": true,
"extendRef": true,
"getActivePinia": true,
"getCurrentInstance": true,
"getCurrentScope": true,
"getCurrentWatcher": true,
"h": true,
"ignorableWatch": true,
"inject": true,
"injectLocal": true,
"isDefined": true,
"isProxy": true,
"isReactive": true,
"isReadonly": true,
"isRef": true,
"isShallow": true,
"makeDestructurable": true,
"mapActions": true,
"mapGetters": true,
"mapState": true,
"mapStores": true,
"mapWritableState": true,
"markRaw": true,
"nextTick": true,
"onActivated": true,
"onBeforeMount": true,
"onBeforeRouteLeave": true,
"onBeforeRouteUpdate": true,
"onBeforeUnmount": true,
"onBeforeUpdate": true,
"onClickOutside": true,
"onDeactivated": true,
"onElementRemoval": true,
"onErrorCaptured": true,
"onKeyStroke": true,
"onLongPress": true,
"onMounted": true,
"onRenderTracked": true,
"onRenderTriggered": true,
"onScopeDispose": true,
"onServerPrefetch": true,
"onStartTyping": true,
"onUnmounted": true,
"onUpdated": true,
"onWatcherCleanup": true,
"pausableWatch": true,
"provide": true,
"provideLocal": true,
"reactify": true,
"reactifyObject": true,
"reactive": true,
"reactiveComputed": true,
"reactiveOmit": true,
"reactivePick": true,
"readonly": true,
"ref": true,
"refAutoReset": true,
"refDebounced": true,
"refDefault": true,
"refManualReset": true,
"refThrottled": true,
"refWithControl": true,
"resolveComponent": true,
"resolveRef": true,
"setActivePinia": true,
"setMapStoreSuffix": true,
"shallowReactive": true,
"shallowReadonly": true,
"shallowRef": true,
"storeToRefs": true,
"syncRef": true,
"syncRefs": true,
"templateRef": true,
"throttledRef": true,
"throttledWatch": true,
"toRaw": true,
"toReactive": true,
"toRef": true,
"toRefs": true,
"toValue": true,
"triggerRef": true,
"tryOnBeforeMount": true,
"tryOnBeforeUnmount": true,
"tryOnMounted": true,
"tryOnScopeDispose": true,
"tryOnUnmounted": true,
"unref": true,
"unrefElement": true,
"until": true,
"useActiveElement": true,
"useAnimate": true,
"useArrayDifference": true,
"useArrayEvery": true,
"useArrayFilter": true,
"useArrayFind": true,
"useArrayFindIndex": true,
"useArrayFindLast": true,
"useArrayIncludes": true,
"useArrayJoin": true,
"useArrayMap": true,
"useArrayReduce": true,
"useArraySome": true,
"useArrayUnique": true,
"useAsyncQueue": true,
"useAsyncState": true,
"useAttrs": true,
"useBase64": true,
"useBattery": true,
"useBluetooth": true,
"useBreakpoints": true,
"useBroadcastChannel": true,
"useBrowserLocation": true,
"useCached": true,
"useClipboard": true,
"useClipboardItems": true,
"useCloned": true,
"useColorMode": true,
"useConfirmDialog": true,
"useCountdown": true,
"useCounter": true,
"useCssModule": true,
"useCssSupports": true,
"useCssVar": true,
"useCssVars": true,
"useCurrentElement": true,
"useCycleList": true,
"useDark": true,
"useDateFormat": true,
"useDebounce": true,
"useDebounceFn": true,
"useDebouncedRefHistory": true,
"useDeviceMotion": true,
"useDeviceOrientation": true,
"useDevicePixelRatio": true,
"useDevicesList": true,
"useDisplayMedia": true,
"useDocumentVisibility": true,
"useDraggable": true,
"useDropZone": true,
"useElementBounding": true,
"useElementByPoint": true,
"useElementHover": true,
"useElementSize": true,
"useElementVisibility": true,
"useEventBus": true,
"useEventListener": true,
"useEventSource": true,
"useEyeDropper": true,
"useFavicon": true,
"useFetch": true,
"useFileDialog": true,
"useFileSystemAccess": true,
"useFocus": true,
"useFocusWithin": true,
"useFps": true,
"useFullscreen": true,
"useGamepad": true,
"useGeolocation": true,
"useId": true,
"useIdle": true,
"useImage": true,
"useInfiniteScroll": true,
"useIntersectionObserver": true,
"useInterval": true,
"useIntervalFn": true,
"useKeyModifier": true,
"useLastChanged": true,
"useLink": true,
"useLocalStorage": true,
"useMagicKeys": true,
"useManualRefHistory": true,
"useMediaControls": true,
"useMediaQuery": true,
"useMemoize": true,
"useMemory": true,
"useModel": true,
"useMounted": true,
"useMouse": true,
"useMouseInElement": true,
"useMousePressed": true,
"useMutationObserver": true,
"useNavigatorLanguage": true,
"useNetwork": true,
"useNow": true,
"useObjectUrl": true,
"useOffsetPagination": true,
"useOnline": true,
"usePageLeave": true,
"useParallax": true,
"useParentElement": true,
"usePerformanceObserver": true,
"usePermission": true,
"usePointer": true,
"usePointerLock": true,
"usePointerSwipe": true,
"usePreferredColorScheme": true,
"usePreferredContrast": true,
"usePreferredDark": true,
"usePreferredLanguages": true,
"usePreferredReducedMotion": true,
"usePreferredReducedTransparency": true,
"usePrevious": true,
"useRafFn": true,
"useRefHistory": true,
"useResizeObserver": true,
"useRoute": true,
"useRouter": true,
"useSSRWidth": true,
"useScreenOrientation": true,
"useScreenSafeArea": true,
"useScriptTag": true,
"useScroll": true,
"useScrollLock": true,
"useSessionStorage": true,
"useShare": true,
"useSlots": true,
"useSorted": true,
"useSpeechRecognition": true,
"useSpeechSynthesis": true,
"useStepper": true,
"useStorage": true,
"useStorageAsync": true,
"useStyleTag": true,
"useSupported": true,
"useSwipe": true,
"useTemplateRef": true,
"useTemplateRefsList": true,
"useTextDirection": true,
"useTextSelection": true,
"useTextareaAutosize": true,
"useThrottle": true,
"useThrottleFn": true,
"useThrottledRefHistory": true,
"useTimeAgo": true,
"useTimeAgoIntl": true,
"useTimeout": true,
"useTimeoutFn": true,
"useTimeoutPoll": true,
"useTimestamp": true,
"useTitle": true,
"useToNumber": true,
"useToString": true,
"useToggle": true,
"useTransition": true,
"useUrlSearchParams": true,
"useUserMedia": true,
"useVModel": true,
"useVModels": true,
"useVibrate": true,
"useVirtualList": true,
"useWakeLock": true,
"useWebNotification": true,
"useWebSocket": true,
"useWebWorker": true,
"useWebWorkerFn": true,
"useWindowFocus": true,
"useWindowScroll": true,
"useWindowSize": true,
"watch": true,
"watchArray": true,
"watchAtMost": true,
"watchDebounced": true,
"watchDeep": true,
"watchEffect": true,
"watchIgnorable": true,
"watchImmediate": true,
"watchOnce": true,
"watchPausable": true,
"watchPostEffect": true,
"watchSyncEffect": true,
"watchThrottled": true,
"watchTriggerable": true,
"watchWithFilter": true,
"whenever": true
}
}

31
.oxfmtrc.json Normal file
View File

@@ -0,0 +1,31 @@
{
"$schema": "./node_modules/oxfmt/configuration_schema.json",
"ignorePatterns": [
"src/types/components.d.ts",
"src/types/auto-imports.d.ts",
".ai_state",
".claude",
".codex",
"doc"
],
"printWidth": 120,
"singleQuote": true,
"trailingComma": "none",
"arrowParens": "avoid",
"htmlWhitespaceSensitivity": "ignore",
"experimentalSortPackageJson": {
"sortScripts": true
},
"sortImports": {
"newlinesBetween": false,
"groups": [
"type-import",
["value-builtin", "value-external"],
"type-internal",
"value-internal",
["type-parent", "type-sibling", "type-index"],
["value-parent", "value-sibling", "value-index"],
"unknown"
]
}
}

25
.oxlintrc.json Normal file
View File

@@ -0,0 +1,25 @@
{
"$schema": "https://raw.githubusercontent.com/oxc-project/oxc/main/npm/oxlint/configuration_schema.json",
"plugins": ["eslint", "typescript", "unicorn", "oxc", "import", "vue"],
"rules": {
"typescript/no-empty-function": "off",
"typescript/no-explicit-any": "off",
"typescript/no-unused-vars": "off",
"typescript/no-this-alias": "off",
"typescript/no-empty-object-type": "off",
"typescript/no-unused-expressions": "off",
"prefer-rest-params": "off",
"import/no-unassigned-import": "off",
"import/no-named-as-default-member": "off",
"import/no-named-as-default": "off",
"no-shadow": "off",
"unicorn/prefer-add-event-listener": "off",
"unicorn/consistent-function-scoping": "off",
"unicorn/no-instanceof-builtins": "off"
},
"categories": {
"correctness": "error",
"suspicious": "error"
},
"ignorePatterns": [".ai_state", ".claude", ".codex", "doc", "dist/**", "dist-ssr/**", "coverage/**"]
}

View File

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

View File

@@ -1,20 +0,0 @@
{
"printWidth": 150,
"tabWidth": 2,
"useTabs": false,
"semi": true,
"singleQuote": true,
"quoteProps": "preserve",
"jsxSingleQuote": false,
"bracketSameLine": false,
"trailingComma": "none",
"bracketSpacing": true,
"embeddedLanguageFormatting": "auto",
"arrowParens": "always",
"requirePragma": false,
"insertPragma": false,
"proseWrap": "preserve",
"htmlWhitespaceSensitivity": "css",
"vueIndentScriptAndStyle": false,
"endOfLine": "auto"
}

View File

@@ -6,10 +6,10 @@
## 配套后端代码仓库地址
| 介绍 | 项目名 | 项目地址 |
|------------|:-----------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 介绍 | 项目名 | 项目地址 |
| ----------------- | :--------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| 🔥 分布式集群框架 | 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
# 安装依赖
npm install --registry=https://registry.npmmirror.com
pnpm install --registry=https://registry.npmmirror.com
# 启动服务
npm run dev
pnpm dev
# 构建生产环境
npm run build:prod
pnpm build:prod
# 前端访问地址 http://localhost:80
```
@@ -35,8 +35,6 @@ npm run build:prod
| 业务 | 功能说明 | 本框架 | RuoYi |
| ------------ | ------------------------------------------------------------- | ------ | ----------------------------- |
| 租户管理 | 系统内租户的管理 如:租户套餐、过期时间、用户数量、企业信息等 | 支持 | 无 |
| 租户套餐管理 | 系统内租户所能使用的套餐管理 如:套餐内所包含的菜单等 | 支持 | 无 |
| 用户管理 | 用户的管理配置 如:新增用户、分配用户所属部门、角色、岗位等 | 支持 | 支持 |
| 部门管理 | 配置系统组织机构(公司、部门、小组) 树结构展现支持数据权限 | 支持 | 支持 |
| 岗位管理 | 配置系统用户所属担任职务 | 支持 | 支持 |
@@ -80,4 +78,4 @@ npm run 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 '屏幕截图') |

View File

@@ -1,12 +0,0 @@
@echo off
echo.
echo [<5B><>Ϣ] <20><><EFBFBD><EFBFBD>Web<65><62><EFBFBD>̣<EFBFBD><CCA3><EFBFBD><EFBFBD><EFBFBD>dist<73>ļ<EFBFBD><C4BC><EFBFBD>
echo.
%~d0
cd %~dp0
cd ..
yarn build:prod
pause

View File

@@ -1,12 +0,0 @@
@echo off
echo.
echo [<5B><>Ϣ] <20><>װWeb<65><62><EFBFBD>̣<EFBFBD><CCA3><EFBFBD><EFBFBD><EFBFBD>node_modules<65>ļ<EFBFBD><C4BC><EFBFBD>
echo.
%~d0
cd %~dp0
cd ..
yarn --registry=https://registry.npmmirror.com
pause

View File

@@ -1,12 +0,0 @@
@echo off
echo.
echo [<5B><>Ϣ] ʹ<><CAB9> Vite <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> Web <20><><EFBFBD>̡<EFBFBD>
echo.
%~d0
cd %~dp0
cd ..
yarn dev
pause

View File

@@ -1,44 +0,0 @@
import pluginVue from 'eslint-plugin-vue';
import globals from 'globals';
import prettier from 'eslint-plugin-prettier';
import { defineConfigWithVueTs, vueTsConfigs } from '@vue/eslint-config-typescript';
import skipFormatting from '@vue/eslint-config-prettier/skip-formatting';
export default defineConfigWithVueTs(
{
name: 'app/files-to-lint',
files: ['**/*.{js,cjs,ts,mts,tsx,vue}']
},
{
name: 'app/files-to-ignore',
ignores: ['**/dist/**', '**/dist-ssr/**', '**/coverage/**']
},
{
languageOptions: {
globals: globals.browser
}
},
pluginVue.configs['flat/essential'],
vueTsConfigs.recommended,
skipFormatting,
{
plugins: { prettier },
rules: {
'@typescript-eslint/no-empty-function': 'off',
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-unused-vars': 'off',
'@typescript-eslint/no-this-alias': 'off',
// vue
'vue/multi-word-component-names': 'off',
'vue/valid-define-props': 'off',
'vue/no-v-model-argument': 'off',
'prefer-rest-params': 'off',
// prettier
'prettier/prettier': 'error',
// 允许使用空Object类型 {}
'@typescript-eslint/no-empty-object-type': 'off',
'@typescript-eslint/no-unused-expressions': 'off'
}
}
);

View File

@@ -144,7 +144,8 @@
font-size: 14px;
line-height: 24px;
color: #454545;
font-family: 'Microsoft YaHei UI', 'Microsoft YaHei', DengXian, SimSun, 'Segoe UI', Tahoma, Helvetica, sans-serif;
font-family:
'Microsoft YaHei UI', 'Microsoft YaHei', DengXian, SimSun, 'Segoe UI', Tahoma, Helvetica, sans-serif;
overflow-y: scroll;
}
h1 {
@@ -208,15 +209,18 @@
</head>
<body style="margin-top: 50px">
<h1>请升级您的浏览器,以便我们更好的为您提供服务!</h1>
<p>您正在使用 Internet Explorer 的早期版本IE11以下版本或使用该内核的浏览器。这意味着在升级浏览器前您将无法访问此网站。</p>
<p>
您正在使用 Internet Explorer
的早期版本IE11以下版本或使用该内核的浏览器。这意味着在升级浏览器前您将无法访问此网站。
</p>
<hr />
<h2>请注意微软公司对Windows XP 及 Internet Explorer 早期版本的支持已经结束</h2>
<p>
自 2016 年 1 月 12 日起Microsoft 不再为 IE 11
以下版本提供相应支持和更新。没有关键的浏览器安全更新,您的电脑可能易受有害病毒、间谍软件和其他恶意软件的攻击,它们可以窃取或损害您的业务数据和信息。请参阅
<a href="https://www.microsoft.com/zh-cn/WindowsForBusiness/End-of-IE-support"
>微软对 Internet Explorer 早期版本的支持将于 2016 年 1 月 12 日结束的说明</a
>
<a href="https://www.microsoft.com/zh-cn/WindowsForBusiness/End-of-IE-support">
微软对 Internet Explorer 早期版本的支持将于 2016 年 1 月 12 日结束的说明
</a>
</p>
<hr />
@@ -224,16 +228,28 @@
<p>推荐使用以下浏览器的最新版本。如果您的电脑已有以下浏览器的最新版本则直接使用该浏览器访问即可。</p>
<ul class="browser">
<li class="browser-chrome">
<a href="https://www.google.cn/chrome/browser/desktop/index.html?hl=zh-CN&standalone=1"> 谷歌浏览器<span>Google Chrome</span></a>
<a href="https://www.google.cn/chrome/browser/desktop/index.html?hl=zh-CN&standalone=1">
谷歌浏览器
<span>Google Chrome</span>
</a>
</li>
<li class="browser-firefox">
<a href="https://www.mozilla.org/zh-CN/firefox/new/"> 火狐浏览器<span>Mozilla Firefox</span></a>
<a href="https://www.mozilla.org/zh-CN/firefox/new/">
火狐浏览器
<span>Mozilla Firefox</span>
</a>
</li>
<li class="browser-ie">
<a href="https://windows.microsoft.com/zh-cn/internet-explorer/download-ie"> IE 11 浏览器<span>Internet Explorer</span></a>
<a href="https://windows.microsoft.com/zh-cn/internet-explorer/download-ie">
IE 11 浏览器
<span>Internet Explorer</span>
</a>
</li>
<li class="browser-360">
<a href="http://se.360.cn/"> 360安全浏览器<span>360 Chrome</span></a>
<a href="http://se.360.cn/">
360安全浏览器
<span>360 Chrome</span>
</a>
</li>
<div class="clean"></div>
</ul>

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

@@ -2,95 +2,86 @@
"$schema": "https://json.schemastore.org/package",
"name": "ruoyi-vue-plus",
"version": "5.5.3-2.5.3",
"description": "RuoYi-Vue-Plus多租户管理系统",
"author": "LionLi",
"description": "RuoYi-Vue-Plus后台管理系统",
"license": "MIT",
"type": "module",
"scripts": {
"dev": "vite serve --mode development",
"build:prod": "vite build --mode production",
"build:dev": "vite build --mode development",
"preview": "vite preview",
"lint:eslint": "eslint",
"lint:eslint:fix": "eslint --fix",
"prettier": "prettier --write ."
},
"author": "LionLi",
"repository": {
"type": "git",
"url": "https://gitee.com/JavaLionLi/plus-ui.git"
},
"type": "module",
"scripts": {
"build:dev": "vite build --mode development",
"build:prod": "vite build --mode production",
"dev": "vite serve --mode development",
"fmt": "oxfmt .",
"lint": "oxlint src",
"lint:fix": "oxlint --fix src",
"preview": "vite preview"
},
"dependencies": {
"@element-plus/icons-vue": "2.3.2",
"@highlightjs/vue-plugin": "2.1.2",
"@vueup/vue-quill": "1.2.0",
"@iconify/vue": "^5.0.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.13.6",
"axios": "1.15.2",
"crypto-js": "4.2.0",
"echarts": "6.0.0",
"element-plus": "2.13.5",
"file-saver": "2.0.5",
"element-plus": "2.13.7",
"highlight.js": "11.11.1",
"image-conversion": "2.1.1",
"js-cookie": "3.0.5",
"jsencrypt": "3.5.4",
"nprogress": "0.2.0",
"pinia": "3.0.4",
"screenfull": "6.0.2",
"vue": "3.5.30",
"vue": "3.5.33",
"vue-cropper": "1.1.4",
"vue-i18n": "11.3.0",
"vue-i18n": "11.4.0",
"vue-json-pretty": "2.6.0",
"vue-router": "5.0.3",
"vue-router": "5.0.6",
"vue-types": "6.0.0",
"vxe-table": "4.18.1"
"vxe-table": "4.18.13"
},
"devDependencies": {
"@iconify/json": "^2.2.448",
"@types/crypto-js": "4.2.2",
"@types/file-saver": "2.0.7",
"@types/js-cookie": "3.0.6",
"@types/node": "^25.4.0",
"@types/node": "^25.6.0",
"@types/nprogress": "0.2.3",
"@unocss/preset-attributify": "66.6.6",
"@unocss/preset-icons": "66.6.6",
"@unocss/preset-uno": "66.6.6",
"@vitejs/plugin-vue": "6.0.4",
"@vue/compiler-sfc": "3.5.30",
"@vue/eslint-config-prettier": "10.2.0",
"@vue/eslint-config-typescript": "14.6.0",
"autoprefixer": "10.4.27",
"eslint": "9.39.1",
"eslint-plugin-prettier": "5.5.5",
"eslint-plugin-vue": "9.33.0",
"globals": "17.4.0",
"prettier": "3.8.1",
"sass": "1.98.0",
"typescript": "~5.9.3",
"unocss": "66.6.6",
"@unocss/preset-attributify": "66.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",
"unplugin-auto-import": "21.0.0",
"unplugin-icons": "23.0.1",
"unplugin-vue-components": "31.0.0",
"unplugin-vue-components": "32.0.0",
"unplugin-vue-setup-extend-plus": "1.0.1",
"vite": "7.3.1",
"vite-plugin-compression": "0.5.1",
"vite-plugin-svg-icons-ng": "^1.5.2",
"vite-plugin-vue-devtools": "8.0.7",
"vitest": "4.0.18",
"vue-tsc": "^3.2.5"
},
"overrides": {
"quill": "1.3.7"
},
"engines": {
"node": ">=20.19.0",
"npm": ">=8.19.0"
"vite": "^8.0.10",
"vite-plugin-svg-icons-ng": "^1.8.0",
"vitest": "4.1.5",
"vue-tsc": "^3.2.7"
},
"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,13 +1,14 @@
import type { DemoForm, DemoQuery, DemoVO } from '@/api/demo/demo/types';
import type { PageResult } from '@/api/types';
import type { AxiosPromise } from '@/utils/api-types';
import request from '@/utils/request';
import { AxiosPromise } from 'axios';
import { DemoVO, DemoForm, DemoQuery } from '@/api/demo/demo/types';
/**
* 查询测试单列表
* @param query
* @returns {*}
*/
export const listDemo = (query?: DemoQuery): AxiosPromise<DemoVO[]> => {
export const listDemo = (query?: DemoQuery): AxiosPromise<PageResult<DemoVO>> => {
return request({
url: '/demo/demo/list',
method: 'get',

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

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 { AxiosPromise } from 'axios';
import { CacheVO } from './types';
import type { CacheVO } from './types';
// 查询缓存详细
export function getCache(): AxiosPromise<CacheVO> {

View File

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

View File

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

View File

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

View File

@@ -2,6 +2,12 @@ export interface OperLogQuery extends PageQuery {
operIp: string;
title: string;
operName: string;
userId: string;
deptId: string;
clientKey: string;
deviceType: string;
browser: string;
os: string;
businessType: string;
status: string;
orderByColumn: string;
@@ -18,7 +24,13 @@ export interface OperLogVO extends BaseEntity {
requestMethod: string;
operatorType: number;
operName: string;
userId: string | number;
deptId: string | number;
deptName: string;
clientKey: string;
deviceType: string;
browser: string;
os: string;
operUrl: string;
operIp: string;
operLocation: string;
@@ -40,7 +52,13 @@ export interface OperLogForm {
requestMethod: string;
operatorType: number;
operName: string;
userId: string | number | undefined;
deptId: string | number | undefined;
deptName: string;
clientKey: string;
deviceType: string;
browser: string;
os: string;
operUrl: string;
operIp: string;
operLocation: string;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,6 +1,6 @@
import type { AxiosPromise } from '@/utils/api-types';
import request from '@/utils/request';
import { AxiosPromise } from 'axios';
import { MenuQuery, MenuVO, MenuForm, MenuTreeOption, RoleMenuTree } from './types';
import type { MenuForm, MenuQuery, MenuTreeOption, MenuVO, RoleMenuTree } from './types';
// 查询菜单列表
export const listMenu = (query?: MenuQuery): AxiosPromise<MenuVO[]> => {
@@ -35,14 +35,6 @@ export const roleMenuTreeselect = (roleId: string | number): AxiosPromise<RoleMe
});
};
// 根据角色ID查询菜单下拉树结构
export const tenantPackageMenuTreeselect = (packageId: string | number): AxiosPromise<RoleMenuTree> => {
return request({
url: '/system/menu/tenantPackageMenuTreeselect/' + packageId,
method: 'get'
});
};
// 新增菜单
export const addMenu = (data: MenuForm) => {
return request({

View File

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

View File

@@ -0,0 +1,10 @@
import type { AxiosPromise } from '@/utils/api-types';
import request from '@/utils/request';
import type { MessageBoxVO } from './types';
export function getMessageBox(): AxiosPromise<MessageBoxVO> {
return request({
url: '/resource/message/box',
method: 'get'
});
}

View File

@@ -0,0 +1,17 @@
export interface MessageVO extends BaseEntity {
messageId: number | string;
category: string;
type: string;
source: string;
title: string;
message: string;
content?: string;
data?: Record<string, any> | null;
path?: string;
}
export interface MessageBoxVO {
systemList: MessageVO[];
noticeList: MessageVO[];
workflowList: MessageVO[];
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,10 +1,10 @@
import { UserVO } from '@/api/system/user/types';
import { UserQuery } from '@/api/system/user/types';
import { AxiosPromise } from 'axios';
import { RoleQuery, RoleVO, RoleDeptTree } from './types';
import type { UserQuery, UserVO } from '@/api/system/user/types';
import type { PageResult } from '@/api/types';
import type { AxiosPromise } from '@/utils/api-types';
import request from '@/utils/request';
import type { RoleDeptTree, RoleQuery, RoleVO } from './types';
export const listRole = (query: RoleQuery): AxiosPromise<RoleVO[]> => {
export const listRole = (query: RoleQuery): AxiosPromise<PageResult<RoleVO>> => {
return request({
url: '/system/role/list',
method: 'get',
@@ -45,7 +45,7 @@ export const addRole = (data: any) => {
};
/**
* 修改角色
* 修改角色基础信息
* @param data
*/
export const updateRole = (data: any) => {
@@ -57,11 +57,11 @@ export const updateRole = (data: any) => {
};
/**
* 角色数据权限
* 修改角色权限(菜单权限 + 数据权限
*/
export const dataScope = (data: any) => {
export const updateRolePermission = (data: any) => {
return request({
url: '/system/role/dataScope',
url: '/system/role/permission',
method: 'put',
data: data
});
@@ -95,7 +95,7 @@ export const delRole = (roleId: Array<string | number> | string | number) => {
/**
* 查询角色已授权用户列表
*/
export const allocatedUserList = (query: UserQuery): AxiosPromise<UserVO[]> => {
export const allocatedUserList = (query: UserQuery): AxiosPromise<PageResult<UserVO>> => {
return request({
url: '/system/role/authUser/allocatedList',
method: 'get',
@@ -106,7 +106,7 @@ export const allocatedUserList = (query: UserQuery): AxiosPromise<UserVO[]> => {
/**
* 查询角色未授权用户列表
*/
export const unallocatedUserList = (query: UserQuery): AxiosPromise<UserVO[]> => {
export const unallocatedUserList = (query: UserQuery): AxiosPromise<PageResult<UserVO>> => {
return request({
url: '/system/role/authUser/unallocatedList',
method: 'get',

View File

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

View File

@@ -1,109 +0,0 @@
import request from '@/utils/request';
import { TenantForm, TenantQuery, TenantVO } from './types';
import { AxiosPromise } from 'axios';
// 查询租户列表
export function listTenant(query: TenantQuery): AxiosPromise<TenantVO[]> {
return request({
url: '/system/tenant/list',
method: 'get',
params: query
});
}
// 查询租户详细
export function getTenant(id: string | number): AxiosPromise<TenantVO> {
return request({
url: '/system/tenant/' + id,
method: 'get'
});
}
// 新增租户
export function addTenant(data: TenantForm) {
return request({
url: '/system/tenant',
method: 'post',
headers: {
isEncrypt: true,
repeatSubmit: false
},
data: data
});
}
// 修改租户
export function updateTenant(data: TenantForm) {
return request({
url: '/system/tenant',
method: 'put',
data: data
});
}
// 租户状态修改
export function changeTenantStatus(id: string | number, tenantId: string | number, status: string) {
const data = {
id,
tenantId,
status
};
return request({
url: '/system/tenant/changeStatus',
method: 'put',
data: data
});
}
// 删除租户
export function delTenant(id: string | number | Array<string | number>) {
return request({
url: '/system/tenant/' + id,
method: 'delete'
});
}
// 动态切换租户
export function dynamicTenant(tenantId: string | number) {
return request({
url: '/system/tenant/dynamic/' + tenantId,
method: 'get'
});
}
// 清除动态租户
export function dynamicClear() {
return request({
url: '/system/tenant/dynamic/clear',
method: 'get'
});
}
// 同步租户套餐
export function syncTenantPackage(tenantId: string | number, packageId: string | number) {
const data = {
tenantId,
packageId
};
return request({
url: '/system/tenant/syncTenantPackage',
method: 'get',
params: data
});
}
// 同步租户字典
export function syncTenantDict() {
return request({
url: '/system/tenant/syncTenantDict',
method: 'get'
});
}
// 同步租户字典
export function syncTenantConfig() {
return request({
url: '/system/tenant/syncTenantConfig',
method: 'get'
});
}

View File

@@ -1,46 +0,0 @@
export interface TenantVO extends BaseEntity {
id: number | string;
tenantId: number | string;
username: string;
contactUserName: string;
contactPhone: string;
companyName: string;
licenseNumber: string;
address: string;
domain: string;
intro: string;
remark: string;
packageId: string | number;
expireTime: string;
accountCount: number;
status: string;
}
export interface TenantQuery extends PageQuery {
tenantId: string | number;
contactUserName: string;
contactPhone: string;
companyName: string;
}
export interface TenantForm {
id: number | string | undefined;
tenantId: number | string | undefined;
username: string;
password: string;
contactUserName: string;
contactPhone: string;
companyName: string;
licenseNumber: string;
domain: string;
address: string;
intro: string;
remark: string;
packageId: string | number;
expireTime: string;
accountCount: number;
status: string;
}

View File

@@ -1,67 +0,0 @@
import request from '@/utils/request';
import { TenantPkgForm, TenantPkgQuery, TenantPkgVO } from './types';
import { AxiosPromise } from 'axios';
// 查询租户套餐列表
export function listTenantPackage(query?: TenantPkgQuery): AxiosPromise<TenantPkgVO[]> {
return request({
url: '/system/tenant/package/list',
method: 'get',
params: query
});
}
// 查询租户套餐下拉选列表
export function selectTenantPackage(): AxiosPromise<TenantPkgVO[]> {
return request({
url: '/system/tenant/package/selectList',
method: 'get'
});
}
// 查询租户套餐详细
export function getTenantPackage(packageId: string | number): AxiosPromise<TenantPkgVO> {
return request({
url: '/system/tenant/package/' + packageId,
method: 'get'
});
}
// 新增租户套餐
export function addTenantPackage(data: TenantPkgForm) {
return request({
url: '/system/tenant/package',
method: 'post',
data: data
});
}
// 修改租户套餐
export function updateTenantPackage(data: TenantPkgForm) {
return request({
url: '/system/tenant/package',
method: 'put',
data: data
});
}
// 租户套餐状态修改
export function changePackageStatus(packageId: number | string, status: string) {
const data = {
packageId,
status
};
return request({
url: '/system/tenant/package/changeStatus',
method: 'put',
data: data
});
}
// 删除租户套餐
export function delTenantPackage(packageId: string | number | Array<string | number>) {
return request({
url: '/system/tenant/package/' + packageId,
method: 'delete'
});
}

View File

@@ -1,20 +0,0 @@
export interface TenantPkgVO extends BaseEntity {
packageId: string | number;
packageName: string;
menuIds: string;
remark: string;
menuCheckStrictly: boolean;
status: string;
}
export interface TenantPkgQuery extends PageQuery {
packageName: string;
}
export interface TenantPkgForm {
packageId: string | number | undefined;
packageName: string;
menuIds: string;
remark: string;
menuCheckStrictly: boolean;
}

View File

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

View File

@@ -1,5 +1,5 @@
import { RoleVO } from '@/api/system/role/types';
import { PostVO } from '@/api/system/post/types';
import type { PostVO } from '@/api/system/post/types';
import type { RoleVO } from '@/api/system/role/types';
/**
* 用户信息
@@ -16,11 +16,11 @@ export interface UserInfo {
export interface UserQuery extends PageQuery {
userName?: string;
nickName?: string;
phonenumber?: string;
phoneNumber?: string;
status?: string;
deptId?: string | number;
roleId?: string | number;
userIds?: string | number | (string | number)[] | undefined;
userIds?: string | number | (string | number)[] | undefined;
}
/**
@@ -34,8 +34,8 @@ export interface UserVO extends BaseEntity {
nickName: string;
userType: string;
email: string;
phonenumber: string;
sex: string;
phoneNumber: string;
gender: string;
avatar: string;
status: string;
delFlag: string;
@@ -43,6 +43,8 @@ export interface UserVO extends BaseEntity {
loginDate: string;
remark: string;
deptName: string;
/** 详情接口可能返回嵌套部门 */
dept?: { deptName?: string };
roles: RoleVO[];
roleIds: any;
postIds: any;
@@ -60,9 +62,9 @@ export interface UserForm {
userName: string;
nickName?: string;
password: string;
phonenumber?: string;
phoneNumber?: string;
email?: string;
sex?: string;
gender?: string;
status: string;
remark?: string;
postIds: string[];

View File

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

View File

@@ -4,8 +4,6 @@ export interface TableVO extends BaseEntity {
dataName: string;
tableName: string;
tableComment: string;
subTableName?: any;
subTableFkName?: any;
className: string;
tplCategory: string;
packageName: string;
@@ -25,6 +23,16 @@ export interface TableVO extends BaseEntity {
menuIds?: any;
parentMenuId?: any;
parentMenuName?: any;
enableExport?: boolean;
enableStatus?: boolean;
statusField?: string;
enableUnique?: boolean;
uniqueFields?: string[];
enableSort?: boolean;
sortField?: string;
treeRootValue?: string;
treeAncestorsField?: string;
treeOrderField?: string;
tree: boolean;
crud: boolean;
}
@@ -72,8 +80,6 @@ export interface DbTableVO {
tableId?: any;
tableName: string;
tableComment: string;
subTableName?: any;
subTableFkName?: any;
className?: any;
tplCategory?: any;
packageName?: any;
@@ -93,6 +99,16 @@ export interface DbTableVO {
menuIds?: any;
parentMenuId?: any;
parentMenuName?: any;
enableExport?: boolean;
enableStatus?: boolean;
statusField?: string;
enableUnique?: boolean;
uniqueFields?: string[];
enableSort?: boolean;
sortField?: string;
treeRootValue?: string;
treeAncestorsField?: string;
treeOrderField?: string;
tree: boolean;
crud: boolean;
}
@@ -103,10 +119,14 @@ export interface DbTableQuery extends PageQuery {
tableComment: string;
}
export interface GenTableVO {
/**
* 代码生成表详情接口 data 结构
* - info当前表 GenTable
* - rows字段列表 GenTableColumn[]
*/
export interface GenTableDetailPayload {
info: DbTableVO;
rows: DbColumnVO[];
tables: DbTableVO[];
}
export interface DbColumnForm extends BaseEntity {
@@ -146,6 +166,16 @@ export interface DbParamForm {
treeName?: any;
treeParentCode?: any;
parentMenuId: string;
enableExport?: boolean;
enableStatus?: boolean;
statusField?: string;
enableUnique?: boolean;
uniqueFields?: string[];
enableSort?: boolean;
sortField?: string;
treeRootValue?: string;
treeAncestorsField?: string;
treeOrderField?: string;
}
export interface DbTableForm extends BaseEntity {
@@ -153,8 +183,6 @@ export interface DbTableForm extends BaseEntity {
tableId: string | string;
tableName: string;
tableComment: string;
subTableName?: any;
subTableFkName?: any;
className: string;
tplCategory: string;
packageName: string;
@@ -174,6 +202,16 @@ export interface DbTableForm extends BaseEntity {
menuIds?: any;
parentMenuId: string;
parentMenuName?: any;
enableExport?: boolean;
enableStatus?: boolean;
statusField?: string;
enableUnique?: boolean;
uniqueFields?: string[];
enableSort?: boolean;
sortField?: string;
treeRootValue?: string;
treeAncestorsField?: string;
treeOrderField?: string;
tree: boolean;
crud: boolean;
params: DbParamForm;

View File

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

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,13 +1,19 @@
import type { PageResult } from '@/api/types';
import type {
definitionXmlVO,
FlowDefinitionForm,
FlowDefinitionQuery,
FlowDefinitionVo
} from '@/api/workflow/definition/types';
import type { AxiosPromise } from '@/utils/api-types';
import request from '@/utils/request';
import { FlowDefinitionQuery, definitionXmlVO, FlowDefinitionForm, FlowDefinitionVo } from '@/api/workflow/definition/types';
import { AxiosPromise } from 'axios';
/**
* 获取流程定义列表
* @param query 流程实例id
* @returns
*/
export const listDefinition = (query: FlowDefinitionQuery): AxiosPromise<FlowDefinitionVo[]> => {
export const listDefinition = (query: FlowDefinitionQuery): AxiosPromise<PageResult<FlowDefinitionVo>> => {
return request({
url: `/workflow/definition/list`,
method: 'get',
@@ -20,7 +26,7 @@ export const listDefinition = (query: FlowDefinitionQuery): AxiosPromise<FlowDef
* @param query 流程实例id
* @returns
*/
export const unPublishList = (query: FlowDefinitionQuery): AxiosPromise<FlowDefinitionVo[]> => {
export const unPublishList = (query: FlowDefinitionQuery): AxiosPromise<PageResult<FlowDefinitionVo>> => {
return request({
url: `/workflow/definition/unPublishList`,
method: 'get',

View File

@@ -1,13 +1,14 @@
import type { PageResult } from '@/api/types';
import type { FlowInstanceQuery, FlowInstanceVO } from '@/api/workflow/instance/types';
import type { AxiosPromise } from '@/utils/api-types';
import request from '@/utils/request';
import { FlowInstanceQuery, FlowInstanceVO } from '@/api/workflow/instance/types';
import { AxiosPromise } from 'axios';
/**
* 查询运行中实例列表
* @param query
* @returns {*}
*/
export const pageByRunning = (query: FlowInstanceQuery): AxiosPromise<FlowInstanceVO[]> => {
export const pageByRunning = (query: FlowInstanceQuery): AxiosPromise<PageResult<FlowInstanceVO>> => {
return request({
url: '/workflow/instance/pageByRunning',
method: 'get',
@@ -20,7 +21,7 @@ export const pageByRunning = (query: FlowInstanceQuery): AxiosPromise<FlowInstan
* @param query
* @returns {*}
*/
export const pageByFinish = (query: FlowInstanceQuery): AxiosPromise<FlowInstanceVO[]> => {
export const pageByFinish = (query: FlowInstanceQuery): AxiosPromise<PageResult<FlowInstanceVO>> => {
return request({
url: '/workflow/instance/pageByFinish',
method: 'get',
@@ -33,7 +34,7 @@ export const pageByFinish = (query: FlowInstanceQuery): AxiosPromise<FlowInstanc
*/
export const flowHisTaskList = (businessId: string | number) => {
return request({
url: `/workflow/instance/flowHisTaskList/${businessId}` + '?t' + Math.random(),
url: `/workflow/instance/flowHisTaskList/${businessId}?t=${Math.random()}`,
method: 'get'
});
};
@@ -43,7 +44,7 @@ export const flowHisTaskList = (businessId: string | number) => {
* @param query
* @returns {*}
*/
export const pageByCurrent = (query: FlowInstanceQuery): AxiosPromise<FlowInstanceVO[]> => {
export const pageByCurrent = (query: FlowInstanceQuery): AxiosPromise<PageResult<FlowInstanceVO>> => {
return request({
url: '/workflow/instance/pageByCurrent',
method: 'get',

View File

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

View File

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

View File

@@ -1,6 +1,7 @@
import type { PageResult } from '@/api/types';
import type { SpelForm, SpelQuery, SpelVO } from '@/api/workflow/spel/types';
import type { AxiosPromise } from '@/utils/api-types';
import request from '@/utils/request';
import { AxiosPromise } from 'axios';
import { SpelVO, SpelForm, SpelQuery } from '@/api/workflow/spel/types';
/**
* 查询流程spel表达式定义列表
@@ -8,7 +9,7 @@ import { SpelVO, SpelForm, SpelQuery } from '@/api/workflow/spel/types';
* @returns {*}
*/
export const listSpel = (query?: SpelQuery): AxiosPromise<SpelVO[]> => {
export const listSpel = (query?: SpelQuery): AxiosPromise<PageResult<SpelVO>> => {
return request({
url: '/workflow/spel/list',
method: 'get',

View File

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

View File

@@ -1,13 +1,14 @@
import type { PageResult } from '@/api/types';
import type { FlowTaskVO, TaskOperationBo, TaskQuery } from '@/api/workflow/task/types';
import type { AxiosPromise } from '@/utils/api-types';
import request from '@/utils/request';
import { AxiosPromise } from 'axios';
import { TaskQuery, FlowTaskVO, TaskOperationBo } from '@/api/workflow/task/types';
/**
* 查询待办列表
* @param query
* @returns {*}
*/
export const pageByTaskWait = (query: TaskQuery): AxiosPromise<FlowTaskVO[]> => {
export const pageByTaskWait = (query: TaskQuery): AxiosPromise<PageResult<FlowTaskVO>> => {
return request({
url: '/workflow/task/pageByTaskWait',
method: 'get',
@@ -20,7 +21,7 @@ export const pageByTaskWait = (query: TaskQuery): AxiosPromise<FlowTaskVO[]> =>
* @param query
* @returns {*}
*/
export const pageByTaskFinish = (query: TaskQuery): AxiosPromise<FlowTaskVO[]> => {
export const pageByTaskFinish = (query: TaskQuery): AxiosPromise<PageResult<FlowTaskVO>> => {
return request({
url: '/workflow/task/pageByTaskFinish',
method: 'get',
@@ -33,7 +34,7 @@ export const pageByTaskFinish = (query: TaskQuery): AxiosPromise<FlowTaskVO[]> =
* @param query
* @returns {*}
*/
export const pageByTaskCopy = (query: TaskQuery): AxiosPromise<FlowTaskVO[]> => {
export const pageByTaskCopy = (query: TaskQuery): AxiosPromise<PageResult<FlowTaskVO>> => {
return request({
url: '/workflow/task/pageByTaskCopy',
method: 'get',
@@ -42,11 +43,11 @@ export const pageByTaskCopy = (query: TaskQuery): AxiosPromise<FlowTaskVO[]> =>
};
/**
* 当前租户所有待办任务
* 查询全部待办任务
* @param query
* @returns {*}
*/
export const pageByAllTaskWait = (query: TaskQuery): AxiosPromise<FlowTaskVO[]> => {
export const pageByAllTaskWait = (query: TaskQuery): AxiosPromise<PageResult<FlowTaskVO>> => {
return request({
url: '/workflow/task/pageByAllTaskWait',
method: 'get',
@@ -55,11 +56,11 @@ export const pageByAllTaskWait = (query: TaskQuery): AxiosPromise<FlowTaskVO[]>
};
/**
* 当前租户所有已办任务
* 查询全部已办任务
* @param query
* @returns {*}
*/
export const pageByAllTaskFinish = (query: TaskQuery): AxiosPromise<FlowTaskVO[]> => {
export const pageByAllTaskFinish = (query: TaskQuery): AxiosPromise<PageResult<FlowTaskVO>> => {
return request({
url: '/workflow/task/pageByAllTaskFinish',
method: 'get',

View File

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

View File

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

View File

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@@ -0,0 +1,90 @@
/* Global document styles and lightweight utility helpers. */
// --- 视口与排版根 ---
body {
height: 100%;
margin: 0;
-moz-osx-font-smoothing: grayscale;
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility;
background: var(--app-shell-bg);
font-family:
'MiSans', 'HarmonyOS Sans SC', 'PingFang SC', 'Source Han Sans SC', 'Noto Sans SC', 'Hiragino Sans GB',
'Microsoft YaHei', sans-serif;
}
label {
font-weight: 600;
}
html {
height: 100%;
box-sizing: border-box;
}
html.dark .svg-icon,
html.dark svg {
fill: var(--el-text-color-regular);
}
#app {
height: 100%;
}
html,
body,
#app {
min-height: 100%;
}
*,
*:before,
*:after {
box-sizing: inherit;
}
a:focus,
a:active {
outline: none;
}
a,
a:focus,
a:hover {
cursor: pointer;
color: inherit;
text-decoration: none;
}
div:focus {
outline: none;
}
.clearfix {
&:after {
visibility: hidden;
display: block;
font-size: 0;
content: ' ';
clear: both;
height: 0;
}
}
.h1,
.h2,
.h3,
.h4,
.h5,
.h6,
h1,
h2,
h3,
h4,
h5,
h6 {
font-family: inherit;
font-weight: 500;
line-height: 1.1;
color: inherit;
}

View File

@@ -1,99 +0,0 @@
@use './variables.module.scss' as *;
@mixin colorBtn($color) {
background: $color;
&:hover {
color: $color;
&:before,
&:after {
background: $color;
}
}
}
.blue-btn {
@include colorBtn($blue);
}
.light-blue-btn {
@include colorBtn($light-blue);
}
.red-btn {
@include colorBtn($red);
}
.pink-btn {
@include colorBtn($pink);
}
.green-btn {
@include colorBtn($green);
}
.tiffany-btn {
@include colorBtn($tiffany);
}
.yellow-btn {
@include colorBtn($yellow);
}
.pan-btn {
font-size: 14px;
color: #fff;
padding: 14px 36px;
border-radius: var(--app-radius-md);
border: none;
outline: none;
transition: 600ms ease all;
position: relative;
display: inline-block;
&:hover {
background: #fff;
&:before,
&:after {
width: 100%;
transition: 600ms ease all;
}
}
&:before,
&:after {
content: '';
position: absolute;
top: 0;
right: 0;
height: 2px;
width: 0;
transition: 400ms ease all;
}
&::after {
right: inherit;
top: inherit;
left: 0;
bottom: 0;
}
}
.custom-button {
display: inline-block;
line-height: 1;
white-space: nowrap;
cursor: pointer;
background: #fff;
color: #fff;
-webkit-appearance: none;
text-align: center;
box-sizing: border-box;
outline: 0;
margin: 0;
padding: 10px 15px;
font-size: 14px;
border-radius: var(--app-radius-sm);
}

View File

@@ -0,0 +1,67 @@
/* 业务列表/卡片页分页、树、列表、el-card 等(与 vendors/element-plus 通用覆盖叠加,入口顺序见 index.scss
* 属「页面模式」而非 EP 公共主题,故放在 components。 */
// --- 历史「表单分区标题」样式 ---
.form-header {
font-size: 15px;
color: var(--el-color-primary);
border-bottom: 1px solid var(--app-surface-border);
margin: 8px 10px 25px 10px;
padding-bottom: 5px;
}
// --- 树形容器描边(暗色覆盖在 _search-panel ---
.tree-border {
margin-top: 8px;
border: 1px solid var(--app-surface-border);
background: var(--app-surface-bg);
border-radius: 22px;
width: 100%;
padding: 12px;
}
@media (max-width: 768px) {
.pagination-container .el-pagination > .el-pagination__jump {
display: none !important;
}
.pagination-container .el-pagination > .el-pagination__sizes {
display: none !important;
}
}
.el-tree-node__content > .el-checkbox {
margin-right: 8px;
}
// --- el-card 本业务页外观(与 EP vendors 叠加) ---
.el-card__header {
padding: 14px 16px 10px !important;
min-height: auto;
background: transparent;
border-bottom: 1px solid var(--app-surface-border);
}
.el-card__body {
padding: 16px !important;
}
.el-card {
border-radius: var(--app-radius-base);
box-shadow: var(--app-shadow-sm);
border-color: var(--app-surface-border);
overflow: hidden;
transition:
box-shadow 0.2s ease,
border-color 0.2s ease;
background: var(--app-surface-bg);
}
.el-card:hover {
box-shadow: var(--app-shadow-md);
border-color: var(--app-accent-soft);
}
.card-box {
margin-bottom: 10px;
}

View File

@@ -0,0 +1,76 @@
/* 工具类 */
.pt5 {
padding-top: 5px;
}
.pr5 {
padding-right: 5px;
}
.pl5 {
padding-left: 5px;
}
.pb5 {
padding-bottom: 5px;
}
.mt5 {
margin-top: 5px;
}
.mr5 {
margin-right: 5px;
}
.mb5 {
margin-bottom: 5px;
}
.mb8 {
margin-bottom: 8px;
}
.ml5 {
margin-left: 5px;
}
.mt10 {
margin-top: 10px;
}
.mr10 {
margin-right: 10px;
}
.mb10 {
margin-bottom: 10px;
}
.ml10 {
margin-left: 10px;
}
.mt20 {
margin-top: 20px;
}
.mr20 {
margin-right: 20px;
}
.mb20 {
margin-bottom: 20px;
}
.ml20 {
margin-left: 20px;
}
.el-dialog.scrollbar .el-dialog__body {
overflow: auto;
overflow-x: hidden;
max-height: 70vh;
padding: 14px 22px 4px;
}

View File

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

View File

@@ -0,0 +1,200 @@
/* Shared vertical page shell spacing helpers. */
.p-2 {
display: flex;
flex-direction: column;
gap: 12px;
}
.app-main > .p-2 {
padding: 0 !important;
margin: 0 !important;
}
.p-2 > .el-row,
.p-2 > .el-card,
.p-2 > .search-wrap,
.p-2 > div,
.p-2 > section {
margin-bottom: 0 !important;
}
// --- CRUD / 树表页用混入(在 .vue 内 @use 后 @include ---
@mixin action-link-buttons($background: rgba(53, 109, 255, 0.08)) {
.data-table {
:deep(.el-button.is-link) {
width: 32px;
height: 32px;
border-radius: 10px;
background: $background;
}
}
}
@mixin toolbar-responsive($mobile-breakpoint: 900px) {
@media (max-width: $mobile-breakpoint) {
.toolbar-shell {
align-items: flex-start;
}
}
}
@mixin content-stack($gap: 12px) {
.content-main {
display: flex;
flex-direction: column;
gap: $gap;
}
}
@mixin dept-tree-panel($padding-top: 6px) {
.dept-tree {
padding-top: $padding-top;
}
}
@mixin collapsible-tree-layout($mobile-breakpoint: 900px) {
.content-grid,
.selector-layout {
align-items: stretch;
}
.tree-panel-col,
.tree-content-col {
min-width: 0;
transition:
max-width 0.24s ease,
flex-basis 0.24s ease;
}
.tree-panel-col.is-collapsed {
max-width: 56px;
flex: 0 0 56px;
}
.tree-content-col.is-tree-collapsed {
max-width: calc(100% - 56px);
flex: 0 0 calc(100% - 56px);
}
.tree-panel-shell,
.side-panel {
height: 100%;
}
.tree-panel-shell {
--tree-panel-max-height: 620px;
}
.tree-panel-shell :deep(.el-card__header) {
display: block;
padding: 12px 16px !important;
}
.tree-panel-shell :deep(.el-card__body) {
display: flex;
flex-direction: column;
height: var(--tree-panel-max-height);
min-height: 0;
max-height: var(--tree-panel-max-height);
overflow: hidden;
}
.tree-panel-header {
cursor: pointer;
user-select: none;
}
.tree-panel-header::after {
display: block;
width: 9px;
min-width: 9px;
height: 9px;
margin-left: auto;
flex-shrink: 0;
transform-origin: center;
transform: rotate(135deg);
}
.tree-panel-header.is-collapsed {
justify-content: center;
}
.side-panel.is-collapsed :deep(.el-card__body) {
display: none;
}
.dept-tree,
.selector-tree {
flex: 1 1 auto;
min-height: 180px;
max-height: 100%;
overflow-y: auto;
overflow-x: hidden;
padding-right: 4px;
scrollbar-width: thin;
}
.dept-tree::-webkit-scrollbar,
.selector-tree::-webkit-scrollbar {
width: 6px;
}
.dept-tree::-webkit-scrollbar-thumb,
.selector-tree::-webkit-scrollbar-thumb {
border-radius: 999px;
background: var(--app-text-muted);
opacity: 0.55;
}
.dept-tree::-webkit-scrollbar-track,
.selector-tree::-webkit-scrollbar-track {
background: transparent;
}
.tree-panel-shell.is-collapsed :deep(.el-card__header) {
padding: 12px 0 !important;
}
.tree-panel-shell.is-collapsed .tree-panel-header::after {
margin-left: 0;
}
.tree-panel-shell.is-collapsed .tree-panel-header::after {
transform: rotate(-45deg);
}
@media (max-width: $mobile-breakpoint) {
.tree-panel-col,
.tree-content-col {
max-width: 100%;
flex: 0 0 100%;
}
.tree-panel-shell {
--tree-panel-max-height: 420px;
}
.side-panel.is-collapsed {
height: auto;
}
}
}
@mixin table-crud-page($mobile-breakpoint: 900px, $background: rgba(53, 109, 255, 0.08)) {
@include action-link-buttons($background);
@include toolbar-responsive($mobile-breakpoint);
}
@mixin tree-table-crud-page(
$mobile-breakpoint: 900px,
$gap: 12px,
$tree-padding-top: 6px,
$background: rgba(53, 109, 255, 0.08)
) {
@include content-stack($gap);
@include dept-tree-panel($tree-padding-top);
@include collapsible-tree-layout($mobile-breakpoint);
@include table-crud-page($mobile-breakpoint, $background);
}

View File

@@ -0,0 +1,89 @@
/* 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

@@ -0,0 +1,83 @@
/* 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

@@ -0,0 +1,79 @@
/* Reusable scoped mixins for selector dialogs backed by cards and vxe tables. */
/* 为多个根选择器统一设置 gap */
@mixin shell-gap($selectors...) {
@each $selector in $selectors {
#{$selector} {
gap: 12px;
}
}
}
/* 卡片区域撑满弹窗 */
@mixin card-shell {
.selector-card {
height: 100%;
}
}
/* 按弹窗根 class 收紧 body 顶内边距 */
@mixin dialog-body-padding($dialog-class) {
.#{$dialog-class} :deep(.el-dialog__body) {
padding-top: 12px;
}
}
/* 头部标题与已选 tag 换行、窄屏左对齐 */
@mixin selector-header-tags($mobile-breakpoint: 768px) {
.selector-header {
align-items: flex-start;
}
.selector-tags {
display: flex;
flex-wrap: wrap;
justify-content: flex-end;
gap: 6px;
max-width: min(100%, 520px);
}
.selector-tags :deep(.el-tag) {
margin: 0;
}
@media (max-width: $mobile-breakpoint) {
.selector-tags {
justify-content: flex-start;
max-width: 100%;
}
}
}
/* vxe 表格圆角与表头色,与全局表格 token 一致 */
@mixin selector-table {
.selector-table {
border-radius: 10px;
overflow: hidden;
}
.selector-table :deep(.vxe-table--render-default) {
border-radius: 10px;
color: var(--app-text-title);
}
.selector-table :deep(.vxe-header--column) {
background: var(--tableHeaderBg);
color: var(--tableHeaderTextColor);
font-weight: 600;
}
.selector-table :deep(.vxe-body--column),
.selector-table :deep(.vxe-header--column) {
border-color: var(--app-surface-border);
}
.selector-table :deep(.vxe-body--row.row--hover),
.selector-table :deep(.vxe-body--row:hover) {
background-color: rgba(53, 109, 255, 0.05);
}
}

View File

@@ -0,0 +1,84 @@
/* Table headers and toolbar action layouts. */
.top-right-btn {
margin-left: auto;
}
.panel-heading {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
}
.panel-heading h3,
.table-heading h3 {
margin: 0;
font-size: 15px;
letter-spacing: 0;
color: var(--app-text-title);
}
.panel-kicker {
display: none;
color: var(--app-accent-strong);
font-size: 11px;
font-weight: 600;
letter-spacing: 0.04em;
text-transform: uppercase;
}
.toolbar-shell {
display: flex;
align-items: center;
justify-content: space-between;
gap: 10px;
flex-wrap: wrap;
}
.table-heading p {
margin: 4px 0 0;
font-size: 13px;
color: var(--app-text-muted);
line-height: 1.5;
}
.toolbar-actions {
display: flex;
align-items: center;
flex-wrap: nowrap;
gap: 8px;
margin-left: auto;
white-space: nowrap;
overflow-x: auto;
overflow-y: hidden;
padding-bottom: 2px;
}
.toolbar-actions::-webkit-scrollbar {
height: 0;
}
.toolbar-actions > * {
flex-shrink: 0;
}
.toolbar-actions .top-right-btn {
margin-left: 0;
flex-shrink: 0;
}
.toolbar-actions .el-button + .el-button,
.toolbar-actions .el-dropdown + .el-button,
.toolbar-actions .el-button + .el-dropdown,
.toolbar-actions .el-dropdown + .el-dropdown,
.toolbar-actions .top-right-btn + .el-button,
.toolbar-actions .el-button + .top-right-btn,
.toolbar-actions .top-right-btn + .el-dropdown,
.toolbar-actions .el-dropdown + .top-right-btn {
margin-left: 0 !important;
}
.table-panel .toolbar-actions .el-button:not(.is-circle):not(.is-link) {
border-radius: 10px !important;
}

View File

@@ -0,0 +1,49 @@
/* Legacy theme helper classes and color accents. */
.el-button--cyan.is-active,
.el-button--cyan:active {
background: #20b2aa;
border-color: #20b2aa;
color: #ffffff;
}
.el-button--cyan:focus,
.el-button--cyan:hover {
background: #48d1cc;
border-color: #48d1cc;
color: #ffffff;
}
.el-button--cyan {
background-color: #20b2aa;
border-color: #20b2aa;
color: #ffffff;
}
.text-navy {
color: #1ab394;
}
.text-primary {
color: inherit;
}
.text-success {
color: #1c84c6;
}
.text-info {
color: #23c6c8;
}
.text-warning {
color: #f8ac59;
}
.text-danger {
color: #ed5565;
}
.text-muted {
color: #888888;
}

View File

@@ -1,277 +0,0 @@
.el-collapse {
.collapse__title {
font-weight: 600;
padding: 0 8px;
font-size: 1.2em;
line-height: 1.1em;
}
.el-collapse-item__content {
padding: 0 8px;
}
}
.el-divider--horizontal {
margin-bottom: 10px;
margin-top: 10px;
}
.el-breadcrumb__inner,
.el-breadcrumb__inner a {
font-weight: 400 !important;
}
.el-upload {
input[type='file'] {
display: none !important;
}
}
.el-upload__input {
display: none;
}
.cell {
.el-tag {
margin-right: 0px;
}
}
.small-padding {
.cell {
padding-left: 5px;
padding-right: 5px;
}
}
.fixed-width {
.el-button--mini {
padding: 7px 10px;
width: 60px;
}
}
.status-col {
.cell {
padding: 0 10px;
text-align: center;
.el-tag {
margin-right: 0px;
}
}
}
/*-------------Dialog-------------**/
.el-overlay {
overflow: hidden;
.el-overlay-dialog {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
.el-dialog {
margin: 0 auto !important;
border-radius: var(--app-radius-lg);
box-shadow: var(--app-shadow-md);
overflow: hidden;
.el-dialog__body {
padding: 15px !important;
}
.el-dialog__header {
padding: 16px 16px 8px 16px;
box-sizing: border-box;
border-bottom: 1px solid var(--brder-color);
margin-right: 0;
}
}
}
}
.el-dialog__body {
max-height: calc(90vh - 111px) !important;
overflow-y: auto;
overflow-x: hidden;
}
// refine element ui upload
.upload-container {
.el-upload {
width: 100%;
.el-upload-dragger {
width: 100%;
height: 200px;
}
}
}
// dropdown
.el-dropdown-menu {
border-radius: var(--app-radius-md);
box-shadow: var(--app-shadow-sm);
a {
display: block;
}
}
// fix date-picker ui bug in filter-item
.el-range-editor.el-input__inner {
display: inline-flex !important;
}
// to fix el-date-picker css style
.el-range-separator {
box-sizing: content-box;
}
.el-menu--collapse > div > .el-submenu > .el-submenu__title .el-submenu__icon-arrow {
display: none;
}
.el-dropdown .el-dropdown-link {
color: var(--el-color-primary) !important;
}
/* 当 el-form 的 inline 属性为 true 时 */
/* 设置 label 的宽度默认为 68px */
.el-form--inline .el-form-item__label {
width: 68px;
}
/* 设置 el-select 的宽度默认为 240px */
.el-form--inline .el-select {
width: 240px;
}
/* 设置 el-input 的宽度默认为 240px */
.el-form--inline .el-input {
width: 240px;
}
/* 设置 el-message-box 消息弹框内容强制换行 */
.el-message-box .el-message-box__message {
word-break: break-word;
}
.el-message-box {
border-radius: var(--app-radius-lg);
box-shadow: var(--app-shadow-md);
}
.el-message,
.el-notification,
.el-alert {
border-radius: var(--app-radius-md);
box-shadow: var(--app-shadow-sm);
}
// Modern rounded inputs
.el-input__wrapper,
.el-textarea__inner,
.el-select__wrapper,
.el-date-editor,
.el-range-editor,
.el-input-number,
.el-input-number__decrease,
.el-input-number__increase {
border-radius: var(--app-radius-md);
transition: box-shadow 0.2s ease, border-color 0.2s ease, background-color 0.2s ease;
}
.el-input__wrapper.is-focus,
.el-textarea__inner:focus,
.el-select__wrapper.is-focus,
.el-date-editor.is-focus,
.el-range-editor.is-focus {
box-shadow: 0 0 0 2px var(--el-color-primary-light-8);
}
// Buttons
.el-button {
border-radius: var(--app-radius-md);
transition: transform 0.15s ease, box-shadow 0.2s ease, background-color 0.2s ease, border-color 0.2s ease;
}
.el-button:not(.is-text):not(.is-link):hover {
transform: translateY(-1px);
box-shadow: var(--app-shadow-sm);
}
// Tags and badges
.el-tag {
border-radius: var(--app-radius-md);
}
// Cards, popovers, drawers
.el-popover,
.el-tooltip__popper,
.el-popper {
border-radius: var(--app-radius-md);
box-shadow: var(--app-shadow-sm);
}
.el-drawer {
border-radius: var(--app-radius-lg);
box-shadow: var(--app-shadow-md);
}
.el-drawer__header {
margin-bottom: 0;
padding: 16px 20px;
border-bottom: 1px solid var(--el-border-color-light);
}
// Table polish
.el-table {
border-radius: var(--app-radius-lg);
overflow: hidden;
box-shadow: var(--app-shadow-sm);
border: 1px solid var(--el-border-color-lighter);
}
.el-table__header-wrapper,
.el-table__body-wrapper {
background: var(--el-bg-color);
}
.el-table__row:hover td.el-table__cell {
background-color: var(--el-fill-color-light) !important;
}
// Tabs
.el-tabs__header {
margin: 0 0 12px 0;
}
.el-tabs__item {
border-radius: var(--app-radius-md);
margin: 0 2px;
}
.el-tabs__nav-wrap::after {
background-color: var(--el-border-color-lighter);
}
// Pagination
.el-pagination .btn-prev,
.el-pagination .btn-next,
.el-pagination .el-pager li {
border-radius: var(--app-radius-sm);
transition: background-color 0.2s ease, color 0.2s ease, box-shadow 0.2s ease;
}
.el-pagination .el-pager li.is-active {
box-shadow: var(--app-shadow-sm);
}
// Breadcrumb
.el-breadcrumb {
padding: 0;
background: transparent;
border: none;
border-radius: 0;
}

View File

@@ -1,226 +1,24 @@
// =============================================================================
// 全局样式入口(加载顺序即最终 CSS 层叠顺序,后载入的同优先级规则会覆盖先载入的)
// =============================================================================
@use './variables.module.scss' as *;
@use './mixin.scss';
@use './transition.scss';
@use './element-ui.scss';
@use './sidebar.scss';
@use './btn.scss';
@use './ruoyi.scss';
@use './base/document';
@use './layout/surface';
@use './layout/sidebar/shell' as sidebar-shell;
@use './layout/sidebar/menu';
@use './layout/sidebar/collapsed';
@use './layout/sidebar/responsive';
@use './layout/sidebar/popper' as sidebar-popper;
@use './components/legacy-utilities';
@use './components/misc';
@use './components/page-shell';
@use './components/card-shell';
@use './components/theme-helpers';
@use './components/table-toolbar';
@use './components/query-form';
@use './components/search-panel';
@use './vendors/element-plus/index' as element-custom;
@use 'animate.css';
@use 'element-plus/dist/index.css';
body {
height: 100%;
margin: 0;
-moz-osx-font-smoothing: grayscale;
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility;
background: var(--el-bg-color-page);
font-family:
'MiSans',
'HarmonyOS Sans SC',
'PingFang SC',
'Source Han Sans SC',
'Noto Sans SC',
'Hiragino Sans GB',
'Microsoft YaHei',
sans-serif;
}
label {
font-weight: 600;
}
html {
height: 100%;
box-sizing: border-box;
}
html.dark .svg-icon,
html.dark svg {
fill: var(--el-text-color-regular);
}
#app {
height: 100%;
}
*,
*:before,
*:after {
box-sizing: inherit;
}
.no-padding {
padding: 0px !important;
}
.padding-content {
padding: 4px 0;
}
a:focus,
a:active {
outline: none;
}
a,
a:focus,
a:hover {
cursor: pointer;
color: inherit;
text-decoration: none;
}
div:focus {
outline: none;
}
.fr {
float: right;
}
.fl {
float: left;
}
.pr-5 {
padding-right: 5px;
}
.pl-5 {
padding-left: 5px;
}
.block {
display: block;
}
.pointer {
cursor: pointer;
}
.inlineBlock {
display: block;
}
.clearfix {
&:after {
visibility: hidden;
display: block;
font-size: 0;
content: ' ';
clear: both;
height: 0;
}
}
aside {
background: #eef1f6;
padding: 8px 24px;
margin-bottom: 20px;
border-radius: var(--app-radius-md);
display: block;
line-height: 32px;
font-size: 16px;
font-family:
'MiSans',
'HarmonyOS Sans SC',
'PingFang SC',
'Source Han Sans SC',
'Noto Sans SC',
'Hiragino Sans GB',
'Microsoft YaHei',
sans-serif;
color: #2c3e50;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
a {
color: #337ab7;
cursor: pointer;
&:hover {
color: rgb(32, 160, 255);
}
}
}
//main-container全局样式
.app-container {
padding: 20px;
background: var(--app-surface-bg);
border: 1px solid var(--app-surface-border);
border-radius: var(--app-radius-lg);
box-shadow: var(--app-shadow-sm);
}
// search面板样式
.panel,
.search {
margin-bottom: 0.75rem;
border-radius: var(--app-radius-lg);
border: 1px solid var(--el-border-color-light);
background-color: var(--el-bg-color-overlay);
padding: 0.75rem;
box-shadow: 0 0 0 rgba(0, 0, 0, 0);
transition: box-shadow 0.2s ease, transform 0.2s ease, border-color 0.2s ease;
&:hover {
box-shadow: var(--app-shadow-sm);
border-color: var(--el-border-color);
transform: translateY(-1px);
}
}
.components-container {
margin: 30px 50px;
position: relative;
}
.text-center {
text-align: center;
}
.sub-navbar {
height: 50px;
line-height: 50px;
position: relative;
width: 100%;
text-align: right;
padding-right: 20px;
transition: 600ms ease position;
background: linear-gradient(90deg, rgba(32, 182, 249, 1) 0%, rgba(32, 182, 249, 1) 0%, rgba(33, 120, 241, 1) 100%, rgba(33, 120, 241, 1) 100%);
.subtitle {
font-size: 20px;
color: #fff;
}
&.draft {
background: #d0d0d0;
}
&.deleted {
background: #d0d0d0;
}
}
.link-type,
.link-type:focus {
color: #337ab7;
cursor: pointer;
&:hover {
color: rgb(32, 160, 255);
}
}
.filter-container {
padding-bottom: 10px;
.filter-item {
display: inline-block;
vertical-align: middle;
margin-bottom: 10px;
}
}

View File

@@ -0,0 +1,24 @@
/* 主内容区面板(.app-container、.panel、.search不依赖 Element 类名,与 components 中 EP 类补丁区分。 */
// --- 整块内容容器(常见于路由视图外包一层) ---
.app-container {
}
// --- 可复用面板条hover 抬升阴影,非 el-card ---
.panel,
.search {
margin-bottom: 0.75rem;
border-radius: var(--app-radius-base);
border: 1px solid var(--app-surface-border);
background-color: var(--app-surface-bg);
padding: 0.75rem;
box-shadow: 0 0 0 rgba(0, 0, 0, 0);
transition:
box-shadow 0.2s ease,
border-color 0.2s ease;
&:hover {
box-shadow: var(--app-shadow-sm);
border-color: rgba(64, 158, 255, 0.2);
}
}

View File

@@ -0,0 +1,141 @@
/* 桌面端折叠侧栏:窄宽、仅图标、子菜单箭头隐藏与 tooltip 触发区域(类名 .hideSidebar。 */
#app {
.hideSidebar {
.sidebar-container {
width: 58px !important;
}
.main-container {
margin-left: 70px;
}
.sidebar-shell {
padding-left: 4px;
padding-right: 4px;
}
.submenu-title-noDropdown {
padding: 0 !important;
position: relative;
height: 40px;
width: 40px !important;
min-width: 40px !important;
margin: 3px auto !important;
display: flex;
align-items: center;
justify-content: center !important;
box-sizing: border-box;
&.is-active {
background-color: var(--side-menu-active-bg) !important;
color: var(--side-menu-active-text) !important;
box-shadow: inset 3px 0 0 var(--side-menu-active-line);
}
.el-tooltip {
padding: 0 !important;
}
.el-menu-tooltip__trigger {
width: 100%;
height: 100%;
display: inline-flex !important;
align-items: center;
justify-content: center;
line-height: 1;
}
.svg-icon {
margin-right: 0 !important;
}
}
& .el-sub-menu {
overflow: hidden;
border-radius: var(--app-radius-md);
.el-sub-menu__title,
.el-sub-menu__title.el-tooltip__trigger {
border-radius: var(--app-radius-md);
height: 40px;
padding: 0 !important;
display: flex;
align-items: center;
justify-content: center !important;
line-height: 1;
}
&.is-active .el-sub-menu__title,
&.is-active .el-sub-menu__title.el-tooltip__trigger {
background-color: var(--side-menu-active-bg) !important;
box-shadow: inset 3px 0 0 var(--side-menu-active-line);
}
& > .el-sub-menu__title {
padding: 0 !important;
}
}
.el-menu--collapse {
> div > .el-sub-menu {
width: 40px !important;
min-width: 40px !important;
margin: 3px auto !important;
}
> div > .el-sub-menu > .el-sub-menu__title,
> div > .el-sub-menu > .el-sub-menu__title.el-tooltip__trigger {
width: 100% !important;
min-width: 100% !important;
margin: 0 !important;
box-sizing: border-box;
transform: none;
position: relative;
}
.is-active .svg-icon {
fill: currentColor;
}
> div > .el-sub-menu > .el-sub-menu__title .svg-icon {
position: absolute;
left: 50%;
top: 50%;
width: 16px;
height: 16px;
transform: translate(-50%, -50%);
display: block;
margin: 0 !important;
margin-right: 0 !important;
}
> div > .el-sub-menu > .el-sub-menu__title {
justify-content: center !important;
.el-sub-menu__icon-arrow {
display: none !important;
width: 0 !important;
margin: 0 !important;
overflow: hidden !important;
}
& > span {
height: 0;
width: 0;
overflow: hidden;
visibility: hidden;
display: inline-block;
}
& > i {
height: 0;
width: 0;
overflow: hidden;
visibility: hidden;
display: inline-block;
}
}
}
}
}

View File

@@ -0,0 +1,111 @@
/* 侧栏内 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

@@ -0,0 +1,22 @@
/* 折叠后弹出子菜单el-popper.is-pure圆角与弹出层内 menu 项样式。 */
.el-menu--vertical {
& > .el-menu {
.svg-icon {
margin-right: 16px;
}
}
}
.el-popper.is-pure {
border-radius: var(--app-radius-md);
box-shadow: var(--app-shadow-md);
.el-menu--popup {
border-radius: var(--app-radius-md);
}
.el-menu-item {
border-radius: var(--app-radius-sm);
}
}

View File

@@ -0,0 +1,31 @@
/* 移动端侧栏:抽屉式滑出、隐藏时 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

@@ -0,0 +1,132 @@
/* 侧栏外壳:固定定位宽度、主区左边距、滚动条与 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,3 +1,4 @@
// 清除浮动(嵌套用)
@mixin clearfix {
&:after {
content: '';
@@ -6,9 +7,10 @@
}
}
// WebKit 滚动条配色(用于可滚动容器)
@mixin scrollBar {
&::-webkit-scrollbar-track-piece {
background: #d3dce6;
background: var(--app-surface-border);
}
&::-webkit-scrollbar {
@@ -16,23 +18,26 @@
}
&::-webkit-scrollbar-thumb {
background: #99a9bf;
background: var(--app-text-muted);
border-radius: 20px;
}
}
// 铺满父级的相对定位层
@mixin relative {
position: relative;
width: 100%;
height: 100%;
}
// 百分比宽度水平居中块
@mixin pct($pct) {
width: #{$pct};
position: relative;
margin: 0 auto;
}
// CSS 三角箭头(极少用,保留兼容)
@mixin triangle($width, $height, $color, $direction) {
$width: $width/2;
$color-border-style: $height solid $color;

View File

@@ -1,305 +0,0 @@
@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: 6vh !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 #e5e6e7;
background: #ffffff none;
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, transform 0.2s ease;
}
.el-card:hover {
box-shadow: var(--app-shadow-md);
transform: translateY(-1px);
}
.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

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

@@ -0,0 +1,21 @@
/* 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

@@ -0,0 +1,42 @@
/* 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

@@ -0,0 +1,205 @@
/* 暗色主题:在 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

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

View File

@@ -1,285 +1,5 @@
// 全局SCSS变量
:root {
--menuBg: #1f2d3d;
--menuColor: #bfcbd9;
--menuActiveText: #f4f4f5;
--menuHover: #263445;
@forward './tokens/sass-vars';
--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-sm: 6px;
--app-radius-md: 10px;
--app-radius-lg: 14px;
--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: #1d1e1f;
--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;
}
@use './tokens/theme';
@use './tokens/theme-dark';
@use './tokens/exports';

View File

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

@@ -0,0 +1,109 @@
/* 弹窗:遮罩模糊、居中 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