112 Commits

Author SHA1 Message Date
疯狂的狮子Li
1b46739799 !241 发布 5.5.0-2.5.0 喜迎国庆
Merge pull request !241 from 疯狂的狮子Li/dev
2025-09-22 03:18:41 +00:00
疯狂的狮子Li
88056a5067 发布 5.5.0-2.5.0 喜迎国庆🧨🧨🧨 2025-09-22 11:16:38 +08:00
lau
3da18c9464 update 调整菜单栏收起时的样式 2025-09-09 20:11:27 +08:00
疯狂的狮子Li
b4a40c94dc update 优化 岗位页面查询权限问题 2025-09-03 14:14:53 +08:00
lau
c11b91a48e fix 修复选择审批人选择组件没有回显的问题 2025-08-29 19:05:24 +08:00
lau
65da8dfa93 fix 修复菜单栏有二级菜单和无二级菜单缩进不一致的问题 2025-08-29 17:51:29 +08:00
疯狂的狮子Li
e10ef50288 fix 修复 路由参数缓存导致分页错误 2025-08-29 17:37:45 +08:00
疯狂的狮子Li
4c607f6915 fix 修复 初始化用户选择组件 数据为空导致的问题 2025-08-29 11:38:14 +08:00
疯狂的狮子Li
43b4e74c9c update 优化 支持前端返回节点扩展数据(按钮权限 抄送人 扩展变量) 2025-08-28 17:55:21 +08:00
疯狂的狮子Li
f84e95d735 fix 修复 遗漏字段 2025-08-25 10:23:51 +08:00
疯狂的狮子Li
dba12f25e2 !239 update 代码生成预览增加高亮
Merge pull request !239 from Lau/dev
2025-08-20 14:27:16 +00:00
lau
257ececa52 update 代码生成预览增加高亮 2025-08-20 22:09:48 +08:00
疯狂的狮子Li
7d36621c44 !238 fix 修复手机号校验的正则表达式错误
Merge pull request !238 from undefined/dev
2025-08-19 10:11:52 +00:00
疯狂的狮子Li
a29d03b231 !237 update: tag和菜单栏样式调整,增加圆角和缩进
Merge pull request !237 from Lau/dev
2025-08-19 10:08:57 +00:00
疯狂的狮子Li
ab99104240 fix 修复 错误提交 2025-08-19 18:03:45 +08:00
ymj666
153758df82 fix 修复手机号校验的正则表达式错误 2025-08-19 17:00:49 +08:00
gssong
5e5fca8f6b add 增加业务扩展 2025-08-15 21:15:47 +08:00
lau
d23bf73a2e update: tag和菜单栏样式调整,增加圆角和缩进 2025-08-14 20:16:26 +08:00
疯狂的狮子Li
ad7058b739 !236 update 收起菜单时从展开列表中移除对应菜单
Merge pull request !236 from Lau/dev
2025-08-14 01:17:48 +00:00
lau
0dd5044bbe update 收起菜单时从展开列表中移除对应菜单 2025-08-14 09:07:35 +08:00
疯狂的狮子Li
ae5dd09ba2 remove 删除无用按钮 2025-08-12 16:10:32 +08:00
疯狂的狮子Li
0e20743c28 update transition enter 2025-08-11 10:36:33 +08:00
疯狂的狮子Li
9223fabde7 !233 fix 修复 流程实例页面deleteHisByInstanceIds函数未定义导致流程实例界面无法正常渲染
Merge pull request !233 from Lapwing/dev
2025-08-04 08:14:21 +00:00
midsumor
1282839f67 fix 修复 流程实例页面deleteHisByInstanceIds函数未定义导致流程实例界面无法正常渲染 2025-08-04 15:59:17 +08:00
gssong
952f56ca2e add 升级1.8增加钉钉设计器 2025-08-03 17:04:11 +08:00
gssong
e08f41dd72 update 升级1.8设计器 2025-08-03 16:41:55 +08:00
疯狂的狮子Li
2392f64233 update 优化 getBackTaskNode 获取驳回节点接口 如果是委派直接返回当前节点 不允许驳回到其他节点 2025-07-28 16:25:35 +08:00
may
1565ec1996 add 增加下一节点执行人是当前任务处理人自动审批
fix 修复已完成的实例删除失败
2025-07-27 10:45:33 +08:00
may
d95f358d1b update 调整变量修改 2025-07-25 23:20:03 +08:00
may
7ea5199fd2 add 增加变量修改 2025-07-25 23:03:43 +08:00
may
4280c7177d add 增加催办 2025-07-25 18:22:32 +08:00
疯狂的狮子Li
4472b24def update 优化 代码结构 2025-07-23 14:15:04 +08:00
疯狂的狮子Li
33cf333b2a !230 fix 修复菜单查询没有正确显示顶级菜单的问题
Merge pull request !230 from Lau/dev
2025-07-23 06:12:41 +00:00
lau
27a427eb97 fix 修复菜单查询没有正确显示顶级菜单的问题 2025-07-23 14:09:41 +08:00
疯狂的狮子Li
e2e1ce4091 !229 fix 修改暗黑模式样式无法覆盖element默认样式的问题
Merge pull request !229 from Lau/dev
2025-07-22 07:17:27 +00:00
lau
4013c06fea fix 修复修改暗黑模式样式无法覆盖element默认样式的问题 2025-07-17 20:57:17 +08:00
疯狂的狮子Li
8a029f6c4c update 优化 增加请求流程后端发起demo案例 2025-07-17 14:32:54 +08:00
疯狂的狮子Li
c785a9fb7f update 优化 增加请求流程后端发起demo案例 2025-07-17 14:28:45 +08:00
疯狂的狮子Li
95cbd2f3af update 优化 将请假申请流程选择框直接放到表单内 减少弹窗 2025-07-17 13:16:12 +08:00
疯狂的狮子Li
edacb79ccb update 优化 删除后端已经不存在的接口 2025-07-15 14:07:24 +08:00
疯狂的狮子Li
0872624adc update 优化 roleOptions 去重处理 2025-07-14 11:37:51 +08:00
疯狂的狮子Li
1bf03053e1 update 优化 sse重试改为5次 避免掉线频繁连接 2025-07-07 15:36:07 +08:00
疯狂的狮子Li
47c2724058 update 优化 删除无用接口 2025-07-07 15:25:01 +08:00
疯狂的狮子Li
219ab65eb7 fix 修复 流程表达式页面权限标识符错误 2025-07-07 15:22:55 +08:00
疯狂的狮子Li
107b2d444b !227 更新 pr!226 页面展示
Merge pull request !227 from MichelleChung/dev
2025-07-06 06:02:40 +00:00
Michelle.Chung
a8bb81c984 update: 更新流程spel页面展示 ; 2025-07-06 13:51:26 +08:00
疯狂的狮子Li
0ca453d549 update 优化 流程表达式页面 2025-07-06 11:47:34 +08:00
疯狂的狮子Li
093c05bda0 fix 修复 提交流程报错 loading未关闭问题 2025-07-06 11:33:18 +08:00
疯狂的狮子Li
94dcc28c8a !226 add: 新增流程spel表达式相关菜单 ;
Merge pull request !226 from MichelleChung/dev
2025-07-06 02:54:15 +00:00
Michelle.Chung
35b016b3ba add: 新增流程spel表达式相关菜单 ; 2025-07-05 19:11:46 +08:00
疯狂的狮子Li
f6d69e2bea update 优化 用户编辑页面展示逻辑 2025-07-04 14:51:56 +08:00
疯狂的狮子Li
9573343afc !225 fix: 菜单级联删除添加按钮权限
Merge pull request !225 from 有梦的人/dev
2025-07-04 04:38:41 +00:00
有梦的人
8f99c76e72 fix: 菜单级联删除添加按钮权限 2025-07-04 12:29:01 +08:00
疯狂的狮子Li
b000788785 !222 发布 5.4.1-2.4.1 小步迭代修复问题
Merge pull request !222 from 疯狂的狮子Li/dev
2025-07-01 01:13:49 +00:00
疯狂的狮子Li
62f7d393f3 🐳🐳🐳发布 5.4.1-2.4.1 小步迭代修复问题 2025-07-01 09:11:36 +08:00
疯狂的狮子Li
31037db627 update 优化 访问流程图页面缓存问题 参数增加时间戳 临时解决 2025-06-27 16:38:56 +08:00
疯狂的狮子Li
4e0d946676 update 优化 删除后端不存在的搜索条件 2025-06-27 16:25:14 +08:00
疯狂的狮子Li
71dceeacc2 update 优化 删除展开折叠按钮 菜单数据量大的清空下 展开会导致页面卡顿问题(在懒加载数据的清空下这个功能不推荐使用了) 2025-06-24 11:08:10 +08:00
疯狂的狮子Li
d59259737f !219 fix 修复菜单改为懒加载后,修改数据没有刷新的问题
Merge pull request !219 from Lau/dev
2025-06-24 02:14:01 +00:00
lau
8afe7c3931 fix: 修复菜单改为懒加载后,修改数据没有刷新的问题 2025-06-24 09:25:13 +08:00
疯狂的狮子Li
d59738b473 !217 update: 优化菜单页面渲染方式避免长时间卡住
Merge pull request !217 from Lau/dev
2025-06-23 08:55:19 +00:00
lau
2f35342782 fix: 修复菜单管理改为懒加载后展开/折叠只能展开一级菜单的问题 2025-06-23 16:11:58 +08:00
lau
720c822bb3 update: 优化菜单页面渲染方式避免长时间卡住 2025-06-22 21:25:05 +08:00
疯狂的狮子Li
48b5d595df fix 修复 从无缓存页面切换到有缓存页面 缓存失效问题 2025-06-16 13:49:24 +08:00
疯狂的狮子Li
1034399fe4 update 优化 租户套餐菜单查询过滤掉 租户管理相关菜单 2025-06-05 18:28:19 +08:00
疯狂的狮子Li
ba257e2357 fix 修复 提交组件变量名使用错误 2025-06-04 09:57:25 +08:00
疯狂的狮子Li
8bd26758dd !214 fix: 修复父组件中UserSelect回调处理逻辑,解决取消选择后参数未正确处理的问题
Merge pull request !214 from burningimlam/dev
2025-06-03 10:00:26 +00:00
imlam
1f1cd489be fix: 修复父组件中UserSelect回调处理逻辑,解决取消选择后参数未正确处理的问题 2025-06-03 17:26:56 +08:00
疯狂的狮子Li
2dc094c1db !212 发布 5.4.0-2.4.0 正式版
Merge pull request !212 from 疯狂的狮子Li/dev
2025-05-29 03:18:30 +00:00
疯狂的狮子Li
9c528d9a8c Merge branch 'ts' of gitee.com:JavaLionLi/plus-ui into dev
Signed-off-by: 疯狂的狮子Li <15040126243@163.com>
2025-05-29 03:18:17 +00:00
疯狂的狮子Li
0472b823e7 🐳🐳🐳发布 5.4.0-2.4.0 正式版 2025-05-29 11:16:20 +08:00
疯狂的狮子Li
f71cf3cfb4 !210 update 优化用户管理查询条件
Merge pull request !210 from AprilWind/dev
2025-05-29 02:32:42 +00:00
AprilWind
8179ee8196 update 优化用户管理查询条件 2025-05-29 09:56:22 +08:00
gssong
8b8099ad09 update 调整流程图渲染样式 2025-05-27 22:28:28 +08:00
gssong
fd30362267 remove 删除无用代码 2025-05-27 22:23:18 +08:00
gssong
1878f49e8d update 调整查询流程图渲染空指针错误,优化流程图样式 2025-05-27 22:20:30 +08:00
疯狂的狮子Li
ca0fe5ebae update 删除logicflow依赖与文件 直接使用warmflow自带的页面 2025-05-27 17:14:28 +08:00
疯狂的狮子Li
ba78f8cc0d !209 新增通过前端显示流程图方式和新增办理人转换接口
Merge pull request !209 from 晓华/dev
2025-05-27 09:02:28 +00:00
warm
a614dee5c6 feat 新增通过前端显示流程图方式
feat 办理人权限处理器,新增办理人转换接口,比如角色转用户
update 升级warm-flow-1.7.3-m1
2025-05-27 16:58:18 +08:00
疯狂的狮子Li
463faba9b9 update 优化 替换过期的写法 2025-05-27 14:52:10 +08:00
疯狂的狮子Li
9dea8369e3 fix 修复 菜单取消按钮不工作问题 2025-05-27 14:47:22 +08:00
疯狂的狮子Li
592fb84aa7 update 优化 添加页签图标显示开关功能 2025-05-27 14:13:45 +08:00
gssong
3019701856 add 增加logicflow流程图预览 2025-05-25 11:47:09 +08:00
gssong
1ea70dd3ce update 表格增加border 2025-05-24 13:13:45 +08:00
gssong
2a5ad70155 update 修改菜单级联删除弹窗无法取消 2025-05-24 13:06:04 +08:00
疯狂的狮子Li
9c8e3404bb !207 feat 新增批量级联删除菜单接口
Merge pull request !207 from 马铃薯头/dev
2025-05-23 10:04:52 +00:00
xlsea
385bbb77a9 feat 新增批量级联删除菜单接口 2025-05-23 14:03:03 +08:00
疯狂的狮子Li
70f7c06e55 update 优化 动态路由迁移到菜单管理 2025-05-22 18:15:02 +08:00
gssong
369f48ced5 update 优化审批按钮,封装成公共组件 2025-05-16 21:00:48 +08:00
疯狂的狮子Li
7f15f0e15a update 优化 执行eslint:fix优化代码 2025-05-15 14:52:41 +08:00
疯狂的狮子Li
7b48bd44a2 !203 修改navbar中消息图标样式与同行元素保持一致
Merge pull request !203 from 愿丶/dev
2025-05-12 02:03:58 +00:00
疯狂的狮子Li
7affcd27b7 update 更新 readme 增加新成员项目 2025-05-12 09:34:40 +08:00
疯狂的狮子Li
7de9f23226 update README.md.
Signed-off-by: 疯狂的狮子Li <15040126243@163.com>
2025-05-08 14:24:37 +00:00
疯狂的狮子Li
afc35feb8c update 更新 readme 2025-05-08 22:11:04 +08:00
愿丶
84d682a4a2 update 修改navbar中消息图标样式与同行元素保持一致
Signed-off-by: 愿丶 <1319542051@qq.com>
2025-04-29 13:06:34 +00:00
gssong
b29c5bd2fa update 放开申请人附件与抄送限制 附件改为按钮权限控制 2025-04-26 12:52:52 +08:00
疯狂的狮子Li
e4c24a511a !202 [任务]: 工作流分类与流程设计新增联动
Merge pull request !202 from MoMyles/dev
2025-04-24 03:52:05 +00:00
疯狂的狮子Li
9955a52059 update 优化 增加oss站点与域名 默认前缀避免填错 2025-04-24 11:36:51 +08:00
疯狂的狮子Li
de22609196 update 升级全部依赖版本
update element-plus 2.9.8
update pinia 3.0.2
update vue-router 4.5.0
update vue-types 6.0.0
update vxe-table 4.13.7
update sass 1.87.0
update typescript 5.8.3
update vite 6.3.2
........等等很多版本
2025-04-22 15:23:36 +08:00
疯狂的狮子Li
e5de3f4e9d update 优化代码 2025-04-22 13:32:15 +08:00
疯狂的狮子Li
660d5b3d4f update 优化 关于excel说明 2025-04-18 17:47:55 +08:00
疯狂的狮子Li
d5eac17097 update 优化 角色禁用不允许分配 2025-04-17 15:19:58 +08:00
疯狂的狮子Li
6fe2317681 update vite 5.4.11 => 5.4.18 2025-04-16 15:28:14 +08:00
Myles
e9e8a2eaaf 优化赋值 2025-04-14 13:14:57 +08:00
疯狂的狮子Li
5ec984ac7d update 增加 流程变量注释 2025-04-11 15:32:27 +08:00
Myles
c98a14e2ac if添加括号 2025-04-10 15:41:30 +08:00
Myles
bbc656a26c [任务]: 工作流分类与流程设计新增联动 2025-04-10 11:33:12 +08:00
疯狂的狮子Li
722acf0ae7 update 优化 删除无用组件 2025-04-07 16:22:04 +08:00
疯狂的狮子Li
15acd995f9 fix 修复 请假时间 时间组件没法和rule规则联动问题(ele的bug手动设置必填) 2025-04-02 14:45:23 +08:00
疯狂的狮子Li
35b90aa746 fix 修复 请假提交未取消按钮loading问题 2025-04-02 14:37:32 +08:00
疯狂的狮子Li
de926211ef update 优化 登出之后清理tabs 2025-03-31 09:40:09 +08:00
疯狂的狮子Li
30e1ea1c6d fix 修复 前端download方法响应json异常问题 2025-03-28 22:51:01 +08:00
103 changed files with 1776 additions and 848 deletions

View File

@@ -315,6 +315,9 @@
"watchThrottled": true,
"watchTriggerable": true,
"watchWithFilter": true,
"whenever": true
"whenever": true,
"Slot": true,
"Slots": true,
"createRef": true
}
}

View File

