177 Commits
uesr ... xqhz

Author SHA1 Message Date
wenhua
9da6e82763 Merge pull request 'master' (#158) from master into xqhz
Reviewed-on: http://git.feashow.cn/clay/mosr-web/pulls/158
2024-05-13 14:31:08 +00:00
lilinyuan
31a88c232f feat: 重新上传表格组件 2024-05-13 22:30:50 +08:00
1c00a040f0 Merge pull request 'dj' (#159) from dj into master
Reviewed-on: http://git.feashow.cn/clay/mosr-web/pulls/159
2024-05-13 14:30:31 +00:00
lilinyuan
f46f95c4ad fix: 删除该分支表格组件 2024-05-13 22:29:07 +08:00
9e8e3e4705 Merge remote-tracking branch 'origin/master' 2024-05-13 22:29:01 +08:00
f55059eed7 fix : 修复功能 2024-05-13 22:28:44 +08:00
lilinyuan
3bec5a6620 fix: 合并冲突 2024-05-13 22:27:35 +08:00
lilinyuan
ad3fa0e688 feat: 表格组件更新 2024-05-13 22:25:37 +08:00
lilinyuan
0041bbbfa8 Merge branch 'xqhz' of http://git.feashow.cn/clay/mosr-web into xqhz 2024-05-13 22:23:00 +08:00
lilinyuan
bdc9c244a4 feat: 新增组件列展示配置 2024-05-13 22:22:55 +08:00
clay
acc38adae1 feat : workflow编辑器 无用功能屏蔽 2024-05-13 22:15:58 +08:00
clay
5911d0f6d2 feat : workflow编辑器 无用功能屏蔽 2024-05-13 19:28:16 +08:00
clay
66454b2023 Merge remote-tracking branch 'origin/master' 2024-05-13 17:41:27 +08:00
clay
8d51d68df1 feat : workflow编辑器 无用功能屏蔽 2024-05-13 17:41:17 +08:00
84a3981df3 Merge pull request 'fix : 修复编辑数据回显功能' (#154) from dj into master
Reviewed-on: http://git.feashow.cn/clay/mosr-web/pulls/154
2024-05-12 16:28:56 +00:00
64981433bf fix : 修复编辑数据回显功能 2024-05-13 00:27:53 +08:00
e5dfadb8c0 Merge pull request 'fix : 修复功能' (#153) from dj into master
Reviewed-on: http://git.feashow.cn/clay/mosr-web/pulls/153
2024-05-12 15:38:17 +00:00
888c01efbf fix : 修复功能 2024-05-12 23:38:04 +08:00
c206844e24 Merge pull request 'fix : 修复功能' (#152) from dj into master
Reviewed-on: http://git.feashow.cn/clay/mosr-web/pulls/152
2024-05-12 15:29:21 +00:00
4070e15e63 fix : 修复功能 2024-05-12 23:28:47 +08:00
92b9dd5e03 Merge pull request 'dd' (#150) from dd into master
Reviewed-on: http://git.feashow.cn/clay/mosr-web/pulls/150
2024-05-12 13:57:00 +00:00
2d73580195 fix : 修复功能 2024-05-12 21:56:44 +08:00
3538813100 Merge remote-tracking branch 'origin/master'
# Conflicts:
#	src/api/project-demand/index.js
#	src/views/projectdemand/demandcollection/add.vue
2024-05-12 21:56:33 +08:00
71d78d9cca fix : 修复功能 2024-05-12 21:53:08 +08:00
clay
14fa25b509 Merge remote-tracking branch 'origin/master' 2024-05-12 20:27:45 +08:00
clay
bd220ad5b1 feat : 重新提交 2024-05-12 20:27:38 +08:00
4a7cffde2e Merge pull request 'fix : 修复功能' (#148) from dj into master
Reviewed-on: http://git.feashow.cn/clay/mosr-web/pulls/148
2024-05-12 11:42:43 +00:00
b170d42263 fix : 修复功能 2024-05-12 19:42:24 +08:00
d0e517e7d9 Merge pull request 'dj' (#147) from dj into master
Reviewed-on: http://git.feashow.cn/clay/mosr-web/pulls/147
2024-05-12 11:40:00 +00:00
55e0f28ebc fix : 修复功能 2024-05-12 19:39:39 +08:00
250f6344f1 fix : 修复功能 2024-05-12 19:08:41 +08:00
abbb4551ad Merge pull request 'fix : 修复功能' (#146) from dj into master
Reviewed-on: http://git.feashow.cn/clay/mosr-web/pulls/146
2024-05-12 11:05:35 +00:00
6ae58eefaf fix : 修复功能 2024-05-12 19:05:13 +08:00
9d63b64c91 Merge pull request 'fix : 修复上传文件功能' (#145) from dj into master
Reviewed-on: http://git.feashow.cn/clay/mosr-web/pulls/145
2024-05-12 09:03:39 +00:00
324cc268b5 fix : 修复上传文件功能 2024-05-12 17:03:12 +08:00
d5c7da1328 Merge pull request 'fix : 修复上传文件功能' (#144) from dj into master
Reviewed-on: http://git.feashow.cn/clay/mosr-web/pulls/144
2024-05-12 08:35:31 +00:00
13774dada9 fix : 修复上传文件功能 2024-05-12 16:34:10 +08:00
68cc3e7a12 Merge pull request 'fix : 修复编辑数据回显功能' (#143) from dj into master
Reviewed-on: http://git.feashow.cn/clay/mosr-web/pulls/143
2024-05-12 07:48:30 +00:00
72a8572910 fix : 修复编辑数据回显功能 2024-05-12 15:47:14 +08:00
86ddbf2ffb Merge pull request 'fix : 修复细节' (#142) from dj into master
Reviewed-on: http://git.feashow.cn/clay/mosr-web/pulls/142
2024-05-12 07:35:54 +00:00
d139b016a2 fix : 修复细节 2024-05-12 15:35:40 +08:00
8bdafdd087 Merge pull request 'dj' (#141) from dj into master
Reviewed-on: http://git.feashow.cn/clay/mosr-web/pulls/141
2024-05-12 07:16:14 +00:00
b4c91f3daf Merge remote-tracking branch 'origin/master'
# Conflicts:
#	src/views/workflow/process/nodes/Node.vue
2024-05-12 15:15:48 +08:00
clay
aaedb362b5 build : 按钮权限区分 2024-05-12 15:14:39 +08:00
548cab4cde fix : 修复文件删除功能 2024-05-12 15:14:28 +08:00
876bd1f9f9 fix : 修复文件删除功能 2024-05-12 15:11:37 +08:00
eeab66d8fb fix : 修复细节 2024-05-12 14:44:04 +08:00
clay
416fa2ed53 build : 流程演示 2024-05-12 14:23:34 +08:00
72210306b6 Merge pull request 'fix : 修复细节' (#139) from dd into master
Reviewed-on: http://git.feashow.cn/clay/mosr-web/pulls/139
2024-05-12 06:00:10 +00:00
6fbcdc82ca fix : 修复细节 2024-05-12 13:59:50 +08:00
8e671e959c Merge pull request 'fix : 修复细节' (#138) from dd into master
Reviewed-on: http://git.feashow.cn/clay/mosr-web/pulls/138
2024-05-12 05:06:10 +00:00
48e5d82acb fix : 修复细节 2024-05-12 13:05:52 +08:00
wenhua
ce2752af9b feat: 新增文件上传组件等 2024-05-12 01:29:49 +08:00
01021b640e Merge pull request 'fix : 修复细节' (#137) from dd into master
Reviewed-on: http://git.feashow.cn/clay/mosr-web/pulls/137
2024-05-11 15:23:20 +00:00
5e08db55fb fix : 修复细节 2024-05-11 23:22:55 +08:00
7810de1327 Merge pull request 'fix : 首页待办列表' (#136) from dd into master
Reviewed-on: http://git.feashow.cn/clay/mosr-web/pulls/136
2024-05-11 13:48:29 +00:00
b58dfcb566 fix : 首页待办列表 2024-05-11 21:48:16 +08:00
clay
44ed106775 build : 需求征集提交 2024-05-11 21:40:49 +08:00
a077e38fdc Merge pull request 'fix : 修复页面' (#135) from dd into master
Reviewed-on: http://git.feashow.cn/clay/mosr-web/pulls/135
2024-05-11 13:37:37 +00:00
9c6c494b80 fix : 修复页面 2024-05-11 21:37:15 +08:00
77301e09e4 Merge pull request 'fix : 修复详情页面' (#134) from dd into master
Reviewed-on: http://git.feashow.cn/clay/mosr-web/pulls/134
2024-05-11 13:16:18 +00:00
a7d9391e9a fix : 修复详情页面 2024-05-11 21:16:03 +08:00
af2667502a Merge pull request 'fix : 对接需求征集详情接口' (#133) from dd into master
Reviewed-on: http://git.feashow.cn/clay/mosr-web/pulls/133
2024-05-11 12:49:14 +00:00
eb061dd1a6 fix : 对接需求征集详情接口 2024-05-11 20:49:03 +08:00
clay
f0212046f3 build : 修改dm发布 2024-05-11 20:38:05 +08:00
5498337d3e Merge pull request 'fix : 对接需求征集详情接口' (#132) from dd into master
Reviewed-on: http://git.feashow.cn/clay/mosr-web/pulls/132
2024-05-11 10:06:40 +00:00
6c27a5f954 fix : 对接需求征集详情接口 2024-05-11 18:06:25 +08:00
615e0f85c7 Merge pull request 'dd' (#131) from dd into master
Reviewed-on: http://git.feashow.cn/clay/mosr-web/pulls/131
2024-05-11 09:50:54 +00:00
04d0e9a7d2 fix : 修复需求征集列表细节 2024-05-11 17:50:35 +08:00
0cf56d875e fix : 对接需求征集列表接口 2024-05-11 17:48:44 +08:00
779ebf093a Merge pull request 'fix : 对接需求征集列表接口' (#130) from dd into master
Reviewed-on: http://git.feashow.cn/clay/mosr-web/pulls/130
2024-05-11 09:41:04 +00:00
0d31b47a56 fix : 对接需求征集列表接口 2024-05-11 17:40:47 +08:00
clay
f7fddab0bd Merge remote-tracking branch 'origin/master' 2024-05-11 15:59:40 +08:00
clay
9da9b763be feat : 详情页修改 2024-05-11 15:59:25 +08:00
e935c870bf Merge pull request 'fix : 修复细节' (#129) from dd into master
Reviewed-on: http://git.feashow.cn/clay/mosr-web/pulls/129
2024-05-11 07:51:50 +00:00
c008aab0d3 fix : 修复细节 2024-05-11 15:51:19 +08:00
d9dce9e8e5 Merge pull request 'fix : 完善需求新增/详情页面' (#128) from dd into master
Reviewed-on: http://git.feashow.cn/clay/mosr-web/pulls/128
2024-05-11 07:42:10 +00:00
e6a515f673 fix : 完善需求新增/详情页面 2024-05-11 15:41:56 +08:00
5ce118a149 Merge pull request 'fix : 完善需求新增页面' (#127) from dd into master
Reviewed-on: http://git.feashow.cn/clay/mosr-web/pulls/127
2024-05-11 07:31:42 +00:00
452e2479e0 fix : 完善需求新增页面 2024-05-11 15:30:53 +08:00
clay
5c0e962626 feat : workflow前端内容检查 2024-05-11 15:10:43 +08:00
cf367951a5 Merge pull request 'fix : 完善页面' (#123) from dj into master
Reviewed-on: http://git.feashow.cn/clay/mosr-web/pulls/123
2024-05-11 06:59:05 +00:00
dde77b1425 fix : 完善页面 2024-05-11 14:58:41 +08:00
wenhua
3ba7845893 feat: 新增需求上报部分页面内容 2024-05-11 00:14:25 +08:00
wenhua
a5822ca276 fix: 修改权限按钮bug 2024-05-11 00:14:03 +08:00
332d843dc2 Merge pull request 'fix : 完善详情页面细节' (#122) from dj into master
Reviewed-on: http://git.feashow.cn/clay/mosr-web/pulls/122
2024-05-10 09:56:23 +00:00
fdca87d870 fix : 完善详情页面细节 2024-05-10 17:56:06 +08:00
1a211fec21 Merge pull request 'fix : 完善详情页面细节' (#120) from dj into master
Reviewed-on: http://git.feashow.cn/clay/mosr-web/pulls/120
2024-05-10 09:31:01 +00:00
5710597454 fix : 完善详情页面细节 2024-05-10 17:30:29 +08:00
clay
81d0987a9a feat : workflow适配 2024-05-10 15:14:11 +08:00
1ee39bfa0c Merge pull request 'dj' (#118) from dj into master
Reviewed-on: http://git.feashow.cn/clay/mosr-web/pulls/118
2024-05-09 16:30:11 +00:00
371a55cfe1 fix : 完善页面细节 2024-05-10 00:29:07 +08:00
90de57698b feat : 需求征集新增/编辑/详情页面初始化 2024-05-10 00:28:40 +08:00
858289ec0e Merge pull request 'fix :完善首页界面' (#116) from dj into master
Reviewed-on: http://git.feashow.cn/clay/mosr-web/pulls/116
2024-05-09 10:27:08 +00:00
bf6a683261 fix :完善首页界面 2024-05-09 18:26:40 +08:00
460dd793cf Merge pull request 'dj' (#115) from dj into master
Reviewed-on: http://git.feashow.cn/clay/mosr-web/pulls/115
2024-05-09 10:11:32 +00:00
be5150c7c4 feat: 初始化需求征集主页面 2024-05-09 18:11:11 +08:00
233f44990d Merge pull request 'master' (#114) from master into dj
Reviewed-on: http://git.feashow.cn/clay/mosr-web/pulls/114
2024-05-09 09:36:35 +00:00
wenhua
3403ca7ecb Merge pull request 'xqhz' (#113) from xqhz into master
Reviewed-on: http://git.feashow.cn/clay/mosr-web/pulls/113
2024-05-09 09:33:33 +00:00
lilinyuan
28b4637e01 feat: 初始化需求汇总主页面 2024-05-09 17:33:01 +08:00
lilinyuan
639caadc46 fix: 修复bug 2024-05-09 17:32:36 +08:00
lilinyuan
6557b5d2c8 fix: 修复bug 2024-05-09 17:32:21 +08:00
4f6b2dcc27 Merge pull request 'feat : 新增首页界面' (#111) from dj into master
Reviewed-on: http://git.feashow.cn/clay/mosr-web/pulls/111
2024-05-09 09:17:30 +00:00
dcc6866211 feat : 新增首页界面 2024-05-09 17:16:39 +08:00
wenhua
0e0e1f9bb9 Merge pull request 'feat: 初始化项目需求目录及页面文件' (#110) from master into xqhz
Reviewed-on: http://git.feashow.cn/clay/mosr-web/pulls/110
2024-05-09 08:19:28 +00:00
lilinyuan
0e0d206cc7 feat: 初始化项目需求目录及页面文件 2024-05-09 16:14:48 +08:00
wenhua
bfe24e1207 Merge pull request 'role' (#109) from role into master
Reviewed-on: http://git.feashow.cn/clay/mosr-web/pulls/109
2024-05-09 07:30:12 +00:00
lilinyuan
d2521765be Merge branch 'role' of http://git.feashow.cn/clay/mosr-web into role 2024-05-09 15:29:05 +08:00
lilinyuan
5c1b66b03d feat: 新增sidermenu首页项 2024-05-09 15:29:00 +08:00
clay
2bb86ced15 feat : 测试sso单点登录之后的重定向路径4 2024-04-29 11:49:46 +08:00
clay
929228974a feat : 测试sso单点登录之后的重定向路径3 2024-04-29 11:27:56 +08:00
clay
eee3c3852e feat : 测试sso单点登录之后的重定向路径 2024-04-29 10:31:18 +08:00
clay
57bfb6f4a0 feat : 测试sso单点登录之后的重定向路径 2024-04-29 10:20:58 +08:00
clay
5d3ab890f3 feat : 测试sso单点登录之后的重定向路径 2024-04-29 10:10:50 +08:00
clay
3eecc3f4b9 Merge pull request 'role' (#102) from role into master
Reviewed-on: http://git.feashow.cn/clay/mosr-web/pulls/102
2024-04-29 02:04:03 +00:00
clay
29ea0d5fe4 feat : 测试sso单点登录之后的重定向路径 2024-04-29 10:03:46 +08:00
lilinyuan
8c1d2eeabe feat: 新增登录重定向之前被拦截的页面 2024-04-29 09:58:18 +08:00
wenhua
9217f019e5 Merge pull request 'fix: 按钮逻辑' (#100) from role into master
Reviewed-on: http://git.feashow.cn/clay/mosr-web/pulls/100
2024-04-01 07:31:24 +00:00
lilinyuan
24dfaac98a fix: 按钮逻辑 2024-04-01 15:30:56 +08:00
wenhua
26b50d6103 Merge pull request 'fix: 调整菜单权限按钮' (#98) from role into master
Reviewed-on: http://git.feashow.cn/clay/mosr-web/pulls/98
2024-04-01 07:08:40 +00:00
lilinyuan
b5acea2e47 fix: 调整菜单权限按钮 2024-04-01 15:02:09 +08:00
wenhua
12053b81b9 Merge pull request 'role' (#96) from role into master
Reviewed-on: http://git.feashow.cn/clay/mosr-web/pulls/96
2024-04-01 06:36:33 +00:00
lilinyuan
39bc3011dd fix: 去掉操作列悬浮 2024-04-01 14:36:02 +08:00
lilinyuan
6e42dd43f7 fix: 组件调整 2024-04-01 14:28:08 +08:00
lilinyuan
1b10b9aa2b fix: 角色管理调整 2024-04-01 14:27:52 +08:00
lilinyuan
8964cbe4bd fix: 注释install 2024-04-01 13:58:03 +08:00
wenhua
360f0dd92d Merge pull request 'fix: 修改依赖源地址' (#94) from role into master
Reviewed-on: http://git.feashow.cn/clay/mosr-web/pulls/94
2024-04-01 05:48:54 +00:00
lilinyuan
de971d224e fix: 修改依赖源地址 2024-04-01 13:48:34 +08:00
wenhua
68c4ca5dc9 Merge pull request 'fix: 修改依赖源' (#92) from role into master
Reviewed-on: http://git.feashow.cn/clay/mosr-web/pulls/92
2024-04-01 05:41:49 +00:00
lilinyuan
5977480404 fix: 修改依赖源 2024-04-01 13:41:23 +08:00
wenhua
8f922a8815 Merge pull request 'role' (#90) from role into master
Reviewed-on: http://git.feashow.cn/clay/mosr-web/pulls/90
2024-04-01 03:28:24 +00:00
lilinyuan
508e391823 feat: 恢复 2024-04-01 11:27:06 +08:00
lilinyuan
2560ed701f fix: 暂时删除 2024-04-01 11:25:27 +08:00
lilinyuan
9123149565 feat: up dependencies 2024-04-01 11:23:51 +08:00
lilinyuan
f1e1def32c feat: up dependencies 2024-04-01 11:20:55 +08:00
wenhua
c3e582ad33 Merge pull request 'feat: up' (#85) from role into master
Reviewed-on: http://git.feashow.cn/clay/mosr-web/pulls/85
2024-04-01 03:00:32 +00:00
lilinyuan
e787128011 feat: up 2024-04-01 11:00:10 +08:00
wenhua
fde73bd7b0 Merge pull request 'fix: 再试一次' (#83) from role into master
Reviewed-on: http://git.feashow.cn/clay/mosr-web/pulls/83
2024-04-01 02:20:57 +00:00
lilinyuan
90f6bd344e fix: 再试一次 2024-04-01 10:20:32 +08:00
wenhua
ef46911a5b Merge pull request 'fix: 使用原始ui库' (#81) from role into master
Reviewed-on: http://git.feashow.cn/clay/mosr-web/pulls/81
2024-04-01 02:14:17 +00:00
lilinyuan
ffc5600b9b fix: 使用原始ui库 2024-04-01 10:13:53 +08:00
wenhua
a3c4a79fd0 Merge pull request 'fix: 添加调试log' (#78) from role into master
Reviewed-on: http://git.feashow.cn/clay/mosr-web/pulls/78
2024-04-01 01:53:04 +00:00
lilinyuan
fbae33730d fix: 添加调试log 2024-04-01 09:52:01 +08:00
wenhua
450d483f39 Merge pull request 'fix: 菜单分配父子联动' (#76) from role into master
Reviewed-on: http://git.feashow.cn/clay/mosr-web/pulls/76
2024-03-30 16:36:29 +00:00
lilinyuan
64c735eb74 fix: 菜单分配父子联动 2024-03-31 00:36:08 +08:00
wenhua
699c7ecc7d Merge pull request 'fix: 同步退出登录后再登录不同权限账号时菜单不更新问题' (#74) from role into master
Reviewed-on: http://git.feashow.cn/clay/mosr-web/pulls/74
2024-03-30 16:24:00 +00:00
lilinyuan
ed0a5e1a7d fix: 同步退出登录后再登录不同权限账号时菜单不更新问题 2024-03-31 00:23:31 +08:00
wenhua
0ffbdbbcc7 Merge pull request 'fix: 修复角色分配菜单父子关联不生效的问题' (#72) from role into master
Reviewed-on: http://git.feashow.cn/clay/mosr-web/pulls/72
2024-03-30 16:13:08 +00:00
lilinyuan
7e0a065944 fix: 修复角色分配菜单父子关联不生效的问题 2024-03-31 00:12:35 +08:00
d784928d05 Merge pull request 'fix : 修改登录框宽度' (#70) from dj into master
Reviewed-on: http://git.feashow.cn/clay/mosr-web/pulls/70
2024-03-30 14:59:26 +00:00
a005409d92 fix : 修改登录框宽度 2024-03-30 22:59:01 +08:00
7b500e7ea1 Merge pull request 'fix : 修改登录界面样式' (#68) from dj into master
Reviewed-on: http://git.feashow.cn/clay/mosr-web/pulls/68
2024-03-29 14:58:32 +00:00
e9acf99ccb fix : 修改登录界面样式 2024-03-29 22:58:00 +08:00
clay
dd659cf135 build : 修改dm发布 2024-03-29 20:53:46 +08:00
clay
0c56e6a161 build : 修改dm发布 2024-03-29 20:50:06 +08:00
clay
bba369abaa build : 修改dm发布 2024-03-29 20:47:49 +08:00
clay
fcff8c9cc4 build : 修改dm发布 2024-03-29 20:44:33 +08:00
clay
13d3428d7a build : 环境区分 2024-03-29 18:22:23 +08:00
clay
99cf83a563 Merge remote-tracking branch 'origin/master' 2024-03-29 18:10:33 +08:00
clay
f98103c61f build : 环境区分 2024-03-29 18:10:26 +08:00
wenhua
5986ab1eb4 Merge pull request 'fix: 生产环境禁用debugger' (#61) from role into master
Reviewed-on: http://git.feashow.cn/clay/mosr-web/pulls/61
2024-03-29 09:14:28 +00:00
lilinyuan
2282f1c834 fix: 生产环境禁用debugger 2024-03-29 17:14:04 +08:00
wenhua
ecf3fecd40 Merge pull request 'fix: 调整删除按钮展示逻辑' (#58) from uesr into master
Reviewed-on: http://git.feashow.cn/clay/mosr-web/pulls/58
2024-03-29 08:49:50 +00:00
wenhua
46a6745f17 Merge pull request 'role' (#56) from role into master
Reviewed-on: http://git.feashow.cn/clay/mosr-web/pulls/56
2024-03-29 07:46:43 +00:00
lilinyuan
d6ce782681 feat: 添加request 2024-03-29 15:45:14 +08:00
lilinyuan
aa3194dcc1 fix: 暂时删除request 2024-03-29 15:42:33 +08:00
lilinyuan
e418b588f6 fix: 修改request提示 2024-03-29 15:41:21 +08:00
lilinyuan
a5a2e099a2 fix: 修改请求报错提示 2024-03-29 15:34:34 +08:00
lilinyuan
19822a539c fix: 修改tablecol显示bug 2024-03-29 15:34:17 +08:00
clay
fa56d63dbb Merge remote-tracking branch 'origin/master' 2024-03-29 15:21:02 +08:00
clay
2ad0dbf8ab feat : 403 权限校验 2024-03-29 15:20:52 +08:00
wenhua
89b42dba2a Merge pull request 'uesr' (#52) from uesr into master
Reviewed-on: http://git.feashow.cn/clay/mosr-web/pulls/52
2024-03-29 07:19:39 +00:00
wenhua
c88be616fc Merge pull request 'fix: 修复role新增编辑bug' (#50) from role into master
Reviewed-on: http://git.feashow.cn/clay/mosr-web/pulls/50
2024-03-29 06:32:08 +00:00
lilinyuan
0fc918b576 fix: 修复role新增编辑bug 2024-03-29 14:31:45 +08:00
wenhua
38b5e2d2af Merge pull request 'role' (#48) from role into master
Reviewed-on: http://git.feashow.cn/clay/mosr-web/pulls/48
2024-03-29 06:15:51 +00:00
lilinyuan
4c17bf7f6a fix: 修改默认用户名为super 2024-03-29 14:15:23 +08:00
lilinyuan
878940c34b fix: 固定role列表操作列 2024-03-29 14:15:04 +08:00
58 changed files with 2657 additions and 855 deletions

View File

@@ -5,6 +5,8 @@ platform:
os: linux
arch: arm64
steps:
- name: build-package
image: node:16.20.0
volumes:
@@ -23,17 +25,16 @@ steps:
- npm -v
- mkdir -p ./node_modules
- export NODE_MODULES_PATH=`pwd`/node_modules
#- npm config set registry https://registry.npm.taobao.org
# - npm config set registry https://registry.npmmirror.com
#- set NODE_OPTIONS=--openssl-legacy-provider
# - npm install
# - npm install
- npm run build
- ls /app/build/$DRONE_REPO_NAME/
- echo $NODE_MODULES_PATH
- cp -r dist /app/build/$DRONE_REPO_NAME
- name: build-docker # 制作docker镜像
- name: build-docker-prod # 制作docker镜像
image: docker # 使用官方docker镜像
volumes: # 将容器内目录挂载到宿主机
- name: docker
@@ -55,6 +56,9 @@ steps:
- sed -i 's/$REGISTRY_NAMESPACE/'"$REGISTRY_NAMESPACE"'/' deployment.yml
- sed -i 's/$DRONE_REPO_NAME/'"$DRONE_REPO_NAME"'/' deployment.yml
- sed -i 's/$DRONE_COMMIT/'"$DRONE_COMMIT"'/' deployment.yml
- sed -i 's/$PORTS_NAME/'"dasdafas"'/' deployment.yml
- sed -i 's/$PORTS_PORT/'"8081"'/' deployment.yml
- sed -i 's/$PROFILES/'"mosr"'/' nginx.conf
# docker登录,不能在脚本中登录,并且不能使用docker login -u -p
- echo $DOCKER_PASSWORD | docker login $REGISTRY --username $DOCKER_USERNAME --password-stdin
- chmod +x docker.sh
@@ -62,9 +66,47 @@ steps:
- sh docker.sh
# 执行完脚本删除本次制作的docker镜像,避免多次后当前runner空间不足
- docker rmi -f $(docker images | grep $DRONE_REPO_NAME | awk '{print $3}')
when:
branch:
- prod
- name: build-docker-dm # 制作docker镜像
image: docker # 使用官方docker镜像
volumes: # 将容器内目录挂载到宿主机
- name: docker
path: /var/run/docker.sock # 挂载宿主机的docker
- name: config
path: /config
environment: # 获取到密文的docker用户名和密码
DOCKER_USERNAME:
from_secret: docker_username
DOCKER_PASSWORD:
from_secret: docker_password
REGISTRY:
from_secret: registry
REGISTRY_NAMESPACE:
from_secret: registry_namespace
commands: # 定义在Docker容器中执行的shell命令
- cat Dockerfile
- sed -i 's/$REGISTRY/'"$REGISTRY"'/' deployment.yml
- sed -i 's/$REGISTRY_NAMESPACE/'"$REGISTRY_NAMESPACE"'/' deployment.yml
- sed -i 's/$DRONE_REPO_NAME/'"$DRONE_REPO_NAME"'/' deployment.yml
- sed -i 's/$DRONE_COMMIT/'"$DRONE_COMMIT"'/' deployment.yml
- sed -i 's/$PORTS_NAME/'"dasdafas"'/' deployment.yml
- sed -i 's/$PORTS_PORT/'"8082"'/' deployment.yml
- sed -i 's/$PROFILES/'"mosr-dm"'/' nginx.conf
# docker登录,不能在脚本中登录,并且不能使用docker login -u -p
- echo $DOCKER_PASSWORD | docker login $REGISTRY --username $DOCKER_USERNAME --password-stdin
- chmod +x docker.sh
- cat docker.sh
- sh docker.sh
# 执行完脚本删除本次制作的docker镜像,避免多次后当前runner空间不足
- docker rmi -f $(docker images | grep $DRONE_REPO_NAME | awk '{print $3}')
when:
branch:
- dm
- name: drone-rancher # rancher运行
- name: drone-rancher-prod # rancher运行
image: bitnami/kubectl:1.26.13-debian-11-r1
volumes: # 将容器内目录挂载到宿主机
- name: config
@@ -72,6 +114,24 @@ steps:
commands: # 定义在Docker容器中执行的shell命令
# 将deployment中定义的变量替换为drone中的内置变量
- kubectl apply -f deployment.yml -n mosr --kubeconfig=/app/config/base-taishan-kubectl.yml
when:
branch:
- prod
- name: drone-rancher-dm # rancher运行
image: bitnami/kubectl:1.26.13-debian-11-r1
volumes: # 将容器内目录挂载到宿主机
- name: config
path: /app/config # 将kubectl 配置文件挂载出来
commands: # 定义在Docker容器中执行的shell命令
# 将deployment中定义的变量替换为drone中的内置变量
- kubectl apply -f deployment.yml -n mosr-dm --kubeconfig=/app/config/base-taishan-kubectl.yml
when:
branch:
- dm
volumes:
- name: cache
@@ -84,8 +144,10 @@ volumes:
host:
path: /var/run/docker.sock
load:
trigger:
branch:
- prod
- dm
event:
- push

View File

@@ -5,8 +5,8 @@ metadata:
spec:
type: NodePort
ports:
- name: damdsdha
nodePort: 8081
- name: $PORTS_NAME
nodePort: $PORTS_PORT
port: 80
protocol: TCP
targetPort: 80

View File

@@ -30,7 +30,7 @@ http {
listen [::]:80;
location /api {
proxy_pass http://gateway.mosr.svc.cluster.local:8080;
proxy_pass http://gateway.$PROFILES.svc.cluster.local:8080;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Port $server_port;

2
package-lock.json generated
View File

@@ -13,7 +13,7 @@
"axios": "^1.4.0",
"d3": "^7.8.5",
"echarts": "^5.4.2",
"element-plus": "^2.3.5",
"element-plus": "^2.6.0",
"highlight.js": "9.18.5",
"jquery": "^3.6.0",
"js-cookie": "^3.0.5",

View File

@@ -13,7 +13,7 @@
"axios": "^1.4.0",
"d3": "^7.8.5",
"echarts": "^5.4.2",
"element-plus": "^2.3.5",
"element-plus": "^2.6.0",
"highlight.js": "9.18.5",
"jquery": "^3.6.0",
"js-cookie": "^3.0.5",

8
src/api/home/index.js Normal file
View File

@@ -0,0 +1,8 @@
import request from '@/utils/request.js'
export const getHomeInfo = () => {
return request({
url: '/workflow/mosr/process/task',
method: "get"
});
};

View File

@@ -0,0 +1,68 @@
import request from '@/utils/request.js'
//需求征集
export const getDemandInfo = (param) => {
return request({
url: '/workflow/mosr/requirement',
method: "get",
params: param
});
};
export const getWorkflowInfo = () => {
return request({
url: '/workflow/mosr/requirement/process',
method: "get"
});
};
export const getInfo = (requirementId) => {
return request({
url: `/workflow/mosr/requirement/info/${requirementId}`,
method: "get"
});
};
export const getFormInfo = (requirementId) => {
return request({
url: `/workflow/mosr/requirement/form/${requirementId}`,
method: "get"
});
};
export const agreeTask = (data) => {
return request({
url: `/workflow/mosr/process/task/agree`,
method: "post",
data: data
});
};
export const rejectTask = (data) => {
return request({
url: `/workflow/mosr/process/task/reject`,
method: "post",
data: data
});
};
export const addRequirement = (data) => {
return request({
url: '/workflow/mosr/requirement',
method: "post",
data: data
});
};
export const resubmit = (data) => {
return request({
url: '/workflow/mosr/requirement/resubmit',
method: "post",
data: data
});
};
export const deleteFile = (fileId) => {
return request({
url: `/workflow/process/file/delete/${fileId}`,
method: "delete"
});
};
export const getCompanyOption = () => {
return request({
url: '/admin/mosr/sub/company/companyOption',
method: "get"
});
};

View File

@@ -14,6 +14,14 @@ export const getRoleOption = () => {
method: "get"
});
};
export const getTemRoleOption = () => {
return request({
url: '/admin/role/option/template',
method: "get"
});
};
//查询角色信息
export const getRoleDetail = (roleId) => {
return request({

View File

@@ -19,7 +19,7 @@ html, body, #app, .el-container, .el-aside, .el-main {
}
.el-main {
background: #F4F6F8;
background: #EFEFEF;
padding: 0 0 0 18px;
position: fixed;
left: 200px;
@@ -27,6 +27,7 @@ html, body, #app, .el-container, .el-aside, .el-main {
margin-left: 0;
transition: margin-left .15s;
width: calc(100vw - 200px);
&::-webkit-scrollbar {
width: 6px;
}
@@ -83,9 +84,11 @@ html, body, #app, .el-container, .el-aside, .el-main {
align-items: center;
margin-bottom: 10px;
}
.el-dialog__body{
.el-dialog__body {
padding: 10px 20px;
}
.stateIcon {
margin-top: 9px;
margin-right: 7px;
@@ -248,7 +251,8 @@ html, body, #app, .el-container, .el-aside, .el-main {
justify-content: center;
z-index: 0;
position: relative;
.el-icon{
.el-icon {
width: 1.4em;
height: 1.4em;
}
@@ -287,9 +291,10 @@ html, body, #app, .el-container, .el-aside, .el-main {
.layout {
display: flex;
justify-content: space-around;
.scrollbar-user {
height: calc(100vh - 250px)!important;
overflow: auto!important;
height: calc(100vh - 250px) !important;
overflow: auto !important;
}
.scrollbar-dict {
@@ -374,6 +379,7 @@ html, body, #app, .el-container, .el-aside, .el-main {
margin-top: 10px;
z-index: 666;
position: absolute;
//top: -20px;
}
.el-overlay-dialog {
@@ -401,6 +407,15 @@ html, body, #app, .el-container, .el-aside, .el-main {
}
//SvgIcon组件的样式
.home-icon {
width: 4em;
height: 4em;
vertical-align: -0.15em;
fill: currentColor;
overflow: hidden;
margin-right: 5px;
}
.svg-icon {
width: 1.2em;
height: 1.2em;
@@ -445,10 +460,12 @@ html, body, #app, .el-container, .el-aside, .el-main {
overflow: hidden;
margin-right: 5px;
}
.fen-icon{
.fen-icon {
width: 1.8em;
height: 1.7em;
}
.middle-icon {
width: 1.4em;
height: 1.4em;

6
src/assets/svg/home1.svg Normal file
View File

@@ -0,0 +1,6 @@
<svg width="71" height="68" viewBox="0 0 71 68" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="Group 229">
<ellipse id="Ellipse 12" cx="35.3747" cy="33.663" rx="35.3747" ry="33.663" fill="#87C3ED"/>
<path id="Subtract" fill-rule="evenodd" clip-rule="evenodd" d="M23.3174 21.8325C23.3174 20.7279 24.2128 19.8325 25.3174 19.8325H28.485V23.3688C28.485 23.9211 28.9327 24.3688 29.485 24.3688H32.7358C33.288 24.3688 33.7358 23.9211 33.7358 23.3688V19.8325H45.4049C46.5094 19.8325 47.4049 20.7279 47.4049 21.8325V44.741C47.4049 45.8456 46.5094 46.741 45.4049 46.741H25.3174C24.2128 46.741 23.3174 45.8456 23.3174 44.741V21.8325ZM32.7358 19.8325H29.485V23.3688L32.7358 23.3688V19.8325ZM28.876 29.1297H39.9933V28.1297H28.876V29.1297ZM41.8462 34.4079H28.876V33.4079H41.8462V34.4079ZM41.8462 39.6862H28.876V38.6862H41.8462V39.6862Z" fill="#0043C5"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 860 B

6
src/assets/svg/home2.svg Normal file
View File

@@ -0,0 +1,6 @@
<svg width="71" height="68" viewBox="0 0 71 68" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="Group 230">
<ellipse id="Ellipse 13" cx="35.3747" cy="33.663" rx="35.3747" ry="33.663" fill="#B487ED"/>
<path id="Subtract" fill-rule="evenodd" clip-rule="evenodd" d="M41.1045 20.8325C41.1045 20.2802 40.6568 19.8325 40.1045 19.8325H26.1016C24.997 19.8325 24.1016 20.7279 24.1016 21.8325V44.741C24.1016 45.8456 24.997 46.741 26.1016 46.741H46.189C47.2936 46.741 48.189 45.8456 48.189 44.741V27.5596C48.189 27.0073 47.7413 26.5596 47.189 26.5596H42.1045C41.5522 26.5596 41.1045 26.1118 41.1045 25.5596V20.8325ZM43.9383 31.0958H31.1861V30.0958H43.9383V31.0958ZM35.4369 23.0234H39.6876V22.0234H35.4369V23.0234ZM43.9383 36.4777H31.1861V35.4777H43.9383V36.4777ZM31.1861 41.8593H43.9383V40.8593H31.1861V41.8593Z" fill="#8600C5"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 838 B

6
src/assets/svg/home3.svg Normal file
View File

@@ -0,0 +1,6 @@
<svg width="71" height="68" viewBox="0 0 71 68" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="Group 231">
<ellipse id="Ellipse 14" cx="35.3747" cy="33.663" rx="35.3747" ry="33.663" fill="#FDCB9D"/>
<path id="Subtract" fill-rule="evenodd" clip-rule="evenodd" d="M54.4044 32.614C54.4044 40.4161 46.1575 46.741 35.9845 46.741C32.9189 46.741 30.0281 46.1666 27.4852 45.1505C27.3501 45.3211 27.1528 45.4524 26.9047 45.5078L19.6131 47.1386C18.844 47.3106 18.1654 46.6256 18.393 45.9072L20.2692 39.9868C18.5537 37.8406 17.5647 35.3159 17.5647 32.614C17.5647 24.8119 25.8115 18.4871 35.9845 18.4871C46.1575 18.4871 54.4044 24.8119 54.4044 32.614ZM28.1916 34.6321C29.3654 34.6321 30.317 33.7286 30.317 32.614C30.317 31.4994 29.3654 30.5958 28.1916 30.5958C27.0178 30.5958 26.0662 31.4994 26.0662 32.614C26.0662 33.7286 27.0178 34.6321 28.1916 34.6321ZM38.8184 32.614C38.8184 33.7286 37.8669 34.6321 36.6931 34.6321C35.5193 34.6321 34.5677 33.7286 34.5677 32.614C34.5677 31.4994 35.5193 30.5958 36.6931 30.5958C37.8669 30.5958 38.8184 31.4994 38.8184 32.614ZM45.1945 34.6321C46.3683 34.6321 47.3198 33.7286 47.3198 32.614C47.3198 31.4994 46.3683 30.5958 45.1945 30.5958C44.0207 30.5958 43.0691 31.4994 43.0691 32.614C43.0691 33.7286 44.0207 34.6321 45.1945 34.6321Z" fill="#F47D0E"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

6
src/assets/svg/home4.svg Normal file
View File

@@ -0,0 +1,6 @@
<svg width="71" height="68" viewBox="0 0 71 68" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="Group 232">
<ellipse id="Ellipse 19" cx="35.3747" cy="33.663" rx="35.3747" ry="33.663" fill="#87EDBC"/>
<path id="Subtract" fill-rule="evenodd" clip-rule="evenodd" d="M18.7023 20.134C18.1358 20.4872 17.7588 21.1158 17.7588 21.8325V42.0502C17.7588 43.1547 18.6542 44.0502 19.7588 44.0502H48.3477C49.4523 44.0502 50.3477 43.1547 50.3477 42.0502V21.8325C50.3477 21.1082 49.9627 20.4738 49.3861 20.1229L49.9153 20.9221L35.7553 30.2976C34.9339 30.8415 33.8697 30.8523 33.0374 30.3252L18.1997 20.9276L18.7023 20.134ZM18.7605 20.0991L33.5725 29.4804C34.0718 29.7966 34.7104 29.7902 35.2032 29.4638L49.3465 20.0993C49.0526 19.9296 48.7115 19.8325 48.3477 19.8325H19.7588C19.3952 19.8325 19.0543 19.9295 18.7605 20.0991Z" fill="#01A054"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 845 B

1
src/assets/svg/sso.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 5.4 KiB

View File

@@ -0,0 +1,88 @@
<template>
<el-upload :file-list="_value"
:action="uploadFileUrl"
:headers="headers"
:limit="maxSize"
with-credentials
:multiple="maxSize > 0"
:data="uploadParams"
:show-file-list="false"
:auto-upload="true"
:before-upload="beforeUpload"
:on-success="handleUploadSuccess"
>
<el-button color="#DED0B2" :loading="loading">上传文件</el-button>
</el-upload>
</template>
<script setup>
import {ElMessage} from "element-plus";
import {getToken} from '@/utils/auth'
const baseURL = import.meta.env.VITE_BASE_URL
const uploadFileUrl = ref(baseURL + "/workflow/process/file/upload")
const headers = reactive({
authorization: getToken()
})
const disabled = ref(false)
const loading = ref(false)
const showTable = ref(false)
const uploadParams = ref({})
const props = defineProps({
value: {
type: Array,
default: () => {
return []
}
},
maxSize: {
type: Number,
default: 30
}
})
const emit = defineEmits(["input", "getFile"])
const fileList = ref([])
const _value = computed({
get() {
return props.value;
},
set(val) {
emit("input", val);
}
})
const beforeUpload = (file) => {
// const FileExt = file.name.replace(/.+\./, "");
// if (['zip', 'rar', 'pdf', 'doc', 'docx', 'xlsx'].indexOf(FileExt.toLowerCase()) === -1) {
// ElMessage.warning('请上传后缀名为pdf、doc、docx、xlsx、zip或rar的文件');
// return false;
// } else
// if (props.maxSize > 0 && file.size / 1024 / 1024 > props.maxSize) {
// ElMessage.warning(`每个文件最大不超过 ${props.maxSize}MB`)
// } else {
loading.value = true
return true
// }
}
const handleUploadSuccess = (res, file) => {
if (res.code !== 1000) {
loading.value = false
ElMessage.error("上传失败")
} else {
loading.value = false
ElMessage.success("上传成功")
}
showTable.value = true
let data = res.data
fileList.value.push(data)
emit("getFile", fileList.value)
}
</script>
<style lang="scss" scoped>
a {
font-size: 14px;
color: #2a99ff;
}
</style>

View File

@@ -60,13 +60,21 @@ const props = defineProps({
toolbar: {
type: [String, Array],
default: [
"fullscreen undo redo restoredraft | cut copy paste pastetext | forecolor backcolor bold italic underline strikethrough link anchor | alignleft aligncenter alignright alignjustify outdent indent | bullist numlist | blockquote subscript superscript removeformat ",
"fullscreen undo redo | cut copy paste pastetext | forecolor backcolor bold italic underline strikethrough link anchor | alignleft aligncenter alignright alignjustify outdent indent | bullist numlist | blockquote subscript superscript removeformat ",
"styleselect formatselect fontselect fontsizeselect | table image axupimgs media pagebreak insertdatetime selectall visualblocks searchreplace | code preview | indent2em lineheight formatpainter",
],
},
fontFormats: {
type: [String, Array],
default: "微软雅黑=Microsoft YaHei,Helvetica Neue,PingFang SC,sans-serif;苹果苹方=PingFang SC,Microsoft YaHei,sans-serif;宋体=simsun,serif;仿宋体=FangSong,serif;黑体=SimHei,sans-serif;Arial=arial,helvetica,sans-serif;Arial Black=arial black,avant garde;Book Antiqua=book antiqua,palatino;"
},
width:{
type: String,
default: 'auto'
},
height:{
type: Number,
default: 450
}
})
const content = ref(props.value);
@@ -78,9 +86,10 @@ const init = reactive({
content_css: '/skins/content/default/content.css',
language: 'zh_CN',
placeholder: "在这里输入文字", //textarea中的提示信息
min_width: 320,
min_height: 220,
height: 500, //注引入autoresize插件时此属性失效
min_width: 300,
min_height: 200,
width:props.width,
height: props.height, //注引入autoresize插件时此属性失效
resize: "both", //编辑器宽高是否可变false-否,true-高可变,'both'-宽高均可,注意引号
promotion: false,
branding: false, //tiny技术支持信息是否显示

View File

@@ -8,7 +8,7 @@ export const hasPerm = (el, binding, vnode) => {
if (value && value instanceof Array && value.length > 0) {
const permissiosFlag = value
const hasPermission = permisstions.some(permission => {
return allPermission === permission || permissiosFlag.includes(permission)
return permissiosFlag[0] === allPermission || permissiosFlag[0] === permission
})
if (!hasPermission) {
el.parentNode && el.parentNode.removeChild(el)

View File

@@ -15,7 +15,7 @@ const props = defineProps({
default: []
},
modelValue: {
type: [Number, String],
type: [Number, String, Boolean],
default: ''
},
cacheKey: {

View File

@@ -1,28 +1,28 @@
<template>
<el-form
:model="form"
v-bind="$attrs"
label-width="auto"
ref="formInstance"
class="search-form"
:model="form"
v-bind="$attrs"
label-width="auto"
ref="formInstance"
class="search-form"
>
<el-row>
<el-col
v-for="(item, index) in filterConfig"
:span="5"
:offset="index == 0 || index / 4 ==1 ? 0 : 1"
:key="item.prop"
>
<el-form-item
v-bind="item"
v-for="(item, index) in filterConfig"
:span="5"
:offset="index == 0 || index / 4 ==1 ? 0 : 1"
:key="item.prop"
>
<el-form-item
v-bind="item"
:key="item.prop"
>
<template #default>
<component
:is="item.component"
v-bind="item.props || {}"
v-on="item.on || {}"
v-model="form[item.prop]"
<component
:is="item.component"
v-bind="item.props || {}"
v-on="item.on || {}"
v-model="form[item.prop]"
>
</component>
</template>
@@ -33,7 +33,7 @@
<el-button v-if="searchConfig.length>=4" link type="primary" @click="showMore = !showMore">
{{ showMore ? '收起' : '展开' }}
</el-button>
<el-button type="primary" @click="getValues" :icon="Search">搜索</el-button>
<el-button @click="getValues" color="#DED0B2" :icon="Search">搜索</el-button>
<el-button @click="handleReset" :icon="Refresh">重置</el-button>
</el-form-item>
</el-col>
@@ -58,24 +58,22 @@ const formInstance = ref(null)
const showMore = ref(false)
const filterConfig = computed(()=>{
const arr = props.searchConfig.filter(item=>{
if(item.prop) return true
const filterConfig = computed(() => {
const arr = props.searchConfig.filter(item => {
if (item.prop) return true
})
return arr.length >= 4 && showMore.value ? arr : arr.slice(0, 3)
})
console.log(filterConfig.value, 'filterConfig');
// 搜索功能表单元素默认值
const setDefaultFormValues = () => {
filterConfig.value.forEach(item=>{
form.value[item.prop] = item.props.defaultValue || null
filterConfig.value.forEach(item => {
form.value[item.prop] = item.props?.defaultValue || null
})
}
watchEffect(()=>{
if(filterConfig.value.length) {
watchEffect(() => {
if (filterConfig.value.length) {
setDefaultFormValues()
}
})
@@ -92,8 +90,8 @@ const handleReset = () => {
emits('search', form.value)
}
onMounted(()=>{
emits('getInstance', Object.assign({}, unref(formInstance),{
onMounted(() => {
emits('getInstance', Object.assign({}, unref(formInstance), {
getValues,
handleReset,
}))
@@ -105,9 +103,10 @@ onMounted(()=>{
.search-form {
padding-top: 18px;
}
.btn-col {
display: flex;
justify-content: space-evenly;
align-items: center;
}
</style>
</style>

View File

@@ -2,15 +2,48 @@
<div class="fv-table-container">
<!-- 表格头部按钮 -->
<div class="fv-table-btn" v-if="tableConfig.btns">
<el-button
v-for="btn in tableConfig.btns"
:key="btn.key"
:type="btn.type || ''"
v-perm="btn.auth || ['*:*:*']"
@click="handleClickBtns(btn.key)"
>
{{ btn.name }}
</el-button>
<div class="table-head-btn">
<el-button
v-for="btn in tableConfig.btns"
:key="btn.key"
:type="btn.type || ''"
v-perm="btn.auth || ['*:*:*']"
@click="handleClickBtns(btn.key)"
>
{{ btn.name }}
</el-button>
</div>
<!-- 列显示配置 -->
<div v-if="isSettingCol">
<el-tooltip effect="dark" content="列配置" placement="bottom">
<el-button ref="buttonRef" link>
<el-icon size="18"><Setting /></el-icon>
</el-button>
</el-tooltip>
<el-popover
placement="bottom"
:width="200"
ref="popoverRef"
:virtual-ref="buttonRef"
virtual-triggering
trigger="click">
<div class="col-setting-checkall">
<el-checkbox label="列展示" v-model="localData.allColShow" :indeterminate="localData.indeterminate" @change="changeIsShowAll"></el-checkbox>
</div>
<div class="col-setting-list">
<el-checkbox-group v-model="localData.checkGroup" @change="changeColShow">
<el-space direction="vertical" alignment="flex-start" :size="0">
<el-checkbox
v-for="item in tableConfig.columns"
:key="item.prop"
:label="item.label"
:value="item.prop"
/>
</el-space>
</el-checkbox-group>
</div>
</el-popover>
</div>
</div>
<!-- 表格部分 -->
<div class="fv-table">
@@ -30,7 +63,7 @@
ref="tableInstance"
>
<template #default>
<fvTableColumn v-for="column in tableConfig.columns" :key="column.prop" :columns="column">
<fvTableColumn v-for="column in localData.columns" :key="column.prop" :columns="column">
<template v-if="column?.slots?.header" #[column?.slots?.header]="params">
<slot :name="column?.slots?.header" v-bind="params || {}"></slot>
</template>
@@ -47,7 +80,7 @@
</template>
</el-table>
<!-- 分页 -->
<fvPagination
<fvPagination
v-if="pagination"
:current-page="localData.query.pageNum"
:page-size="localData.query.pageSize"
@@ -80,10 +113,17 @@ const props = defineProps({
pagination: {
type: Boolean,
default: true
},
// 是否显示列配置
isSettingCol: {
type: Boolean,
default: true
}
})
const tableInstance = ref()
const buttonRef = ref()
const popoverRef = ref()
const localData = reactive({
list: [], // 表格数据
@@ -92,7 +132,12 @@ const localData = reactive({
pageNum: 1
},
total: 0,
loading: false
loading: false,
// 列展示设置
columns: [],
allColShow: true, // 默认全部列都展示
indeterminate: false,
checkGroup: []
})
const emits = defineEmits(['headBtnClick', 'selectionChange', 'rowClick', 'rowDblclick', 'getBaseQuery', 'cellClick'])
@@ -101,6 +146,52 @@ const handleClickBtns = (key) => {
emits('headBtnClick', key)
}
const filterColumns = () => {
localData.columns = props.tableConfig.columns.map(item=>{
if(item.prop) {
return {
...item
}
}
})
}
const changeIsShowAll = (val) => {
if(val) {
filterColumns()
localData.indeterminate = false
localData.checkGroup = props.tableConfig.columns.map(item=>item.prop)
} else {
localData.columns.length = 0
localData.checkGroup.length = 0
localData.indeterminate = false
}
}
const changeColShow = (val) => {
if(val.length == props.tableConfig.columns.length) {
localData.indeterminate = false
localData.allColShow = true
} else if(val.length !== props.tableConfig.columns.length && val.length != 0) {
localData.allColShow = false
localData.indeterminate = true
} else {
localData.indeterminate = false
localData.allColShow = false
}
const template = []
props.tableConfig.columns.forEach(item=>{
val.forEach(v=>{
if(item.prop == v) {
template.push(item)
}
})
})
localData.columns = template
}
filterColumns()
const getList = async () => {
const { api, params } = props.tableConfig
const queryParmas = {...localData.query, ...params}
@@ -154,13 +245,11 @@ const handleCurrentChange = (val) => {
getList()
}
// watchEffect(()=>{
// if(!props.tableConfig.api) {
// localData.list = props.data
// }else {
// getList()
// }
// })
watchEffect(()=>{
if(localData.allColShow) {
localData.checkGroup = props.tableConfig.columns.map(item=>item.prop)
}
})
//刷新
const refresh = ({resetPage=false}={}) => {
resetPage ? localData.query.pageNum = 1 : null
@@ -180,9 +269,25 @@ defineExpose({ refresh, updateLoading, getQuery, tableInstance })
onMounted(() => {
getList()
})
</script>
<style lang="scss" scoped>
</style>
.col-setting-checkall {
border-bottom: 1px solid rgb(210, 210, 213);
}
.col-setting-list {
max-height: 45vh;
overflow-y: auto;
}
.table-head-btn {
display: flex;
justify-content: flex-start;
align-items: center;
}
.fv-table-btn {
display: flex;
justify-content: space-between;
align-content: center;
}
</style>

View File

@@ -49,7 +49,7 @@ import { isVNode } from 'vue'
if(column && column?.formatter) {
return column.formatter(row, column, value, $index) || '--'
}
return value ? value.toString() : '--'
return value !== null && value !== undefined ? value.toString() : '--'
}
</script>

View File

@@ -1,37 +1,37 @@
<template>
<div>
<el-popover
:width="300"
trigger="click"
placement="bottom"
popper-style="box-shadow: rgb(14 18 22 / 35%) 0px 10px 38px -10px, rgb(14 18 22 / 20%) 0px 10px 20px -15px; padding: 10px;"
>
<template #reference>
<el-badge :hidden="total===0" :value="total" class="item">
<el-icon size="22px" style="cursor: pointer">
<Bell/>
</el-icon>
</el-badge>
</template>
<template #default>
<div v-if="total===0" style="height: 100px;display: flex;align-items: center;justify-content: center">
暂无数据~
</div>
<ul v-else>
<li v-for="(notice,index) in noticeList" :key="index">
<span @click="handleToNotifyDetail(notice,index)">{{ notice.noticeTitle }}</span>
<span v-if="notice.state==='0'" @click="handleRead(notice)">已读</span>
</li>
</ul>
<!-- <el-popover-->
<!-- :width="300"-->
<!-- trigger="click"-->
<!-- placement="bottom"-->
<!-- popper-style="box-shadow: rgb(14 18 22 / 35%) 0px 10px 38px -10px, rgb(14 18 22 / 20%) 0px 10px 20px -15px; padding: 10px;"-->
<!-- >-->
<!-- <template #reference>-->
<!-- <el-badge :hidden="total===0" :value="total" class="item">-->
<!-- <el-icon size="22px" style="cursor: pointer">-->
<!-- <Bell/>-->
<!-- </el-icon>-->
<!-- </el-badge>-->
<!-- </template>-->
<!-- <template #default>-->
<!-- <div v-if="total===0" style="height: 100px;display: flex;align-items: center;justify-content: center">-->
<!-- 暂无数据~-->
<!-- </div>-->
<!-- <ul v-else>-->
<!-- <li v-for="(notice,index) in noticeList" :key="index">-->
<!-- <span @click="handleToNotifyDetail(notice,index)">{{ notice.noticeTitle }}</span>-->
<!-- <span v-if="notice.state==='0'" @click="handleRead(notice)">已读</span>-->
<!-- </li>-->
<!-- </ul>-->
<div class="notify-btn">
<el-button type="primary" @click="handlePrevious" :disabled="pageInfo.pageNum===1" link>上一页</el-button>
<span @click="handleMoreRead">本页已读</span>
<el-button type="primary" @click="handleNext" :disabled="pageInfo.pageNum*pageInfo.pageSize>total" link>下一页
</el-button>
</div>
</template>
</el-popover>
<!-- <div class="notify-btn">-->
<!-- <el-button type="primary" @click="handlePrevious" :disabled="pageInfo.pageNum===1" link>上一页</el-button>-->
<!-- <span @click="handleMoreRead">本页已读</span>-->
<!-- <el-button type="primary" @click="handleNext" :disabled="pageInfo.pageNum*pageInfo.pageSize>total" link>下一页-->
<!-- </el-button>-->
<!-- </div>-->
<!-- </template>-->
<!-- </el-popover>-->
<el-dialog width="1200px" title="通知公告详情" v-model="visible" @close="visible=false">
<el-form :model="viewForm" label-width="100px">
<el-row>

View File

@@ -6,8 +6,8 @@
<bell-socket/>
<div class="user-box">
<div>
<img :src="userInfo.avatar" alt="" @click.stop="handleVisitedP">
<span>{{userInfo.userName}}</span>
<!-- <img :src="userInfo.avatar" alt="" @click.stop="handleVisitedP">-->
<span @click.stop="handleVisitedP">欢迎回来{{userInfo.userName}}</span>
</div>
<div class="person" v-if="visitedP">
<ul>
@@ -27,8 +27,10 @@ import Hamburger from './Hamburger.vue';
import {useAuthStore} from '@/stores/userstore.js'
import BellSocket from "./BellSocket.vue";
import {getUserInfo} from "../../api/login";
import {usePermisstionStroe} from '@/stores/permisstion'
const authStore = useAuthStore()
const permisstionStore = usePermisstionStroe()
const userInfo = ref({})
const visitedP = ref(false)
const router = useRouter()
@@ -58,6 +60,7 @@ const handleToAuth = () => {
const handleLogout = () => {
visitedP.value = !visitedP.value
authStore.userLogout()
permisstionStore.removeMenu()
router.push('/login')
}
</script>
@@ -78,6 +81,7 @@ const handleLogout = () => {
align-items: center;
.user-box{
cursor: pointer;
margin-left: 10px;
position: relative;
>div:first-child{

View File

@@ -9,209 +9,216 @@ import {useAuthStore} from '@/stores/userstore.js'
NProgress.configure({showSpinner: false})
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: '/login',
name: 'login',
component: () => import('@/views/login/index.vue'),
meta: {
hidden: true,
title: '登录'
}
},
{
path: '/cas/login',
name: 'casLogin',
component: () => import('@/views/cas-login/index.vue'),
},
{
path: '/',
name: 'layout',
component: Layout,
redirect: '/home',
meta: {
hidden: false
},
children: [
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: '/home',
name: 'home',
component: () => import('@/views/home/index.vue'),
meta: {
title: '首页',
breadcrumb: true
}
path: '/login',
name: 'login',
component: () => import('@/views/login/index.vue'),
meta: {
hidden: true,
title: '登录'
}
},
{
path: '/auth',
name: 'auth',
component: () => import('@/views/auth/index.vue'),
meta: {
title: '个人中心',
breadcrumb: true
}
path: '/cas/login',
name: 'casLogin',
component: () => import('@/views/cas-login/index.vue'),
},
{
path: '/rapid/gen/edit/:tableId(\\d+)',
name: 'genEdit',
component: () => import('@/views/rapid/gen/editTable.vue'),
meta: {
title: '数据库生成配置',
breadcrumb: true
}
path: '/',
name: 'layout',
component: Layout,
redirect: '/home',
meta: {
hidden: false
},
children: [
{
path: '/home',
name: 'home',
component: () => import('@/views/home/index.vue'),
meta: {
title: '首页',
breadcrumb: true
}
},
{
path: '/auth',
name: 'auth',
component: () => import('@/views/auth/index.vue'),
meta: {
title: '个人中心',
breadcrumb: true
}
},
{
path: '/rapid/gen/edit/:tableId(\\d+)',
name: 'genEdit',
component: () => import('@/views/rapid/gen/editTable.vue'),
meta: {
title: '数据库生成配置',
breadcrumb: true
}
},
{
path: '/workflow/process/edit/:deploymentId',
name: 'processEdit',
component: () => import('@/views/workflow/process/ProcessEdit.vue'),
meta: {
title: '编辑流程',
breadcrumb: true
}
},
{
path: '/workflow/process',
name: 'process',
component: () => import('@/views/workflow/process/index.vue'),
meta: {
title: '流程管理',
breadcrumb: true
}
},
{
path: '/workflow/process/edit',
name: 'processAdd',
component: () => import('@/views/workflow/process/ProcessEdit.vue'),
meta: {
title: '新增流程',
breadcrumb: true
}
},
{
path: '/role-auth/user/:roleId(\\d+)/:roleName',
name: 'distribute',
component: () => import('@/views/system/role/DistributeUser.vue'),
meta: {
title: '角色分配用户',
breadcrumb: true
}
},
{
path: '/post-auth/user/:postId(\\d+)/:postName',
name: 'assignUser',
component: () => import('@/views/system/post/DistributeUser.vue'),
meta: {
title: '岗位分配用户',
breadcrumb: true
}
},
{
path: '/menu-auth/role/:menuId(\\d+)/:menuName',
name: 'assignRole',
component: () => import('@/views/system/menu/DistributeRole.vue'),
meta: {
title: '菜单分配角色',
breadcrumb: true
}
},
{
path: '/system/notice/inform/index/:queryId',
name: 'notify',
component: () => import('@/views/system/notice/inform/index.vue'),
meta: {
title: '通知公告',
breadcrumb: false
}
},
{
path: '/custom/query/sql/design/:queryId',
name: 'sql',
component: () => import('@/views/custom-query/sql/SqlDesign.vue'),
meta: {
title: '自定义sql查询配置',
breadcrumb: true
}
},
{
path: '/custom/query/data/adapter/design/:queryId',
name: 'dataAdapter',
component: () => import('@/views/custom-query/data-adapter/DataAdapterDesign.vue'),
meta: {
title: '自定义数据适配器',
breadcrumb: true
}
},
{
path: '/custom/query/echarts/design/:queryId',
name: 'echarts',
component: () => import('@/views/custom-query/echarts-editor/EchartsDesign.vue'),
meta: {
title: '自定义echarts查询配置',
breadcrumb: true
}
},
{
path: '/rapid/data/:dsId(\\d+)',
name: 'rapid',
component: () => import('@/views/rapid/gen/index.vue'),
meta: {
title: '数据源关联数据',
breadcrumb: true
}
},
]
},
{
path: '/workflow/process/edit/:deploymentId',
name: 'processEdit',
component: () => import('@/views/workflow/process/ProcessEdit.vue'),
meta: {
title: '编辑流程',
breadcrumb: true
}
path: '/topo/design/:queryId',
name: 'topEdit',
component: () => import('@/views/custom-query/topo/topologyDesign.vue'),
meta: {
title: 'top',
breadcrumb: false
}
},
{
path: '/workflow/process',
name: 'process',
component: () => import('@/views/workflow/process/index.vue'),
meta: {
title: '流程管理',
breadcrumb: true
}
},
{
path: '/workflow/process/edit',
name: 'processAdd',
component: () => import('@/views/workflow/process/ProcessEdit.vue'),
meta: {
title: '新增流程',
breadcrumb: true
}
},
{
path: '/role-auth/user/:roleId(\\d+)/:roleName',
name: 'distribute',
component: () => import('@/views/system/role/DistributeUser.vue'),
meta: {
title: '角色分配用户',
breadcrumb: true
}
},
{
path: '/post-auth/user/:postId(\\d+)/:postName',
name: 'assignUser',
component: () => import('@/views/system/post/DistributeUser.vue'),
meta: {
title: '岗位分配用户',
breadcrumb: true
}
},
{
path: '/menu-auth/role/:menuId(\\d+)/:menuName',
name: 'assignRole',
component: () => import('@/views/system/menu/DistributeRole.vue'),
meta: {
title: '菜单分配角色',
breadcrumb: true
}
},
{
path: '/system/notice/inform/index/:queryId',
name: 'notify',
component: () => import('@/views/system/notice/inform/index.vue'),
meta: {
title: '通知公告',
breadcrumb: false
}
},
{
path: '/custom/query/sql/design/:queryId',
name: 'sql',
component: () => import('@/views/custom-query/sql/SqlDesign.vue'),
meta: {
title: '自定义sql查询配置',
breadcrumb: true
}
},
{
path: '/custom/query/data/adapter/design/:queryId',
name: 'dataAdapter',
component: () => import('@/views/custom-query/data-adapter/DataAdapterDesign.vue'),
meta: {
title: '自定义数据适配器',
breadcrumb: true
}
},
{
path: '/custom/query/echarts/design/:queryId',
name: 'echarts',
component: () => import('@/views/custom-query/echarts-editor/EchartsDesign.vue'),
meta: {
title: '自定义echarts查询配置',
breadcrumb: true
}
},
{
path: '/rapid/data/:dsId(\\d+)',
name: 'rapid',
component: () => import('@/views/rapid/gen/index.vue'),
meta: {
title: '数据源关联数据',
breadcrumb: true
}
},
]
},
{
path: '/topo/design/:queryId',
name: 'topEdit',
component: () => import('@/views/custom-query/topo/topologyDesign.vue'),
meta: {
title: 'top',
breadcrumb: false
}
},
{
path: '/forbidden',
name: 'forbidden',
component: () => import('@/views/forbidden/index.vue'),
}
]
path: '/forbidden',
name: 'forbidden',
component: () => import('@/views/forbidden/index.vue'),
}
]
})
router.beforeEach(async (to, form, next) => {
const permisstionStore = usePermisstionStroe()
const authStore = useAuthStore()
NProgress.start()
if (!getToken()) {
if (to.path === '/login' || to.path === '/cas/login' || to.path === '/forbidden') {
next()
NProgress.done()
const permisstionStore = usePermisstionStroe()
const authStore = useAuthStore()
NProgress.start()
if (!getToken()) {
if (to.path === '/login' || to.path === '/cas/login' || to.path === '/forbidden') {
next()
NProgress.done()
} else {
let path = window.location.pathname;
let query = window.location.search
sessionStorage.setItem('toView', JSON.stringify({
path: path,
query: query
}))
window.location.href = `${window.location.origin}/api/auth/cas/login`
// next({path: '/api/auth/cas/login'})
}
} else {
next({path: '/login'})
}
} else {
if (to.path === '/login'|| to.path === '/cas/login') {
next('/')
NProgress.done()
} else {
permisstionStore.setIsLoadRoutes(true)
if (permisstionStore.isLoadRoutes && permisstionStore.asyncRouters.length == 0) {
await permisstionStore.setAsyncRouters()
await authStore.setUserInfo()
next({...to, replace: true})
} else {
next()
}
}
if (to.path === '/login' || to.path === '/cas/login') {
next('/')
NProgress.done()
} else {
permisstionStore.setIsLoadRoutes(true)
if (permisstionStore.isLoadRoutes && permisstionStore.asyncRouters.length == 0) {
await permisstionStore.setAsyncRouters()
await authStore.setUserInfo()
next({...to, replace: true})
} else {
next()
}
}
}
}
})
router.afterEach(() => {
NProgress.done()
NProgress.done()
})
export default router

View File

@@ -10,7 +10,17 @@ export const usePermisstionStroe = defineStore('permisstion', () => {
const asyncRouters = ref([])
//定义是否加载路由变量
const isLoadRoutes = ref(false)
const menuList = ref([])
const menuList = ref([
{
name: 'home',
path: '/home',
icon: 'home',
title: '首页',
meta: {
breadcrumb: true
}
}
])
const setIsLoadRoutes = (status) => {
return isLoadRoutes.value = status
@@ -21,7 +31,7 @@ export const usePermisstionStroe = defineStore('permisstion', () => {
const sRouter = JSON.parse(JSON.stringify(res.data))
const mData = JSON.parse(JSON.stringify(res.data))
asyncRouters.value = formatAsyncRouters(sRouter)
menuList.value = generateMenu(mData)
menuList.value = [...menuList.value, ...generateMenu(mData)]
addAsyncRouters(asyncRouters.value)
isLoadRoutes.value = false
} else {
@@ -96,12 +106,28 @@ export const usePermisstionStroe = defineStore('permisstion', () => {
}
}
const removeMenu = () => {
menuList.value = [
{
name: 'home',
path: '/home',
icon: 'home',
title: '首页',
meta: {
breadcrumb: true
}
}
]
asyncRouters.value.length = 0
}
return {
asyncRouters,
menuList,
isLoadRoutes,
setAsyncRouters,
setIsLoadRoutes
setIsLoadRoutes,
removeMenu
}
})

View File

@@ -1,72 +1,102 @@
import axios from "axios";
import { AxiosCanceler } from "./axiosCanceler";
import { ElMessage, ElMessageBox } from "element-plus";
import { getToken, removeToken } from "./auth";
import {AxiosCanceler} from "./axiosCanceler";
import {ElMessageBox, ElNotification} from "element-plus";
import {getToken, removeToken} from "./auth";
axios.defaults.headers['Content-Type']='application/json'
axios.defaults.headers['Content-Type'] = 'application/json'
const serveice = axios.create({
baseURL: import.meta.env.VITE_BASE_URL,
timeout: 6000
baseURL: import.meta.env.VITE_BASE_URL,
timeout: 6000
})
const axiosCanceler = new AxiosCanceler()
serveice.interceptors.request.use(config=>{
const ACCESS_TOKEN = getToken() || ''
if(ACCESS_TOKEN!==undefined && ACCESS_TOKEN!=='') {
config.headers['Authorization']=ACCESS_TOKEN
}
// 检查是否有重复请求, 有则取消掉
axiosCanceler.removePendingRequest(config)
// 将请求加入pendingMap
axiosCanceler.addPendingRequest(config)
return config
},error=>{
Promise.reject(error)
serveice.interceptors.request.use(config => {
const ACCESS_TOKEN = getToken() || ''
if (ACCESS_TOKEN !== undefined && ACCESS_TOKEN !== '') {
config.headers['Authorization'] = ACCESS_TOKEN
}
// 检查是否有重复请求, 有则取消掉
axiosCanceler.removePendingRequest(config)
// 将请求加入pendingMap
axiosCanceler.addPendingRequest(config)
return config
}, error => {
Promise.reject(error)
})
serveice.interceptors.response.use(response=>{
axiosCanceler.removePendingRequest(response.config)
//二进制数据直接返回
if(response.request.responseType === 'blob' || response.request.responseType === 'arraybuffer') {
return response.data
}
return response.data
},error=>{
let response = error.response
const status = response.status;
switch(status) {
case 401:
ElMessageBox.confirm('登录状态已过期,请重新登录','系统提示',{
confirmButtonText: '重新登录',
cancelButtonText: '取消',
type: 'warning',
closeOnClickModal: false
}).then(()=>{
removeToken()
window.location = '/login'
})
return Promise.reject('会话已过期,请重新登录')
case 402:
break;
case 403:
ElMessage.warning('禁止访问')
removeToken()
window.location = '/forbidden'
break;
case 404:
ElMessage.warning('不存在的地址')
break;
case 405:
ElMessage.warning('传输格式错误,请检查')
break;
case 500:
if (response.data){
serveice.interceptors.response.use(response => {
axiosCanceler.removePendingRequest(response.config)
//二进制数据直接返回
if (response.request.responseType === 'blob' || response.request.responseType === 'arraybuffer') {
return response.data
}else {
ElMessage.error('系统未知错误')
break;
}
}
return Promise.reject(error)
}
return response.data
}, error => {
let response = error.response
const status = response.status;
switch (status) {
case 401:
ElMessageBox.confirm('登录状态已过期,请重新登录', '系统提示', {
confirmButtonText: '重新登录',
cancelButtonText: '取消',
type: 'warning',
closeOnClickModal: false
}).then(() => {
removeToken()
let path = window.location.pathname;
let query = window.location.search
sessionStorage.setItem('toView', JSON.stringify({
path: path,
query: query
}))
window.location.href = `${window.location.origin}/api/auth/cas/login`
})
return Promise.reject('会话已过期,请重新登录')
case 402:
break;
case 403:
console.log(response)
ElNotification({
title: '系统提示',
message: response.data.msg,
type: 'warning'
})
break;
case 404:
ElNotification({
title: '系统提示',
message: '不存在的地址',
type: 'error'
})
break;
case 405:
ElNotification({
title: '系统提示',
message: '传输格式错误,请检查',
type: 'error'
})
break;
case 511:
ElNotification({
title: '系统提示',
message: '禁止访问',
type: 'error'
})
removeToken()
window.location = '/forbidden'
break;
case 500:
if (response.data) {
return response.data
} else {
ElNotification({
title: '系统提示',
message: '系统未知错误',
type: 'error'
})
break;
}
}
return Promise.reject(error)
})
export default serveice

View File

@@ -15,7 +15,14 @@ const init = () => {
})
if (authStore.casToken(tokenValue)) {
loading.close()
router.push('/')
const toView = sessionStorage.getItem('toView')
console.log(toView, 'toView');
if (toView) {
let parse = JSON.parse(toView);
window.location.href = window.location.origin + parse.path + parse.query
} else {
router.push('/')
}
}
}
init()

View File

@@ -1,246 +1,318 @@
<template>
<div class="home-container">
<div class="home-top">
<el-image :src="homeImage" style="width: 380px"/>
<div class="top-right">
<div>Admin欢迎回来</div>
</div>
</div>
<div class="home-top-right">
<el-image :src="coffee" style="height: 100px"/>
<span>科研管理平台</span>
</div>
</div>
<!-- <el-row :gutter="10" type="flex">-->
<!-- <el-col :span="6" :xs="8" :sm="16" :md="18" :lg="20" :xl="24" >-->
<!-- <div v-for="(item,index) in list" :key="index" class="block">-->
<!-- <div>{{item.title}}</div>-->
<!-- <div>-->
<!-- <el-icon><User/></el-icon>-->
<!-- <span>{{item.num}}</span>-->
<!-- </div>-->
<!-- </div>-->
<!-- </el-col>-->
<!-- </el-row>-->
<el-row :gutter="10">
<el-col :span="6" v-for="item in 4">1</el-col>
<!-- <el-col :xs="4" :sm="6" :md="8" :lg="9" :xl="11"-->
<!-- >2<div class="grid-content ep-bg-purple-light"-->
<!-- /></el-col>-->
<!-- <el-col :xs="4" :sm="6" :md="8" :lg="9" :xl="11"-->
<!-- >3<div class="grid-content ep-bg-purple"-->
<!-- /></el-col>-->
<!-- <el-col :xs="8" :sm="6" :md="4" :lg="3" :xl="1"-->
<!-- >4<div class="grid-content ep-bg-purple-light"-->
<!-- /></el-col>-->
</el-row>
<div class="container">
<div id="bar1" ref="bar1"></div>
<div id="bar2" ref="bar2"></div>
<div id="bar3" ref="bar3"></div>
</div>
<div class="container">
<div id="pie1" ref="pie1"></div>
<div id="pie2" ref="pie2"></div>
<div id="pie3" ref="pie3"></div>
<div class="home-bg">
<el-row gutter="20">
<el-col :xs="24" :sm="24" :md="18" :lg="18" :xl="18">
<div class="left">
<h3>我的科创工作</h3>
<el-row :gutter="20" class="statistics">
<el-col :xs="24" :sm="12" :md="12" :lg="6" :xl="6" v-for="(item,index) in list" :key="index">
<div class="block" :style="{background: item.color}">
<svg-icon :name="item.icon" :class-name="'home-icon'"/>
<div class="block-right">
<span>{{ item.title }}</span>
<span :style="{color: item.textColor}">{{ item.num }}<span></span></span>
</div>
</div>
</el-col>
</el-row>
<h4>待办 ({{ todoNum }})</h4>
<fvTable ref="tableIns" class="home-table" :tableConfig="tableConfig">
<template #empty>
<el-empty description="暂无待办"/>
</template>
</fvTable>
</div>
</el-col>
<el-col :xs="24" :sm="24" :md="6" :lg="6" :xl="6">
<div class="right">
<div class="right-top ">
<div>
<h3>帮助文档</h3>
<span>查看更多</span>
</div>
<el-divider/>
<div v-for="item in helpDocList" class="help">
{{ item.title }}
</div>
</div>
<div class="right-top">
<div>
<h3>工具下载</h3>
<span>常用网站</span>
</div>
<el-divider/>
</div>
</div>
</el-col>
</el-row>
</div>
</template>
<script setup>
import * as echarts from 'echarts'
import homeImage from "@/assets/home/home.png"
import coffee from "@/assets/home/coffee.png"
const list=ref([
<script setup lang="jsx">
import 'element-plus/theme-chalk/display.css'
const router = useRouter()
const list = ref([
{
title: '在线用户量',
num:2142
title: '待立项',
color: '#CEE8FA',
textColor: '#0043C5',
icon: 'home1',
num: 21
},
{
title: '在线用户量',
num:2142
title: '待评审',
color: '#DCCEFA',
textColor: '#8600C5',
icon: 'home2',
num: 2
},
{
title: '在线用户量',
num:2142
title: '待验收',
color: '#FAE6CE',
textColor: '#F47D0E',
icon: 'home3',
num: 4
},
{
title: '在线用户量',
num:2142
title: '待归档',
color: '#CEFAD8',
textColor: '#01A089',
icon: 'home4',
num: 1
}
])
const barOption = {
title: {
text: 'World Population'
const helpDocList = ref([
{
title: '业务流程'
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
}
{
title: '业务流程'
},
legend: {},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
{
title: '业务流程'
},
xAxis: {
type: 'value',
boundaryGap: [0, 0.01]
},
yAxis: {
type: 'category',
data: ['Brazil', 'Indonesia', 'USA', 'India', 'China', 'World']
},
series: [
{
title: '业务流程'
}
])
const todoNum = ref(20)
const tableConfig = reactive({
columns: [
{
name: '2011',
type: 'bar',
data: [18203, 23489, 29034, 104970, 131744, 630230]
prop: 'processName',
label: '流程名称',
align: 'center',
},
{
name: '2012',
type: 'bar',
data: [19325, 23438, 31000, 121594, 134141, 681807]
}
]
};
const pieOption = {
tooltip: {
trigger: 'item'
},
legend: {
top: '5%',
left: 'center'
},
series: [
prop: 'initiatorName',
label: '发起人',
align: 'center',
},
{
name: 'Access From',
type: 'pie',
radius: ['40%', '70%'],
avoidLabelOverlap: false,
itemStyle: {
borderRadius: 10,
borderColor: '#fff',
borderWidth: 2
},
label: {
show: false,
position: 'center'
},
emphasis: {
label: {
show: true,
fontSize: 40,
fontWeight: 'bold'
}
},
labelLine: {
show: false
},
data: [
{value: 1048, name: 'Search Engine'},
{value: 735, name: 'Direct'},
{value: 580, name: 'Email'},
{value: 484, name: 'Union Ads'},
{value: 300, name: 'Video Ads'}
]
prop: 'targetState',
label: '类型',
align: 'center',
showOverflowTooltip: false,
currentRender: ({row, index}) => (<Tag dictType={'todo_type'} value={row.targetState}/>)
},
{
prop: 'submitTime',
label: '提交时间',
align: 'center',
},
{
prop: 'taskName',
label: '当前节点',
align: 'center',
},
{
prop: 'oper',
label: '操作',
fixed: 'right',
width: '150',
align: 'center',
showOverflowTooltip: false,
currentRender: ({row, index}) => {
return (
<div>
<el-button type="primary" link onClick={() => handleView(row)}>查看</el-button>
<el-button type="primary" link onClick={() => handleEdit(row)}>已读</el-button>
</div>
)
}
}
]
};
const data = reactive({
barCharts: null,
pieCharts: null,
bar1: null,
bar2: null,
bar3: null,
pie1: null,
pie2: null,
pie3: null,
],
api: '/workflow/mosr/process/task',
params: {},
})
const init = () => {
data.barCharts = echarts.init(document.getElementById('bar1')).setOption(barOption)
data.barCharts = echarts.init(document.getElementById('bar2')).setOption(barOption)
data.barCharts = echarts.init(document.getElementById('bar3')).setOption(barOption)
data.pieCharts = echarts.init(document.getElementById('pie1')).setOption(pieOption)
data.pieCharts = echarts.init(document.getElementById('pie2')).setOption(pieOption)
data.pieCharts = echarts.init(document.getElementById('pie3')).setOption(pieOption)
const handleView = (row) => {
console.log('row', row)
if(row.targetState=='00'&&row.targetId){
router.push({
path: '/projectdemand/demanddetail',
query: {
id: row.targetId
}
})
}
}
onMounted(() => {
init()
})
window.addEventListener('resize', () => {
data.bar1 = null
data.bar2 = null
data.bar3 = null
data.pie1 = null
data.pie2 = null
data.pie3 = null
data.barCharts = null
data.pieCharts = null
init()
})
</script>
<style lang="scss" scoped>
.home-container {
display: flex;
align-items: center;
@media only screen and (max-width: 767px) {
.right {
margin-top: 10px;
}
:deep(.el-table) {
height: 300px !important;
}
.home-top {
width: 1000px;
height: 150px;
}
@media only screen and (max-width: 1000px) {
.right {
margin-top: 10px;
}
:deep(.el-table) {
height: 300px !important;
}
}
.home-bg {
height: calc(100vh - 130px);
max-height: calc(100vh - 96px);
background-color: #EFEFEF;
position: absolute;
left: 18px;
right: 0;
overflow-x: hidden;
overflow-y: auto;
&::-webkit-scrollbar {
width: 6px;
}
// 滚动条轨道
&::-webkit-scrollbar-track {
background: rgb(239, 239, 239);
border-radius: 2px;
}
// 小滑块
&::-webkit-scrollbar-thumb {
background: rgba(80, 81, 82, 0.29);
border-radius: 10px;
}
.left {
//flex: 0.8;
padding: 15px;
height: 100%;
border-radius: 10px;
background-color: #ffffff;
.el-table__empty-block {
.el-empty {
padding: 10px 0;
}
}
.statistics {
width: 99%;
margin-bottom: 20px;
.block {
display: flex;
align-items: center;
background-color: #e9edf2;
border-radius: 4px;
padding: 25px;
margin-top: 15px;
.block-right {
margin-left: 15%;
display: flex;
flex-direction: column;
justify-content: center;
color: #92969a;
font-size: 17px;
> span:first-child {
white-space: nowrap;
color: #000000;
margin-bottom: 10px;
}
> span:last-child {
white-space: nowrap;
font-size: 20px;
font-weight: bold;
> span {
margin-left: 10px;
}
}
}
}
}
}
.right {
height: calc(100vh - 130px);
display: flex;
align-items: center;
padding: 10px;
background-color: #e1eaf9;
flex-direction: column;
justify-content: space-between;
.top-right {
font-size: 16px;
margin-left: 10px;
letter-spacing: 1.5px;
.right-top {
flex: 0.5;
padding: 15px;
border-radius: 10px;
background-color: #ffffff;
}
.right-top {
flex: 0.48;
> div:first-child {
color: #557ad2;
font-weight: bold;
font-size: 20px;
margin-bottom: 10px;
display: flex;
justify-content: space-between;
align-items: center;
h3 {
white-space: nowrap;
margin-left: 15px;
}
> span {
white-space: nowrap;
color: #927648;
font-size: 14px;
}
}
.el-divider--horizontal {
height: 3px;
background: #D9D9D9;
border: none;
}
.help {
height: 61px;
line-height: 61px;
padding-left: 15px;
&:hover {
background: #FBFBF7;
}
}
}
}
.home-top-right {
flex: 1;
display: flex;
align-items: center;
flex-direction: column;
}
}
.statistics{
//display: flex;
.block{
}
}
.container {
height: calc((100vh / 2) - 150px);
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
> div {
height: 300px;
flex: 0.9;
}
}
</style>

View File

@@ -4,14 +4,14 @@
:model="loginForm"
ref="formInstance"
:rules="rules"
label-width="65px"
label-width="70px"
>
<h3>科研管理平台</h3>
<h2>科研管理平台</h2>
<el-form-item prop="username" label="账号">
<el-input v-model="loginForm.username" :prefix-icon="User"></el-input>
</el-form-item>
<el-form-item prop="password" label="密码">
<el-input v-model="loginForm.password" type="password" :prefix-icon="Lock"></el-input>
<el-input v-model="loginForm.password" type="password" :prefix-icon="Lock" :show-password="true"></el-input>
</el-form-item>
<el-form-item prop="code" label="验证码">
<div class="code">
@@ -20,6 +20,12 @@
<img :src="codeImg" alt="" @click="getCode">
</div>
</el-form-item>
<div class="sso">
<a href="/api/auth/cas/login">
<svg-icon name="sso"/>
<span>统一身份认证</span>
</a>
</div>
<el-form-item>
<el-button @click="handleLogin(formInstance)" type="primary">登录</el-button>
</el-form-item>
@@ -37,7 +43,7 @@ import {User, Lock, Key} from '@element-plus/icons-vue'
const router = useRouter()
const authStore = useAuthStore()
const loginForm = reactive({
username: 'admin',
username: 'superAdmin',
password: '123456',
code: '',
uuid: ''
@@ -98,26 +104,77 @@ onBeforeUnmount(() => {
<style lang="scss" scoped>
.login-box {
height: 100%;
background-color: #4158D0;
background-image: linear-gradient(43deg, #4158D0 0%, #C850C0 46%, #FFCC70 100%);
background-color: #F3F3F3;
//background-image: linear-gradient(43deg, #4158D0 0%, #C850C0 46%, #FFCC70 100%);
display: flex;
justify-content: center;
align-items: center;
.sso {
display: flex;
justify-content: center;
align-items: center;
margin-top: -5px;
margin-bottom: 10px;
a {
font-size: 16px;
color: #BEA266;
&:hover {
text-decoration: underline;
}
}
}
.el-form {
padding: 12px 15px;
border-radius: 12px;
width: 25%;
width: 560px;
background-color: #fff;
h3 {
.el-form-item {
margin-bottom: 25px;
:deep(.el-form-item__label) {
font-size: 15px;
}
.el-input {
height: 40px;
:deep(.el-input__inner) {
font-size: 18px;
}
:deep(.el-input__suffix) {
.el-input__password {
font-size: 20px;
}
}
:deep(.el-input__prefix ) {
.el-input__prefix-inner {
.el-icon {
font-size: 20px;
}
}
}
}
}
h2 {
width: 100%;
text-align: center;
margin-bottom: 15px;
margin-bottom: 25px;
}
.el-button {
width: 100%;
background-color: #BEA266;
border-color: #BEA266;
font-size: 17px;
}
}
}
@@ -134,7 +191,7 @@ onBeforeUnmount(() => {
}
img {
height: 32px;
height: 40px;
flex: 1;
}
}

View File

@@ -0,0 +1,272 @@
<template>
<div v-loading="loading" class="add-block">
<baseTitle title="需求征集信息录入"></baseTitle>
<el-form :model="formData" inline class="query-form" ref="demandForm" :rules="rules">
<div class="left-info">
<el-form-item label="名称" prop="requirementName">
<el-input v-model="formData.requirementName" placeholder="请输入名称" clearable></el-input>
</el-form-item>
<el-form-item label="所属公司" prop="companyIds">
<el-tree-select v-model="formData.companyIds" :data="companyOption" style="width: 100%;"
filterable clearable :check-strictly="true" multiple/>
</el-form-item>
<el-form-item label="征集类型" prop="collectType">
<el-select v-model="formData.collectType" placeholder="征集类型" clearable filterable>
<el-option
v-for="item in typeOption"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="截止时间" prop="deadline">
<el-config-provider>
<el-date-picker
v-model="formData.deadline"
type="datetime"
placeholder="截止时间"
value-format="YYYY-MM-DD HH:mm:ss"
/>
</el-config-provider>
</el-form-item>
</div>
</el-form>
<baseTitle title="征集说明"></baseTitle>
<Tinymce image-url="/notice/file" file-url="/notice/file" v-model:value="formData.collectExplain" height="300"/>
<baseTitle title="申请文件"></baseTitle>
<file-upload @getFile="getFile"/>
<el-table :data="formData.fileList" style="width: 100%">
<el-table-column label="序号" type="index" align="center" width="80"/>
<el-table-column prop="originalFileName" label="文件名" align="center"/>
<el-table-column prop="tag" label="标签" align="center"/>
<el-table-column prop="size" label="文件大小" align="center">
<template #default="scope">
{{ parseInt(scope.row.size / 1024) }}KB
</template>
</el-table-column>
<el-table-column align="center" label="操作">
<template #default="scope">
<a :href="scope.row.url">
下载
</a>
<el-button link type="primary" size="small" @click="beforeRemove(scope.row)">
删除
</el-button>
</template>
</el-table-column>
</el-table>
<div class="approval-record">
<baseTitle title="流程"></baseTitle>
<process-diagram-viewer mode="view" v-if="processDiagramViewer"/>
<!-- <div class="process" id="approvalRecord">-->
<!-- <process-tree ref="processTree" mode="view" id-name="approvalRecord"/>-->
<!-- </div>-->
</div>
<div class="oper-page-btn">
<el-button color="#DED0B2" @click="handleSubmit(demandForm)">提交</el-button>
<el-button color="#DED0B2" @click="handleResubmit">重新提交</el-button>
<el-button @click="handleBack">返回</el-button>
</div>
</div>
</template>
<script setup lang="jsx">
import {useAuthStore} from '@/stores/userstore.js'
import {useProcessStore} from '@/stores/processStore.js';
import {getWorkflowInfo, addRequirement, getFormInfo, resubmit, deleteFile} from "@/api/project-demand/index.js";
import FileUpload from "@/components/FileUpload.vue";
import ProcessDiagramViewer from '@/views/workflow/common/ProcessDiagramViewer.vue';
import {ElMessage, ElMessageBox} from "element-plus";
import {useRoute, useRouter} from 'vue-router'
import {getSubCompOpt} from '@/api/user/user.js'
import {useTagsView} from '@/stores/tagsview.js'
const tagsViewStore = useTagsView()
const authStore = useAuthStore()
const router = useRouter()
const route = useRoute()
const demandForm = ref()
const dateValue = ref()
const formData = ref({
requirementName: '',
companyIds: '',
collectType: '',
deadline: '',
collectExplain: ''
})
const processDiagramViewer = ref(false)
const typeOption = ref([
{
label: "需求征集",
value: '需求征集'
}
])
const companyOption = ref([])
const form = ref(null)
const fileList = ref(null)
const loading = ref(false)
const processStore = useProcessStore()
const processInstanceData = ref()
const rules = reactive({
requirementName: [{required: true, message: '请输入名称', trigger: 'blur'}],
companyIds: [{required: true, message: '请选择所属公司', trigger: 'blur'}],
collectType: [{required: true, message: '请选择征集类型', trigger: 'blur'}],
deadline: [{required: true, message: '请选择截止时间', trigger: 'blur'}],
})
const compositeParam=(item)=>{
return {
fileId: item.id,
size: item.size,
originalFileName: item.originalFilename,
fileType: item.fileType,
url: item.url,
processNodeTag: null,
tag: formData.value.collectType,
userId: authStore.userinfo.userId
}
}
const getFile = (val) => {
console.log('上传文件',val)
let fileObj = {}
let newFileArray = []
if (route.query.isAdd === undefined) {
val.forEach(item => {
fileObj =compositeParam(item)
newFileArray.push(fileObj)
formData.value.fileList.push(fileObj)
})
fileList.value = formData.value.fileList
} else {
val.forEach(item => {
fileObj =compositeParam(item)
newFileArray.push(fileObj)
})
formData.value.fileList = newFileArray
fileList.value = newFileArray
}
}
const init = async () => {
const res = await getSubCompOpt()
companyOption.value = res.data
getWorkflowInfo().then(res => {
let data = res.data
processInstanceData.value = data
processStore.setDesign(data)
processStore.runningList.value = data.runningList;
processStore.endList.value = data.endList;
processStore.noTakeList.value = data.noTakeList;
processStore.refuseList.value = data.refuseList;
processStore.passList.value = data.passList;
nextTick(() => {
processDiagramViewer.value = true
})
})
}
const handleSubmit = async (instance) => {
console.log('fileList.value',fileList.value)
if (!instance) return
instance.validate(async (valid) => {
if (!valid) return
let params = {
...formData.value,
requirementId: 0,
files: fileList.value,
deploymentId: processInstanceData.value.deploymentId
}
let res = await addRequirement(params)
if (res.code === 1000) {
ElMessage.success(res.msg)
await router.push({
path: '/projectdemand/demandcollection'
})
tagsViewStore.delVisitedViews(router.currentRoute.value.path)
} else {
ElMessage.error(res.msg)
}
})
}
const handleResubmit = () => {
resubmit({
...formData.value,
files: fileList.value,
deploymentId: processInstanceData.value.deploymentId
}).then(res => {
if (res.code === 1000) {
ElMessage.success(res.msg)
formData.value = res.data.formData
router.push({
path: '/projectdemand/demandcollection'
})
} else {
ElMessage.error(res.msg)
}
})
}
const getDetailInfo = async () => {
getFormInfo(route.query.id).then(res => {
if (res.code === 1000) {
console.log(res)
ElMessage.success(res.msg)
formData.value = res.data
} else {
ElMessage.error(res.msg)
}
})
}
const handleBack = () => {
history.back()
}
const beforeRemove = (row) => {
ElMessageBox.confirm(`确认删除名称为${row.originalFileName}的表格吗?`, '系统提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
handleRemove(row)
}).catch(() => {
ElMessage.warning("用户取消删除! ");
})
}
const handleRemove = (row) => {
deleteFile(row.fileId).then(res => {
if (res.code === 1000) {
ElMessage.success("删除成功");
fileList.value.splice(fileList.value.findIndex((item) => item.id === row.id), 1);
}
});
};
onMounted(async () => {
loading.value = true
await init()
if (route.query.id) {
await getDetailInfo()
}
loading.value = false
})
</script>
<style lang="scss" scoped>
.add-block {
//display: flex;
//justify-content: space-between;
overflow-x: hidden;
overflow-y: auto;
a {
cursor: pointer;
font-size: 14px;
color: #2a99ff;
}
.approval-record {
position: relative;
}
}
</style>

View File

@@ -0,0 +1,221 @@
<template>
<div class="detail-block">
<el-form :model="formData" label-width="auto">
<baseTitle title="需求征集详情"></baseTitle>
<div class="left-info">
<el-row>
<el-col :span="12">
<el-form-item label="名称">
<span>{{ formData.requirementName }}</span>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="所属公司">
<span>{{ formData.companyIds }}</span>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="征集类型">
<span>{{ formData.collectType }}</span>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="截止时间">
<span>{{ formData.deadline }}</span>
</el-form-item>
</el-col>
<baseTitle title="征集说明"></baseTitle>
<el-col :span="24">
<el-form-item>
<el-card style="width: 100%">
<div v-html="formData.collectExplain">
</div>
</el-card>
</el-form-item>
</el-col>
<baseTitle title="附件列表"></baseTitle>
<el-col :span="24">
<el-form-item>
<el-table :data="formData.fileList" style="width: 100%">
<el-table-column label="序号" type="index" align="center" width="80"/>
<el-table-column prop="originalFileName" label="文件名" align="center"/>
<el-table-column prop="tag" label="标签" align="center"/>
<el-table-column prop="size" label="文件大小" align="center">
<template #default="scope">
{{ parseInt(scope.row.size / 1024) }}KB
</template>
</el-table-column>
<el-table-column align="center" label="操作">
<template #default="scope">
<a :href="scope.row.url">
下载
</a>
</template>
</el-table-column>
</el-table>
</el-form-item>
</el-col>
<el-col :span="24">
<div v-if="processInstanceData.taskId">
<baseTitle title="审核意见"></baseTitle>
<el-form-item>
<el-input
v-model="auditOpinion"
:rows="3"
type="textarea"
placeholder="请输入审核意见"
/>
</el-form-item>
</div>
</el-col>
</el-row>
</div>
<div class="approval-record">
<baseTitle title="审批记录"></baseTitle>
<div class="process">
<operation-render v-if="processDiagramViewer" :operation-list="processInstanceData.operationList"
:state="processInstanceData.state"/>
<process-diagram-viewer v-if="processDiagramViewer"/>
</div>
</div>
</el-form>
<div class="oper-page-btn" v-if="processInstanceData.state === '1' && processInstanceData.taskId">
<el-button @click="handleReject">驳回</el-button>
<el-button color="#DED0B2" @click="handleSubmit">同意</el-button>
</div>
</div>
</template>
<script setup lang="jsx">
import OperationRender from '@/views/workflow/common/OperationRender.vue'
import ProcessDiagramViewer from '@/views/workflow/common/ProcessDiagramViewer.vue'
import {useProcessStore} from '@/stores/processStore.js';
import {getInfo, agreeTask, rejectTask} from "@/api/project-demand/index.js";
import {getSubCompOpt} from '@/api/user/user.js'
import {ElMessage} from "element-plus";
import {useRouter} from "vue-router";
const router = useRouter()
const route = useRoute()
const form = ref();
const processStore = useProcessStore()
const companyOption = ref([])
const processInstanceData = ref({})
const processDiagramViewer = ref(false)
const processTree = ref()
const companyNameArray = ref([])
const formData = ref({})
const auditOpinion = ref('')
const handleSubmit = () => {
let approve = {
taskId: processInstanceData.value.taskId,
auditOpinion: auditOpinion.value,
formData: formData.value
}
agreeTask(approve).then(res => {
console.log(res)
})
}
const handleReject = () => {
let approve = {
taskId: processInstanceData.value.taskId,
auditOpinion: auditOpinion.value,
}
rejectTask(approve).then(res => {
console.log(res)
if (res.code === 1000) {
ElMessage.success(res.msg)
router.push({
path: '/projectdemand/demandcollection'
})
}else {
ElMessage.error(res.msg)
}
})
}
const getCompanyOption = async () => {
const res = await getSubCompOpt()
companyOption.value = res.data
}
const matterTree = (data, id) => {
if(id){
for (let i = 0; i < data.length; i++) {
if (data[i].value == id) {
companyNameArray.value.push(data[i].label);
}
if (data[i].children && data[i].children.length > 0) {
matterTree(data[i].children)
}
}
return companyNameArray.value;
}
}
const getDataSourceOptionItem = (val) => {
if (val !== undefined) {
val.forEach(item => {
matterTree(companyOption.value,item)
})
}
return companyNameArray.value.join('');
}
const init = async () => {
await getCompanyOption()
getInfo(route.query.id).then(res => {
let data = res.data
formData.value = data.formData;
data.formData.companyIds= getDataSourceOptionItem(data.formData.companyIds)
processInstanceData.value = data
processStore.setDesign(data)
processStore.runningList.value = data.runningList;
processStore.endList.value = data.endList;
processStore.noTakeList.value = data.noTakeList;
processStore.refuseList.value = data.refuseList;
processStore.passList.value = data.passList;
nextTick(() => {
processDiagramViewer.value = true
})
})
}
init()
</script>
<style lang="scss" scoped>
a {
cursor: pointer;
font-size: 14px;
color: #2a99ff;
}
.detail-block {
overflow: hidden;
padding-right: 10px;
.left-info {
.info {
display: flex;
flex-wrap: wrap;
> div {
width: 350px;
margin-bottom: 15px;
margin-right: 10px;
> span:first-child {
color: black;
font-size: 16px;
font-weight: bold;
}
}
}
}
.approval-record {
padding-bottom: 30px;
.process {
position: relative;
}
}
}
</style>

View File

@@ -0,0 +1,160 @@
<template>
<fvSearchForm :searchConfig="searchConfig" @search="search"></fvSearchForm>
<fvTable ref="tableIns" :tableConfig="tableConfig" @headBtnClick="headBtnClick"></fvTable>
</template>
<script setup lang="jsx">
import Tag from '@/components/Tag.vue'
import fvSelect from '@/fvcomponents/fvSelect/index.vue'
const router = useRouter()
const searchConfig = reactive([
{
label: '需求名称',
prop: 'requirementName',
component: 'el-input',
props: {
placeholder: '请输入名称查询',
clearable: true,
filterable: true,
checkStrictly: true
}
},
{
label: '征集类型',
prop: 'collectType',
component: shallowRef(fvSelect),
props: {
placeholder: '请选择征集类型',
clearable: true,
filterable: true,
cacheKey: 'todo_type'
}
}
])
const tableIns = ref()
const tableConfig = reactive({
columns: [
{
type: 'selection',
prop: 'selection'
},
{
prop: 'requirementName',
label: '需求名称',
align: 'center'
},
{
prop: 'collectType',
label: '征集类型',
align: 'center'
},
{
prop: 'approveName',
label: '审批人',
align: 'center'
},
{
prop: 'deadline',
label: '截止时间',
align: 'center'
},
{
prop: 'taskNode',
label: '当前节点',
align: 'center'
},
{
prop: 'state',
label: '状态',
align: 'center',
width: 200,
showOverflowTooltip: false,
currentRender: ({row, index}) => (<Tag dictType={'process_state'} value={row.state}/>)
},
{
prop: 'oper',
label: '操作',
align: 'center',
showOverflowTooltip: false,
currentRender: ({row, index}) => {
let btn = [{label: '详情', func: () => handleDetail(row), type: 'primary'}]
if (row.state === '3' || row.state === '2') {
btn.push({label: '编辑', func: () => handleEdit(row), type: 'primary'})
btn.push({label: '删除', func: () => handleDelete(row), type: 'danger'})
} else if (row.state === '4') {
btn.push({label: '上报', func: () => handleReport(row), type: 'primary'})
}
return (
<div style={{width: '100%'}}>
{
btn.map(item => (
<el-button
type={item.type}
// v-perm={item.auth}
onClick={() => item.func()}
link
>
{item.label}
</el-button>
))
}
</div>
)
}
}
],
api: '/workflow/mosr/requirement',
btns: [
{name: '新增', key: 'add', color: '#DED0B2'},
{name: '导出', key: 'add', type: ''},
],
params: {}
})
const search = (val) => {
tableConfig.params = {...val}
tableIns.value.refresh()
}
const handleAdd = () => {
router.push({
path: '/projectdemand/demandadd',
query: {
isAdd: 1
}
})
}
const handleEdit = (row) => {
router.push({
path: '/projectdemand/demandedit',
query: {
id: row.requirementId
}
})
}
const handleDetail = (row) => {
router.push({
path: '/projectdemand/demanddetail',
query: {
id: row.requirementId
}
})
}
const headBtnClick = (key) => {
switch (key) {
case 'add':
handleAdd()
break;
case 'edit':
handleEdit()
break;
case 'detail':
handleDetail()
break;
}
}
</script>
<style lang="scss" scoped>
</style>

View File

@@ -0,0 +1,182 @@
<template>
<fvForm :schema="schame" @getInstance="getInstance"></fvForm>
<div class="oper-page-btn">
<el-button type="primary" @click="staging">存为草稿</el-button>
<el-button type="primary" @click="handleSubmit">发布</el-button>
</div>
</template>
<script setup lang="jsx">
import fvSelect from '@/fvcomponents/fvSelect/index.vue'
import { markRaw } from 'vue';
import FileUpload from './components/FileUpload.vue';
const localData = reactive({
form: null
})
const schame = computed(()=>{
let arr = [
{
label: '名称',
prop: 'name',
component: 'el-input',
props: {},
colProps: {
span: 12
}
},
{
label: '开始时间',
prop: 'startTime',
component: 'el-date-picker',
props: {},
colProps: {
span: 12
}
},
{
label: '所属公司',
prop: 'company',
component: 'el-input',
props: {},
colProps: {
span: 12
}
},
{
label: '项目类型',
prop: 'projectType',
component: markRaw(fvSelect),
props: {},
colProps: {
span: 12
}
},
{
label: '结束时间',
prop: 'endTime',
component: 'el-date-picker',
props: {},
colProps: {
span: 12
}
},
{
label: '出资类型',
prop: 'monenyType',
component: markRaw(fvSelect),
props: {},
colProps: {
span: 12
}
},
{
label: '项目影响',
prop: 'projectEffect',
component: markRaw(fvSelect),
props: {},
colProps: {
span: 12
}
},
{
label: '所属业务板块',
prop: 'ywbank',
component: markRaw(fvSelect),
props: {},
colProps: {
span: 12
}
},
{
label: '预期成果形式',
prop: 'yuqichengg',
component: markRaw(fvSelect),
props: {},
colProps: {
span: 12
}
},
{
label: '预期技术标准制定',
prop: 'yuqizhidingbiaozhu',
component: markRaw(fvSelect),
props: {},
colProps: {
span: 12
}
},
{
label: '产学研联合',
prop: 'chanxue',
component: markRaw(fvSelect),
props: {},
colProps: {
span: 12
}
},
{
label: '开展政府申报',
prop: 'zhengfushenbao',
component: markRaw(fvSelect),
props: {},
colProps: {
span: 12
}
},
{
label: '知识产权状况',
prop: 'zhishichanquan',
component: markRaw(fvSelect),
props: {},
colProps: {
span: 12
}
},
{
label: '现有业务描述',
prop: 'yewudes',
component: 'el-input',
props: {
type: 'textarea'
},
colProps: {
span: 24
}
},
{
label: '研发项目关键内容描述',
prop: 'contentnesss',
component: 'el-input',
props: {
type: 'textarea'
},
colProps: {
span: 24
}
},
{
label: '',
prop: 'fileList',
component: markRaw(FileUpload),
props: {
url: '',
tip: '上传txt文件'
},
colProps: {
span: 24
}
}
]
return arr
})
const getInstance = (e) => {
localData.form = e
}
const handleSubmit = async () => {}
const staging = async () => {}
</script>

View File

@@ -0,0 +1,12 @@
import request from '@/utils/request'
export const fileUp = (url, data) => {
return request({
url,
method: 'post',
data,
headers: {
'Content-Type': 'multipart/form-data'
}
})
}

View File

@@ -0,0 +1,75 @@
<template>
<el-upload
ref="uploadRef"
class="upload-demo"
v-bind="$attrs"
drag
:action="url"
multiple
:on-change="change"
:on-progress="progress"
:on-remove="remove"
:before-remove="beforeRemove"
v-model:file-list="fileList"
:http-request="uploadRequest"
style="width: 50%;"
>
<el-icon class="el-icon--upload"><upload-filled /></el-icon>
<div class="el-upload__text">
拖拽上传/<em>点击上传</em>
</div>
<template #tip>
<div class="el-upload__tip">
{{ tip }}
</div>
</template>
</el-upload>
</template>
<script setup lang="jsx">
import { reactive, ref } from 'vue';
import { fileUp } from '../api';
const props = defineProps({
url: {
type: String,
default: ''
},
tip: {
type: String,
default: ''
}
})
const fileList = ref([])
const uploadRef = ref()
const localData = reactive({
})
const change = (file, files) => {
console.log(file, 'file');
}
const progress = (UploadProgressEvent, UploadFile, UploadFiles) => {
}
const remove = (file, files) => {}
const beforeRemove = (file, files) => {}
const uploadRequest = (UploadRequestOptions) => {
console.log(UploadRequestOptions, 'UploadRequestOptions');
// UploadRequestOptions.data = {}
const formData = new FormData()
formData.append('file', UploadRequestOptions.file)
// formData.append('params', UploadRequestOptions.data)
fileUp(url, formData)
}
</script>
<style lang="scss" scoped>
</style>

View File

@@ -0,0 +1,168 @@
<template>
<fvSearchForm :searchConfig="searchConfig" @search="search"></fvSearchForm>
<fvTable ref="tableIns" :tableConfig="tableConfig" @headBtnClick="headBtnClick"></fvTable>
</template>
<script setup lang="jsx">
import { reactive, ref, shallowRef } from 'vue';
import fvSelect from '@/fvcomponents/fvSelect/index.vue'
import { useRouter } from 'vue-router';
const localData = reactive({
})
const tableIns= ref()
const router = useRouter()
const searchConfig = reactive([
{
label: '名称',
prop: 'name',
props: {
placeholder: '请输入'
},
component: 'el-input',
colProps: {}
},
{
label: '项目类型',
prop: 'projectType',
component: shallowRef(fvSelect),
props: {},
colProps: {}
},
{
label: '研发主体',
prop: 'productMainBody',
component: shallowRef(fvSelect),
props: {},
colProps: {}
},
{
label: '项目影响',
prop: 'projectEffect',
component: shallowRef(fvSelect),
props: {},
colProps: {}
},
{
label: '起止时间',
prop: 'time',
component: 'el-date-picker',
props: {},
colProps: {}
},
{
label: '最小金额',
prop: 'minMoney',
component: 'el-input',
colProps: {}
},
{
label: '最大金额',
prop: 'maxMoney',
component: 'el-input',
colProps: {}
},
])
const tableConfig = reactive({
columns: [
{
type: 'selection',
prop: 'selection'
},
{
prop: 'name',
label: '名称',
align: 'center'
},
{
prop: 'company',
label: '所属公司',
align: 'center'
},
{
prop: 'projectType',
label: '项目类型',
align: 'center'
},
{
prop: 'productMainBody',
label: '研发主体',
align: 'center'
},
{
prop: 'projectEffect',
label: '项目影响',
align: 'center'
},
{
prop: 'survey',
label: '经营概况',
align: 'center'
},
{
prop: 'time',
label: '起止时间',
align: 'center'
},
{
prop: 'status',
label: '发布状态',
align: 'center'
},
{
prop: 'oper',
label: '操作',
align: 'center',
currentRender: ({row, index}) => {
return (
<div>
<el-button type={'primary'} link onClick={()=>{}} >详情</el-button>
<el-button type={'primary'} link onClick={()=>{}} >上报</el-button>
</div>
)
}
}
],
api: '',
params: {},
btns: [
{name: '新增上报', key: 'add', type: 'primary', auth: ''},
{name: '年度计划导出', key: '_export', type: 'primary', auth: ''},
{name: '经费预算生成', key: 'preMonty', type: 'primary', auth: ''},
]
})
const search = (val) => {
let obj = {...val}
if(obj.time) {
obj.startTime = obj.time[0]
obj.endTime = obj.time[1]
delete obj.dateValue
}
tableConfig.params = obj
tableIns.value.refresh()
}
const headBtnClick = (key) => {
switch(key) {
case 'add': handleAdd()
break;
}
}
const handleAdd = () => {
router.push({
name: 'Demandsummaryadd',
query: {}
})
}
</script>
<style lang="scss" scoped>
</style>

View File

@@ -39,7 +39,7 @@
@select="handleSelect"
v-tabh
>
<el-table-column type="selection" width="55"/>
<el-table-column type="selection" width="55" fixed="left" />
<el-table-column label="序号" type="index" align="center" width="60"/>
<el-table-column prop="roleName" label="角色名称" align="center"/>
<el-table-column prop="roleKey" label="角色标识符" align="center"/>
@@ -59,7 +59,7 @@
</template>
</el-table-column>
<el-table-column prop="createTime" label="创建时间" align="center" width="180px"/>
<el-table-column label="操作" align="center">
<el-table-column label="操作" align="center" fixed="right">
<template #default="scope">
<popover-delete :name="scope.row.roleName" :type="'角色'" :btn-text="'取消授权'"
@delete="handleCancelAuthorization([scope.row.roleId])"/>
@@ -98,7 +98,7 @@
v-loading="dialogLoading"
@select="handleDialogSelect"
>
<el-table-column type="selection" width="55"/>
<el-table-column type="selection" width="55" fixed="left" />
<el-table-column label="序号" type="index" align="center" width="60"/>
<el-table-column prop="roleName" label="角色名称" align="center"/>
<el-table-column prop="roleKey" label="角色标识符" align="center"/>
@@ -117,7 +117,7 @@
</template>
</el-table-column>
<el-table-column prop="createTime" label="创建时间" align="center" width="180px"/>
<el-table-column label="操作" align="center">
<el-table-column label="操作" align="center" fixed="right">
<template #default="scope">
<el-button type="primary" size="mini"
@click="handleSubmit(scope.row.roleId)" link>添加

View File

@@ -16,7 +16,7 @@
</el-form-item>
</el-form>
<div class="table-header-btn">
<el-button type="primary" @click="handleAdd" :icon="Plus">新增</el-button>
<el-button type="primary" @click="handleAdd" :icon="Plus" v-perm="['admin:menu:add']">新增</el-button>
<el-button type="info" @click="handleExpand" :icon="Sort">{{ isExpand ? '全部收起' : '全部展开' }}</el-button>
</div>
@@ -42,15 +42,13 @@
</template>
</el-table-column>
<el-table-column prop="createTime" label="创建时间"/>
<el-table-column label="操作" prop="operation">
<el-table-column label="操作" prop="operation" align="right">
<template #default="scope">
<el-button type="primary" v-if="scope.row.menuType!=='B'" size="mini" @click="handleAdd(scope.row)" link>新增
<el-button type="primary" v-if="scope.row.menuType!=='B'" @click="handleAdd(scope.row)" link v-perm="['admin:menu:add']">新增
</el-button>
<div v-else style="display: inline-block">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
</div>
<el-button type="primary" size="mini" @click="handleEdit(scope.row.menuId)" link>修改</el-button>
<el-button type="primary" size="mini" @click="handleAssignRoles(scope.row)" link>分配角色</el-button>
<popover-delete :name="scope.row.menuName" :type="'菜单'" @delete="handleDel(scope.row.menuId)"/>
<el-button type="primary" @click="handleEdit(scope.row.menuId)" link v-perm="['admin:menu:edit']">修改</el-button>
<!-- <el-button type="primary" @click="handleAssignRoles(scope.row)" link>分配角色</el-button> -->
<popover-delete :name="scope.row.menuName" :type="'菜单'" @delete="handleDel(scope.row.menuId)" :perm="['admin:menu:del']" />
</template>
</el-table-column>
</el-table>

View File

@@ -2,7 +2,16 @@
<div v-loading="loading">
<baseTitle title="角色信息录入"></baseTitle>
<fvForm :schema="schame" @getInstance="getInstance" :rules="rules"></fvForm>
<baseTitle title="分配菜单"></baseTitle>
<div class="assign-menu-title" >
<baseTitle title="分配菜单"></baseTitle>
<fvSelect
:options="localData.tempRoleOpt"
v-model="localData.tempRoleSelect"
style="width: 200px;"
placeholder="请选择模版角色"
@change="roleTempChange"
/>
</div>
<fvCheckbox :options="localData.checkOptions" v-model="localData.checkList" @change="checkBoxChange" />
<el-input v-model="localData.filterText" placeholder="请输入关键词" style="width: 400px;" />
<div class="menu-assign">
@@ -11,7 +20,7 @@
:data="localData.menuData"
:filter-node-method="filterMenu"
:props="localData.menuTreeProps"
:check-strictly="localData.checkStrictly"
:check-strictly="!localData.checkStrictly"
show-checkbox
node-key="menuId"
@check-change="checkChange"
@@ -30,7 +39,7 @@ import { useAuthStore } from '@/stores/userstore.js'
import fvRadio from '@/fvcomponents/fvRadio/index.vue'
import { ElLoading, ElNotification } from 'element-plus';
import { getMenuList } from '@/api/system/menuman.js'
import { getRoleDetail, operate} from "@/api/role/role";
import { getRoleDetail, operate, getTemRoleOption } from "@/api/role/role";
const tagsViewStore = useTagsView()
const authStore = useAuthStore()
@@ -50,7 +59,7 @@ const localData = reactive({
label: 'menuName',
children: 'children'
},
checkStrictly: false,
checkStrictly: true,
checkList: ['3'],
checkOptions: [
{
@@ -65,7 +74,9 @@ const localData = reactive({
label: '父子联动',
value: '3'
},
]
],
tempRoleOpt: [],
tempRoleSelect: ''
})
const schame = computed(()=>{
@@ -115,7 +126,7 @@ const schame = computed(()=>{
}
},
]
return !authStore.roles.includes('superAdmin') ? arr.slice(-1) : arr
return !authStore.roles.includes('superAdmin') ? arr.slice(0, arr.length - 1) : arr
})
const rules = reactive({
@@ -130,6 +141,8 @@ const getInstance = (e) => {
const init = async () => {
form.value.setValues({state: '1', template: false})
const res = await getTemRoleOption()
localData.tempRoleOpt = res.data
const { data } = await getMenuList()
localData.menuData = data
}
@@ -140,12 +153,25 @@ const getInfo = async () => {
data.menuIds.forEach(key=>{
menuTree.value.setChecked(key, true, false)
})
if(route.query.isAdd) {
form.value.setValues(data)
}
const roleTempChange = async (val) => {
try {
loading.value = true
const { data } = await getRoleDetail(val.value)
data.menuIds.forEach(key=>{
menuTree.value.setChecked(key, true, false)
})
delete data.roleId
delete data.roleName
delete data.roleKey
form.value.setValues(data)
loading.value = false
} catch (error) {
loading.value = false
}
form.value.setValues(data)
}
const filterMenu = (value, data) => {
@@ -222,6 +248,14 @@ onMounted( async ()=>{
</script>
<style lang="scss" scoped>
.assign-menu-title {
display: flex;
justify-content: flex-start;
align-items: center;
>div:first-child {
margin-right: 15px;
}
}
.menu-assign {
width: 400px;
max-height: 500px;

View File

@@ -129,17 +129,10 @@ const tableConfig = reactive({
prop: 'oper',
label: '操作',
align: 'right',
fixed: 'right',
showOverflowTooltip: false,
currentRender: ({row, index}) => {
let btn = [{label: '修改', auth: auths.edit, func: ()=>handleEdit(row) , type: 'primary'}]
row.template ?
btn.push(
{
label: '使用模版',
auth: auths.edit,
func: ()=>handleUseTemp(row),
type: 'primary'
}
) :
btn.push(
{
label: '分配用户',
@@ -221,16 +214,6 @@ const handleEdit = (row) => {
})
}
const handleUseTemp = (row) => {
router.push({
path: '/system/roleadd',
query: {
id: row.roleId,
isAdd: 1
}
})
}
const handleAssign = (row) => {
}

View File

@@ -2,8 +2,9 @@
<div v-loading="loading" class="initiate_process">
<div v-if="!loading" style="min-width:30%">
<!--渲染表单-->
<form-render class="process-form" ref="initiateForm" :form-items="processDefinition.formItems"
v-model:value="formData" mode="E"/>
<!-- todo 关闭以前的表单渲染 , 此处需要根据参数来定制当前需要展示的页面信息 -->
<!-- <form-render class="process-form" ref="initiateForm" :form-items="processDefinition.formItems"-->
<!-- v-model:value="formData" mode="E"/>-->
</div>
<div v-if="!loading" id="approveTree"
style="display: flex;justify-content: center;flex-direction: column;min-width:60%">
@@ -18,8 +19,9 @@ import {getInitiateInfo} from "@/api/workflow/process-definition.js";
import ProcessTree from '@/views/workflow/process/ProcessTree.vue'
import FormRender from '@/views/workflow/form/FormRender.vue'
import {useProcessStore} from '@/stores/processStore.js'
const processStore = useProcessStore()
import {defineProps,defineExpose} from 'vue'
import {defineProps, defineExpose} from 'vue'
import {ElMessage} from "element-plus";
const props = defineProps({

View File

@@ -101,6 +101,7 @@ const handleReset = () => {
}
const submitForm = () => {
// todo 重新编写表单数据, 不适用当前的动态表单
let formData = processInstance.value.formData
let paramsData = {
processDefinitionId: selectItem.value.processDefinitionId,

View File

@@ -29,8 +29,8 @@
</div>
</div>
<div style="height: 15px;background:#f5f5f5;"></div>
<form-render ref="taskViewForm" :form-items="processInstanceData.formItems"
v-model:value="processInstanceData.formData"/>
<!-- <form-render ref="taskViewForm" :form-items="processInstanceData.formItems"-->
<!-- v-model:value="processInstanceData.formData"/>-->
<div style="height: 15px;background:#f5f5f5;"></div>
<operation-render :operation-list="processInstanceData.operationList"
:state="instance.state"/>
@@ -45,7 +45,7 @@
<script setup>
import Tag from '@/components/Tag.vue'
import FormRender from '@/views/workflow/form/FormRender.vue'
// import FormRender from '@/views/workflow/form/FormRender.vue'
import OperationRender from '@/views/workflow/common/OperationRender.vue'
import ProcessDiagramViewer from '@/views/workflow/common/ProcessDiagramViewer.vue'
import {getInitiatedInstanceInfo} from "@/api/workflow/process-instance.js";
@@ -95,4 +95,4 @@ defineExpose({
init
})
</script>
</script>

View File

@@ -7,6 +7,7 @@
:color="operation.color"
size="large"
placement="top">
<el-card>
<div style="display: flex;">
<div v-for="(user,index) in operation.userInfo" :key="index" class="avatar_name">
@@ -19,34 +20,43 @@
<component :is="user.icon"/>
</el-icon>
</div>
<el-tooltip effect="dark" :content="user.name" placement="bottom-start">
<el-tooltip effect="dark" :content="user.name" placement="bottom-start">
<span class="username">{{ user.name }}</span>
</el-tooltip>
<template v-if="user.auditOpinion">
<div style="margin-top: 10px;background:#f5f5f5;padding: 10px;">
<div>
{{ user.auditOpinion }}
</div>
</div>
</template>
</div>
<div style="margin-left: 10px;">
<div style="color: #c0bebe">{{ operation.operationName }}</div>
<div style="font-size: 14px; font-weight: bold;">{{ operation.remark }}</div>
</div>
</div>
<template v-if="operation.comment">
<div style="margin-top: 10px;background:#f5f5f5;padding: 10px;">
<div>
{{ operation.comment.context }}
</div>
<div style="margin-top: 10px;" v-if="operation.comment.attachments && operation.comment.attachments.length > 0">
<template v-for="(item) in getAttachmentList(operation.comment.attachments,true)">
<el-image
style="width: 100px; height: 100px"
:src="item.url"
:preview-src-list="[item.url]">
</el-image>
</template>
<div v-for="(file) in getAttachmentList(operation.comment.attachments,false)">
<el-link style="color: #2a99ff" :href="file.url" icon="el-icon-document">{{ file.name }}</el-link>
</div>
</div>
</div>
</template>
<!-- <template v-if="operation.comment">-->
<!-- <div style="margin-top: 10px;background:#f5f5f5;padding: 10px;">-->
<!-- <div>-->
<!-- {{ operation.comment.context }}-->
<!-- </div>-->
<!-- <div style="margin-top: 10px;"-->
<!-- v-if="operation.comment.attachments && operation.comment.attachments.length > 0">-->
<!-- <template v-for="(item) in getAttachmentList(operation.comment.attachments,true)">-->
<!-- <el-image-->
<!-- style="width: 100px; height: 100px"-->
<!-- :src="item.url"-->
<!-- :preview-src-list="[item.url]">-->
<!-- </el-image>-->
<!-- </template>-->
<!-- <div v-for="(file) in getAttachmentList(operation.comment.attachments,false)">-->
<!-- <el-link style="color: #2a99ff" :href="file.url" icon="el-icon-document">{{ file.name }}</el-link>-->
<!-- </div>-->
<!-- </div>-->
<!-- </div>-->
<!-- </template>-->
</el-card>
</el-timeline-item>
<el-timeline-item :color="timeline.color" :icon="timeline.icon" size="large">
@@ -60,7 +70,7 @@
<script setup>
import {CircleCheckFilled, Close, Loading, MoreFilled} from "@element-plus/icons-vue";
import {ref,defineProps} from 'vue'
import {ref, defineProps} from 'vue'
const props = defineProps({
operationList: {
@@ -113,12 +123,12 @@ const init = () => {
break
}
// let operationListNew = []
for (let i = 0;i<props.operationList.length;i++) {
for (let i = 0; i < props.operationList.length; i++) {
let operationNew = initOperationFun(props.operationList[i])
let userList = []
if (operationNew.userInfo){
if (operationNew.userInfo) {
for (let user of operationNew.userInfo) {
let userNew = initUser(user,operationNew.operation)
let userNew = initUser(user, operationNew.operation)
userList.push(userNew)
}
operationNew.userInfo = userList
@@ -132,7 +142,7 @@ const init = () => {
const getAttachmentList = (attachments, image) => {
let result = [];
if (attachments){
if (attachments) {
for (let attachment of attachments) {
if (attachment.isImage === image) {
result.push(attachment)
@@ -142,7 +152,7 @@ const getAttachmentList = (attachments, image) => {
return result;
}
const initUser = (user,type) => {
const initUser = (user, type) => {
let state = user.state
//创建节点
if (state === 'CREATE') {
@@ -154,7 +164,7 @@ const initUser = (user,type) => {
user["icon"] = 'CircleCheckFilled'
user["color"] = "#0bbd87"
}
if (type === "CC"){
if (type === "CC") {
user["icon"] = "Promotion"
user["color"] = "#3395f8"
}
@@ -169,7 +179,7 @@ const initUser = (user,type) => {
user["icon"] = 'Close'
user["color"] = "#f56c6c"
}
if (state === 'PASS'){
if (state === 'PASS') {
user["icon"] = 'MoreFilled'
user["color"] = "#c0c4cc"
}
@@ -277,7 +287,8 @@ init()
position: relative;
margin-right: 5px;
}
.el-timeline-item__node{
.el-timeline-item__node {
position: absolute;
bottom: 20px;
right: 1px;

View File

@@ -9,7 +9,7 @@
<div style="margin-top: 40px">
<div :style="'transform: scale('+ scale / 100 +');'">
<div id="previewProcess">
<process-tree mode="preview" ref="processTreePreview" id-name="previewProcess"/>
<process-tree :mode="mode" ref="processTreePreview" id-name="previewProcess"/>
</div>
</div>
</div>
@@ -20,6 +20,12 @@ import ProcessTree from '@/views/workflow/process/ProcessTree.vue'
const processTreePreview = ref()
const scale = ref(100)
const props = defineProps({
mode: {
type: String,
default: 'preview'
}
})
nextTick(()=>{
processTreePreview.value.init()

View File

@@ -1,7 +1,7 @@
<template>
<div style="margin-top: 15px">
<el-button @click="changPan('processSetting')">流程设置</el-button>
<el-button @click="changPan('formDesign')">表单</el-button>
<!-- <el-button @click="changPan('formDesign')">表单</el-button>-->
<el-button @click="changPan('processDesign')">流程</el-button>
<el-button @click="publishProcess">发布</el-button>
<div class="layout-body" v-if="visible">
@@ -11,9 +11,9 @@
<div v-show="activeSelect === 'processDesign'">
<process-design ref="processDesign"/>
</div>
<div v-show="activeSelect === 'formDesign'">
<form-design ref="formDesign"/>
</div>
<!-- <div v-show="activeSelect === 'formDesign'">-->
<!-- <form-design ref="formDesign"/>-->
<!-- </div>-->
</div>
</div>
@@ -72,7 +72,8 @@ import {ElMessage, ElMessageBox} from "element-plus";
const processDesign = ref()
const visible = ref(false)
const timer = ref(null)
const validComponents = ref(['processSetting', 'formDesign', 'processDesign'])
// const validComponents = ref(['processSetting', 'formDesign', 'processDesign'])
const validComponents = ref(['processSetting', 'processDesign'])
// const activeSelect = ref('formDesign')
// const activeSelect = ref('processSetting')
const activeSelect = ref('processDesign')
@@ -81,7 +82,7 @@ const validStep = ref(0)
const validResult = ref({})
const validOptions = ref([
{title: '基础信息', description: '', icon: '', status: ''},
{title: '审批表单', description: '', icon: '', status: ''},
// {title: '审批表单', description: '', icon: '', status: ''},
{title: '审批流程', description: '', icon: '', status: ''},
// {title: '扩展设置', description: '', icon: '', status: ''}
])
@@ -118,21 +119,21 @@ const loadInitFrom = () => {
let design = {
processDefinitionKey: 'pro' + getRandomId(),
deploymentName: "未命名表单",
logo: {
icon: "el-icon-eleme",
background: "#1e90ff"
},
settings: {
commiter: [],
admin: [],
sign: false,
notify: {
types: ["APP"],
title: "消息通知标题"
}
},
// logo: {
// icon: "el-icon-eleme",
// background: "#1e90ff"
// },
// settings: {
// commiter: [],
// admin: [],
// sign: false,
// notify: {
// types: ["APP"],
// title: "消息通知标题"
// }
// },
groupId: 1,
formItems: [],
// formItems: [],
process: [
{
id: "root",
@@ -151,6 +152,27 @@ const loadInitFrom = () => {
type: "END",
}
],
processFromPerms: [{
id: "projectName",
title: "项目名称",
required: true,
perm: "R"
}, {
id: "projectType",
title: "项目类型",
required: true,
perm: "R"
}, {
id: "projectDesc",
title: "项目描述",
required: true,
perm: "R"
}, {
id: "projectManager",
title: "项目经理",
required: true,
perm: "R"
}],
remark: "备注说明"
}
processStore.setDesign(design)

View File

@@ -36,6 +36,7 @@ const valid = ref(true)
let vNode = {}
const init = () => {
// console.log("sdsdsdsdsdsdsd",processStore.getProcess())
processStore.init()
initMapping(processStore.getProcess())
// 定义类名(可忽略)
@@ -48,7 +49,7 @@ const init = () => {
// 初始化map集合,以便数据整理
const initMapping = (node) => {
node.forEach(nodeItem => {
node?.forEach(nodeItem => {
processStore.nodeMap.set(nodeItem.id, nodeItem)
processStore.parentMap.set(nodeItem.parentId, nodeItem)
})
@@ -57,15 +58,15 @@ const initMapping = (node) => {
const initHeaderBgc = (node) => {
if (node.props && props.mode === 'preview') {
let headerBgc = '#ff943e'
if (processStore.runningList.value.includes(node.id)) {
if (processStore.runningList.value?.includes(node.id)) {
headerBgc = '#1e90ff'
} else if (processStore.endList.value.includes(node.id)) {
} else if (processStore.endList.value?.includes(node.id)) {
headerBgc = '#20b2aa'
} else if (processStore.noTakeList.value.includes(node.id)) {
} else if (processStore.noTakeList.value?.includes(node.id)) {
headerBgc = '#909399'
} else if (processStore.refuseList.value.includes(node.id)) {
} else if (processStore.refuseList.value?.includes(node.id)) {
headerBgc = '#f56c6c'
} else if (processStore.passList.value.includes(node.id)) {
} else if (processStore.passList.value?.includes(node.id)) {
headerBgc = '#ff943e'
}
node.props.headerBgc = headerBgc
@@ -309,9 +310,9 @@ const insertNode = debounce((type, parentNode) => {
case 'DELAY':
insertDelayNode(children);
break;
case 'TRIGGER':
insertTriggerNode(children);
break;
// case 'TRIGGER':
// insertTriggerNode(children);
// break;
case 'CONDITIONS':
insertConditionsNode(children);
break;

View File

@@ -5,8 +5,11 @@
<slot name="pre"></slot>
<div style="display: flex;flex-wrap: wrap;">
<div v-for="(user,index) in userInfo" :key="index" class="avatar_name">
<el-avatar size="large"
:src="user.avatar"></el-avatar>
<div class="circle-user">
<el-tooltip class="item" effect="dark" :content="user.name" placement="bottom-start">
<span class="item_name">{{ user.name }}</span>
</el-tooltip>
</div>
<div v-if="user.icon"
class="el-timeline-item__node" :style="{
backgroundColor: user.color
@@ -15,17 +18,15 @@
<component :is="user.icon"/>
</el-icon>
</div>
<el-tooltip class="item" effect="dark" :content="user.name" placement="bottom-start">
<span class="item_name">{{ user.name }}</span>
</el-tooltip>
</div>
</div>
</div>
</template>
<script setup>
import {Loading,Close,CircleCheckFilled,MoreFilled} from '@element-plus/icons-vue'
import {Loading, Close, CircleCheckFilled, MoreFilled} from '@element-plus/icons-vue'
import {defineProps} from "vue";
const props = defineProps({
row: {
type: Number,
@@ -38,6 +39,10 @@ const props = defineProps({
userInfo: {
type: Array,
default: []
},
mode: {
type: String,
default: 'design'
}
})
@@ -70,7 +75,7 @@ const initUser = (user) => {
user["icon"] = Close
user["color"] = "#f56c6c"
}
if (state === 'PASS'){
if (state === 'PASS') {
user["icon"] = MoreFilled
user["color"] = "#c0c4cc"
}
@@ -80,7 +85,27 @@ const initUser = (user) => {
init()
</script>
<style scoped>
<style scoped lang="scss">
.circle-user {
width: 50px;
height: 50px;
display: flex;
justify-content: center;
align-items: center;
border-radius: 50%;
border: 1px solid #ACACAC;
position: relative;
.circle-icon {
width: 10px;
height: 10px;
position: absolute;
top: auto !important;
bottom: -9px;
right: 15px !important;
}
}
.avatar_name {
display: flex;
flex-direction: column;
@@ -92,7 +117,7 @@ init()
.el-timeline-item__node {
position: absolute;
bottom: 20px;
bottom: 0;
right: 1px;
}

View File

@@ -31,12 +31,12 @@
</el-icon>
<span>延迟等待</span>
</div>
<div @click="addTriggerNode">
<el-icon style="color:#15BC83;">
<SetUp/>
</el-icon>
<span>触发器</span>
</div>
<!-- <div @click="addTriggerNode">-->
<!-- <el-icon style="color:#15BC83;">-->
<!-- <SetUp/>-->
<!-- </el-icon>-->
<!-- <span>触发器</span>-->
<!-- </div>-->
</div>
<template #reference>
<!-- <el-button :icon="Plus" slot="reference" type="primary" @click="visible = !visible" size="small"-->

View File

@@ -39,7 +39,7 @@
<el-input-number :min="1" :max="20" :step="1" size="mini"
v-model="nodeProps.leader.level"></el-input-number>
<span> 级主管</span>
<div style="color: #409EFF; font-size: small;">👉 直接主管为 1 级主管</div>
<div style="color: #409EFF; font-size: small;">直接主管为 1 级主管</div>
</el-form-item>
</div>
<div v-else-if="nodeProps.assignedType === 'ROLE'">
@@ -56,12 +56,12 @@
</el-select>
</el-form-item>
</div>
<div v-else>
<span class="item-desc">发起人自己作为审批人进行审批</span>
</div>
<!-- <div v-else>-->
<!-- <span class="item-desc">发起人自己作为审批人进行审批</span>-->
<!-- </div>-->
</el-form-item>
<el-divider></el-divider>
<el-form-item label="👤 审批人为空时" prop="text" class="line-mode">
<el-form-item label="审批人为空时" prop="text" class="line-mode">
<el-radio-group v-model="nodeProps.nobody.handler">
<el-radio label="TO_PASS">自动通过</el-radio>
<el-radio label="TO_REFUSE">自动驳回</el-radio>
@@ -80,7 +80,7 @@
<div v-if="showMode">
<el-divider/>
<el-form-item label="👩‍👦‍👦 多人审批时审批方式" prop="text" class="approve-mode">
<el-form-item label="多人审批时审批方式" prop="text" class="approve-mode">
<el-radio-group v-model="nodeProps.mode">
<el-radio label="NEXT">会签 按选择顺序审批每个人必须同意</el-radio>
<el-radio label="AND">会签可同时审批每个人必须同意</el-radio>
@@ -90,13 +90,16 @@
</div>
<el-divider>高级设置</el-divider>
<el-form-item label="✍ 审批同意时是否需要签字" prop="text">
<el-switch inactive-text="不用" active-text="需要" v-model="nodeProps.sign"></el-switch>
<el-tooltip class="item" effect="dark" content="如果全局设置了需要签字,则此处不生效" placement="top-start">
<i class="el-icon-question" style="margin-left: 10px; font-size: medium; color: #b0b0b1"></i>
</el-tooltip>
<!-- <el-form-item label="✍ 审批同意时是否需要签字" prop="text">-->
<!-- <el-switch inactive-text="不用" active-text="需要" v-model="nodeProps.sign"></el-switch>-->
<!-- <el-tooltip class="item" effect="dark" content="如果全局设置了需要签字,则此处不生效" placement="top-start">-->
<!-- <i class="el-icon-question" style="margin-left: 10px; font-size: medium; color: #b0b0b1"></i>-->
<!-- </el-tooltip>-->
<!-- </el-form-item>-->
<el-form-item label="是否使用矩阵审批" prop="text">
<el-switch inactive-text="不用" active-text="使用" v-model="nodeProps.matrixApproval"></el-switch>
</el-form-item>
<el-form-item label="审批期限(为 0 则不生效)" prop="timeLimit">
<el-form-item label="审批期限(为 0 则不生效)" prop="timeLimit">
<el-input style="width: 180px;" placeholder="时长" type="number"
v-model="nodeProps.timeLimit.timeout.value">
<el-select style="width: 75px;" v-model="nodeProps.timeLimit.timeout.unit" slot="append" placeholder="请选择" filterable>
@@ -122,54 +125,54 @@
</span>
</div>
</el-form-item>
<el-form-item label="🙅‍ 如果审批被驳回 👇">
<el-radio-group v-model="nodeProps.refuse.type">
<el-radio label="TO_INITIAL">重新开始流程</el-radio>
<el-radio label="TO_BEFORE">驳回到上级审批节点</el-radio>
<el-radio label="TO_NODE">驳回到指定节点</el-radio>
</el-radio-group>
<div v-if="nodeProps.refuse.type === 'TO_NODE'">
<span>指定节点:</span>
<el-select style="margin-left: 10px; width: 150px;" placeholder="选择跳转步骤" size="small"
v-model="nodeProps.refuse.target" filterable>
<el-option v-for="(node, index) in nodeOptions" :key="index" :label="node.name"
:value="node.id"></el-option>
</el-select>
</div>
</el-form-item>
<el-form-item label="自定义监听器">
<div slot="label">
<span style="margin-left: 20px">使用自定义监听器: </span>
<!-- <el-switch v-model="config.listener.state" @change="getListener"></el-switch>-->
</div>
<div v-if="config.listener.state">
<div slot="label">
<span style="margin-right: 10px">设置监听器</span>
<el-button type="primary" @click="addListener(config.listener.list)" link> + 添加</el-button>
</div>
<div v-for="(listen, index) in config.listener.list" :key="index">
<el-input v-if="listen.isSys" placeholder="监听器名称" :disabled="true" size="small" style="width: 100px;"
v-model="listen.listenerName"/>
<el-input v-if="!listen.isSys" placeholder="监听器名称" size="small" style="width: 100px;"
v-model="listen.listenerName"/>
<el-radio-group size="small" style="margin: 0 5px;" @change="typeChange(listen)" v-model="listen.isSys">
<el-radio-button :label="true">内置</el-radio-button>
<el-radio-button :label="false">自定义</el-radio-button>
</el-radio-group>
<el-select v-if="listen.isSys" style="width: 180px;" v-model="listen.listenerValue" size="small"
@change="listenerOptionChange(listen)"
placeholder="请选择表单字段" filterable>
<el-option v-for="option in listenerOption" :key="option.value" :label="option.label"
:value="option.value"/>
</el-select>
<!-- <el-input v-if="listen.isSys" placeholder="请设置字段值" size="small" v-model="listen.listenerValue" style="width: 180px;"/>-->
<el-button v-if="!listen.isSys" type="primary" size="small" @click="settingListener(listen)" link>设置</el-button>
<el-button @click="delListener(config.listener.list, index)"
class="el-icon-delete" type="primary"
style="margin-left: 5px; color: #c75450;" link/>
</div>
</div>
</el-form-item>
<!-- <el-form-item label="🙅‍ 如果审批被驳回 👇">-->
<!-- <el-radio-group v-model="nodeProps.refuse.type">-->
<!-- <el-radio label="TO_INITIAL">重新开始流程</el-radio>-->
<!-- <el-radio label="TO_BEFORE">驳回到上级审批节点</el-radio>-->
<!-- <el-radio label="TO_NODE">驳回到指定节点</el-radio>-->
<!-- </el-radio-group>-->
<!-- <div v-if="nodeProps.refuse.type === 'TO_NODE'">-->
<!-- <span>指定节点:</span>-->
<!-- <el-select style="margin-left: 10px; width: 150px;" placeholder="选择跳转步骤" size="small"-->
<!-- v-model="nodeProps.refuse.target" filterable>-->
<!-- <el-option v-for="(node, index) in nodeOptions" :key="index" :label="node.name"-->
<!-- :value="node.id"></el-option>-->
<!-- </el-select>-->
<!-- </div>-->
<!-- </el-form-item>-->
<!-- <el-form-item label="自定义监听器">-->
<!-- <div slot="label">-->
<!-- <span style="margin-left: 20px">使用自定义监听器: </span>-->
<!-- &lt;!&ndash; <el-switch v-model="config.listener.state" @change="getListener"></el-switch>&ndash;&gt;-->
<!-- </div>-->
<!-- <div v-if="config.listener.state">-->
<!-- <div slot="label">-->
<!-- <span style="margin-right: 10px">设置监听器</span>-->
<!-- <el-button type="primary" @click="addListener(config.listener.list)" link> + 添加</el-button>-->
<!-- </div>-->
<!-- <div v-for="(listen, index) in config.listener.list" :key="index">-->
<!-- <el-input v-if="listen.isSys" placeholder="监听器名称" :disabled="true" size="small" style="width: 100px;"-->
<!-- v-model="listen.listenerName"/>-->
<!-- <el-input v-if="!listen.isSys" placeholder="监听器名称" size="small" style="width: 100px;"-->
<!-- v-model="listen.listenerName"/>-->
<!-- <el-radio-group size="small" style="margin: 0 5px;" @change="typeChange(listen)" v-model="listen.isSys">-->
<!-- <el-radio-button :label="true">内置</el-radio-button>-->
<!-- <el-radio-button :label="false">自定义</el-radio-button>-->
<!-- </el-radio-group>-->
<!-- <el-select v-if="listen.isSys" style="width: 180px;" v-model="listen.listenerValue" size="small"-->
<!-- @change="listenerOptionChange(listen)"-->
<!-- placeholder="请选择表单字段" filterable>-->
<!-- <el-option v-for="option in listenerOption" :key="option.value" :label="option.label"-->
<!-- :value="option.value"/>-->
<!-- </el-select>-->
<!-- &lt;!&ndash; <el-input v-if="listen.isSys" placeholder="请设置字段值" size="small" v-model="listen.listenerValue" style="width: 180px;"/>&ndash;&gt;-->
<!-- <el-button v-if="!listen.isSys" type="primary" size="small" @click="settingListener(listen)" link>设置</el-button>-->
<!-- <el-button @click="delListener(config.listener.list, index)"-->
<!-- class="el-icon-delete" type="primary"-->
<!-- style="margin-left: 5px; color: #c75450;" link/>-->
<!-- </div>-->
<!-- </div>-->
<!-- </el-form-item>-->
</el-form>
<!--
<el-dialog custom-class="custom-dialog" class="border" width="600px" title="定义监听器设置"
@@ -230,12 +233,13 @@ const showOrgSelect = ref(false)
const orgPickerSelected = ref([])
const approvalTypes = reactive([
{name: "指定人员", type: "ASSIGN_USER"},
{name: "发起人自选", type: "SELF_SELECT"},
{name: "连续多级主管", type: "LEADER_TOP"},
{name: "主管", type: "LEADER"},
{name: "角色", type: "ROLE"},
// {name: "发起人自选", type: "SELF_SELECT"},
// {name: "连续多级主管", type: "LEADER_TOP"},
// {name: "主管", type: "LEADER"},
// {name: "角色", type: "ROLE"},
{name: "发起人自己", type: "SELF"},
{name: "表单内联系人", type: "FORM_USER"}
// {name: "表单内联系人", type: "FORM_USER"},
// {name: "矩阵审批", type: "MATRIX_APPROVAL"},
])
const listenerOption = ref([])
const selectListen = ref({})

View File

@@ -94,7 +94,9 @@
<org-items :modelValue="users"/>
</span>
<span v-else-if="condition.valueType === ValueType.date"></span>
<el-icon class="delete-icon" @click="delSubCondition(group, cindex)"><Minus /></el-icon>
<el-icon class="delete-icon" @click="delSubCondition(group, cindex)">
<Minus/>
</el-icon>
</el-form-item>
</el-form>
</div>
@@ -119,7 +121,7 @@ const users = ref([])
// const orgType = ref('user')
const showOrgSelect = ref(false)
const groupNames = ref(['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J'])
const supportTypes = ref([ValueType.number, ValueType.string, ValueType.date, ValueType.dateRange, ValueType.dept, ValueType.user])
const supportTypes = ref([ValueType.number, ValueType.string])
const explains = ref(
[
{label: '等于', value: '='},
@@ -140,14 +142,20 @@ const selectedNode = computed(() => {
return processStore.getSelectedNode()
})
const processFromPerms = computed(() => {
return processStore.getDesign().processFromPerms;
});
const conditionList = computed(() => {
//条件数组
//构造可用条件选项
const conditionItems = []
processStore.getDesign().formItems.forEach(item => filterCondition(item, conditionItems))
if (conditionItems.length === 0 || conditionItems[0].id !== 'root') {
conditionItems.unshift({id: 'root', title: '发起人', valueType: 'User'})
}
filterConditionMosr(conditionItems)
// processStore.getDesign().formItems.forEach(item => filterCondition(item, conditionItems))
// if (conditionItems.length === 0 || conditionItems[0].id !== 'root') {
// conditionItems.unshift({id: 'root', title: '发起人', valueType: 'User'})
// }
// conditionItems.unshift({id: 'root', title: '发起人', valueType: 'User'})
return conditionItems
})
@@ -184,6 +192,14 @@ const selectUser = (value, orgType) => {
users.value = value
orgPicker.value.showUserPicker()
}
const filterConditionMosr = (list) => {
processFromPerms.value.forEach((item) => {
console.log(item)
if (item.required && supportTypes.value.indexOf(item.valueType) > -1){
list.push({title: item.title, id: item.id, valueType: item.valueType})
}
})
}
const filterCondition = (item, list) => {
//从表单中过滤出可以选择的条件
@@ -206,8 +222,7 @@ const selected = (selected) => {
users.value = userInfoList
//组织选择器的选中回调函数
// users.value.length = 0
// console.log('processStore.getAssignedUser()',processStore.getAssignedUser())
processStore.getAssignedUser().forEach(u => users.value=userInfoList)
processStore.getAssignedUser().forEach(u => users.value = userInfoList)
}
const delGroup = (index) => {
@@ -228,7 +243,6 @@ const conditionChange = (index, group) => {
if (0 > group.conditions.findIndex(cd => cd.id === cid)) {
//新增条件
let condition = {...conditionList.value[index]}
console.log('fs', condition, conditionList.value, index)
condition.compare = '';
condition.value = []
group.conditions.push(condition)

View File

@@ -18,24 +18,24 @@
</template>
</el-popover>
</el-form-item>
<el-form-item label="条件组关系" label-width="150px">
<el-switch v-model="selectedNode.props.groupsType" active-color="#409EFF"
inactive-color="#c1c1c1" active-value="AND" inactive-value="OR"
active-text="" inactive-text="">
</el-switch>
</el-form-item>
<el-form-item label="条件组表达式">
<el-input v-model="config.expression" placeholder="输入条件组关系表达式 &为与,|为或"/>
<span class="item-desc">使用表达式构建复杂逻辑例如: (A & B) | C</span>
</el-form-item>
<!-- <el-form-item label="条件组关系" label-width="150px">-->
<!-- <el-switch v-model="selectedNode.props.groupsType" active-color="#409EFF"-->
<!-- inactive-color="#c1c1c1" active-value="AND" inactive-value="OR"-->
<!-- active-text="且" inactive-text="或">-->
<!-- </el-switch>-->
<!-- </el-form-item>-->
<!-- <el-form-item label="条件组表达式">-->
<!-- <el-input v-model="config.expression" placeholder="输入条件组关系表达式 &为与,|为或"/>-->
<!-- <span class="item-desc">使用表达式构建复杂逻辑例如: (A & B) | C</span>-->
<!-- </el-form-item>-->
</el-form>
<div>
<el-button type="primary" icon="Plus" style="margin: 0 15px 15px 0" round
@click="addConditionGroup">
添加条件组
</el-button>
<span class="item-desc">只有必填选项才能作为审批条件</span>
</div>
<!-- <div>-->
<!-- <el-button type="primary" icon="Plus" style="margin: 0 15px 15px 0" round-->
<!-- @click="addConditionGroup">-->
<!-- 添加条件组-->
<!-- </el-button>-->
<!-- <span class="item-desc">只有必填选项才能作为审批条件</span>-->
<!-- </div>-->
<group-item/>
</template>

View File

@@ -34,8 +34,7 @@
</template>
<script setup>
import {defineProps, watch,computed} from "vue";
import {defineProps, computed} from "vue";
import {useProcessStore} from "@/stores/processStore.js";
const props = defineProps({
@@ -45,18 +44,7 @@ const props = defineProps({
}
});
const processStore = useProcessStore();
const tableData = ref([]);
const isIndeterminate = ref(false);
const permSelect = ref("");
const checkStatus = reactive({
readOnly: true,
editable: false,
hide: false
});
const init = () => {
let oldPermMap = new Map()
@@ -66,7 +54,7 @@ const init = () => {
}
}
processStore.getSelectedNode().props.formPerms = [];
formPermsLoad(oldPermMap, processStore.getDesign().formItems);
formPermsLoadMosr(oldPermMap, processFromPerms.value);
};
const formPerms = computed(() => {
@@ -81,12 +69,39 @@ const formItems = computed(() => {
return processStore.getDesign().formItems;
});
const processFromPerms = computed(() => {
return processStore.getDesign().processFromPerms;
});
const allSelect = (type) => {
permSelect.value = type;
formPerms.value.forEach(f => f.perm = type);
};
const formPermsLoadMosr = (oldPermMap, perms) => {
perms.forEach(perm =>{
//刷新名称
let old = oldPermMap.get(perm.id)
if (old) {
old.title = perm.title;
old.required = perm.required;
formPerms.value.push(old);
} else {
formPerms.value.push({
id: perm.id, //todo ,id 就是字段名称
title: perm.title,
required: perm.required,
perm: nowNode.value.type === "ROOT" ? "E" : "R"
});
}
})
}
//todo 初始化表单权限的位置,表单字段信息放在config配置中心
const formPermsLoad = (oldPermMap, forms) => {
forms.forEach(form => {
if (form.name === "SpanLayout") {
@@ -100,7 +115,7 @@ const formPermsLoad = (oldPermMap, forms) => {
formPerms.value.push(old);
} else {
formPerms.value.push({
id: form.id,
id: form.id, //todo ,id 就是字段名称
title: form.title,
required: form.props.required,
perm: nowNode.type === "ROOT" ? "E" : "R"

View File

@@ -1,15 +1,16 @@
<template>
<el-tabs v-model="active" v-if="visible && name && formConfig.length > 0">
<el-tab-pane :label="name" name="properties">
<component :is="com" :config="selectNode.props" @initRender="emit('initRender')"/>
</el-tab-pane>
<el-tab-pane label="表单权限设置" name="permissions">
<form-authority-config :node-type="selectNode.type"/>
</el-tab-pane>
</el-tabs>
<div v-else>
<!-- <el-tabs v-model="active" v-if="visible && name && formConfig.length > 0">-->
<el-tabs v-model="active" v-if="visible && name">
<el-tab-pane :label="name" name="properties">
<component :is="com" :config="selectNode.props" @initRender="emit('initRender')"/>
</div>
</el-tab-pane>
<el-tab-pane label="表单权限设置" name="permissions">
<form-authority-config :node-type="selectNode.type"/>
</el-tab-pane>
</el-tabs>
<div v-else>
<component :is="com" :config="selectNode.props" @initRender="emit('initRender')"/>
</div>
</template>
<script setup>
@@ -21,7 +22,8 @@ import CcNode from './CcNodeConfig.vue'
import Condition from './ConditionNodeConfig.vue'
import Trigger from './TriggerNodeConfig.vue'
import {useProcessStore} from '@/stores/processStore.js'
import {computed,defineEmits} from 'vue'
import {computed, defineEmits} from 'vue'
const emit = defineEmits()
const processStore = useProcessStore()
@@ -31,9 +33,9 @@ const selectNode = computed(() => {
})
const formConfig = computed(() => {
return processStore.getDesign().formItems
})
// const formConfig = computed(() => {
// return processStore.getDesign().formItems
// })
const com = ref()
@@ -41,10 +43,11 @@ const active = ref('properties')
const visible = ref(false)
const name = computed(()=>{
const name = computed(() => {
switch (processStore.getSelectedNode().type) {
case 'ROOT':
return '设置发起人';
// return '设置发起人';
return '节点设置';
case 'APPROVAL':
return '设置审批人';
case 'CC':
@@ -56,7 +59,6 @@ const name = computed(()=>{
const init = () => {
console.log(processStore.getSelectedNode().type)
switch (processStore.getSelectedNode().type) {
case 'APPROVAL' :
com.value = Approval;

View File

@@ -1,10 +1,11 @@
<template>
<div>
<p class="desc">选择能发起该审批的人员/部门不选则默认开放给所有人</p>
<el-button size="mini" @click="selectOrg" icon="el-icon-plus" type="primary" round>请选择</el-button>
<org-items v-model="select"/>
<org-picker title="请选择可发起本审批的人员/部门" multiple ref="orgPicker" :selected="select" @ok="selected"/>
</div>
<!-- <div>-->
<!-- <p class="desc">选择能发起该审批的人员/部门不选则默认开放给所有人</p>-->
<!-- <el-button size="mini" @click="selectOrg" icon="el-icon-plus" type="primary" round>请选择</el-button>-->
<!-- <org-items v-model="select"/>-->
<!-- <org-picker title="请选择可发起本审批的人员/部门" multiple ref="orgPicker" :selected="select" @ok="selected"/>-->
<!-- </div>-->
无需设置
</template>

View File

@@ -12,6 +12,7 @@
import Node from './Node.vue'
import {computed, defineExpose} from 'vue'
import {Stamp} from '@element-plus/icons-vue'
const emit = defineEmits(['insertNode', 'selected', 'delNode'])
const props = defineProps({
config: {
@@ -80,7 +81,7 @@ const content = computed(() => {
if (text && text.title) {
return `表单(${text.title})内的人员`
} else {
return '该表单已被移除😥'
return '该表单已被移除'
}
}
case "ROLE":
@@ -91,8 +92,10 @@ const content = computed(() => {
} else {
return '指定角色(未设置)'
}
case "MATRIX_APPROVAL":
return '矩阵审批'
default:
return '未知设置项😥'
return '未知设置项'
}
})

View File

@@ -6,7 +6,8 @@
<component :is="headerIcon"/>
</el-icon>
<ellipsis class="name" hover-tip :content="title"/>
<el-icon v-if="!isRoot && designState" size="20" style="float:right;cursor: pointer" @click.stop="emit('delNode')">
<el-icon v-if="!isRoot && designState" size="20" style="float:right;cursor: pointer"
@click.stop="emit('delNode')">
<Close/>
</el-icon>
</div>
@@ -16,20 +17,17 @@
</el-icon>
<template v-if="selectUser.show && mode === 'view'">
<div class="avatar_button">
<avatar-ellipsis :row="3" v-if="userInfo.length > 0" :user-info="userInfo"/>
<avatar-ellipsis :row="3" v-if="userInfo.length > 0" :mode="mode" :user-info="userInfo"/>
<el-button type="primary" :icon="Plus" circle/>
</div>
</template>
<template v-else-if="showAvatar">
<span class="placeholder" v-if="userInfo.length === 0">{{ placeholder }}</span>
<div v-else v-for="item in userInfo" class="circle-user">
<span >{{item.name}}</span>
</div>
<!-- <avatar-ellipsis :row="3" :user-info="userInfo"/>-->
<avatar-ellipsis :row="3" v-if="userInfo.length > 0" :user-info="userInfo"/>
</template>
<template v-else>
<span class="placeholder" v-if="(content || '').trim() === ''">{{ placeholder }}</span>
<ellipsis :row="3" :content="content" v-else/>
<ellipsis :row="3" :content="content" :mode="mode" v-else/>
</template>
</div>
<div class="node-error" v-if="showError">
@@ -43,9 +41,9 @@
<div class="node-footer">
<div v-if="merge" class="branch-merge">
<svg-icon name="fenzhi" :class-name="'fen-icon'"/>
<!-- <img data-v-1e7b1da5=""-->
<!-- src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAAXNSR0IArs4c6QAABXlJREFUeF7tmm2IVGUUx3/nLkQUqxmkpAVhSPUllZX8EEGhvVgZQWCU6wcpsqi2mXtnoaBwoqLCufeO9oJYUJQiFZWlZPQCfYiIXkylstTyS1oWWeYWCe6cuLsz7t3r3Ll3du6907oz35bnPM9z/r895zznmWeECf6RCa6fDoBOBExwAp0UmOAB0CmCnRTopECbCZi2nivK1CMD7FpXlH+ydqctKZBz9QyjwkqFG4HzfKK/0gpvl/ulmBWIzAHkVmlRDFY2EihwwLFkRhYQxgzAtHW2CpcBc4CpOsg26eK7yVPYVFwu/9ZzPufq2VLhQBxhAk85lvSF2Y5l/3prjQlAvqR3IjwJTKqz6DeGssQuyLfBsbyjz6PcFgeAZyOwzLFk/QnrjHH/RACYtj6ncHuUCDWYXs7Lz367XEn3inB+1NzauMLGsiW3+u1b2b9lAH22LuiCD2IJED5yTbmiZpt3dAbKT7Hmjhjtdi25oPZnK/uH7dtUCpi2vqVwQ1wRWuHhWkW3StpTEb6IO3fIThhwTemuzWll/0QA5G39FTgrtghhs2vKELA7inra6d38HXvusOEnriWXHo+iFvZvGUDfap3VdYzdzQgIHmd5W7cBc2Ovoax1C3KXZ5/E/i3VgCQciNMD+J1Ug55yXjxo7QfgOdFKCvjyeL/C9MgoEBa5przrt0ti/+C+mRVB/8amrWsU7m2Ql/c5lqwJjre9CDZ1DMFnriXzw0SatvZW4FqBW6o2vwGvCGxyLPmw3rym9g8cwy0XQV8Ix2qEgH0C1ziWNCyceVu1uvZrriVLGqVGsainHO7mRyDynlCvEWupCI7Kxcat6BFg+OwW9ohyfSMIcQF44v/sZovAlRH1I7QVTwyAt1Cjy8hfh9hYvepGQogDoI74HYbSXzHwusTYl7FEAURVcdPWN+NAiAJQTzzCda4p+6N8iDPe1CkQZ8FAtY+E0AhA2uKHszTlT1QkhAHIQnwmAKr1IjQS6gHISnxmABpBCALIUnymAMIgKHxf6wMmH6E3cNTtSLLgZXoKNOgAR6UDyqyq7esKk3znfOriM48AXzc5AmGE1EFgWvXPTMS3DcAJ6TA6XDIT31YAoRCEc5JqcuKc8Kn3AVFO+E4BryvJVHzbI8BzwAdgi2vJ4ihgSY//nyIg8jqctPi2RYBp61wV5qsyR2BFVdhBhBe0wtdisNU15VAagoNrZh4BOVsfFcVCODVUoLBn6E2hIBvShpApgLyt3nvhRXFFCWxwLOmNaz8Wu8wA5G31ntQWNOukwD2OJc80Oy+ufSYAciVdJsJLcZ0K2nV1MbuUk51jnd9oXuoACmW9eHCQj49/T+j3RtiJslPgFxXmoVxe11nhPdeUq8clANPWPoXVQef9D6e1saEHVINHUBYF7Q1lnl2QL5OGkHoE5G19GRhVyBQ+L1tyST0xoa/Iygq3IOvGI4BdwIWjHBeecE15IExM3tYfgJmBOetcU2o9Q2IcsoiAw8Gf0qhyU7kgb4QCcPRTlNGvSspmtzD81J7kJ30AJX0fYeHo2seLjiXLG0TA78CZ/nGFB8uWPJakeG+t9AE4+jjK/XGLWtgTusBix5It4w5ArqRLRTjhl14IW40KD/kre66ki0R4p57IrgozS/2yb9wByDvqhbKX07Xv/oIavMfOP6pFb0pdgcqrbkFuTlp8JingbRIaBfEUHUOYltbtMPUaUNNo2rpeYWk8zSNWqvSmeSvMDIAnybT1boWnY0I4aigL7YJ4bXRqn0wBeCqG7gYVVqFcFapKWXt0gP5nizKQmvLqwpkDqAkaanmhB4MelOkI27XCDjHY7pqyN23htfXbBiArgVH7dABEETrZxzsRcLL/h6P0/Qc1qphfvB2K3wAAAABJRU5ErkJggg=="-->
<!-- alt="">-->
<!-- <img data-v-1e7b1da5=""-->
<!-- src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAAXNSR0IArs4c6QAABXlJREFUeF7tmm2IVGUUx3/nLkQUqxmkpAVhSPUllZX8EEGhvVgZQWCU6wcpsqi2mXtnoaBwoqLCufeO9oJYUJQiFZWlZPQCfYiIXkylstTyS1oWWeYWCe6cuLsz7t3r3Ll3du6907oz35bnPM9z/r895zznmWeECf6RCa6fDoBOBExwAp0UmOAB0CmCnRTopECbCZi2nivK1CMD7FpXlH+ydqctKZBz9QyjwkqFG4HzfKK/0gpvl/ulmBWIzAHkVmlRDFY2EihwwLFkRhYQxgzAtHW2CpcBc4CpOsg26eK7yVPYVFwu/9ZzPufq2VLhQBxhAk85lvSF2Y5l/3prjQlAvqR3IjwJTKqz6DeGssQuyLfBsbyjz6PcFgeAZyOwzLFk/QnrjHH/RACYtj6ncHuUCDWYXs7Lz367XEn3inB+1NzauMLGsiW3+u1b2b9lAH22LuiCD2IJED5yTbmiZpt3dAbKT7Hmjhjtdi25oPZnK/uH7dtUCpi2vqVwQ1wRWuHhWkW3StpTEb6IO3fIThhwTemuzWll/0QA5G39FTgrtghhs2vKELA7inra6d38HXvusOEnriWXHo+iFvZvGUDfap3VdYzdzQgIHmd5W7cBc2Ovoax1C3KXZ5/E/i3VgCQciNMD+J1Ug55yXjxo7QfgOdFKCvjyeL/C9MgoEBa5przrt0ti/+C+mRVB/8amrWsU7m2Ql/c5lqwJjre9CDZ1DMFnriXzw0SatvZW4FqBW6o2vwGvCGxyLPmw3rym9g8cwy0XQV8Ix2qEgH0C1ziWNCyceVu1uvZrriVLGqVGsainHO7mRyDynlCvEWupCI7Kxcat6BFg+OwW9ohyfSMIcQF44v/sZovAlRH1I7QVTwyAt1Cjy8hfh9hYvepGQogDoI74HYbSXzHwusTYl7FEAURVcdPWN+NAiAJQTzzCda4p+6N8iDPe1CkQZ8FAtY+E0AhA2uKHszTlT1QkhAHIQnwmAKr1IjQS6gHISnxmABpBCALIUnymAMIgKHxf6wMmH6E3cNTtSLLgZXoKNOgAR6UDyqyq7esKk3znfOriM48AXzc5AmGE1EFgWvXPTMS3DcAJ6TA6XDIT31YAoRCEc5JqcuKc8Kn3AVFO+E4BryvJVHzbI8BzwAdgi2vJ4ihgSY//nyIg8jqctPi2RYBp61wV5qsyR2BFVdhBhBe0wtdisNU15VAagoNrZh4BOVsfFcVCODVUoLBn6E2hIBvShpApgLyt3nvhRXFFCWxwLOmNaz8Wu8wA5G31ntQWNOukwD2OJc80Oy+ufSYAciVdJsJLcZ0K2nV1MbuUk51jnd9oXuoACmW9eHCQj49/T+j3RtiJslPgFxXmoVxe11nhPdeUq8clANPWPoXVQef9D6e1saEHVINHUBYF7Q1lnl2QL5OGkHoE5G19GRhVyBQ+L1tyST0xoa/Iygq3IOvGI4BdwIWjHBeecE15IExM3tYfgJmBOetcU2o9Q2IcsoiAw8Gf0qhyU7kgb4QCcPRTlNGvSspmtzD81J7kJ30AJX0fYeHo2seLjiXLG0TA78CZ/nGFB8uWPJakeG+t9AE4+jjK/XGLWtgTusBix5It4w5ArqRLRTjhl14IW40KD/kre66ki0R4p57IrgozS/2yb9wByDvqhbKX07Xv/oIavMfOP6pFb0pdgcqrbkFuTlp8JingbRIaBfEUHUOYltbtMPUaUNNo2rpeYWk8zSNWqvSmeSvMDIAnybT1boWnY0I4aigL7YJ4bXRqn0wBeCqG7gYVVqFcFapKWXt0gP5nizKQmvLqwpkDqAkaanmhB4MelOkI27XCDjHY7pqyN23htfXbBiArgVH7dABEETrZxzsRcLL/h6P0/Qc1qphfvB2K3wAAAABJRU5ErkJggg=="-->
<!-- alt="">-->
</div>
<div class="btn">
<insert-button v-if="designState" @insertNode="type => emit('insertNode', type)"/>
@@ -62,7 +60,7 @@ import InsertButton from '../common/InsertButton.vue'
import Ellipsis from '../common/Ellipsis.vue'
import AvatarEllipsis from '../common/AvatarEllipsis.vue'
import SvgIcon from '@/components/svgIcon/index.vue'
import {Close, Warning, Plus} from '@element-plus/icons-vue'
import {Close, Warning, Plus, Check, More} from '@element-plus/icons-vue'
const emit = defineEmits(['insertNode'])
const props = defineProps({
@@ -154,7 +152,16 @@ const props = defineProps({
const designState = computed(() => {
return props.mode === 'design'
})
const getState = (state) => {
switch (state) {
case 'finish':
return 'check'
case 'UNACTIVATED':
return 'more'
case 'RUNNING':
return 'loading'
}
}
const init = () => {
// let userInfo = this.$store.state.selectUserMap.get(this.nodeId);
@@ -176,15 +183,6 @@ const init = () => {
</script>
<style lang="scss" scoped>
.circle-user{
width: 50px;
height: 50px;
display: flex;
justify-content: center;
align-items: center;
border-radius: 50%;
border: 1px solid #ACACAC;
}
.root {
&:before {
display: none !important;

View File

@@ -18,7 +18,6 @@ export default defineConfig({
AutoImport({
//自动导入vue相关函数
imports: ['vue','vue-router'],
resolvers: [
ElementPlusResolver(),
//自动导入图标组件
@@ -59,7 +58,7 @@ export default defineConfig({
terserOptions: {
compress: {
drop_console: false, // 生产环境移除log
drop_debugger: false // 生产环境禁用debugger
drop_debugger: true // 生产环境禁用debugger
}
}
},
@@ -69,12 +68,13 @@ export default defineConfig({
strictPort: false,
open: true,
proxy: {
// '/api/admin': {
// target: 'http://dev-mosr.frp.feashow.cn/',
// // target: 'http://192.168.31.175:8000',
// changeOrigin: true,
// rewrite: (path) => path.replace(/^\/api/, '')
// },
'/api/workflow': {
target: 'http://frp.feashow.cn:31800/',
// target: 'http://clay.frp.feashow.cn/',
// target: 'http://192.168.31.175:8000',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, '')
},
'/api': {
target: 'http://mosr.feashow.cn',
changeOrigin: true,