@@ -2,16 +2,23 @@
- 本仓库为前端技术栈 [Vue3](https://v3.cn.vuejs.org) + [TS](https://www.typescriptlang.org/) + [Element Plus](https://element-plus.org/zh-CN) + [Vite](https://cn.vitejs.dev) 版本。
- 成员项目: 基于 vben5(ant-design-vue) 的前端项目 [ruoyi-plus-vben5](https://gitee.com/dapppp/ruoyi-plus-vben5)
- 配套后端代码仓库地址
- [RuoYi-Vue-Plus 5.X(注意版本号)](https://gitee.com/dromara/RuoYi-Vue-Plus)
- [RuoYi-Cloud-Plus 2.X(注意版本号)](https://gitee.com/dromara/RuoYi-Cloud-Plus)
- 成员项目: 基于soybean 的前端项目 [ruoyi-plus-soybean](https://gitee.com/xlsea/ruoyi-plus-soybean)
## 配套后端代码仓库地址
| 介绍 | 项目名 | 项目地址 |
|------------|:-----------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 🔥 分布式集群框架 | RuoYi-Vue-Plus | - [Gitee](https://gitee.com/dromara/RuoYi-Vue-Plus)<br> - [GitHub](https://github.com/dromara/RuoYi-Vue-Plus)<br> - [GitCode](https://gitcode.com/dromara/RuoYi-Vue-Plus) |
| 🔥 微服务框架 | RuoYi-Cloud-Plus | - [Gitee](https://gitee.com/dromara/RuoYi-Cloud-Plus)<br>- [GitHub](https://github.com/dromara/RuoYi-Cloud-Plus)<br> - [GitCode](https://gitcode.com/dromara/RuoYi-Cloud-Plus) |
## 分支说明
- ts分支(稳定发布主分支 生产可用)
- dev分支(开发分支 开发过程中使用)
## 前端运行
```bash
# 克隆项目
git clone https://gitee.com/JavaLionLi/plus-ui.git
# 安装依赖
npm install --registry=https://registry.npmmirror.com

View File

@@ -1,7 +1,7 @@
{
"$schema": "https://json.schemastore.org/package",
"name": "ruoyi-vue-plus",
"version": "5.3.1-2.3.0",
"version": "5.5.0-2.5.0",
"description": "RuoYi-Vue-Plus多租户管理系统",
"author": "LionLi",
"license": "MIT",
@@ -23,31 +23,31 @@
"@element-plus/icons-vue": "2.3.1",
"@highlightjs/vue-plugin": "2.1.0",
"@vueup/vue-quill": "1.2.0",
"@vueuse/core": "12.7.0",
"@vueuse/core": "13.1.0",
"animate.css": "4.1.1",
"await-to-js": "3.0.0",
"axios": "1.7.8",
"axios": "1.8.4",
"crypto-js": "4.2.0",
"echarts": "5.5.0",
"element-plus": "2.8.8",
"echarts": "5.6.0",
"element-plus": "2.9.8",
"file-saver": "2.0.5",
"highlight.js": "11.9.0",
"image-conversion": "2.1.1",
"js-cookie": "3.0.5",
"jsencrypt": "3.3.2",
"nprogress": "0.2.0",
"pinia": "2.2.6",
"pinia": "3.0.2",
"screenfull": "6.0.2",
"vue": "3.5.13",
"vue-cropper": "1.1.1",
"vue-i18n": "10.0.5",
"vue-i18n": "11.1.3",
"vue-json-pretty": "2.4.0",
"vue-router": "4.4.5",
"vue-types": "5.1.3",
"vxe-table": "4.5.22"
"vue-router": "4.5.0",
"vue-types": "6.0.0",
"vxe-table": "4.13.7"
},
"devDependencies": {
"@iconify/json": "2.2.276",
"@iconify/json": "^2.2.276",
"@types/crypto-js": "4.2.2",
"@types/file-saver": "2.0.7",
"@types/js-cookie": "3.0.6",
@@ -56,8 +56,8 @@
"@unocss/preset-attributify": "66.0.0",
"@unocss/preset-icons": "66.0.0",
"@unocss/preset-uno": "66.0.0",
"@vitejs/plugin-vue": "5.2.1",
"@vue/compiler-sfc": "3.4.23",
"@vitejs/plugin-vue": "5.2.3",
"@vue/compiler-sfc": "3.5.13",
"@vue/eslint-config-prettier": "10.2.0",
"@vue/eslint-config-typescript": "14.4.0",
"autoprefixer": "10.4.20",
@@ -66,19 +66,22 @@
"eslint-plugin-vue": "9.32.0",
"globals": "16.0.0",
"prettier": "3.5.2",
"sass": "1.84.0",
"typescript": "~5.7.3",
"sass": "1.87.0",
"typescript": "~5.8.3",
"unocss": "66.0.0",
"unplugin-auto-import": "0.17.5",
"unplugin-icons": "0.18.5",
"unplugin-vue-components": "28.0.0",
"unplugin-auto-import": "19.1.2",
"unplugin-icons": "22.1.0",
"unplugin-vue-components": "28.5.0",
"unplugin-vue-setup-extend-plus": "1.0.1",
"vite": "5.4.11",
"vite": "6.3.2",
"vite-plugin-compression": "0.5.1",
"vite-plugin-svg-icons-ng": "^1.2.2",
"vite-plugin-vue-devtools": "7.7.1",
"vitest": "3.0.5",
"vue-tsc": "^2.2.2"
"vite-plugin-svg-icons-ng": "^1.4.0",
"vite-plugin-vue-devtools": "7.7.5",
"vitest": "3.1.2",
"vue-tsc": "^2.2.8"
},
"overrides": {
"quill": "2.0.2"
},
"engines": {
"node": ">=18.18.0",

View File

@@ -1,6 +1,6 @@
import request from '@/utils/request';
import { AxiosPromise } from 'axios';
import {DeptForm, DeptQuery, DeptTreeVO, DeptVO} from './types';
import { DeptForm, DeptQuery, DeptTreeVO, DeptVO } from './types';
// 查询部门列表
export const listDept = (query?: DeptQuery) => {
@@ -38,14 +38,6 @@ export const getDept = (deptId: string | number): AxiosPromise<DeptVO> => {
});
};
// 查询部门下拉树结构
export const treeselect = (): AxiosPromise<DeptTreeVO[]> => {
return request({
url: '/system/dept/treeselect',
method: 'get'
});
};
// 新增部门
export const addDept = (data: DeptForm) => {
return request({

View File

@@ -68,3 +68,11 @@ export const delMenu = (menuId: string | number) => {
method: 'delete'
});
};
// 级联删除菜单
export const cascadeDelMenu = (menuIds: Array<string | number>) => {
return request({
url: '/system/menu/cascade/' + menuIds,
method: 'delete'
});
};

View File

@@ -1,6 +1,7 @@
import request from '@/utils/request';
import { PostForm, PostQuery, PostVO } from './types';
import { AxiosPromise } from 'axios';
import { DeptTreeVO } from '../dept/types';
// 查询岗位列表
export function listPost(query: PostQuery): AxiosPromise<PostVO[]> {
@@ -56,3 +57,13 @@ export function delPost(postId: string | number | (string | number)[]) {
method: 'delete'
});
}
/**
* 查询部门下拉树结构
*/
export const deptTreeSelect = (): AxiosPromise<DeptTreeVO[]> => {
return request({
url: '/system/post/deptTree',
method: 'get'
});
};

View File

@@ -1,4 +1,4 @@
import {DeptTreeVO, DeptVO} from './../dept/types';
import { DeptTreeVO } from './../dept/types';
import { RoleVO } from '@/api/system/role/types';
import request from '@/utils/request';
import { AxiosPromise } from 'axios';

View File

@@ -15,11 +15,12 @@ export interface UserInfo {
*/
export interface UserQuery extends PageQuery {
userName?: string;
nickName?: string;
phonenumber?: string;
status?: string;
deptId?: string | number;
roleId?: string | number;
userIds?: string;
userIds?: string | number | (string | number)[] | undefined;
}
/**

View File

@@ -22,7 +22,10 @@ export interface FlowDefinitionForm {
flowName: string;
flowCode: string;
category: string;
ext: string;
formPath: string;
formCustom: string;
modelValue: string;
}
export interface definitionXmlVO {

View File

@@ -31,9 +31,9 @@ export const pageByFinish = (query: FlowInstanceQuery): AxiosPromise<FlowInstanc
/**
* 通过业务id获取历史流程图
*/
export const flowImage = (businessId: string | number) => {
export const flowHisTaskList = (businessId: string | number) => {
return request({
url: `/workflow/instance/flowImage/${businessId}` + '?t' + Math.random(),
url: `/workflow/instance/flowHisTaskList/${businessId}` + '?t' + Math.random(),
method: 'get'
});
};
@@ -87,6 +87,18 @@ export const deleteByInstanceIds = (instanceIds: Array<string | number> | string
method: 'delete'
});
};
/**
* 删除历史流程实例
* @param instanceIds
*/
export const deleteHisByInstanceIds = (instanceIds: Array<string | number> | string | number) => {
return request({
url: `/workflow/instance/deleteHisByInstanceIds/${instanceIds}`,
method: 'delete'
});
};
/**
* 作废流程
* @param data 参数
@@ -99,3 +111,15 @@ export const invalid = (data: any) => {
data: data
});
};
/**
* 修改流程变量
* @param data 参数
* @returns
*/
export const updateVariable = (data: any) => {
return request({
url: `/workflow/instance/updateVariable`,
method: 'put',
data: data
});
};

View File

@@ -23,4 +23,6 @@ export interface FlowInstanceVO extends BaseEntity {
flowStatus: string;
flowStatusName: string;
flowTaskList: FlowTaskVO[];
businessCode: string;
businessTitle: string;
}

View File

@@ -39,6 +39,18 @@ export const addLeave = (data: LeaveForm): AxiosPromise<LeaveVO> => {
});
};
/**
* 提交请假并发起流程
* @param data
*/
export const submitAndFlowStart = (data: LeaveForm): AxiosPromise<LeaveVO> => {
return request({
url: '/workflow/leave/submitAndFlowStart',
method: 'post',
data: data
});
};
/**
* 修改请假
* @param data

View File

@@ -1,5 +1,6 @@
export interface LeaveVO {
id: string | number;
applyCode?: string;
leaveType: string;
startDate: string;
endDate: string;
@@ -10,6 +11,7 @@ export interface LeaveVO {
export interface LeaveForm extends BaseEntity {
id?: string | number;
applyCode?: string;
leaveType?: string;
startDate?: string;
endDate?: string;

View File

@@ -0,0 +1,63 @@
import request from '@/utils/request';
import { AxiosPromise } from 'axios';
import { SpelVO, SpelForm, SpelQuery } from '@/api/workflow/spel/types';
/**
* 查询流程spel表达式定义列表
* @param query
* @returns {*}
*/
export const listSpel = (query?: SpelQuery): AxiosPromise<SpelVO[]> => {
return request({
url: '/workflow/spel/list',
method: 'get',
params: query
});
};
/**
* 查询流程spel表达式定义详细
* @param id
*/
export const getSpel = (id: string | number): AxiosPromise<SpelVO> => {
return request({
url: '/workflow/spel/' + id,
method: 'get'
});
};
/**
* 新增流程spel表达式定义
* @param data
*/
export const addSpel = (data: SpelForm) => {
return request({
url: '/workflow/spel',
method: 'post',
data: data
});
};
/**
* 修改流程spel表达式定义
* @param data
*/
export const updateSpel = (data: SpelForm) => {
return request({
url: '/workflow/spel',
method: 'put',
data: data
});
};
/**
* 删除流程spel表达式定义
* @param id
*/
export const delSpel = (id: string | number | Array<string | number>) => {
return request({
url: '/workflow/spel/' + id,
method: 'delete'
});
};

View File

@@ -0,0 +1,111 @@
export interface SpelVO {
/**
* 主键id
*/
id: string | number;
/**
* 组件名称
*/
componentName: string;
/**
* 方法名
*/
methodName: string;
/**
* 参数
*/
methodParams: string;
/**
* 预览spel值
*/
viewSpel: string;
/**
* 状态0正常 1停用
*/
status: string;
/**
* 备注
*/
remark?: string;
}
export interface SpelForm extends BaseEntity {
/**
* 主键id
*/
id?: string | number;
/**
* 组件名称
*/
componentName?: string;
/**
* 方法名
*/
methodName?: string;
/**
* 参数
*/
methodParams?: string;
/**
* 预览spel值
*/
viewSpel?: string;
/**
* 状态0正常 1停用
*/
status?: string;
/**
* 备注
*/
remark?: string;
}
export interface SpelQuery extends PageQuery {
/**
* 组件名称
*/
componentName?: string;
/**
* 方法名
*/
methodName?: string;
/**
* 参数
*/
methodParams?: string;
/**
* 预览spel值
*/
viewSpel?: string;
/**
* 状态0正常 1停用
*/
status?: string;
/**
* 日期范围参数
*/
params?: any;
}

View File

@@ -148,9 +148,9 @@ export const terminationTask = (data: any) => {
* 获取可驳回得任务节点
* @returns
*/
export const getBackTaskNode = (definitionId: string, nodeCode: string) => {
export const getBackTaskNode = (taskId: string | number, nodeCode: string) => {
return request({
url: `/workflow/task/getBackTaskNode/${definitionId}/${nodeCode}`,
url: `/workflow/task/getBackTaskNode/${taskId}/${nodeCode}`,
method: 'get'
});
};
@@ -191,3 +191,16 @@ export const getNextNodeList = (data: any): any => {
data: data
});
};
/**
* 催办任务
* @param data参数
* @returns
*/
export const urgeTask = (data: any): any => {
return request({
url: '/workflow/task/urgeTask',
method: 'post',
data: data
});
};

View File

@@ -30,16 +30,20 @@ export interface FlowTaskVO {
nodeRatio: string | number;
version?: string;
applyNode?: boolean;
buttonList?: buttonList[];
buttonList?: ButtonList[];
copyList?: FlowCopyVo[];
varList?: Map<string, string>;
businessCode: string;
businessTitle: string;
}
export interface buttonList {
export interface ButtonList {
code: string;
show: boolean;
}
export interface VariableVo {
key: string;
value: string;
export interface FlowCopyVo {
userId: string | number;
userName: string;
}
export interface TaskOperationBo {

View File

@@ -10,4 +10,5 @@ export interface StartProcessBo {
businessId: string | number;
flowCode: string;
variables: any;
flowInstanceBizExtBo: any;
}

View File

@@ -26,7 +26,7 @@
z-index: 1001;
overflow: hidden;
-webkit-box-shadow: 2px 0 6px rgba(0, 21, 41, 0.35);
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 {
@@ -65,7 +65,7 @@
}
.svg-icon {
margin-right: 16px;
margin-right: 10px;
}
.el-menu {
@@ -88,12 +88,16 @@
// menu hover
.theme-dark .sub-menu-title-noDropdown,
.theme-dark .el-sub-menu__title {
border-radius: 8px;
margin: 1px 5px 1px 5px;
&:hover {
background-color: $base-sub-menu-title-hover !important;
}
}
.sub-menu-title-noDropdown,
.el-sub-menu__title {
border-radius: 8px;
margin: 1px 5px 1px 5px;
&:hover {
background-color: rgba(0, 0, 0, 0.05) !important;
}
@@ -105,8 +109,11 @@
& .nest-menu .el-sub-menu > .el-sub-menu__title,
& .el-sub-menu .el-menu-item {
min-width: $base-sidebar-width !important;
&:hover {
min-width: calc($base-sidebar-width - 20px) !important;
border-radius: 8px;
height: 45px;
margin: 1px 5px 1px 5px;
&:not(.is-active):hover {
background-color: rgba(0, 0, 0, 0.1) !important;
}
}
@@ -114,28 +121,54 @@
& .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: 8px;
height: 45px;
margin: 1px 5px 1px 5px;
&:hover {
&.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 {
&:hover {
border-radius: 8px;
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 {
&:hover {
border-radius: 8px;
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;
@@ -148,29 +181,48 @@
.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;
.svg-icon {
margin-left: 20px;
}
}
}
.el-sub-menu {
& .el-sub-menu {
overflow: hidden;
border-radius: 8px;
.el-sub-menu__title.el-tooltip__trigger {
border-radius: 8px;
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;
.svg-icon {
margin-left: 20px;
}
}
}
.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 {
@@ -232,3 +284,13 @@
}
}
}
// 收起菜单后悬浮的菜单样式
.el-popper.is-pure{
border-radius: 8px;
.el-menu--popup{
border-radius: 8px;
}
.el-menu-item{
border-radius: 4px;
}
}

View File

@@ -6,7 +6,7 @@
transition: opacity 0.28s;
}
.fade-enter,
.fade-enter-from,
.fade-leave-active {
opacity: 0;
}
@@ -18,7 +18,7 @@
transition: all 0.5s;
}
.fade-transform-enter {
.fade-transform-enter-from {
opacity: 0;
transform: translateX(-30px);
}
@@ -34,7 +34,7 @@
transition: all 0.5s;
}
.breadcrumb-enter,
.breadcrumb-enter-from,
.breadcrumb-leave-active {
opacity: 0;
transform: translateX(20px);

View File

@@ -1,6 +1,6 @@
// 全局SCSS变量
:root {
--menuBg: #304156;
--menuBg: #1f2d3d;
--menuColor: #bfcbd9;
--menuActiveText: #f4f4f5;
--menuHover: #263445;
@@ -10,6 +10,11 @@
--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;

View File

@@ -44,7 +44,7 @@ const findPathNum = (str, char = '/') => {
return str.split(char).length - 1;
};
const getMatched = (pathList, routeList, matched) => {
let data = routeList.find((item) => item.path == pathList[0] || (item.name += '').toLowerCase() == pathList[0]);
const data = routeList.find((item) => item.path == pathList[0] || (item.name += '').toLowerCase() == pathList[0]);
if (data) {
matched.push(data);
if (data.children && pathList.length) {

View File

@@ -95,7 +95,7 @@ const options = ref<any>({
});
const styles = computed(() => {
let style: any = {};
const style: any = {};
if (props.minHeight) {
style.minHeight = `${props.minHeight}px`;
}
@@ -121,9 +121,9 @@ const handleUploadSuccess = (res: any) => {
// 如果上传成功
if (res.code === 200) {
// 获取富文本实例
let quill = toRaw(quillEditorRef.value).getQuill();
const quill = toRaw(quillEditorRef.value).getQuill();
// 获取光标位置
let length = quill.selection.savedRange.index;
const length = quill.selection.savedRange.index;
// 插入图片res为服务器返回的图片链接地址
quill.insertEmbed(length, 'image', res.data.url);
// 调整光标到最后

View File

@@ -176,7 +176,7 @@ const handleUploadSuccess = (res: any, file: UploadFile) => {
// 删除文件
const handleDelete = (index: number) => {
let ossId = fileList.value[index].ossId;
const ossId = fileList.value[index].ossId;
delOss(ossId);
fileList.value.splice(index, 1);
emit('update:modelValue', listToString(fileList.value));

View File

@@ -27,7 +27,7 @@ const realSrc = computed(() => {
if (!props.src) {
return;
}
let real_src = props.src.split(',')[0];
const real_src = props.src.split(',')[0];
return real_src;
});
@@ -35,8 +35,8 @@ const realSrcList = computed(() => {
if (!props.src) {
return [];
}
let real_src_list = props.src.split(',');
let srcList: string[] = [];
const real_src_list = props.src.split(',');
const srcList: string[] = [];
real_src_list.forEach((item: string) => {
if (item.trim() === '') {
return;

View File

@@ -189,7 +189,7 @@ const handleUploadSuccess = (res: any, file: UploadFile) => {
const handleDelete = (file: UploadFile): boolean => {
const findex = fileList.value.map((f) => f.name).indexOf(file.name);
if (findex > -1 && uploadList.value.length === number.value) {
let ossId = fileList.value[findex].ossId;
const ossId = fileList.value[findex].ossId;
delOss(ossId);
fileList.value.splice(findex, 1);
emit('update:modelValue', listToString(fileList.value));
@@ -225,7 +225,7 @@ const handlePictureCardPreview = (file: any) => {
const listToString = (list: any[], separator?: string) => {
let strs = '';
separator = separator || ',';
for (let i in list) {
for (const i in list) {
if (undefined !== list[i].ossId && list[i].url.indexOf('blob:') !== 0) {
strs += list[i].ossId + separator;
}

View File

@@ -0,0 +1,100 @@
<template>
<el-dialog v-model="visible" :title="props.title" width="50%" draggable :before-close="cancel" center :close-on-click-modal="false">
<el-form v-loading="loading" ref="ruleFormRef" :model="form" :rules="rules" label-width="120px">
<el-form-item label="消息提醒" prop="messageType">
<el-checkbox-group v-model="form.messageType">
<el-checkbox value="1" name="type" disabled>站内信</el-checkbox>
<el-checkbox value="2" name="type">邮件</el-checkbox>
<el-checkbox value="3" name="type">短信</el-checkbox>
</el-checkbox-group>
</el-form-item>
<el-form-item label="消息内容" prop="message">
<el-input v-model="form.message" type="textarea" resize="none" />
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer" style="float: right; padding-bottom: 20px">
<el-button :disabled="buttonDisabled" type="primary" @click="submit(ruleFormRef)">确认</el-button>
<el-button :disabled="buttonDisabled" @click="cancel">取消</el-button>
</div>
</template>
</el-dialog>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { ComponentInternalInstance } from 'vue';
import { ElForm, FormInstance } from 'element-plus';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const emits = defineEmits(['submitCallback', 'cancelCallback']);
const props = defineProps({
title: {
type: String,
default: '提示'
}
});
const ruleFormRef = ref<FormInstance>();
//遮罩层
const loading = ref(true);
const visible = ref(false);
const buttonDisabled = ref(true);
const form = ref<Record<string, any>>({
message: undefined,
messageType: ['1']
});
const rules = reactive<Record<string, any>>({
messageType: [
{
required: true,
message: '请选择消息提醒',
trigger: 'change'
}
],
message: [
{
required: true,
message: '请输入消息内容',
trigger: 'blur'
}
]
});
//确认
//打开弹窗
const open = async () => {
reset();
visible.value = true;
loading.value = false;
buttonDisabled.value = false;
};
//关闭弹窗
const close = async () => {
reset();
visible.value = false;
};
const submit = async (formEl: FormInstance | undefined) => {
if (!formEl) return;
await formEl.validate((valid, fields) => {
if (valid) {
emits('submitCallback', form.value);
}
});
};
//取消
const cancel = async () => {
visible.value = false;
buttonDisabled.value = false;
emits('cancelCallback');
};
//重置
const reset = async () => {
form.value.taskIdList = [];
form.value.message = '';
form.value.messageType = ['1'];
};
/**
* 对外暴露子组件方法
*/
defineExpose({
open,
close
});
</script>

View File

@@ -0,0 +1,57 @@
<template>
<div style="display: flex; justify-content: space-between">
<div>
<el-button v-if="submitButtonShow" :loading="props.buttonLoading" type="info" @click="submitForm('draft', mode)">暂存</el-button>
<el-button v-if="submitButtonShow" :loading="props.buttonLoading" type="primary" @click="submitForm('submit', mode)"> </el-button>
<el-button v-if="approvalButtonShow" :loading="props.buttonLoading" type="primary" @click="approvalVerifyOpen">审批</el-button>
<el-button v-if="props.id && props.status !== 'draft'" type="primary" @click="handleApprovalRecord">流程进度</el-button>
<slot />
</div>
<div>
<el-button style="float: right" @click="goBack()">返回</el-button>
</div>
</div>
</template>
<script setup lang="ts">
import { propTypes } from '@/utils/propTypes';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const props = defineProps({
status: propTypes.string.def(''),
pageType: propTypes.string.def(''),
buttonLoading: propTypes.bool.def(false),
id: propTypes.string.def('') || propTypes.number.def(),
mode: propTypes.bool.def(false)
});
const emits = defineEmits(['submitForm', 'approvalVerifyOpen', 'handleApprovalRecord']);
//暂存,提交
const submitForm = async (type, mode) => {
emits('submitForm', type, mode);
};
//审批
const approvalVerifyOpen = async () => {
emits('approvalVerifyOpen');
};
//审批记录
const handleApprovalRecord = () => {
emits('handleApprovalRecord');
};
//校验提交按钮是否显示
const submitButtonShow = computed(() => {
return (
props.pageType === 'add' ||
(props.pageType === 'update' && props.status && (props.status === 'draft' || props.status === 'cancel' || props.status === 'back'))
);
});
//校验审批按钮是否显示
const approvalButtonShow = computed(() => {
return props.pageType === 'approval' && props.status && props.status === 'waiting';
});
//返回
const goBack = () => {
proxy.$tab.closePage(proxy.$route);
proxy.$router.go(-1);
};
</script>

View File

@@ -3,21 +3,7 @@
<el-dialog v-model="visible" draggable title="审批记录" :width="props.width" :height="props.height" :close-on-click-modal="false">
<el-tabs v-model="tabActiveName" class="demo-tabs">
<el-tab-pane v-loading="loading" label="流程图" name="image" style="height: 68vh">
<div
ref="imageWrapperRef"
class="image-wrapper"
@wheel="handleMouseWheel"
@mousedown="handleMouseDown"
@mousemove="handleMouseMove"
@mouseup="handleMouseUp"
@mouseleave="handleMouseLeave"
@dblclick="resetTransform"
:style="transformStyle"
>
<el-card class="box-card">
<el-image :src="imgUrl" class="scalable-image" />
</el-card>
</div>
<flowChart :ins-id="insId" v-if="insId" />
</el-tab-pane>
<el-tab-pane v-loading="loading" label="审批信息" name="info">
<div>
@@ -73,10 +59,10 @@
</div>
</template>
<script setup lang="ts">
import { flowImage } from '@/api/workflow/instance';
import { flowHisTaskList } from '@/api/workflow/instance';
import { propTypes } from '@/utils/propTypes';
import { listByIds } from '@/api/system/oss';
import FlowChart from '@/components/Process/flowChart.vue';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { wf_task_status } = toRefs<any>(proxy?.useDict('wf_task_status'));
const props = defineProps({
@@ -87,7 +73,7 @@ const loading = ref(false);
const visible = ref(false);
const historyList = ref<Array<any>>([]);
const tabActiveName = ref('image');
const imgUrl = ref('');
const insId = ref(null);
//初始化查询审批记录
const init = async (businessId: string | number) => {
@@ -95,10 +81,10 @@ const init = async (businessId: string | number) => {
loading.value = true;
tabActiveName.value = 'image';
historyList.value = [];
flowImage(businessId).then((resp) => {
flowHisTaskList(businessId).then((resp) => {
if (resp.data) {
historyList.value = resp.data.list;
imgUrl.value = 'data:image/gif;base64,' + resp.data.image;
insId.value = resp.data.instanceId;
if (historyList.value.length > 0) {
historyList.value.forEach((item) => {
if (item.ext) {
@@ -124,109 +110,6 @@ const handleDownload = (ossId: string) => {
proxy?.$download.oss(ossId);
};
const imageWrapperRef = ref<HTMLElement | null>(null);
const scale = ref(1); // 初始缩放比例
const maxScale = 3; // 最大缩放比例
const minScale = 0.5; // 最小缩放比例
let isDragging = false;
let startX = 0;
let startY = 0;
let currentTranslateX = 0;
let currentTranslateY = 0;
const handleMouseWheel = (event: WheelEvent) => {
event.preventDefault();
let newScale = scale.value - event.deltaY / 1000;
newScale = Math.max(minScale, Math.min(newScale, maxScale));
if (newScale !== scale.value) {
scale.value = newScale;
resetDragPosition(); // 重置拖拽位置,使图片居中
}
};
const handleMouseDown = (event: MouseEvent) => {
if (scale.value > 1) {
event.preventDefault(); // 阻止默认行为,防止拖拽
isDragging = true;
startX = event.clientX;
startY = event.clientY;
}
};
const handleMouseMove = (event: MouseEvent) => {
if (!isDragging || !imageWrapperRef.value) return;
const deltaX = event.clientX - startX;
const deltaY = event.clientY - startY;
startX = event.clientX;
startY = event.clientY;
currentTranslateX += deltaX;
currentTranslateY += deltaY;
// 边界检测,防止图片被拖出容器
const bounds = getBounds();
if (currentTranslateX > bounds.maxTranslateX) {
currentTranslateX = bounds.maxTranslateX;
} else if (currentTranslateX < bounds.minTranslateX) {
currentTranslateX = bounds.minTranslateX;
}
if (currentTranslateY > bounds.maxTranslateY) {
currentTranslateY = bounds.maxTranslateY;
} else if (currentTranslateY < bounds.minTranslateY) {
currentTranslateY = bounds.minTranslateY;
}
applyTransform();
};
const handleMouseUp = () => {
isDragging = false;
};
const handleMouseLeave = () => {
isDragging = false;
};
const resetTransform = () => {
scale.value = 1;
currentTranslateX = 0;
currentTranslateY = 0;
applyTransform();
};
const resetDragPosition = () => {
currentTranslateX = 0;
currentTranslateY = 0;
applyTransform();
};
const applyTransform = () => {
if (imageWrapperRef.value) {
imageWrapperRef.value.style.transform = `translate(${currentTranslateX}px, ${currentTranslateY}px) scale(${scale.value})`;
}
};
const getBounds = () => {
if (!imageWrapperRef.value) return { minTranslateX: 0, maxTranslateX: 0, minTranslateY: 0, maxTranslateY: 0 };
const imgRect = imageWrapperRef.value.getBoundingClientRect();
const containerRect = imageWrapperRef.value.parentElement?.getBoundingClientRect() ?? imgRect;
const minTranslateX = (containerRect.width - imgRect.width * scale.value) / 2;
const maxTranslateX = -(containerRect.width - imgRect.width * scale.value) / 2;
const minTranslateY = (containerRect.height - imgRect.height * scale.value) / 2;
const maxTranslateY = -(containerRect.height - imgRect.height * scale.value) / 2;
return { minTranslateX, maxTranslateX, minTranslateY, maxTranslateY };
};
const transformStyle = computed(() => ({
transition: isDragging ? 'none' : 'transform 0.2s ease'
}));
/**
* 对外暴露子组件方法
*/
@@ -235,46 +118,10 @@ defineExpose({
});
</script>
<style lang="scss" scoped>
.triangle {
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
border-radius: 6px;
}
.triangle::after {
content: ' ';
position: absolute;
top: 8em;
right: 215px;
border: 15px solid;
border-color: transparent #fff transparent transparent;
}
.container {
:deep(.el-dialog .el-dialog__body) {
max-height: calc(100vh - 170px) !important;
min-height: calc(100vh - 170px) !important;
}
}
.image-wrapper {
width: 100%;
overflow: hidden;
position: relative;
margin: 0 auto;
display: flex;
justify-content: center;
align-items: center;
user-select: none; /* 禁用文本选择 */
cursor: grab; /* 设置初始鼠标指针为可拖动 */
}
.image-wrapper:active {
cursor: grabbing; /* 当正在拖动时改变鼠标指针 */
}
.scalable-image {
object-fit: contain;
width: 100%;
padding: 15px;
}
</style>

View File

@@ -0,0 +1,40 @@
<template>
<div>
<div style="height: 68vh" class="iframe-wrapper">
<iframe :src="iframeUrl" style="width: 100%; height: 100%" frameborder="0" scrolling="no" class="custom-iframe" />
</div>
</div>
</template>
<script setup lang="ts">
import { getToken } from '@/utils/auth';
// Props 定义方式变化
const props = defineProps({
insId: {
type: [String, Number],
default: null
}
});
const iframeUrl = ref('');
const baseUrl = import.meta.env.VITE_APP_BASE_API;
onMounted(async () => {
const url = baseUrl + `/warm-flow-ui/index.html?id=${props.insId}&type=FlowChart&t=${Date.now()}`;
iframeUrl.value = url + '&Authorization=Bearer ' + getToken() + '&clientid=' + import.meta.env.VITE_APP_CLIENT_ID;
});
</script>
<style scoped>
.iframe-wrapper {
border-radius: 12px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
overflow: hidden;
}
.custom-iframe {
width: 100%;
border: none;
background: transparent;
}
</style>

View File

@@ -0,0 +1,154 @@
<template>
<div
ref="imageWrapperRef"
class="image-wrapper"
@wheel="handleMouseWheel"
@mousedown="handleMouseDown"
@mousemove="handleMouseMove"
@mouseup="handleMouseUp"
@mouseleave="handleMouseLeave"
@dblclick="resetTransform"
:style="transformStyle"
>
<el-card class="box-card">
<el-image :src="props.imgUrl" class="scalable-image" />
</el-card>
</div>
</template>
<script setup lang="ts">
// Props 定义方式变化
const props = defineProps({
imgUrl: {
type: String,
default: () => ''
}
});
const imageWrapperRef = ref<HTMLElement | null>(null);
const scale = ref(1); // 初始缩放比例
const maxScale = 3; // 最大缩放比例
const minScale = 0.5; // 最小缩放比例
let isDragging = false;
let startX = 0;
let startY = 0;
let currentTranslateX = 0;
let currentTranslateY = 0;
const handleMouseWheel = (event: WheelEvent) => {
event.preventDefault();
let newScale = scale.value - event.deltaY / 1000;
newScale = Math.max(minScale, Math.min(newScale, maxScale));
if (newScale !== scale.value) {
scale.value = newScale;
resetDragPosition(); // 重置拖拽位置,使图片居中
}
};
const handleMouseDown = (event: MouseEvent) => {
if (scale.value > 1) {
event.preventDefault(); // 阻止默认行为,防止拖拽
isDragging = true;
startX = event.clientX;
startY = event.clientY;
}
};
const handleMouseMove = (event: MouseEvent) => {
if (!isDragging || !imageWrapperRef.value) return;
const deltaX = event.clientX - startX;
const deltaY = event.clientY - startY;
startX = event.clientX;
startY = event.clientY;
currentTranslateX += deltaX;
currentTranslateY += deltaY;
// 边界检测,防止图片被拖出容器
const bounds = getBounds();
if (currentTranslateX > bounds.maxTranslateX) {
currentTranslateX = bounds.maxTranslateX;
} else if (currentTranslateX < bounds.minTranslateX) {
currentTranslateX = bounds.minTranslateX;
}
if (currentTranslateY > bounds.maxTranslateY) {
currentTranslateY = bounds.maxTranslateY;
} else if (currentTranslateY < bounds.minTranslateY) {
currentTranslateY = bounds.minTranslateY;
}
applyTransform();
};
const handleMouseUp = () => {
isDragging = false;
};
const handleMouseLeave = () => {
isDragging = false;
};
const resetTransform = () => {
scale.value = 1;
currentTranslateX = 0;
currentTranslateY = 0;
applyTransform();
};
const resetDragPosition = () => {
currentTranslateX = 0;
currentTranslateY = 0;
applyTransform();
};
const applyTransform = () => {
if (imageWrapperRef.value) {
imageWrapperRef.value.style.transform = `translate(${currentTranslateX}px, ${currentTranslateY}px) scale(${scale.value})`;
}
};
const getBounds = () => {
if (!imageWrapperRef.value) return { minTranslateX: 0, maxTranslateX: 0, minTranslateY: 0, maxTranslateY: 0 };
const imgRect = imageWrapperRef.value.getBoundingClientRect();
const containerRect = imageWrapperRef.value.parentElement?.getBoundingClientRect() ?? imgRect;
const minTranslateX = (containerRect.width - imgRect.width * scale.value) / 2;
const maxTranslateX = -(containerRect.width - imgRect.width * scale.value) / 2;
const minTranslateY = (containerRect.height - imgRect.height * scale.value) / 2;
const maxTranslateY = -(containerRect.height - imgRect.height * scale.value) / 2;
return { minTranslateX, maxTranslateX, minTranslateY, maxTranslateY };
};
const transformStyle = computed(() => ({
transition: isDragging ? 'none' : 'transform 0.2s ease'
}));
</script>
<style scoped>
.image-wrapper {
width: 100%;
overflow: hidden;
position: relative;
margin: 0 auto;
display: flex;
justify-content: center;
align-items: center;
user-select: none; /* 禁用文本选择 */
cursor: grab; /* 设置初始鼠标指针为可拖动 */
}
.image-wrapper:active {
cursor: grabbing; /* 当正在拖动时改变鼠标指针 */
}
.scalable-image {
object-fit: contain;
width: 100%;
padding: 15px;
}
</style>

View File

@@ -8,13 +8,13 @@
<el-checkbox value="3" name="type">短信</el-checkbox>
</el-checkbox-group>
</el-form-item>
<el-form-item v-if="task.flowStatus === 'waiting'" label="附件">
<el-form-item label="附件">
<fileUpload v-model="form.fileId" :file-type="['png', 'jpg', 'jpeg', 'doc', 'docx', 'xlsx', 'xls', 'ppt', 'txt', 'pdf']" :file-size="20" />
</el-form-item>
<el-form-item label="抄送" v-if="task.flowStatus === 'waiting' && buttonObj.copy">
<el-form-item label="抄送" v-if="buttonObj.copy">
<el-button type="primary" icon="Plus" circle @click="openUserSelectCopy" />
<el-tag v-for="user in selectCopyUserList" :key="user.userId" closable style="margin: 2px" @close="handleCopyCloseTag(user)">
{{ user.nickName }}
{{ user.userName }}
</el-tag>
</el-form-item>
<el-form-item v-if="buttonObj.pop && nestNodeList && nestNodeList.length > 0" label="下一步审批人" prop="assigneeMap">
@@ -80,7 +80,7 @@
<!-- 加签组件 -->
<UserSelect ref="multiInstanceUserRef" :multiple="true" @confirm-call-back="addMultiInstanceUser"></UserSelect>
<!-- 弹窗选人 -->
<UserSelect ref="porUserRef" :multiple="true" :userIds="popUserIds" @confirm-call-back="handlePopUser"></UserSelect>
<UserSelect ref="porUserRef" :data="form.assigneeMap[nodeCode]" :multiple="true" :userIds="popUserIds" @confirm-call-back="handlePopUser"></UserSelect>
<!-- 驳回开始 -->
<el-dialog v-model="backVisible" draggable title="驳回" width="40%" :close-on-click-modal="false">
@@ -149,8 +149,7 @@ import {
import UserSelect from '@/components/UserSelect';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
import { UserVO } from '@/api/system/user/types';
import { FlowTaskVO, TaskOperationBo } from '@/api/workflow/task/types';
import { FlowCopyVo, FlowTaskVO, TaskOperationBo } from '@/api/workflow/task/types';
const userSelectCopyRef = ref<InstanceType<typeof UserSelect>>();
const transferTaskRef = ref<InstanceType<typeof UserSelect>>();
@@ -171,9 +170,11 @@ const buttonDisabled = ref(true);
//任务id
const taskId = ref<string>('');
//抄送人
const selectCopyUserList = ref<UserVO[]>([]);
const selectCopyUserList = ref<FlowCopyVo[]>([]);
//抄送人id
const selectCopyUserIds = ref<string>(undefined);
//自定义节点变量
const varNodeList = ref<Map<string, string>>(undefined);
//可减签的人员
const deleteUserList = ref<any>([]);
//弹窗可选择的人员id
@@ -217,7 +218,11 @@ const task = ref<FlowTaskVO>({
nodeType: undefined,
nodeRatio: undefined,
applyNode: false,
buttonList: []
buttonList: [],
copyList: [],
varList: undefined,
businessCode: undefined,
businessTitle: undefined
});
const dialog = reactive<DialogOption>({
visible: false,
@@ -257,14 +262,21 @@ const openDialog = async (id?: string) => {
task.value.buttonList.forEach((e) => {
buttonObj.value[e.code] = e.show;
});
selectCopyUserList.value = task.value.copyList;
selectCopyUserIds.value = task.value.copyList.map((e) => e.userId).join(',');
varNodeList.value = task.value.varList;
console.log('varNodeList', varNodeList.value)
buttonDisabled.value = false;
const data = {
taskId: taskId.value,
variables: props.taskVariables
};
const nextData = await getNextNodeList(data);
nestNodeList.value = nextData.data;
loading.value = false;
try {
const data = {
taskId: taskId.value,
variables: props.taskVariables
};
const nextData = await getNextNodeList(data);
nestNodeList.value = nextData.data;
} finally {
loading.value = false;
}
};
onMounted(() => {});
@@ -273,7 +285,7 @@ const emits = defineEmits(['submitCallback', 'cancelCallback']);
/** 办理流程 */
const handleCompleteTask = async () => {
form.value.taskId = taskId.value;
form.value.taskVariables = props.taskVariables;
form.value.variables = props.taskVariables;
let verify = false;
if (buttonObj.value.pop && nestNodeList.value && nestNodeList.value.length > 0) {
nestNodeList.value.forEach((e) => {
@@ -298,7 +310,7 @@ const handleCompleteTask = async () => {
selectCopyUserList.value.forEach((e) => {
const copyUser = {
userId: e.userId,
userName: e.nickName
userName: e.userName
};
flowCopyList.push(copyUser);
});
@@ -325,7 +337,7 @@ const handleBackProcessOpen = async () => {
backVisible.value = true;
backLoading.value = true;
backButtonDisabled.value = true;
const data = await getBackTaskNode(task.value.definitionId, task.value.nodeCode);
const data = await getBackTaskNode(task.value.id, task.value.nodeCode);
taskNodeList.value = data.data;
backLoading.value = false;
backButtonDisabled.value = false;
@@ -361,14 +373,14 @@ const openUserSelectCopy = () => {
userSelectCopyRef.value.open();
};
//确认抄送人员
const userSelectCopyCallBack = (data: UserVO[]) => {
const userSelectCopyCallBack = (data: FlowCopyVo[]) => {
if (data && data.length > 0) {
selectCopyUserList.value = data;
selectCopyUserIds.value = selectCopyUserList.value.map((item) => item.userId).join(',');
}
};
//删除抄送人员
const handleCopyCloseTag = (user: UserVO) => {
const handleCopyCloseTag = (user: FlowCopyVo) => {
const userId = user.userId;
// 使用split删除用户
const index = selectCopyUserList.value.findIndex((item) => item.userId === userId);

View File

@@ -47,11 +47,11 @@ const routers = computed(() => permissionStore.getTopbarRoutes());
// 顶部显示菜单
const topMenus = computed(() => {
let topMenus: RouteRecordRaw[] = [];
const topMenus: RouteRecordRaw[] = [];
routers.value.map((menu) => {
if (menu.hidden !== true) {
// 兼容顶部栏一级菜单内部跳转
if (menu.path === '/') {
if (menu.path === '/' && menu.children) {
topMenus.push(menu.children ? menu.children[0] : menu);
} else {
topMenus.push(menu);
@@ -63,7 +63,7 @@ const topMenus = computed(() => {
// 设置子路由
const childrenMenus = computed(() => {
let childrenMenus: RouteRecordRaw[] = [];
const childrenMenus: RouteRecordRaw[] = [];
routers.value.map((router) => {
router.children?.forEach((item) => {
if (item.parentPath === undefined) {
@@ -118,7 +118,7 @@ const handleSelect = (key: string) => {
// 没有子路由路径内部打开
const routeMenu = childrenMenus.value.find((item) => item.path === key);
if (routeMenu && routeMenu.query) {
let query = JSON.parse(routeMenu.query);
const query = JSON.parse(routeMenu.query);
router.push({ path: key, query: query });
} else {
router.push({ path: key });
@@ -132,7 +132,7 @@ const handleSelect = (key: string) => {
};
const activeRoutes = (key: string) => {
let routes: RouteRecordRaw[] = [];
const routes: RouteRecordRaw[] = [];
if (childrenMenus.value && childrenMenus.value.length > 0) {
childrenMenus.value.map((item) => {
if (key == item.parentPath || (key == 'index' && '' == item.path)) {

View File

@@ -1,147 +0,0 @@
<template>
<div class="el-tree-select">
<el-select
ref="treeSelect"
v-model="valueId"
style="width: 100%"
:filterable="true"
:clearable="true"
:filter-method="selectFilterData as any"
:placeholder="placeholder"
@clear="clearHandle"
>
<el-option :value="valueId" :label="valueTitle">
<el-tree
id="tree-option"
ref="selectTree"
:accordion="accordion"
:data="options"
:props="objMap as any"
:node-key="objMap.value"
:expand-on-click-node="false"
:default-expanded-keys="defaultExpandedKey"
:filter-node-method="filterNode"
@node-click="handleNodeClick"
></el-tree>
</el-option>
</el-select>
</div>
</template>
<script setup lang="ts">
interface ObjMap {
value: string;
label: string;
children: string;
}
interface Props {
objMap: ObjMap;
accordion: boolean;
value: string | number;
options: any[];
placeholder: string;
}
const props = withDefaults(defineProps<Props>(), {
objMap: () => {
return {
value: 'id',
label: 'label',
children: 'children'
};
},
accordion: false,
value: '',
options: () => [],
placeholder: ''
});
const selectTree = ref<ElTreeSelectInstance>();
const emit = defineEmits(['update:value']);
const valueId = computed({
get: () => props.value,
set: (val) => {
emit('update:value', val);
}
});
const valueTitle = ref('');
const defaultExpandedKey = ref<any[]>([]);
const initHandle = () => {
nextTick(() => {
const selectedValue = valueId.value;
if (selectedValue !== null && typeof selectedValue !== 'undefined') {
const node = selectTree.value?.getNode(selectedValue);
if (node) {
valueTitle.value = node.data[props.objMap.label];
selectTree.value?.setCurrentKey(selectedValue); // 设置默认选中
defaultExpandedKey.value = [selectedValue]; // 设置默认展开
}
} else {
clearHandle();
}
});
};
const handleNodeClick = (node: any) => {
valueTitle.value = node[props.objMap.label];
valueId.value = node[props.objMap.value];
defaultExpandedKey.value = [];
selectTree.value?.blur();
selectFilterData('');
};
const selectFilterData = (val: any) => {
selectTree.value?.filter(val);
};
const filterNode = (value: any, data: any) => {
if (!value) return true;
return data[props.objMap['label']].indexOf(value) !== -1;
};
const clearHandle = () => {
valueTitle.value = '';
valueId.value = '';
defaultExpandedKey.value = [];
clearSelected();
};
const clearSelected = () => {
const allNode = document.querySelectorAll('#tree-option .el-tree-node');
allNode.forEach((element) => element.classList.remove('is-current'));
};
onMounted(() => {
initHandle();
});
watch(valueId, () => {
initHandle();
});
</script>
<style lang="scss" scoped>
@import '@/assets/styles/variables.module.scss';
.el-scrollbar .el-scrollbar__view .el-select-dropdown__item {
padding: 0;
background-color: #fff;
height: auto;
}
.el-select-dropdown__item.selected {
font-weight: normal;
}
ul li .el-tree .el-tree-node__content {
height: auto;
padding: 0 20px;
box-sizing: border-box;
}
:deep(.el-tree-node__content:hover),
:deep(.el-tree-node__content:active),
:deep(.is-current > div:first-child),
:deep(.el-tree-node__content:focus) {
background-color: mix(#fff, $--color-primary, 90%);
color: $--color-primary;
}
</style>

View File

@@ -168,12 +168,15 @@ const confirm = () => {
};
const computedIds = (data) => {
if (data === '' || data === null || data === undefined) {
return [];
}
if (data instanceof Array) {
return data.map((item) => String(item));
} else if (typeof data === 'string') {
return data.split(',');
} else if (typeof data === 'number') {
return [data];
return [String(data)];
} else {
console.warn('<UserSelect> The data type of data should be array or string or number, but I received other');
return [];

View File

@@ -1,14 +1,11 @@
<template>
<section class="app-main">
<router-view v-slot="{ Component, route }">
<transition v-if="!route.meta.noCache" :enter-active-class="animate" mode="out-in">
<keep-alive v-if="!route.meta.noCache" :include="tagsViewStore.cachedViews">
<transition :enter-active-class="animate" mode="out-in">
<keep-alive :include="tagsViewStore.cachedViews">
<component :is="Component" v-if="!route.meta.link" :key="route.path" />
</keep-alive>
</transition>
<transition v-if="route.meta.noCache" :enter-active-class="animate" mode="out-in">
<component :is="Component" v-if="!route.meta.link && route.meta.noCache" :key="route.path" />
</transition>
</router-view>
<iframe-toggle />
</section>
@@ -40,16 +37,16 @@ watch(
);
onMounted(() => {
addIframe()
})
addIframe();
});
watchEffect((route) => {
addIframe()
})
watchEffect(() => {
addIframe();
});
function addIframe() {
if (route.meta.link) {
useTagsViewStore().addIframeView(route)
useTagsViewStore().addIframeView(route);
}
}
</script>

View File

@@ -18,7 +18,7 @@ const tagsViewStore = useTagsViewStore();
function iframeUrl(url: string | undefined, query: any) {
if (Object.keys(query).length > 0) {
let params = Object.keys(query)
const params = Object.keys(query)
.map((key) => key + '=' + query[key])
.join('&');
return url + '?' + params;

View File

@@ -33,7 +33,7 @@
<el-popover placement="bottom" trigger="click" transition="el-zoom-in-top" :width="300" :persistent="false">
<template #reference>
<el-badge :value="newNotice > 0 ? newNotice : ''" :max="99">
<svg-icon icon-class="message" />
<div class="right-menu-item hover-effect" style="display: block"><svg-icon icon-class="message" /></div>
</el-badge>
</template>
<template #default>
@@ -98,7 +98,7 @@ import { dynamicClear, dynamicTenant } from '@/api/system/tenant';
import { TenantVO } from '@/api/types';
import notice from './notice/index.vue';
import router from '@/router';
import {ElMessageBoxOptions} from "element-plus/es/components/message-box/src/message-box.type";
import { ElMessageBoxOptions } from 'element-plus/es/components/message-box/src/message-box.type';
const appStore = useAppStore();
const userStore = useUserStore();
@@ -171,6 +171,7 @@ const logout = async () => {
redirect: encodeURIComponent(router.currentRoute.value.fullPath || '/')
}
});
proxy?.$tab.closeAllPage();
});
};

View File

@@ -59,6 +59,13 @@
</span>
</div>
<div class="drawer-item">
<span>显示页签图标</span>
<span class="comp-style">
<el-switch v-model="settingsStore.tagsIcon" :disabled="!settingsStore.tagsView" class="drawer-switch" />
</span>
</div>
<div class="drawer-item">
<span>固定 Header</span>
<span class="comp-style">
@@ -153,6 +160,7 @@ const saveSetting = () => {
const settings = useStorage<LayoutSetting>('layout-setting', defaultSettings);
settings.value.topNav = storeSettings.value.topNav;
settings.value.tagsView = storeSettings.value.tagsView;
settings.value.tagsIcon = storeSettings.value.tagsIcon;
settings.value.fixedHeader = storeSettings.value.fixedHeader;
settings.value.sidebarLogo = storeSettings.value.sidebarLogo;
settings.value.dynamicTitle = storeSettings.value.dynamicTitle;

View File

@@ -86,7 +86,7 @@ const resolvePath = (routePath: string, routeQuery?: string): any => {
return props.basePath;
}
if (routeQuery) {
let query = JSON.parse(routeQuery);
const query = JSON.parse(routeQuery);
return { path: getNormalPath(props.basePath + '/' + routePath), query: query };
}
return getNormalPath(props.basePath + '/' + routePath);

View File

@@ -11,6 +11,7 @@
:unique-opened="true"
:active-text-color="theme"
:collapse-transition="false"
:popper-offset="12"
mode="vertical"
>
<sidebar-item v-for="(r, index) in sidebarRouters" :key="r.path + index" :item="r" :base-path="r.path" />

View File

@@ -63,9 +63,9 @@ const loginByCode = async (data: LoginData) => {
const init = async () => {
// 如果域名不相等 则重定向处理
let host = window.location.host;
const host = window.location.host;
if (domain !== host) {
let urlFull = new URL(window.location.href);
const urlFull = new URL(window.location.href);
urlFull.host = domain;
window.location.href = urlFull.toString();
return;

View File

@@ -5,14 +5,15 @@
v-for="tag in visitedViews"
:key="tag.path"
:data-path="tag.path"
:class="isActive(tag) ? 'active' : ''"
:class="{ 'active': isActive(tag), 'has-icon': tagsIcon }"
:to="{ path: tag.path ? tag.path : '', query: tag.query, fullPath: tag.fullPath ? tag.fullPath : '' }"
class="tags-view-item"
:style="activeStyle(tag)"
@click.middle="!isAffix(tag) ? closeSelectedTag(tag) : ''"
@contextmenu.prevent="openMenu(tag, $event)"
>
{{ tag.title }}
<svg-icon v-if="tagsIcon && tag.meta && tag.meta.icon && tag.meta.icon !== '#'" :icon-class="tag.meta.icon"/>
<span class="tags-view-item-title">{{ tag.title }}</span>
<span v-if="!isAffix(tag)" @click.prevent.stop="closeSelectedTag(tag)">
<close class="el-icon-close" style="width: 1em; height: 1em; vertical-align: middle" />
</span>
@@ -51,6 +52,7 @@ const router = useRouter();
const visitedViews = computed(() => useTagsViewStore().getVisitedViews());
const routes = computed(() => usePermissionStore().getRoutes());
const theme = computed(() => useSettingsStore().theme);
const tagsIcon = computed(() => useSettingsStore().tagsIcon)
watch(route, () => {
addTags();
@@ -251,7 +253,7 @@ onMounted(() => {
position: relative;
cursor: pointer;
height: 26px;
line-height: 23px;
line-height: 25px;
background-color: var(--el-bg-color);
border: 1px solid var(--el-border-color-light);
color: #495060;
@@ -259,6 +261,7 @@ onMounted(() => {
font-size: 12px;
margin-left: 5px;
margin-top: 4px;
border-radius: 4px;
&:hover {
color: var(--el-color-primary);
}
@@ -285,6 +288,13 @@ onMounted(() => {
}
}
}
.tags-view-item.active.has-icon::before {
content: none !important;
}
.tags-view-item-title {
margin-left: 4px;
margin-right: 3px;
}
.contextmenu {
margin: 0;
background: var(--el-bg-color);

View File

@@ -67,7 +67,7 @@ const closeSearch = () => {
};
// 菜单搜索数据过滤
const menuSearch = (queryString: string, cb: (options: any[]) => void) => {
let options = state.menuList.filter((item) => {
const options = state.menuList.filter((item) => {
return item.title.indexOf(queryString) > -1;
});
cb(options);

View File

@@ -24,10 +24,9 @@
</template>
<script setup lang="ts" name="layoutBreadcrumbUserNews">
import { storeToRefs } from 'pinia';
import { useNoticeStore } from '@/store/modules/notice';
const noticeStore = storeToRefs(useNoticeStore());
const noticeStore = useNoticeStore();
const { readAll } = useNoticeStore();
// 定义变量内容
@@ -42,7 +41,7 @@ const newsList = ref([]) as any;
*/
const getTableData = async () => {
state.loading = true;
newsList.value = noticeStore.state.value.notices;
newsList.value = noticeStore.state.notices;
state.loading = false;
};
@@ -50,7 +49,7 @@ const getTableData = async () => {
const onNewsClick = (item: any) => {
newsList.value[item].read = true;
//并且写入pinia
noticeStore.state.value.notices = newsList.value;
noticeStore.state.notices = newsList.value;
};
// 前往通知中心点击

View File

@@ -1,8 +1,8 @@
import { createApp } from 'vue';
// global css
import 'virtual:uno.css';
import '@/assets/styles/index.scss';
import 'element-plus/theme-chalk/dark/css-vars.css';
import '@/assets/styles/index.scss';
// App、router、store
import App from './App.vue';
@@ -34,7 +34,7 @@ import i18n from '@/lang/index';
// vxeTable
import VXETable from 'vxe-table';
import 'vxe-table/lib/style.css';
VXETable.config({
VXETable.setConfig({
zIndex: 999999
});

View File

@@ -14,8 +14,8 @@ NProgress.configure({ showSpinner: false });
const whiteList = ['/login', '/register', '/social-callback', '/register*', '/register/*'];
const isWhiteList = (path: string) => {
return whiteList.some(pattern => isPathMatch(pattern, path))
}
return whiteList.some((pattern) => isPathMatch(pattern, path));
};
router.beforeEach(async (to, from, next) => {
NProgress.start();

View File

@@ -93,104 +93,7 @@ export const constantRoutes: RouteRecordRaw[] = [
// 动态路由,基于用户权限动态去加载
export const dynamicRoutes: RouteRecordRaw[] = [
{
path: '/system/user-auth',
component: Layout,
hidden: true,
permissions: ['system:user:edit'],
children: [
{
path: 'role/:userId(\\d+)',
component: () => import('@/views/system/user/authRole.vue'),
name: 'AuthRole',
meta: { title: '分配角色', activeMenu: '/system/user', icon: '', noCache: true }
}
]
},
{
path: '/system/role-auth',
component: Layout,
hidden: true,
permissions: ['system:role:edit'],
children: [
{
path: 'user/:roleId(\\d+)',
component: () => import('@/views/system/role/authUser.vue'),
name: 'AuthUser',
meta: { title: '分配用户', activeMenu: '/system/role', icon: '', noCache: true }
}
]
},
{
path: '/system/dict-data',
component: Layout,
hidden: true,
permissions: ['system:dict:list'],
children: [
{
path: 'index/:dictId(\\d+)',
component: () => import('@/views/system/dict/data.vue'),
name: 'Data',
meta: { title: '字典数据', activeMenu: '/system/dict', icon: '', noCache: true }
}
]
},
{
path: '/system/oss-config',
component: Layout,
hidden: true,
permissions: ['system:ossConfig:list'],
children: [
{
path: 'index',
component: () => import('@/views/system/oss/config.vue'),
name: 'OssConfig',
meta: { title: '配置管理', activeMenu: '/system/oss', icon: '', noCache: true }
}
]
},
{
path: '/tool/gen-edit',
component: Layout,
hidden: true,
permissions: ['tool:gen:edit'],
children: [
{
path: 'index/:tableId(\\d+)',
component: () => import('@/views/tool/gen/editTable.vue'),
name: 'GenEdit',
meta: { title: '修改生成配置', activeMenu: '/tool/gen', icon: '', noCache: true }
}
]
},
{
path: '/workflow/leaveEdit',
component: Layout,
hidden: true,
permissions: ['workflow:leave:edit'],
children: [
{
path: 'index',
component: () => import('@/views/workflow/leave/leaveEdit.vue'),
name: 'leaveEdit',
meta: { title: '请假申请', activeMenu: '/workflow/leave', noCache: true }
}
]
},
{
path: '/workflow/design',
component: Layout,
hidden: true,
permissions: ['workflow:leave:edit'],
children: [
{
path: 'index',
component: () => import('@/views/workflow/processDefinition/design.vue'),
name: 'design',
meta: { title: '流程设计', activeMenu: '/workflow/processDefinition', noCache: true }
}
]
}
];
/**

View File

@@ -27,6 +27,11 @@ const setting: DefaultSettings = {
*/
tagsView: true,
/**
* 显示页签图标
*/
tagsIcon: false,
/**
* 是否固定头部
*/
@@ -42,14 +47,6 @@ const setting: DefaultSettings = {
*/
dynamicTitle: false,
/**
* @type {string | array} 'production' | ['production', 'development']
* @description Need show err logs component.
* The default is only used in the production env
* If you want to also use it in dev, you can pass ['production', 'development']
*/
errorLog: 'production',
/**
* 是否开启动画 开启随机 关闭渐进渐出
*/

View File

@@ -1,4 +1,4 @@
import { createPinia } from "pinia";
import { createPinia } from 'pinia';
const store = createPinia();

View File

@@ -99,14 +99,14 @@ export const usePermissionStore = defineStore('permission', () => {
};
const filterChildren = (childrenMap: RouteRecordRaw[], lastRouter?: RouteRecordRaw): RouteRecordRaw[] => {
let children: RouteRecordRaw[] = [];
childrenMap.forEach(el => {
childrenMap.forEach((el) => {
el.path = lastRouter ? lastRouter.path + '/' + el.path : el.path;
if (el.children && el.children.length && el.component?.toString() === 'ParentView') {
children = children.concat(filterChildren(el.children, el));
} else {
children.push(el);
}
})
});
return children;
};
return {

View File

@@ -5,10 +5,10 @@ import { useStorage } from '@vueuse/core';
import { ref } from 'vue';
export const useSettingsStore = defineStore('setting', () => {
// @ts-ignore
const storageSetting = useStorage<LayoutSetting>('layout-setting', {
topNav: defaultSettings.topNav,
tagsView: defaultSettings.tagsView,
tagsIcon: defaultSettings.tagsIcon,
fixedHeader: defaultSettings.fixedHeader,
sidebarLogo: defaultSettings.sidebarLogo,
dynamicTitle: defaultSettings.dynamicTitle,
@@ -21,6 +21,7 @@ export const useSettingsStore = defineStore('setting', () => {
const showSettings = ref<boolean>(defaultSettings.showSettings);
const topNav = ref<boolean>(storageSetting.value.topNav);
const tagsView = ref<boolean>(storageSetting.value.tagsView);
const tagsIcon = ref<boolean>(storageSetting.value.tagsIcon);
const fixedHeader = ref<boolean>(storageSetting.value.fixedHeader);
const sidebarLogo = ref<boolean>(storageSetting.value.sidebarLogo);
const dynamicTitle = ref<boolean>(storageSetting.value.dynamicTitle);
@@ -38,6 +39,7 @@ export const useSettingsStore = defineStore('setting', () => {
showSettings,
topNav,
tagsView,
tagsIcon,
fixedHeader,
sidebarLogo,
dynamicTitle,

View File

@@ -98,6 +98,10 @@ declare global {
* 是否显示多标签导航
*/
tagsView: boolean;
/**
* 显示页签图标
*/
tagsIcon: boolean;
/**
* 是否固定头部
*/
@@ -157,8 +161,6 @@ declare global {
* false: 明亮模式
*/
dark: boolean;
errorLog: string;
}
}
export {};

View File

@@ -48,4 +48,3 @@ export type ObjKeysToUnion<T, P extends string = ''> = T extends object
[K in keyof T]: ObjKeysToUnion<T[K], P extends '' ? `${K & string}` : `${P}.${K & string}`>;
}[keyof T]
: P;

View File

@@ -191,7 +191,8 @@ export function download(url: string, params: any, fileName: string) {
const blob = new Blob([resp]);
FileSaver.saveAs(blob, fileName);
} else {
const resText = await resp.data.text();
const blob = new Blob([resp]);
const resText = await blob.text();
const rspObj = JSON.parse(resText);
const errMsg = errorCode[rspObj.code] || rspObj.msg || errorCode['default'];
ElMessage.error(errMsg);

View File

@@ -178,11 +178,11 @@ export const handleTree = <T>(data: any[], id?: string, parentId?: string, child
for (const d of data) {
const parentId = d[config.parentId];
const parentObj = childrenListMap[parentId]
const parentObj = childrenListMap[parentId];
if (!parentObj) {
tree.push(d);
} else {
parentObj[config.childrenList].push(d)
parentObj[config.childrenList].push(d);
}
}
return tree;

View File

@@ -11,10 +11,10 @@ export const initSSE = (url: any) => {
url = url + '?Authorization=Bearer ' + getToken() + '&clientid=' + import.meta.env.VITE_APP_CLIENT_ID;
const { data, error } = useEventSource(url, [], {
autoReconnect: {
retries: 10,
delay: 3000,
retries: 5,
delay: 5000,
onFailed() {
console.log('Failed to connect after 10 retries');
console.log('Failed to connect after 5 retries');
}
}
});

View File

@@ -4,15 +4,6 @@
<div v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover">
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
<el-form-item label="部门id" prop="deptId">
<el-input v-model="queryParams.deptId" placeholder="请输入部门id" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="用户id" prop="userId">
<el-input v-model="queryParams.userId" placeholder="请输入用户id" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="排序号" prop="orderNum">
<el-input v-model="queryParams.orderNum" placeholder="请输入排序号" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="key键" prop="testKey">
<el-input v-model="queryParams.testKey" placeholder="请输入key键" clearable @keyup.enter="handleQuery" />
</el-form-item>
@@ -49,7 +40,7 @@
</el-row>
</template>
<el-table v-loading="loading" :data="demoList" @selection-change="handleSelectionChange">
<el-table v-loading="loading" border :data="demoList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column v-if="true" label="主键" align="center" prop="id" />
<el-table-column label="部门id" align="center" prop="deptId" />

View File

@@ -33,6 +33,7 @@
v-loading="loading"
:data="treeList"
row-key="id"
border
:default-expand-all="isExpandAll"
:tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
>

View File

@@ -22,7 +22,7 @@
<script setup lang="ts">
import errImage from '@/assets/401_images/401.gif';
let { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const errGif = ref(errImage + '?' + +new Date());

View File

@@ -22,7 +22,7 @@
</template>
<script setup lang="ts">
let message = computed(() => {
const message = computed(() => {
return '找不到网页!';
});
</script>

View File

@@ -26,14 +26,14 @@
* 文件存储 七牛阿里腾讯 云存储<br />
* 监控框架 SpringBoot-Admin 全方位服务监控<br />
* 校验框架 Validation 增强接口安全性 严谨性<br />
* Excel框架 Alibaba EasyExcel 性能优异 扩展性强<br />
* Excel框架 FastExcel(Alibaba EasyExcel) 性能优异 扩展性强<br />
* 文档框架 SpringDocjavadoc 无注解零入侵基于java注释<br />
* 工具类框架 HutoolLombok 减少代码冗余 增加安全性<br />
* 代码生成器 适配MPSpringDoc规范化代码 一键生成前后端代码<br />
* 部署方式 Docker 容器编排 一键部署业务集群<br />
* 国际化 SpringMessage Spring标准国际化方案<br />
</p>
<p><b>当前版本:</b> <span>v5.3.1</span></p>
<p><b>当前版本:</b> <span>v5.5.0</span></p>
<p>
<el-tag type="danger">&yen;免费开源</el-tag>
</p>
@@ -77,7 +77,7 @@
* 分布式监控 PrometheusGrafana 全方位性能监控<br />
* 其余与 Vue 版本一致<br />
</p>
<p><b>当前版本:</b> <span>v2.3.0</span></p>
<p><b>当前版本:</b> <span>v2.5.0</span></p>
<p>
<el-tag type="danger">&yen;免费开源</el-tag>
</p>

View File

@@ -260,10 +260,9 @@ onMounted(() => {
background: #ffffff;
width: 400px;
padding: 25px 25px 5px 25px;
z-index: 1;
.el-input {
height: 40px;
input {
height: 40px;
}

View File

@@ -63,6 +63,7 @@
v-loading="loading"
:data="loginInfoList"
:default-sort="defaultSort"
border
@selection-change="handleSelectionChange"
@sort-change="handleSortChange"
>

View File

@@ -19,6 +19,7 @@
<el-card shadow="hover">
<el-table
v-loading="loading"
border
:data="onlineList.slice((queryParams.pageNum - 1) * queryParams.pageSize, queryParams.pageNum * queryParams.pageSize)"
style="width: 100%"
>

View File

@@ -65,6 +65,7 @@
ref="operLogTableRef"
v-loading="loading"
:data="operlogList"
border
:default-sort="defaultSort"
@selection-change="handleSelectionChange"
@sort-change="handleSortChange"

View File

@@ -203,7 +203,6 @@ onMounted(() => {
line-height: 0;
color: #7483a3;
}
}
.register-form {

View File

@@ -45,7 +45,7 @@
</el-row>
</template>
<el-table v-loading="loading" :data="clientList" @selection-change="handleSelectionChange">
<el-table v-loading="loading" :data="clientList" border @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column v-if="true" label="id" align="center" prop="id" />
<el-table-column label="客户端id" align="center" prop="clientId" />
@@ -300,7 +300,7 @@ const handleExport = () => {
/** 状态修改 */
const handleStatusChange = async (row: ClientVO) => {
let text = row.status === '0' ? '启用' : '停用';
const text = row.status === '0' ? '启用' : '停用';
try {
await proxy?.$modal.confirm('确认要"' + text + '"吗?');
await changeStatus(row.clientId, row.status);

View File

@@ -60,7 +60,7 @@
</el-row>
</template>
<el-table v-loading="loading" :data="configList" @selection-change="handleSelectionChange">
<el-table v-loading="loading" border :data="configList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column v-if="false" label="参数主键" align="center" prop="configId" />
<el-table-column label="参数名称" align="center" prop="configName" :show-overflow-tooltip="true" />

View File

@@ -42,6 +42,7 @@
v-loading="loading"
:data="deptList"
row-key="deptId"
border
:tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
:default-expand-all="isExpandAll"
>
@@ -196,7 +197,7 @@ const initData: PageData<DeptForm, DeptQuery> = {
deptName: [{ required: true, message: '部门名称不能为空', trigger: 'blur' }],
orderNum: [{ required: true, message: '显示排序不能为空', trigger: 'blur' }],
email: [{ type: 'email', message: '请输入正确的邮箱地址', trigger: ['blur', 'change'] }],
phone: [{ pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/, message: '请输入正确的手机号码', trigger: 'blur' }]
phone: [{ pattern: /^1[3456789][0-9]\d{8}$/, message: '请输入正确的手机号码', trigger: 'blur' }]
}
};
const data = reactive<PageData<DeptForm, DeptQuery>>(initData);

View File

@@ -44,7 +44,7 @@
</el-row>
</template>
<el-table v-loading="loading" :data="dataList" @selection-change="handleSelectionChange">
<el-table v-loading="loading" border :data="dataList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column v-if="false" label="字典编码" align="center" prop="dictCode" />
<el-table-column label="字典标签" align="center" prop="dictLabel">

View File

@@ -53,7 +53,7 @@
</el-row>
</template>
<el-table v-loading="loading" :data="typeList" @selection-change="handleSelectionChange">
<el-table v-loading="loading" border :data="typeList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column v-if="false" label="字典编号" align="center" prop="dictId" />
<el-table-column label="字典名称" align="center" prop="dictName" :show-overflow-tooltip="true" />

View File

@@ -25,10 +25,10 @@
<template #header>
<el-row :gutter="10">
<el-col :span="1.5">
<el-button v-hasPermi="['system:menu:add']" type="primary" plain icon="Plus" @click="handleAdd()">新增 </el-button>
<el-button v-hasPermi="['system:menu:add']" type="primary" plain icon="Plus" @click="handleAdd()">新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="info" plain icon="Sort" @click="handleToggleExpandAll">展开/折叠</el-button>
<el-button v-hasPermi="['system:menu:remove']" type="danger" plain icon="Delete" @click="handleCascadeDelete" :loading="deleteLoading">级联删除</el-button>
</el-col>
<right-toolbar v-model:show-search="showSearch" @query-table="getList"></right-toolbar>
</el-row>
@@ -39,8 +39,12 @@
v-loading="loading"
:data="menuList"
row-key="menuId"
border
:tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
:default-expand-all="isExpandAll"
:default-expand-all="false"
lazy
:load="getChildrenList"
:expand-change="expandMenuHandle"
>
<el-table-column prop="menuName" label="菜单名称" :show-overflow-tooltip="true" width="160"></el-table-column>
<el-table-column prop="icon" label="图标" align="center" width="100">
@@ -129,8 +133,8 @@
</span>
</template>
<el-radio-group v-model="form.isFrame">
<el-radio label="0"></el-radio>
<el-radio label="1"></el-radio>
<el-radio value="0"></el-radio>
<el-radio value="1"></el-radio>
</el-radio-group>
</el-form-item>
</el-col>
@@ -207,8 +211,8 @@
</span>
</template>
<el-radio-group v-model="form.isCache">
<el-radio label="0">缓存</el-radio>
<el-radio label="1">不缓存</el-radio>
<el-radio value="0">缓存</el-radio>
<el-radio value="1">不缓存</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
@@ -225,7 +229,7 @@
</span>
</template>
<el-radio-group v-model="form.visible">
<el-radio v-for="dict in sys_show_hide" :key="dict.value" :label="dict.value">{{ dict.label }} </el-radio>
<el-radio v-for="dict in sys_show_hide" :key="dict.value" :value="dict.value">{{ dict.label }} </el-radio>
</el-radio-group>
</el-form-item>
</el-col>
@@ -242,7 +246,7 @@
</span>
</template>
<el-radio-group v-model="form.status">
<el-radio v-for="dict in sys_normal_disable" :key="dict.value" :label="dict.value">
<el-radio v-for="dict in sys_normal_disable" :key="dict.value" :value="dict.value">
{{ dict.label }}
</el-radio>
</el-radio-group>
@@ -257,11 +261,31 @@
</div>
</template>
</el-dialog>
<el-dialog v-model="deleteDialog.visible" :title="deleteDialog.title" destroy-on-close append-to-bod width="750px">
<el-tree
ref="menuTreeRef"
class="tree-border"
:data="menuOptions"
show-checkbox
node-key="menuId"
:check-strictly="false"
empty-text="加载中请稍候"
:default-expanded-keys="[0]"
:props="{ value: 'menuId', label: 'menuName', children: 'children' } as any"
/>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="submitDeleteForm" :loading="deleteLoading"> </el-button>
<el-button @click="cancelCascade"> </el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup name="Menu" lang="ts">
import { addMenu, delMenu, getMenu, listMenu, updateMenu } from '@/api/system/menu';
import { addMenu, cascadeDelMenu, delMenu, getMenu, listMenu, updateMenu } from '@/api/system/menu';
import { MenuForm, MenuQuery, MenuVO } from '@/api/system/menu/types';
import { MenuTypeEnum } from '@/enums/MenuTypeEnum';
@@ -275,10 +299,11 @@ const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { sys_show_hide, sys_normal_disable } = toRefs<any>(proxy?.useDict('sys_show_hide', 'sys_normal_disable'));
const menuList = ref<MenuVO[]>([]);
const menuChildrenListMap = ref({});
const menuExpandMap = ref({});
const loading = ref(true);
const showSearch = ref(true);
const menuOptions = ref<MenuOptionsType[]>([]);
const isExpandAll = ref(false);
const dialog = reactive<DialogOption>({
visible: false,
@@ -316,14 +341,73 @@ const data = reactive<PageData<MenuForm, MenuQuery>>({
const menuTableRef = ref<ElTableInstance>();
const { queryParams, form, rules } = toRefs<PageData<MenuForm, MenuQuery>>(data);
/** 获取子菜单列表 */
const getChildrenList = async (row: any, treeNode: unknown, resolve: (data: any[]) => void) => {
menuExpandMap.value[row.menuId] = { row, treeNode, resolve };
const children = menuChildrenListMap.value[row.menuId] || [];
// 菜单的子菜单清空后关闭展开
if (children.length == 0) {
// fix: 处理当菜单只有一个子菜单并被删除,需要将父菜单的展开状态关闭
menuTableRef.value?.updateKeyChildren(row.menuId, children);
}
resolve(children);
};
/** 收起菜单时从menuExpandMap中删除对应菜单id数据 */
const expandMenuHandle = async (row: any, expanded: boolean) => {
if (!expanded) {
menuExpandMap.value[row.menuId] = undefined;
}
};
/** 刷新展开的菜单数据 */
const refreshLoadTree = (parentId: string | number) => {
if (menuExpandMap.value[parentId]) {
const { row, treeNode, resolve } = menuExpandMap.value[parentId];
if (row) {
getChildrenList(row, treeNode, resolve);
if (row.parentId) {
const grandpaMenu = menuExpandMap.value[row.parentId];
getChildrenList(grandpaMenu.row, grandpaMenu.treeNode, grandpaMenu.resolve);
}
}
}
};
/** 重新加载所有已展开的菜单的数据 */
const refreshAllExpandMenuData = () => {
for (const menuId in menuExpandMap.value) {
refreshLoadTree(menuId);
}
};
/** 查询菜单列表 */
const getList = async () => {
loading.value = true;
const res = await listMenu(queryParams.value);
const data = proxy?.handleTree<MenuVO>(res.data, 'menuId');
if (data) {
menuList.value = data;
const tempMap = {};
// 存储 父菜单:子菜单列表
for (const menu of res.data) {
const parentId = menu.parentId;
if (!tempMap[parentId]) {
tempMap[parentId] = [];
}
tempMap[parentId].push(menu);
}
// 创建一个当前所有 menuId 的 Set用于查找父菜单是否存在于当前数据中
const menuIdSet = new Set();
// 设置有没有子菜单
for (const menu of res.data) {
menu['hasChildren'] = tempMap[menu.menuId]?.length > 0;
menuIdSet.add(menu.menuId);
}
menuChildrenListMap.value = tempMap;
// 找出所有父ID不在当前菜单ID集合中的菜单项作为新的顶层菜单
menuList.value = res.data.filter((menu) => !menuIdSet.has(menu.parentId));
// 根据新数据重新加载子菜单数据
refreshAllExpandMenuData();
loading.value = false;
};
/** 查询菜单下拉树结构 */
@@ -362,18 +446,6 @@ const handleAdd = (row?: MenuVO) => {
dialog.visible = true;
dialog.title = '添加菜单';
};
/** 展开/折叠操作 */
const handleToggleExpandAll = () => {
isExpandAll.value = !isExpandAll.value;
toggleExpandAll(menuList.value, isExpandAll.value);
};
/** 展开/折叠所有 */
const toggleExpandAll = (data: MenuVO[], status: boolean) => {
data.forEach((item: MenuVO) => {
menuTableRef.value?.toggleRowExpansion(item, status);
if (item.children && item.children.length > 0) toggleExpandAll(item.children, status);
});
};
/** 修改按钮操作 */
const handleUpdate = async (row: MenuVO) => {
reset();
@@ -404,7 +476,50 @@ const handleDelete = async (row: MenuVO) => {
proxy?.$modal.msgSuccess('删除成功');
};
const deleteLoading = ref<boolean>(false);
const menuTreeRef = ref<ElTreeInstance>();
const deleteDialog = reactive<DialogOption>({
visible: false,
title: '级联删除菜单'
});
/** 级联删除按钮操作 */
const handleCascadeDelete = () => {
menuTreeRef.value?.setCheckedKeys([]);
getTreeselect();
deleteDialog.visible = true;
};
/** 取消按钮 */
const cancelCascade = () => {
menuTreeRef.value?.setCheckedKeys([]);
deleteDialog.visible = false;
};
/** 删除提交按钮 */
const submitDeleteForm = async () => {
const menuIds = menuTreeRef.value?.getCheckedKeys();
if (menuIds.length < 0) {
proxy?.$modal.msgWarning('请选择要删除的菜单');
return;
}
deleteLoading.value = true;
await cascadeDelMenu(menuIds).finally(() => (deleteLoading.value = false));
await getList();
proxy?.$modal.msgSuccess('删除成功');
deleteDialog.visible = false;
};
onMounted(() => {
getList();
});
</script>
<style scoped lang="scss">
.tree-border {
height: 300px;
overflow: auto;
}
</style>

View File

@@ -44,7 +44,7 @@
</el-row>
</template>
<el-table v-loading="loading" :data="noticeList" @selection-change="handleSelectionChange">
<el-table v-loading="loading" border :data="noticeList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column v-if="false" label="序号" align="center" prop="noticeId" width="100" />
<el-table-column label="公告标题" align="center" prop="noticeTitle" :show-overflow-tooltip="true" />

View File

@@ -45,7 +45,7 @@
</el-row>
</template>
<el-table v-loading="loading" :data="ossConfigList" @selection-change="handleSelectionChange">
<el-table v-loading="loading" border :data="ossConfigList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column v-if="columns[0].visible" label="主建" align="center" prop="ossConfigId" />
<el-table-column v-if="columns[1].visible" label="配置key" align="center" prop="configKey" />
@@ -87,10 +87,18 @@
<el-input v-model="form.configKey" placeholder="请输入配置key" />
</el-form-item>
<el-form-item label="访问站点" prop="endpoint">
<el-input v-model="form.endpoint" placeholder="请输入访问站点" />
<el-input v-model="form.endpoint" placeholder="请输入访问站点">
<template #prefix>
<span style="color: #999">{{ protocol }}</span>
</template>
</el-input>
</el-form-item>
<el-form-item label="自定义域名" prop="domain">
<el-input v-model="form.domain" placeholder="请输入自定义域名" />
<el-input v-model="form.domain" placeholder="请输入自定义域名">
<template #prefix>
<span style="color: #999">{{ protocol }}</span>
</template>
</el-input>
</el-form-item>
<el-form-item label="accessKey" prop="accessKey">
<el-input v-model="form.accessKey" placeholder="请输入accessKey" />
@@ -239,6 +247,8 @@ const data = reactive<PageData<OssConfigForm, OssConfigQuery>>({
const { queryParams, form, rules } = toRefs(data);
const protocol = computed(() => (form.value.isHttps === 'Y' ? 'https://' : 'http://'));
/** 查询对象存储配置列表 */
const getList = async () => {
loading.value = true;
@@ -306,7 +316,7 @@ const submitForm = () => {
};
/** 状态修改 */
const handleStatusChange = async (row: OssConfigVO) => {
let text = row.status === '0' ? '启用' : '停用';
const text = row.status === '0' ? '启用' : '停用';
try {
await proxy?.$modal.confirm('确认要"' + text + '""' + row.configKey + '"配置吗?');
await changeOssConfigStatus(row.ossConfigId, row.status, row.configKey);

View File

@@ -70,6 +70,7 @@
v-if="showTable"
v-loading="loading"
:data="ossList"
border
:header-cell-class-name="handleHeaderClass"
@selection-change="handleSelectionChange"
@header-click="handleHeaderCLick"
@@ -255,9 +256,9 @@ const handleHeaderCLick = (column: any) => {
handleOrderChange(column.property, column.multiOrder);
};
const handleOrderChange = (prop: string, order: string) => {
let orderByArr = queryParams.value.orderByColumn ? queryParams.value.orderByColumn.split(',') : [];
let isAscArr = queryParams.value.isAsc ? queryParams.value.isAsc.split(',') : [];
let propIndex = orderByArr.indexOf(prop);
const orderByArr = queryParams.value.orderByColumn ? queryParams.value.orderByColumn.split(',') : [];
const isAscArr = queryParams.value.isAsc ? queryParams.value.isAsc.split(',') : [];
const propIndex = orderByArr.indexOf(prop);
if (propIndex !== -1) {
if (order) {
//排序里已存在 只修改排序
@@ -306,7 +307,7 @@ const handleDownload = (row: OssVO) => {
};
/** 预览开关按钮 */
const handlePreviewListResource = async (preview: boolean) => {
let text = preview ? '启用' : '停用';
const text = preview ? '启用' : '停用';
try {
await proxy?.$modal.confirm('确认要"' + text + '""预览列表图片"配置吗?');
await proxy?.updateConfigByKey('sys.oss.previewListResource', preview);

View File

@@ -84,7 +84,7 @@
<right-toolbar v-model:show-search="showSearch" @query-table="getList"></right-toolbar>
</el-row>
</template>
<el-table v-loading="loading" :data="postList" @selection-change="handleSelectionChange">
<el-table v-loading="loading" border :data="postList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column v-if="false" label="岗位编号" align="center" prop="postId" />
<el-table-column label="岗位编码" align="center" prop="postCode" />
@@ -170,10 +170,9 @@
</template>
<script setup name="Post" lang="ts">
import { listPost, addPost, delPost, getPost, updatePost } from '@/api/system/post';
import { listPost, addPost, delPost, getPost, updatePost, deptTreeSelect } from '@/api/system/post';
import { PostForm, PostQuery, PostVO } from '@/api/system/post/types';
import { DeptVO } from '@/api/system/dept/types';
import api from '@/api/system/user';
import { DeptTreeVO, DeptVO } from '@/api/system/dept/types';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { sys_normal_disable } = toRefs<any>(proxy?.useDict('sys_normal_disable'));
@@ -186,7 +185,7 @@ const single = ref(true);
const multiple = ref(true);
const total = ref(0);
const deptName = ref('');
const deptOptions = ref<DeptVO[]>([]);
const deptOptions = ref<DeptTreeVO[]>([]);
const deptTreeRef = ref<ElTreeInstance>();
const postFormRef = ref<ElFormInstance>();
const queryFormRef = ref<ElFormInstance>();
@@ -212,6 +211,8 @@ const data = reactive<PageData<PostForm, PostQuery>>({
queryParams: {
pageNum: 1,
pageSize: 10,
deptId: undefined,
belongDeptId: undefined,
postCode: '',
postName: '',
postCategory: '',
@@ -245,7 +246,7 @@ watchEffect(
/** 查询部门下拉树结构 */
const getTreeSelect = async () => {
const res = await api.deptTreeSelect();
const res = await deptTreeSelect();
deptOptions.value = res.data;
};

View File

@@ -33,7 +33,7 @@
<right-toolbar v-model:show-search="showSearch" :search="true" @query-table="getList"></right-toolbar>
</el-row>
</template>
<el-table v-loading="loading" :data="userList" @selection-change="handleSelectionChange">
<el-table v-loading="loading" border :data="userList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="用户名称" prop="userName" :show-overflow-tooltip="true" />
<el-table-column label="用户昵称" prop="nickName" :show-overflow-tooltip="true" />

View File

@@ -55,7 +55,7 @@
</el-row>
</template>
<el-table ref="roleTableRef" v-loading="loading" :data="roleList" @selection-change="handleSelectionChange">
<el-table ref="roleTableRef" border v-loading="loading" :data="roleList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column v-if="false" label="角色编号" prop="roleId" width="120" />
<el-table-column label="角色名称" prop="roleName" :show-overflow-tooltip="true" width="150" />
@@ -323,7 +323,7 @@ const handleSelectionChange = (selection: RoleVO[]) => {
/** 角色状态修改 */
const handleStatusChange = async (row: RoleVO) => {
let text = row.status === '0' ? '启用' : '停用';
const text = row.status === '0' ? '启用' : '停用';
try {
await proxy?.$modal.confirm('确认要"' + text + '""' + row.roleName + '"角色吗?');
await changeRoleStatus(row.roleId, row.status);
@@ -346,11 +346,11 @@ const getMenuTreeselect = async () => {
/** 所有部门节点数据 */
const getDeptAllCheckedKeys = (): any => {
// 目前被选中的部门节点
let checkedKeys = deptRef.value?.getCheckedKeys();
const checkedKeys = deptRef.value?.getCheckedKeys();
// 半选中的部门节点
let halfCheckedKeys = deptRef.value?.getHalfCheckedKeys();
const halfCheckedKeys = deptRef.value?.getHalfCheckedKeys();
if (halfCheckedKeys) {
checkedKeys?.unshift.apply(checkedKeys, halfCheckedKeys);
checkedKeys?.unshift(...halfCheckedKeys);
}
return checkedKeys;
};
@@ -404,14 +404,14 @@ const getRoleDeptTreeSelect = async (roleId: string | number) => {
/** 树权限(展开/折叠)*/
const handleCheckedTreeExpand = (value: boolean, type: string) => {
if (type == 'menu') {
let treeList = menuOptions.value;
const treeList = menuOptions.value;
for (let i = 0; i < treeList.length; i++) {
if (menuRef.value) {
menuRef.value.store.nodesMap[treeList[i].id].expanded = value;
}
}
} else if (type == 'dept') {
let treeList = deptOptions.value;
const treeList = deptOptions.value;
for (let i = 0; i < treeList.length; i++) {
if (deptRef.value) {
deptRef.value.store.nodesMap[treeList[i].id].expanded = value;
@@ -438,11 +438,11 @@ const handleCheckedTreeConnect = (value: any, type: string) => {
/** 所有菜单节点数据 */
const getMenuAllCheckedKeys = (): any => {
// 目前被选中的菜单节点
let checkedKeys = menuRef.value?.getCheckedKeys();
const checkedKeys = menuRef.value?.getCheckedKeys();
// 半选中的菜单节点
let halfCheckedKeys = menuRef.value?.getHalfCheckedKeys();
const halfCheckedKeys = menuRef.value?.getHalfCheckedKeys();
if (halfCheckedKeys) {
checkedKeys?.unshift.apply(checkedKeys, halfCheckedKeys);
checkedKeys?.unshift(...halfCheckedKeys);
}
return checkedKeys;
};

View File

@@ -14,7 +14,7 @@
</el-form-item>
</el-form>
<el-row>
<el-table ref="tableRef" :data="userList" height="260px" @row-click="clickRow" @selection-change="handleSelectionChange">
<el-table ref="tableRef" border :data="userList" height="260px" @row-click="clickRow" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55"></el-table-column>
<el-table-column label="用户名称" prop="userName" :show-overflow-tooltip="true" />
<el-table-column label="用户昵称" prop="nickName" :show-overflow-tooltip="true" />

View File

@@ -51,7 +51,7 @@
</el-row>
</template>
<el-table v-loading="loading" :data="tenantList" @selection-change="handleSelectionChange">
<el-table v-loading="loading" border :data="tenantList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column v-if="false" label="id" align="center" prop="id" />
<el-table-column label="租户编号" align="center" prop="tenantId" />
@@ -245,7 +245,7 @@ const getList = async () => {
// 租户套餐状态修改
const handleStatusChange = async (row: TenantVO) => {
let text = row.status === '0' ? '启用' : '停用';
const text = row.status === '0' ? '启用' : '停用';
try {
await proxy?.$modal.confirm('确认要"' + text + '""' + row.companyName + '"租户吗?');
await changeTenantStatus(row.id, row.tenantId, row.status);
@@ -361,7 +361,7 @@ const handleExport = () => {
/**同步租户字典*/
const handleSyncTenantDict = async () => {
await proxy?.$modal.confirm('确认要同步所有租户字典吗?');
let res = await syncTenantDict();
const res = await syncTenantDict();
proxy?.$modal.msgSuccess(res.msg);
};

View File

@@ -39,7 +39,7 @@
</el-row>
</template>
<el-table v-loading="loading" :data="tenantPackageList" @selection-change="handleSelectionChange">
<el-table v-loading="loading" border :data="tenantPackageList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column v-if="false" label="租户套餐id" align="center" prop="packageId" />
<el-table-column label="套餐名称" align="center" prop="packageName" />
@@ -108,7 +108,7 @@ import {
updateTenantPackage,
changePackageStatus
} from '@/api/system/tenantPackage';
import { treeselect as menuTreeselect, tenantPackageMenuTreeselect } from '@/api/system/menu';
import { tenantPackageMenuTreeselect } from '@/api/system/menu';
import { TenantPkgForm, TenantPkgQuery, TenantPkgVO } from '@/api/system/tenantPackage/types';
import { MenuTreeOption } from '@/api/system/menu/types';
import to from 'await-to-js';
@@ -158,20 +158,14 @@ const data = reactive<PageData<TenantPkgForm, TenantPkgQuery>>({
const { queryParams, form, rules } = toRefs(data);
/** 查询菜单树结构 */
const getMenuTreeselect = async () => {
const { data } = await menuTreeselect();
menuOptions.value = data;
};
// 所有菜单节点数据
const getMenuAllCheckedKeys = (): any => {
// 目前被选中的菜单节点
let checkedKeys = menuTreeRef.value?.getCheckedKeys();
const checkedKeys = menuTreeRef.value?.getCheckedKeys();
// 半选中的菜单节点
let halfCheckedKeys = menuTreeRef.value?.getHalfCheckedKeys();
const halfCheckedKeys = menuTreeRef.value?.getHalfCheckedKeys();
if (halfCheckedKeys) {
checkedKeys?.unshift.apply(checkedKeys, halfCheckedKeys);
checkedKeys?.unshift(...halfCheckedKeys);
}
return checkedKeys;
};
@@ -194,7 +188,7 @@ const getList = async () => {
// 租户套餐状态修改
const handleStatusChange = async (row: TenantPkgVO) => {
let text = row.status === '0' ? '启用' : '停用';
const text = row.status === '0' ? '启用' : '停用';
const [err] = await to(proxy?.$modal.confirm('确认要"' + text + '""' + row.packageName + '"套餐吗?') as Promise<any>);
if (err) {
row.status = row.status === '0' ? '1' : '0';
@@ -241,7 +235,7 @@ const handleSelectionChange = (selection: TenantPkgVO[]) => {
// 树权限(展开/折叠)
const handleCheckedTreeExpand = (value: CheckboxValueType, type: string) => {
if (type == 'menu') {
let treeList = menuOptions.value;
const treeList = menuOptions.value;
for (let i = 0; i < treeList.length; i++) {
if (menuTreeRef.value) {
menuTreeRef.value.store.nodesMap[treeList[i].id].expanded = value as boolean;
@@ -265,9 +259,9 @@ const handleCheckedTreeConnect = (value: CheckboxValueType, type: string) => {
};
/** 新增按钮操作 */
const handleAdd = () => {
const handleAdd = async () => {
reset();
getMenuTreeselect();
await getPackageMenuTreeselect(0);
dialog.visible = true;
dialog.title = '添加租户套餐';
};

View File

@@ -23,6 +23,7 @@
<el-table
ref="tableRef"
v-loading="loading"
border
:row-key="getRowKey"
:data="roles.slice((pageNum - 1) * pageSize, pageNum * pageSize)"
@row-click="clickRow"
@@ -33,7 +34,7 @@
<span>{{ (pageNum - 1) * pageSize + scope.$index + 1 }}</span>
</template>
</el-table-column>
<el-table-column type="selection" :reserve-selection="true" width="55"></el-table-column>
<el-table-column type="selection" :reserve-selection="true" :selectable="checkSelectable" width="55"></el-table-column>
<el-table-column label="角色编号" align="center" prop="roleId" />
<el-table-column label="角色名称" align="center" prop="roleName" />
<el-table-column label="权限字符" align="center" prop="roleKey" />
@@ -80,8 +81,10 @@ const tableRef = ref<ElTableInstance>();
/** 单击选中行数据 */
const clickRow = (row: RoleVO) => {
row.flag = !row.flag;
tableRef.value?.toggleRowSelection(row, row.flag);
if (checkSelectable(row)) {
row.flag = !row.flag;
tableRef.value?.toggleRowSelection(row, row.flag);
}
};
/** 多选框选中数据 */
const handleSelectionChange = (selection: RoleVO[]) => {
@@ -91,6 +94,10 @@ const handleSelectionChange = (selection: RoleVO[]) => {
const getRowKey = (row: RoleVO): string => {
return String(row.roleId);
};
/** 检查角色状态 */
const checkSelectable = (row: RoleVO): boolean => {
return row.status === '0';
};
/** 关闭按钮 */
const close = () => {
const obj: RouteLocationNormalized = {

View File

@@ -27,6 +27,9 @@
<el-form-item label="用户名称" prop="userName">
<el-input v-model="queryParams.userName" placeholder="请输入用户名称" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="用户昵称" prop="nickName">
<el-input v-model="queryParams.nickName" placeholder="请输入用户昵称" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="手机号码" prop="phonenumber">
<el-input v-model="queryParams.phonenumber" placeholder="请输入手机号码" clearable @keyup.enter="handleQuery" />
</el-form-item>
@@ -92,7 +95,7 @@
</el-row>
</template>
<el-table v-loading="loading" :data="userList" @selection-change="handleSelectionChange">
<el-table v-loading="loading" border :data="userList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="50" align="center" />
<el-table-column v-if="columns[0].visible" key="userId" label="用户编号" align="center" prop="userId" />
<el-table-column v-if="columns[1].visible" key="userName" label="用户名称" align="center" prop="userName" :show-overflow-tooltip="true" />
@@ -151,7 +154,7 @@
<el-input v-model="form.nickName" placeholder="请输入用户昵称" maxlength="30" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-col :span="12" v-if="form.userId == null || form.userId != useUserStore().userId">
<el-form-item label="归属部门" prop="deptId">
<el-tree-select
v-model="form.deptId"
@@ -206,7 +209,7 @@
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-col :span="12" v-if="form.userId == null || form.userId != useUserStore().userId">
<el-form-item label="岗位">
<el-select v-model="form.postIds" multiple placeholder="请选择">
<el-option
@@ -219,7 +222,7 @@
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-col :span="12" v-if="form.userId == null || form.userId != useUserStore().userId">
<el-form-item label="角色" prop="roleIds">
<el-select v-model="form.roleIds" filterable multiple placeholder="请选择">
<el-option
@@ -290,13 +293,12 @@ import api from '@/api/system/user';
import { UserForm, UserQuery, UserVO } from '@/api/system/user/types';
import { DeptTreeVO, DeptVO } from '@/api/system/dept/types';
import { RoleVO } from '@/api/system/role/types';
import { PostQuery, PostVO } from '@/api/system/post/types';
import { treeselect } from '@/api/system/dept';
import { PostVO } from '@/api/system/post/types';
import { globalHeaders } from '@/utils/request';
import { to } from 'await-to-js';
import { optionselect } from '@/api/system/post';
import { hasPermi } from '@/directive/permission';
import { checkPermi } from '@/utils/permission';
import { useUserStore } from '@/store/modules/user';
const router = useRouter();
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
@@ -408,7 +410,7 @@ const initData: PageData<UserForm, UserQuery> = {
],
phonenumber: [
{
pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/,
pattern: /^1[3456789][0-9]\d{8}$/,
message: '请输入正确的手机号码',
trigger: 'blur'
}
@@ -498,7 +500,7 @@ const handleDelete = async (row?: UserVO) => {
/** 用户状态修改 */
const handleStatusChange = async (row: UserVO) => {
let text = row.status === '0' ? '启用' : '停用';
const text = row.status === '0' ? '启用' : '停用';
try {
await proxy?.$modal.confirm('确认要"' + text + '""' + row.userName + '"用户吗?');
await api.changeUserStatus(row.userId, row.status);
@@ -613,7 +615,9 @@ const handleUpdate = async (row?: UserForm) => {
dialog.title = '修改用户';
Object.assign(form.value, data.user);
postOptions.value = data.posts;
roleOptions.value = data.roles;
roleOptions.value = Array.from(
new Map([...data.roles, ...data.user.roles].map(role => [role.roleId, role])).values()
);
form.value.postIds = data.postIds;
form.value.roleIds = data.roleIds;
form.value.password = '';
@@ -623,7 +627,17 @@ const handleUpdate = async (row?: UserForm) => {
const submitForm = () => {
userFormRef.value?.validate(async (valid: boolean) => {
if (valid) {
form.value.userId ? await api.updateUser(form.value) : await api.addUser(form.value);
if (form.value.userId) {
// 自己编辑自己的情况下 不允许编辑角色部门岗位
if (form.value.userId == useUserStore().userId) {
form.value.roleIds = null;
form.value.deptId = null;
form.value.postIds = null;
}
await api.updateUser(form.value);
} else {
await api.addUser(form.value);
}
proxy?.$modal.msgSuccess('操作成功');
dialog.visible = false;
await getList();

View File

@@ -1,6 +1,6 @@
<template>
<div>
<el-table :data="devices" style="width: 100%; height: 100%; font-size: 14px">
<el-table :data="devices" border style="width: 100%; height: 100%; font-size: 14px">
<el-table-column label="设备类型" align="center">
<template #default="scope">
<dict-tag :options="sys_device_type" :value="scope.row.deviceType" />

View File

@@ -1,6 +1,6 @@
<template>
<div>
<el-table :data="auths" style="width: 100%; height: 100%; font-size: 14px">
<el-table :data="auths" border style="width: 100%; height: 100%; font-size: 14px">
<el-table-column label="序号" width="50" type="index" />
<el-table-column label="绑定账号平台" width="140" align="center" prop="source" show-overflow-tooltip />
<el-table-column label="头像" width="120" align="center" prop="avatar">

View File

@@ -134,7 +134,7 @@ const beforeUpload = (file: UploadRawFile): any => {
/** 上传图片 */
const uploadImg = async () => {
cropper.value.getCropBlob(async (data: any) => {
let formData = new FormData();
const formData = new FormData();
formData.append('avatarfile', data, options.fileName);
const res = await uploadAvatar(formData);
open.value = false;

View File

@@ -48,7 +48,7 @@ const rule: ElFormRules = {
message: '手机号码不能为空',
trigger: 'blur'
},
{ pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/, message: '请输入正确的手机号码', trigger: 'blur' }
{ pattern: /^1[3456789][0-9]\d{8}$/, message: '请输入正确的手机号码', trigger: 'blur' }
]
};
const rules = ref<ElFormRules>(rule);

View File

@@ -5,7 +5,7 @@
<basic-info-form ref="basicInfo" :info="info" />
</el-tab-pane>
<el-tab-pane label="字段信息" name="columnInfo">
<el-table ref="dragTable" :data="columns" row-key="columnId" :max-height="tableHeight">
<el-table ref="dragTable" border :data="columns" row-key="columnId" :max-height="tableHeight">
<el-table-column label="序号" type="index" min-width="5%" />
<el-table-column label="字段列名" prop="columnName" min-width="10%" :show-overflow-tooltip="true" />
<el-table-column label="字段描述" min-width="10%">

View File

@@ -19,7 +19,7 @@
</el-form-item>
</el-form>
<el-row>
<el-table ref="tableRef" :data="dbTableList" height="260px" @row-click="clickRow" @selection-change="handleSelectionChange">
<el-table ref="tableRef" border :data="dbTableList" height="260px" @row-click="clickRow" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55"></el-table-column>
<el-table-column prop="tableName" label="表名称" :show-overflow-tooltip="true"></el-table-column>
<el-table-column prop="tableComment" label="表描述" :show-overflow-tooltip="true"></el-table-column>

View File

@@ -56,7 +56,7 @@
</el-row>
</template>
<el-table v-loading="loading" :data="tableList" @selection-change="handleSelectionChange">
<el-table v-loading="loading" border :data="tableList" @selection-change="handleSelectionChange">
<el-table-column type="selection" align="center" width="55"></el-table-column>
<el-table-column label="序号" type="index" width="50" align="center">
<template #default="scope">
@@ -104,7 +104,7 @@
<el-link v-copyText="value" v-copyText:callback="copyTextSuccess" :underline="false" icon="DocumentCopy" style="float: right">
&nbsp;复制
</el-link>
<pre>{{ value }}</pre>
<highlightjs :code="value" />
</el-tab-pane>
</el-tabs>
</el-dialog>
@@ -113,8 +113,8 @@
</template>
<script setup name="Gen" lang="ts">
import {delTable, genCode, getDataNames, listTable, previewTable, synchDb} from '@/api/tool/gen';
import {TableQuery, TableVO} from '@/api/tool/gen/types';
import { delTable, genCode, getDataNames, listTable, previewTable, synchDb } from '@/api/tool/gen';
import { TableQuery, TableVO } from '@/api/tool/gen/types';
import router from '@/router';
import ImportTable from './importTable.vue';
@@ -248,3 +248,12 @@ onMounted(() => {
getDataNameList();
});
</script>
<style lang="scss" scoped>
.el-tab-pane {
background-color: #282c34;
.el-link {
color: #fff;
}
}
</style>

View File

@@ -31,10 +31,11 @@
v-loading="loading"
:data="categoryList"
row-key="categoryId"
border
:default-expand-all="isExpandAll"
:tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
>
<el-table-column label="分类名称" prop="categoryName" width="260"/>
<el-table-column label="分类名称" prop="categoryName" width="260" />
<el-table-column label="显示顺序" align="center" prop="orderNum" width="200" />
<el-table-column label="创建时间" align="center" prop="createTime" width="180" />
<el-table-column label="操作" fixed="right" align="center" class-name="small-padding fixed-width">
@@ -77,7 +78,7 @@
</el-col>
</el-row>
</el-form>
<template #footer>
<template #footer>
<div class="dialog-footer">
<el-button :loading="buttonLoading" type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
@@ -88,17 +89,16 @@
</template>
<script setup name="Category" lang="ts">
import { listCategory, getCategory, delCategory, addCategory, updateCategory } from "@/api/workflow/category";
import { listCategory, getCategory, delCategory, addCategory, updateCategory } from '@/api/workflow/category';
import { CategoryVO, CategoryQuery, CategoryForm } from '@/api/workflow/category/types';
type CategoryOption = {
categoryId: number;
categoryName: string;
children?: CategoryOption[];
}
const { proxy } = getCurrentInstance() as ComponentInternalInstance;;
};
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const categoryList = ref<CategoryVO[]>([]);
const categoryOptions = ref<CategoryOption[]>([]);
@@ -109,32 +109,29 @@ const loading = ref(false);
const queryFormRef = ref<ElFormInstance>();
const categoryFormRef = ref<ElFormInstance>();
const categoryTableRef = ref<ElTableInstance>()
const categoryTableRef = ref<ElTableInstance>();
const dialog = reactive<DialogOption>({
visible: false,
title: ''
});
const initFormData: CategoryForm = {
categoryId: undefined,
categoryName: "",
categoryName: '',
parentId: undefined,
orderNum: 0,
}
orderNum: 0
};
const data = reactive<PageData<CategoryForm, CategoryQuery>>({
form: {...initFormData},
form: { ...initFormData },
queryParams: {
categoryName: undefined,
categoryName: undefined
},
rules: {
categoryId: [
{ required: true, message: "流程分类ID不能为空", trigger: "blur" }
],
parentId: [{ required: true, message: "请选择上级分类", trigger: "change" }],
categoryName: [{ required: true, message: "请输入分类名称", trigger: "blur" }]
categoryId: [{ required: true, message: '流程分类ID不能为空', trigger: 'blur' }],
parentId: [{ required: true, message: '请选择上级分类', trigger: 'change' }],
categoryName: [{ required: true, message: '请输入分类名称', trigger: 'blur' }]
}
});
@@ -144,19 +141,19 @@ const { queryParams, form, rules } = toRefs(data);
const getList = async () => {
loading.value = true;
const res = await listCategory(queryParams.value);
const data = proxy?.handleTree<CategoryVO>(res.data, "categoryId", "parentId");
const data = proxy?.handleTree<CategoryVO>(res.data, 'categoryId', 'parentId');
if (data) {
categoryList.value = data;
loading.value = false;
}
}
};
/** 查询流程分类下拉树结构 */
const getTreeselect = async () => {
const res = await listCategory();
categoryOptions.value = [];
// 处理树形数据
const data = proxy?.handleTree<CategoryOption>(res.data, "categoryId", "parentId");
const data = proxy?.handleTree<CategoryOption>(res.data, 'categoryId', 'parentId');
if (data) {
categoryOptions.value = data; // 将处理后的树形数据赋值
}
@@ -166,24 +163,24 @@ const getTreeselect = async () => {
const cancel = () => {
reset();
dialog.visible = false;
}
};
// 表单重置
const reset = () => {
form.value = {...initFormData}
form.value = { ...initFormData };
categoryFormRef.value?.resetFields();
}
};
/** 搜索按钮操作 */
const handleQuery = () => {
getList();
}
};
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value?.resetFields();
handleQuery();
}
};
/** 新增按钮操作 */
const handleAdd = (row?: CategoryVO) => {
@@ -195,22 +192,22 @@ const handleAdd = (row?: CategoryVO) => {
form.value.parentId = undefined;
}
dialog.visible = true;
dialog.title = "添加流程分类";
}
dialog.title = '添加流程分类';
};
/** 展开/折叠操作 */
const handleToggleExpandAll = () => {
isExpandAll.value = !isExpandAll.value;
toggleExpandAll(categoryList.value, isExpandAll.value)
}
toggleExpandAll(categoryList.value, isExpandAll.value);
};
/** 展开/折叠操作 */
const toggleExpandAll = (data: CategoryVO[], status: boolean) => {
data.forEach((item) => {
categoryTableRef.value?.toggleRowExpansion(item, status)
if (item.children && item.children.length > 0) toggleExpandAll(item.children, status)
})
}
categoryTableRef.value?.toggleRowExpansion(item, status);
if (item.children && item.children.length > 0) toggleExpandAll(item.children, status);
});
};
/** 修改按钮操作 */
const handleUpdate = async (row: CategoryVO) => {
@@ -222,8 +219,8 @@ const handleUpdate = async (row: CategoryVO) => {
const res = await getCategory(row.categoryId);
Object.assign(form.value, res.data);
dialog.visible = true;
dialog.title = "修改流程分类";
}
dialog.title = '修改流程分类';
};
/** 提交按钮 */
const submitForm = () => {
@@ -231,25 +228,25 @@ const submitForm = () => {
if (valid) {
buttonLoading.value = true;
if (form.value.categoryId) {
await updateCategory(form.value).finally(() => buttonLoading.value = false);
await updateCategory(form.value).finally(() => (buttonLoading.value = false));
} else {
await addCategory(form.value).finally(() => buttonLoading.value = false);
await addCategory(form.value).finally(() => (buttonLoading.value = false));
}
proxy?.$modal.msgSuccess("操作成功");
proxy?.$modal.msgSuccess('操作成功');
dialog.visible = false;
getList();
}
});
}
};
/** 删除按钮操作 */
const handleDelete = async (row: CategoryVO) => {
await proxy?.$modal.confirm('是否确认删除"' + row.categoryName + '"的分类?');
loading.value = true;
await delCategory(row.categoryId).finally(() => loading.value = false);
await delCategory(row.categoryId).finally(() => (loading.value = false));
await getList();
proxy?.$modal.msgSuccess("删除成功");
}
proxy?.$modal.msgSuccess('删除成功');
};
onMounted(() => {
getList();

View File

@@ -222,7 +222,7 @@ const handleExport = () => {
const handleCancelProcessApply = async (id: string) => {
await proxy?.$modal.confirm('是否确认撤销当前单据?');
loading.value = true;
let data = {
const data = {
businessId: id,
message: '申请人撤销流程!'
};

View File

@@ -1,26 +1,32 @@
<template>
<div class="p-2">
<el-card shadow="never">
<div style="display: flex; justify-content: space-between">
<div>
<el-button v-if="submitButtonShow" :loading="buttonLoading" type="info" @click="submitForm('draft')">暂存</el-button>
<el-button v-if="submitButtonShow" :loading="buttonLoading" type="primary" @click="submitForm('submit')"> </el-button>
<el-button v-if="approvalButtonShow" :loading="buttonLoading" type="primary" @click="approvalVerifyOpen">审批</el-button>
<el-button v-if="form && form.id && form.status !== 'draft'" type="primary" @click="handleApprovalRecord">流程进度</el-button>
</div>
<div>
<el-button style="float: right" @click="goBack()">返回</el-button>
</div>
</div>
<!-- mode用于直接后端发起流程 不同接口实现方式可查看具体后端代码 -->
<!-- 默认前端发起 前端发起更多样性 比如可以选审批人 选抄送人 上传附件等等 后端发起需要用户自行编写代码传这些参数 -->
<approvalButton
@submitForm="submitForm"
@approvalVerifyOpen="approvalVerifyOpen"
@handleApprovalRecord="handleApprovalRecord"
:buttonLoading="buttonLoading"
:id="form.id"
:status="form.status"
:pageType="routeParams.type"
:mode="false"
/>
</el-card>
<el-card shadow="never" style="height: 78vh; overflow-y: auto">
<el-form ref="leaveFormRef" v-loading="loading" :disabled="routeParams.type === 'view'" :model="form" :rules="rules" label-width="80px">
<el-form-item label="流程定义" v-if="routeParams.type === 'add'">
<el-select v-model="flowCode" placeholder="选择流程定义" style="width: 100%">
<el-option v-for="item in flowCodeOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<el-form-item label="请假类型" prop="leaveType">
<el-select v-model="form.leaveType" placeholder="请选择请假类型" style="width: 100%">
<el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<el-form-item label="请假时间">
<el-form-item label="请假时间" required>
<el-date-picker
v-model="leaveTime"
value-format="YYYY-MM-DD HH:mm:ss"
@@ -44,26 +50,16 @@
<submitVerify ref="submitVerifyRef" :task-variables="taskVariables" @submit-callback="submitCallback" />
<!-- 审批记录 -->
<approvalRecord ref="approvalRecordRef" />
<el-dialog v-model="dialogVisible.visible" :title="dialogVisible.title" :before-close="handleClose" width="500">
<el-select v-model="flowCode" placeholder="Select" style="width: 240px">
<el-option v-for="item in flowCodeOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
<template #footer>
<div class="dialog-footer">
<el-button @click="handleClose">取消</el-button>
<el-button type="primary" @click="submitFlow()"> 确认 </el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup name="Leave" lang="ts">
import { addLeave, getLeave, updateLeave } from '@/api/workflow/leave';
import { addLeave, getLeave, submitAndFlowStart, updateLeave } from '@/api/workflow/leave';
import { LeaveForm, LeaveQuery, LeaveVO } from '@/api/workflow/leave/types';
import { startWorkFlow } from '@/api/workflow/task';
import SubmitVerify from '@/components/Process/submitVerify.vue';
import ApprovalRecord from '@/components/Process/approvalRecord.vue';
import ApprovalButton from '@/components/Process/approvalButton.vue';
import { AxiosResponse } from 'axios';
import { StartProcessBo } from '@/api/workflow/workflowCommon/types';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
@@ -117,13 +113,9 @@ const flowCodeOptions = [
label: '请假申请-排他并行会签'
}
];
// 自定义流程可不选择 直接填写flowCode 例如 'leave1'
const flowCode = ref<string>('leave1');
const flowCode = ref<string>('');
const dialogVisible = reactive<DialogOption>({
visible: false,
title: '流程定义'
});
//提交组件
const submitVerifyRef = ref<InstanceType<typeof SubmitVerify>>();
//审批记录组件
@@ -134,9 +126,11 @@ const leaveFormRef = ref<ElFormInstance>();
const submitFormData = ref<StartProcessBo>({
businessId: '',
flowCode: '',
variables: {}
variables: {},
flowInstanceBizExtBo: {}
});
const taskVariables = ref<Record<string, any>>({});
const flowInstanceBizExtBo = ref<Record<string, any>>({});
const initFormData: LeaveForm = {
id: undefined,
@@ -163,11 +157,6 @@ const data = reactive<PageData<LeaveForm, LeaveQuery>>({
}
});
const handleClose = () => {
dialogVisible.visible = false;
flowCode.value = '';
buttonLoading.value = false;
};
const { form, rules } = toRefs(data);
/** 表单重置 */
@@ -199,7 +188,7 @@ const getInfo = () => {
};
/** 提交按钮 */
const submitForm = (status: string) => {
const submitForm = (status: string, mode: boolean) => {
if (leaveTime.value.length === 0) {
proxy?.$modal.msgError('请假时间不能为空');
return;
@@ -210,29 +199,30 @@ const submitForm = (status: string) => {
form.value.endDate = leaveTime.value[1];
if (valid) {
buttonLoading.value = true;
let res: AxiosResponse<LeaveVO>;
if (form.value.id) {
res = await updateLeave(form.value);
} else {
res = await addLeave(form.value);
}
form.value = res.data;
if (status === 'draft') {
// 设置后端发起和不等于草稿状态 直接走流程发起
if (mode && status != 'draft') {
const res = await submitAndFlowStart(form.value).finally(() => (buttonLoading.value = false));
form.value = res.data;
buttonLoading.value = false;
proxy?.$modal.msgSuccess('暂存成功');
proxy?.$modal.msgSuccess('操作成功');
proxy.$tab.closePage(proxy.$route);
proxy.$router.go(-1);
} else {
if ((form.value.status === 'draft' && (flowCode.value === '' || flowCode.value === null)) || routeParams.value.type === 'add') {
flowCode.value = flowCodeOptions[0].value;
dialogVisible.visible = true;
return;
let res;
if (form.value.id) {
res = await updateLeave(form.value).finally(() => (buttonLoading.value = false));
} else {
res = await addLeave(form.value).finally(() => (buttonLoading.value = false));
}
//说明启动过先随意穿个参数
if (flowCode.value === '' || flowCode.value === null) {
flowCode.value = 'xx';
form.value = res.data;
if (status === 'draft') {
buttonLoading.value = false;
proxy?.$modal.msgSuccess('暂存成功');
proxy.$tab.closePage(proxy.$route);
proxy.$router.go(-1);
} else {
await handleStartWorkFlow(res.data);
}
await handleStartWorkFlow(res.data);
}
}
});
@@ -241,10 +231,6 @@ const submitForm = (status: string) => {
}
};
const submitFlow = async () => {
handleStartWorkFlow(form.value);
dialogVisible.visible = false;
};
//提交申请
const handleStartWorkFlow = async (data: LeaveForm) => {
try {
@@ -252,10 +238,18 @@ const handleStartWorkFlow = async (data: LeaveForm) => {
submitFormData.value.businessId = data.id;
//流程变量
taskVariables.value = {
// leave2/6 使用的流程变量
leaveDays: data.leaveDays,
// leave4/5 使用的流程变量
userList: ['1', '3', '4']
};
//流程实例业务扩展字段
flowInstanceBizExtBo.value = {
businessTitle: '请假申请',
businessCode: data.applyCode
};
submitFormData.value.variables = taskVariables.value;
submitFormData.value.flowInstanceBizExtBo = flowInstanceBizExtBo.value;
const resp = await startWorkFlow(submitFormData.value);
if (submitVerifyRef.value) {
buttonLoading.value = false;
@@ -274,30 +268,10 @@ const submitCallback = async () => {
await proxy.$tab.closePage(proxy.$route);
proxy.$router.go(-1);
};
//返回
const goBack = () => {
proxy.$tab.closePage(proxy.$route);
proxy.$router.go(-1);
};
//审批
const approvalVerifyOpen = async () => {
submitVerifyRef.value.openDialog(routeParams.value.taskId);
};
//校验提交按钮是否显示
const submitButtonShow = computed(() => {
return (
routeParams.value.type === 'add' ||
(routeParams.value.type === 'update' &&
form.value.status &&
(form.value.status === 'draft' || form.value.status === 'cancel' || form.value.status === 'back'))
);
});
//校验审批按钮是否显示
const approvalButtonShow = computed(() => {
return routeParams.value.type === 'approval' && form.value.status && form.value.status === 'waiting';
});
onMounted(() => {
nextTick(async () => {

View File

@@ -4,7 +4,7 @@
</div>
</template>
<script setup name="WarmFlow">
<script setup name="WarmFlow" lang="ts">
const { proxy } = getCurrentInstance();
import { onMounted } from 'vue';
import { getToken } from '@/utils/auth';
@@ -24,12 +24,12 @@ const iframeLoaded = () => {
};
};
const open = async (definitionId, disabled) => {
let url = baseUrl + `/warm-flow-ui/index.html?id=${definitionId}&disabled=${disabled}`;
const url = baseUrl + `/warm-flow-ui/index.html?id=${definitionId}&onlyDesignShow=true`;
iframeUrl.value = url + '&Authorization=Bearer ' + getToken() + '&clientid=' + import.meta.env.VITE_APP_CLIENT_ID;
};
/** 关闭按钮 */
function close() {
const obj = { path: '/workflow/processDefinition', query: {activeName: proxy.$route.query.activeName}};
const obj = { path: '/workflow/processDefinition', query: { activeName: proxy.$route.query.activeName } };
proxy.$tab.closeOpenPage(obj);
}

View File

@@ -159,7 +159,7 @@
<!-- 新增/编辑流程定义 -->
<el-dialog v-model="modelDialog.visible" :title="modelDialog.title" width="650px" append-to-body :close-on-click-modal="false">
<template #footer>
<el-form ref="defFormRef" :model="form" :rules="rules" label-width="110px">
<el-form ref="defFormRef" :model="form" :rules="rules" label-width="120px">
<el-form-item label="流程类别" prop="category">
<el-tree-select
v-model="form.category"
@@ -178,6 +178,21 @@
<el-form-item label="流程名称" prop="flowName">
<el-input v-model="form.flowName" placeholder="请输入流程名称" maxlength="100" show-word-limit />
</el-form-item>
<el-form-item label="设计器模式" prop="modelValue">
<el-radio-group v-model="form.modelValue" :disabled="!!form.id">
<el-radio value="CLASSICS" size="large" border>经典模式</el-radio>
<el-radio value="MIMIC" size="large" border>仿钉钉模式</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="流程配置">
<el-checkbox v-model="autoPass" label="下一节点执行人是当前任务处理人自动审批" />
</el-form-item>
<el-form-item label="是否动态表单" prop="formCustom">
<el-radio-group v-model="form.formCustom">
<el-radio value="Y" size="large" border></el-radio>
<el-radio value="N" size="large" border></el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="表单路径" prop="formPath">
<el-input v-model="form.formPath" placeholder="请输入表单路径" maxlength="100" show-word-limit />
</el-form-item>
@@ -197,7 +212,7 @@ import { categoryTree } from '@/api/workflow/category';
import { CategoryTreeVO } from '@/api/workflow/category/types';
import { FlowDefinitionQuery, FlowDefinitionVo, FlowDefinitionForm } from '@/api/workflow/definition/types';
import { UploadRequestOptions, TabsPaneContext } from 'element-plus';
import { ElMessageBoxOptions } from "element-plus/es/components/message-box/src/message-box.type";
import { ElMessageBoxOptions } from 'element-plus/es/components/message-box/src/message-box.type';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
@@ -215,6 +230,7 @@ const uploadDialogLoading = ref(false);
const processDefinitionList = ref<FlowDefinitionVo[]>([]);
const categoryOptions = ref<CategoryTreeVO[]>([]);
const categoryName = ref('');
const autoPass = ref(false);
/** 部署文件分类选择 */
const selectCategory = ref();
const defFormRef = ref<ElFormInstance>();
@@ -245,6 +261,8 @@ const queryParams = ref<FlowDefinitionQuery>({
const rules = {
category: [{ required: true, message: '分类名称不能为空', trigger: 'blur' }],
flowName: [{ required: true, message: '流程定义名称不能为空', trigger: 'blur' }],
formCustom: [{ required: true, message: '请选择是否动态表单', trigger: 'change' }],
modelValue: [{ required: true, message: '设计器模式不能为空', trigger: 'change' }],
flowCode: [{ required: true, message: '流程定义编码不能为空', trigger: 'blur' }]
};
const initFormData: FlowDefinitionForm = {
@@ -252,7 +270,10 @@ const initFormData: FlowDefinitionForm = {
flowName: '',
flowCode: '',
category: '',
formPath: ''
ext: '',
formPath: '',
formCustom: '',
modelValue: ''
};
//流程定义参数
const form = ref<FlowDefinitionForm>({
@@ -260,7 +281,10 @@ const form = ref<FlowDefinitionForm>({
flowName: '',
flowCode: '',
category: '',
formPath: ''
ext: '',
formPath: '',
formCustom: '',
modelValue: ''
});
onMounted(() => {
getPageList();
@@ -326,9 +350,10 @@ const handleSelectionChange = (selection: any) => {
};
//分页
const getPageList = async () => {
let query = proxy.$route.query;
const query = proxy.$route.query;
if (query.activeName) {
activeName.value = query.activeName;
proxy.$route.query.activeName = '';
}
if (activeName.value === '0') {
getList();
@@ -411,7 +436,7 @@ const handlerBeforeUpload = () => {
};
//部署文件
const handlerImportDefinition = (data: UploadRequestOptions): XMLHttpRequest => {
let formData = new FormData();
const formData = new FormData();
uploadDialogLoading.value = true;
formData.append('file', data.file);
formData.append('category', selectCategory.value);
@@ -466,6 +491,11 @@ const reset = () => {
*/
const handleAdd = async () => {
reset();
if (queryParams.value.category != '') {
form.value.category = queryParams.value.category;
}
form.value.modelValue = 'CLASSICS';
form.value.formCustom = 'N';
modelDialog.visible = true;
modelDialog.title = '新增流程';
};
@@ -475,6 +505,13 @@ const handleUpdate = async (row?: FlowDefinitionVo) => {
const id = row?.id || ids.value[0];
const res = await getInfo(id);
Object.assign(form.value, res.data);
autoPass.value = false;
if (form.value.ext != null && form.value.ext != '') {
const extJson = JSON.parse(form.value.ext);
if (extJson.autoPass != null && extJson.autoPass != '') {
autoPass.value = extJson.autoPass;
}
}
modelDialog.visible = true;
modelDialog.title = '修改流程';
};
@@ -483,10 +520,14 @@ const handleSubmit = async () => {
defFormRef.value.validate(async (valid: boolean) => {
if (valid) {
loading.value = true;
const ext = {};
ext.autoPass = autoPass.value;
form.value.ext = JSON.stringify(ext);
if (form.value.id) {
await edit(form.value).finally(() => (loading.value = false));
} else {
await add(form.value).finally(() => (loading.value = false));
activeName.value = '1';
}
proxy?.$modal.msgSuccess('操作成功');
modelDialog.visible = false;

View File

@@ -20,14 +20,6 @@
</el-card>
</el-col>
<el-col :lg="20" :xs="24">
<!-- <div class="mb-[10px]">
<el-card shadow="hover" class="text-center">
<el-radio-group v-model="tab" @change="changeTab(tab)">
<el-radio-button value="running">运行中</el-radio-button>
<el-radio-button value="finish">已完成</el-radio-button>
</el-radio-group>
</el-card>
</div>-->
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover">
@@ -69,15 +61,17 @@
<el-table v-loading="loading" border :data="processInstanceList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column align="center" type="index" label="序号" width="60"></el-table-column>
<el-table-column :show-overflow-tooltip="true" align="center" label="流程定义名称">
<el-table-column :show-overflow-tooltip="true" prop="businessCode" align="center" label="业务编码"></el-table-column>
<el-table-column :show-overflow-tooltip="true" prop="businessTitle" align="center" label="业务标题"></el-table-column>
<el-table-column :show-overflow-tooltip="true" align="center" width="120" label="流程定义名称">
<template #default="scope">
<span>{{ scope.row.flowName }}v{{ scope.row.version }}</span>
</template>
</el-table-column>
<el-table-column align="center" prop="nodeName" label="任务名称"></el-table-column>
<el-table-column align="center" prop="flowCode" label="流程定义编码"></el-table-column>
<el-table-column align="center" prop="flowCode" width="120" label="流程定义编码"></el-table-column>
<el-table-column align="center" prop="categoryName" label="流程分类"></el-table-column>
<el-table-column align="center" prop="createByName" label="申请人"></el-table-column>
<el-table-column align="center" prop="nodeName" label="任务名称"></el-table-column>
<el-table-column align="center" prop="createByName" :show-overflow-tooltip="true" label="申请人"></el-table-column>
<el-table-column align="center" prop="version" label="版本号" width="90">
<template #default="scope"> v{{ scope.row.version }}.0</template>
</el-table-column>
@@ -87,14 +81,14 @@
<el-tag v-else type="danger">挂起</el-tag>
</template>
</el-table-column>
<el-table-column align="center" label="流程状态" min-width="70">
<el-table-column align="center" label="流程状态" min-width="80">
<template #default="scope">
<dict-tag :options="wf_business_status" :value="scope.row.flowStatus"></dict-tag>
</template>
</el-table-column>
<el-table-column align="center" prop="createTime" label="启动时间" width="160"></el-table-column>
<el-table-column v-if="tab === 'finish'" align="center" prop="updateTime" label="结束时间" width="160"></el-table-column>
<el-table-column label="操作" align="center" :width="165">
<el-table-column label="操作" align="center" :width="165" fixed="right">
<template #default="scope">
<el-row v-if="tab === 'running'" :gutter="10" class="mb8">
<el-col :span="1.5">
@@ -154,8 +148,8 @@
</el-table>
</el-dialog>
<!-- 流程变量开始 -->
<el-dialog v-model="variableVisible" draggable title="流程变量" width="60%" :close-on-click-modal="false">
<el-card v-loading="variableLoading" class="box-card">
<el-dialog v-model="variableVisible" v-if="variableVisible" draggable title="流程变量" width="60%" :close-on-click-modal="false">
<el-card v-loading="variableLoading">
<template #header>
<div class="clearfix">
<span
@@ -167,6 +161,19 @@
<VueJsonPretty :data="formatToJsonObject(variables)" />
</div>
</el-card>
<el-card v-loading="variableLoading">
<el-form ref="ruleFormRef" :model="form" :inline="true" :rules="rules" label-width="120px">
<el-form-item label="变量KEY" prop="key">
<el-input v-model="form.key" placeholder="请输入变量KEY" />
</el-form-item>
<el-form-item label="变量值" prop="value">
<el-input v-model="form.value" placeholder="请输入变量值" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleVariable(ruleFormRef)">确认</el-button>
</el-form-item>
</el-form>
</el-card>
</el-dialog>
<!-- 流程变量结束 -->
@@ -176,7 +183,15 @@
</template>
<script setup lang="ts">
import { pageByRunning, pageByFinish, deleteByInstanceIds, instanceVariable, invalid } from '@/api/workflow/instance';
import {
pageByRunning,
pageByFinish,
deleteByInstanceIds,
deleteHisByInstanceIds,
instanceVariable,
invalid,
updateVariable
} from '@/api/workflow/instance';
import { categoryTree } from '@/api/workflow/category';
import { CategoryTreeVO } from '@/api/workflow/category/types';
import { FlowInstanceQuery, FlowInstanceVO } from '@/api/workflow/instance/types';
@@ -185,6 +200,7 @@ import { RouterJumpVo } from '@/api/workflow/workflowCommon/types';
import VueJsonPretty from 'vue-json-pretty';
import 'vue-json-pretty/lib/styles.css';
import UserSelect from '@/components/UserSelect/index.vue';
import { ElForm, FormInstance } from 'element-plus';
//审批记录组件
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { wf_business_status } = toRefs<any>(proxy?.useDict('wf_business_status'));
@@ -192,7 +208,12 @@ const queryFormRef = ref<ElFormInstance>();
const categoryTreeRef = ref<ElTreeInstance>();
import { ref } from 'vue';
import { UserVO } from '@/api/system/user/types';
const form = ref<Record<string, any>>({
instanceId: undefined,
key: undefined,
value: undefined
});
const ruleFormRef = ref<FormInstance>();
const userSelectRef = ref<InstanceType<typeof UserSelect>>();
// 遮罩层
const loading = ref(true);
@@ -208,6 +229,8 @@ const multiple = ref(true);
const showSearch = ref(true);
// 总条数
const total = ref(0);
// 实例id
const instanceId = ref(undefined);
// 流程变量是否显示
const variableVisible = ref(false);
@@ -333,7 +356,7 @@ const handleDelete = async (row: FlowInstanceVO) => {
await deleteByInstanceIds(instanceIdList).finally(() => (loading.value = false));
getProcessInstanceRunningList();
} else {
await deleteByInstanceIds(instanceIdList).finally(() => (loading.value = false));
await deleteHisByInstanceIds(instanceIdList).finally(() => (loading.value = false));
getProcessInstanceFinishList();
}
proxy?.$modal.msgSuccess('删除成功');
@@ -352,7 +375,7 @@ const handleInvalid = async (row: FlowInstanceVO) => {
await proxy?.$modal.confirm('是否确认作废?');
loading.value = true;
if ('running' === tab.value) {
let param = {
const param = {
id: row.id,
comment: deleteReason.value
};
@@ -378,12 +401,16 @@ const handleView = (row) => {
//查询流程变量
const handleInstanceVariable = async (row: FlowInstanceVO) => {
instanceId.value = row.id;
variableLoading.value = true;
variableVisible.value = true;
processDefinitionName.value = row.flowName;
let data = await instanceVariable(row.id);
const data = await instanceVariable(row.id);
variables.value = data.data.variable;
variableLoading.value = false;
form.value.instanceId = undefined;
form.value.key = undefined;
form.value.value = undefined;
};
/**
@@ -405,12 +432,45 @@ const openUserSelect = () => {
//确认选择申请人
const userSelectCallBack = (data: UserVO[]) => {
userSelectCount.value = 0;
selectUserIds.value = [];
queryParams.value.createByIds = [];
if (data && data.length > 0) {
userSelectCount.value = data.length;
selectUserIds.value = data.map((item) => item.userId);
queryParams.value.createByIds = selectUserIds.value;
}
};
const rules = reactive<Record<string, any>>({
key: [
{
required: true,
message: '请输入KEY',
trigger: 'blur'
}
],
value: [
{
required: true,
message: '请输入变量值',
trigger: 'blur'
}
]
});
const handleVariable = async (formEl: FormInstance | undefined) => {
await formEl.validate(async (valid, fields) => {
if (valid) {
form.value.instanceId = instanceId.value;
await proxy?.$modal.confirm('是否确认提交?');
await updateVariable(form.value);
proxy?.$modal.msgSuccess('操作成功');
const data = await instanceVariable(instanceId.value);
variables.value = data.data.variable;
}
});
};
onMounted(() => {
getProcessInstanceRunningList();
getTreeselect();

View File

@@ -0,0 +1,360 @@
<template>
<div class="p-2">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover">
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
<el-form-item label="组件名称" prop="componentName">
<el-input v-model="queryParams.componentName" placeholder="请输入组件名称" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="方法名" prop="methodName">
<el-input v-model="queryParams.methodName" placeholder="请输入方法名" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
</el-card>
</div>
</transition>
<el-card shadow="never">
<template #header>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['workflow:spel:add']">新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['workflow:spel:edit']">修改</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['workflow:spel:remove']">删除</el-button>
</el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
</template>
<el-table v-loading="loading" border :data="spelList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="序号" type="index" width="60" align="center">
<template #default="scope">
<span>{{ (queryParams.pageNum - 1) * queryParams.pageSize + scope.$index + 1 }}</span>
</template>
</el-table-column>
<el-table-column label="组件名称" align="center">
<template #default="scope">
{{ scope.row.componentName || '-' }}
</template>
</el-table-column>
<el-table-column label="方法名称" align="center">
<template #default="scope">
{{ scope.row.methodName || '-' }}
</template>
</el-table-column>
<el-table-column label="参数名称" align="center">
<template #default="scope">
{{ scope.row.methodParams || '-' }}
</template>
</el-table-column>
<el-table-column label="SPEL表达式" align="center" prop="viewSpel" />
<el-table-column label="状态" align="center" prop="status">
<template #default="scope">
<el-tag v-if="scope.row.status === '0'">正常</el-tag>
<el-tag v-else>停用</el-tag>
</template>
</el-table-column>
<el-table-column label="备注" align="center">
<template #default="scope">
{{ scope.row.remark || '-' }}
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template #default="scope">
<el-tooltip content="修改" placement="top">
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['workflow:spel:edit']"></el-button>
</el-tooltip>
<el-tooltip content="删除" placement="top">
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['workflow:spel:remove']"></el-button>
</el-tooltip>
</template>
</el-table-column>
</el-table>
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
</el-card>
<!-- 添加或修改流程spel表达式定义对话框 -->
<el-dialog :title="dialog.title" v-model="dialog.visible" width="550px" append-to-body>
<el-form ref="spelFormRef" :model="form" :rules="rules" label-width="100px">
<!-- 组件名称 -->
<el-form-item label="组件名称" prop="componentName">
<el-input v-model="form.componentName" placeholder="请输入组件名称" @input="updateViewSpel" />
<template #label>
<span>
<el-tooltip content="注册到Spring容器中的组件名spelRuleComponent" placement="top">
<el-icon><question-filled /></el-icon>
</el-tooltip>
组件名称
</span>
</template>
</el-form-item>
<el-form-item label="方法名称" prop="methodName">
<el-input v-model="form.methodName" placeholder="请输入方法名称" @input="updateViewSpel" />
<template #label>
<span>
<el-tooltip content="组件中的方法名称selectDeptLeaderById" placement="top">
<el-icon><question-filled /></el-icon>
</el-tooltip>
方法名称
</span>
</template>
</el-form-item>
<el-form-item label="方法参数" prop="methodParams">
<el-input v-model="form.methodParams" placeholder="请输入方法参数" @input="updateViewSpel" />
<template #label>
<span>
<el-tooltip content="方法参数deptId, 多个使用 ',' 分隔,单参数变量仅支持单个方法参数" placement="top">
<el-icon><question-filled /></el-icon>
</el-tooltip>
方法参数
</span>
</template>
</el-form-item>
<!-- 改为只读文本展示 -->
<el-form-item label="SPEL表达式">
<span class="preview-box">
{{ form.viewSpel || '例如:#{@组件名.方法名(#方法参数)} 或 ${方法参数}' }}
</span>
</el-form-item>
<el-form-item label="状态" prop="status">
<el-radio-group v-model="form.status">
<el-radio v-for="dict in sys_normal_disable" :key="dict.value" :label="dict.value">
{{ dict.label }}
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="form.remark" type="textarea" :rows="3" placeholder="请输入备注" />
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button :loading="buttonLoading" type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup name="Spel" lang="ts">
import { listSpel, getSpel, delSpel, addSpel, updateSpel } from '@/api/workflow/spel';
import { SpelVO, SpelQuery, SpelForm } from '@/api/workflow/spel/types';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { sys_show_hide, sys_normal_disable } = toRefs<any>(proxy?.useDict('sys_show_hide', 'sys_normal_disable'));
const spelList = ref<SpelVO[]>([]);
const buttonLoading = ref(false);
const loading = ref(true);
const showSearch = ref(true);
const ids = ref<Array<string | number>>([]);
const single = ref(true);
const multiple = ref(true);
const total = ref(0);
const queryFormRef = ref<ElFormInstance>();
const spelFormRef = ref<ElFormInstance>();
const dialog = reactive<DialogOption>({
visible: false,
title: ''
});
const initFormData: SpelForm = {
id: undefined,
componentName: undefined,
methodName: undefined,
methodParams: undefined,
viewSpel: undefined,
status: '0',
remark: undefined,
}
const data = reactive<PageData<SpelForm, SpelQuery>>({
form: {...initFormData},
queryParams: {
pageNum: 1,
pageSize: 10,
componentName: undefined,
methodName: undefined,
methodParams: undefined,
viewSpel: undefined,
status: '0',
params: {
}
},
rules: {
status: [
{ required: true, message: "状态不能为空", trigger: "change" }
],
}
});
const { queryParams, form, rules } = toRefs(data);
/** 查询流程spel表达式定义列表 */
const getList = async () => {
loading.value = true;
const res = await listSpel(queryParams.value);
spelList.value = res.rows;
total.value = res.total;
loading.value = false;
}
/** 取消按钮 */
const cancel = () => {
reset();
dialog.visible = false;
}
/** 表单重置 */
const reset = () => {
form.value = {...initFormData};
spelFormRef.value?.resetFields();
}
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.value.pageNum = 1;
getList();
}
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value?.resetFields();
handleQuery();
}
/** 多选框选中数据 */
const handleSelectionChange = (selection: SpelVO[]) => {
ids.value = selection.map(item => item.id);
single.value = selection.length != 1;
multiple.value = !selection.length;
}
/** 新增按钮操作 */
const handleAdd = () => {
reset();
dialog.visible = true;
dialog.title = "添加流程spel表达式定义";
}
/** 修改按钮操作 */
const handleUpdate = async (row?: SpelVO) => {
reset();
const _id = row?.id || ids.value[0]
const res = await getSpel(_id);
Object.assign(form.value, res.data);
dialog.visible = true;
dialog.title = "修改流程spel表达式定义";
}
/** 提交按钮 */
const submitForm = () => {
spelFormRef.value?.validate(async (valid: boolean) => {
if (valid) {
buttonLoading.value = true;
if (form.value.id) {
await updateSpel(form.value).finally(() => buttonLoading.value = false);
} else {
await addSpel(form.value).finally(() => buttonLoading.value = false);
}
proxy?.$modal.msgSuccess("操作成功");
dialog.visible = false;
await getList();
}
});
}
/** 删除按钮操作 */
const handleDelete = async (row?: SpelVO) => {
const _ids = row?.id || ids.value;
await proxy?.$modal.confirm('是否确认删除流程spel表达式定义编号为"' + _ids + '"的数据项?').finally(() => loading.value = false);
await delSpel(_ids);
proxy?.$modal.msgSuccess("删除成功");
await getList();
}
/** 控制是否显示 viewSpel 输入框 */
const showViewSpelInput = ref(false);
/** 更新 spel 预览值并决定是否显示输入框 */
const updateViewSpel = () => {
const comp = (form.value.componentName || '').trim();
const method = (form.value.methodName || '').trim();
const paramStr = (form.value.methodParams || '').trim();
if (!comp && !method && !paramStr) {
form.value.viewSpel = '';
return;
}
// 替换变量值:只有参数存在,组件和方法都不存在
if (!comp && !method && paramStr) {
const paramList = paramStr.split(',')
.map(p => p.trim())
.filter(p => p.length > 0);
if (paramList.length === 1) {
form.value.viewSpel = `\${${paramList[0]}}`;
return;
}
}
// 如果缺少组件或方法,提示填写
if (!comp || !method) {
form.value.viewSpel = '请填写组件名称和方法名';
return;
}
let paramList = [];
if (paramStr) {
// 分割并过滤掉空参数
paramList = paramStr.split(',')
.map(p => p.trim())
.filter(p => p.length > 0);
}
const paramPart = paramList.length > 0
? '(' + paramList.map(p => `#${p}`).join(',') + ')'
: '()';
form.value.viewSpel = `#{@${comp}.${method}${paramPart}}`;
};
/** 监听所有字段变化 */
watch(
() => [form.value.componentName, form.value.methodName, form.value.methodParams],
updateViewSpel
);
onMounted(() => {
getList();
});
</script>
<style lang="scss">
.preview-box {
width: 100%;
padding: 10px 12px;
background-color: #f5f7fa;
border-radius: 4px;
color: #333;
font-family: monospace; /* 等宽字体更清晰 */
white-space: nowrap; /* 禁止换行 */
overflow-x: auto; /* 超出宽度时显示水平滚动条 */
min-height: 36px; /* 与 el-input 高度对齐 */
line-height: 1.5;
}
</style>

View File

@@ -27,7 +27,8 @@
<template #header>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5" v-if="tab === 'waiting'">
<el-button type="primary" plain icon="Edit" :disabled="multiple" @click="handleUpdate">修改办理人 </el-button>
<el-button type="primary" plain icon="Edit" :disabled="multiple" @click="handleUserOpen()">修改办理人 </el-button>
<el-button type="primary" plain icon="Bell" :disabled="multiple" @click="handleUrgeTaskOpen()">催办 </el-button>
</el-col>
<right-toolbar v-model:show-search="showSearch" @query-table="handleQuery"></right-toolbar>
</el-row>
@@ -38,14 +39,16 @@
<el-table v-loading="loading" border :data="taskList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column align="center" type="index" label="序号" width="60"></el-table-column>
<el-table-column :show-overflow-tooltip="true" prop="flowName" align="center" label="流程定义名称"></el-table-column>
<el-table-column align="center" prop="flowCode" label="流程定义编码"></el-table-column>
<el-table-column :show-overflow-tooltip="true" prop="businessCode" align="center" label="业务编码"></el-table-column>
<el-table-column :show-overflow-tooltip="true" prop="businessTitle" align="center" label="业务标题"></el-table-column>
<el-table-column :show-overflow-tooltip="true" prop="flowName" align="center" width="120" label="流程定义名称"></el-table-column>
<el-table-column align="center" prop="flowCode" width="120" label="流程定义编码"></el-table-column>
<el-table-column align="center" prop="categoryName" label="流程分类"></el-table-column>
<el-table-column align="center" prop="version" label="版本号" width="90">
<template #default="scope"> v{{ scope.row.version }}.0</template>
</el-table-column>
<el-table-column align="center" prop="nodeName" label="任务名称"></el-table-column>
<el-table-column align="center" prop="createByName" label="申请人"></el-table-column>
<el-table-column align="center" prop="nodeName" :show-overflow-tooltip="true" label="任务名称"></el-table-column>
<el-table-column align="center" prop="createByName" :show-overflow-tooltip="true" label="申请人"></el-table-column>
<el-table-column align="center" label="办理人">
<template #default="scope">
<template v-if="tab === 'waiting'">
@@ -97,21 +100,24 @@
</el-tabs>
</el-card>
<!-- 选人组件 -->
<UserSelect ref="userSelectRef" :multiple="false" @confirm-call-back="submitCallback"></UserSelect>
<UserSelect ref="userSelectRef" :multiple="userMultiple" @confirm-call-back="submitCallback"></UserSelect>
<!-- 流程干预组件 -->
<processMeddle ref="processMeddleRef" @submitCallback="getWaitingList"></processMeddle>
<!-- 申请人 -->
<UserSelect ref="applyUserSelectRef" :multiple="true" :data="selectUserIds" @confirm-call-back="userSelectCallBack"></UserSelect>
<!-- 流程干预组件 -->
<messageType ref="messageTypeRef" @submitCallback="handleUserTask"></messageType>
</div>
</template>
<script setup lang="ts">
import { pageByAllTaskWait, pageByAllTaskFinish, updateAssignee } from '@/api/workflow/task';
import { pageByAllTaskWait, pageByAllTaskFinish, updateAssignee, urgeTask } from '@/api/workflow/task';
import UserSelect from '@/components/UserSelect';
import { TaskQuery } from '@/api/workflow/task/types';
import workflowCommon from '@/api/workflow/workflowCommon';
import { RouterJumpVo } from '@/api/workflow/workflowCommon/types';
import processMeddle from '@/components/Process/processMeddle';
import messageType from '@/components/Process/MessageType';
import { UserVO } from '@/api/system/user/types';
import { TabsPaneContext } from 'element-plus';
//选人组件
@@ -120,6 +126,8 @@ const userSelectRef = ref<InstanceType<typeof UserSelect>>();
const processMeddleRef = ref<InstanceType<typeof processMeddle>>();
//选人组件
const applyUserSelectRef = ref<InstanceType<typeof UserSelect>>();
//消息组件
const messageTypeRef = ref<InstanceType<typeof messageType>>();
const queryFormRef = ref<ElFormInstance>();
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { wf_business_status } = toRefs<any>(proxy?.useDict('wf_business_status'));
@@ -132,13 +140,14 @@ const ids = ref<Array<any>>([]);
const single = ref(true);
// 非多个禁用
const multiple = ref(true);
const userMultiple = ref(false);
// 显示搜索条件
const showSearch = ref(true);
// 总条数
const total = ref(0);
// 模型定义表格数据
const taskList = ref([]);
const title = ref('');
const buttonType = ref('');
//申请人id
const selectUserIds = ref<Array<number | string>>([]);
//申请人选择数量
@@ -204,10 +213,24 @@ const getFinishList = () => {
loading.value = false;
});
};
// 打开催办
const handleUrgeTaskOpen = () => {
messageTypeRef.value.open();
};
//打开修改选人
const handleUpdate = () => {
const handleUserOpen = () => {
userSelectRef.value.open();
};
//打开修改选人
const handleUserTask = async (data) => {
await proxy?.$modal.confirm('是否确认提交?');
data.taskIdList = ids.value;
await urgeTask(data);
messageTypeRef.value.close();
proxy?.$modal.msgSuccess('操作成功');
handleQuery();
};
//修改办理人
const submitCallback = async (data) => {
if (data && data.length > 0) {
@@ -241,6 +264,9 @@ const openUserSelect = () => {
//确认选择申请人
const userSelectCallBack = (data: UserVO[]) => {
userSelectCount.value = 0;
selectUserIds.value = [];
queryParams.value.createByIds = [];
if (data && data.length > 0) {
userSelectCount.value = data.length;
selectUserIds.value = data.map((item) => item.userId);

View File

@@ -222,7 +222,7 @@ const handleCancelProcessApply = async (businessId: string) => {
await proxy?.$modal.confirm('是否确认撤销当前单据?');
loading.value = true;
if ('running' === tab.value) {
let data = {
const data = {
businessId: businessId,
message: '申请人撤销流程!'
};

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