470 Commits

Author SHA1 Message Date
sjl
d050dbc6e7 Merge branch 'master' of http://192.168.1.22:3000/ClientApps/pqs-9100_client
# Conflicts:
#	package.json
2025-10-15 15:23:34 +08:00
sjl
3b2f6b2517 1 2025-10-15 15:21:51 +08:00
sjl
45a010b3a4 微调 2025-10-15 15:20:07 +08:00
081a77ac4c 升级electron egg脚手架版本 2025-10-15 14:12:24 +08:00
sjl
ca8f173394 10550处理 2025-10-15 13:33:40 +08:00
caozehui
2e377bcca2 Merge remote-tracking branch 'origin/master' 2025-10-15 09:59:26 +08:00
caozehui
2a8757c9f1 微调 2025-10-15 09:59:10 +08:00
sjl
35f21b7140 去除日志,表格宽度 2025-10-15 08:49:11 +08:00
贾同学
b0ca84c8fd UPDATE: 优化 2025-10-15 08:45:37 +08:00
sjl
045acfa061 计划默认不勾选闪变 2025-10-14 20:54:35 +08:00
贾同学
55ff45f9a9 ADD: 模块激活流程 2025-10-14 19:00:47 +08:00
sjl
7afccb58fd 正式检测重复初始化 2025-10-14 18:41:36 +08:00
sjl
11c6704f11 闪变 2025-10-14 11:40:39 +08:00
sjl
97d1f08bbe 正式检测闪变 2025-10-13 13:56:37 +08:00
sjl
7d0053eb71 被检设备监测点台账线路号占满不可新增 2025-10-11 14:12:15 +08:00
贾同学
666fb22c49 UPDATE: 优化客户端桌面窗口内容自适应 2025-10-10 15:29:54 +08:00
贾同学
b319a89501 UPDATE: 处理控制台警告问题 2025-10-10 13:23:40 +08:00
贾同学
8e0b3be438 Merge remote-tracking branch 'origin/master' 2025-10-10 08:24:55 +08:00
sjl
b0d92de738 微调 2025-10-09 15:51:45 +08:00
sjl
92b3e25989 冲突 2025-10-09 15:49:29 +08:00
sjl
43c75c96a7 录波数据 2025-10-09 15:47:00 +08:00
贾同学
da0aa0cd0f UPDATE: 调整超时时间到1200秒 2025-10-09 14:34:45 +08:00
贾同学
b0c88b9df2 UPDATE: 优化; 2025-09-30 08:42:45 +08:00
caozehui
7c0ec5844a 微调 2025-09-29 14:49:29 +08:00
贾同学
a4a64ef0f9 UPDATE: 优化; 2025-09-29 09:47:53 +08:00
贾同学
58bb25500e UPDATE: 优化; 2025-09-25 14:40:45 +08:00
贾同学
3d73c34343 Merge remote-tracking branch 'origin/master' 2025-09-25 11:17:15 +08:00
贾同学
f74fedc213 UPDATE: 优化; 2025-09-25 11:16:57 +08:00
sjl
8ea49f9609 预检测实时数据对齐失败展示 2025-09-25 10:11:15 +08:00
caozehui
9ee71d29d4 Merge remote-tracking branch 'origin/master'
# Conflicts:
#	frontend/src/views/home/components/table.vue
2025-09-25 08:54:05 +08:00
caozehui
039a67c35a 微调 2025-09-25 08:53:32 +08:00
sjl
e17749d47e Merge branch 'master' of http://192.168.1.125:3000/root/pqs-9100_client 2025-09-25 08:53:28 +08:00
sjl
21c859c8f1 微调 2025-09-25 08:51:40 +08:00
贾同学
4fe239c86f UPDATE: 1、子计划管理,筛选条件改成搜索、设备厂家、是否分配;
2、重复导入子计划时,增量被检设备并删除未检设备;
        3、优化删除子计划后,刷新主计划信息;
2025-09-25 08:49:15 +08:00
ab62e56bbb 微调 2025-09-25 08:39:06 +08:00
5730b9c5cf 比对模式的检测报告生成和下载 2025-09-23 16:14:03 +08:00
贾同学
d4992db198 UPDATE: 优化批量导入提示; 2025-09-23 15:15:20 +08:00
sjl
5ccd1709a5 Merge branch 'master' of http://192.168.1.22:3000/frontend/pqs-9100_client 2025-09-23 11:14:44 +08:00
sjl
b48c1e0d78 微调 2025-09-23 11:14:34 +08:00
贾同学
fcdbbce7a9 UPDATE: 1、测试项改成检测项;
2、检测项勾选闪变添加提示;
        3、检测项由误差体系反推为下拉框让用户多选,默认全选;
        4、修改检测计划有子计划后部分字段不可编辑;
2025-09-23 10:55:07 +08:00
sjl
d08194bfd8 相序校验拦截错误 2025-09-23 08:39:15 +08:00
贾同学
55f579ef64 UPDATE: 优化 2025-09-22 16:16:45 +08:00
sjl
783e1c080b 预检测,正式检测,数据查询能处理只有录波的情况 2025-09-22 15:51:58 +08:00
guanj
44cdb3273c 微调 2025-09-22 15:37:28 +08:00
guanj
dbc21cdbfa 修改通道配对布局 2025-09-22 15:26:13 +08:00
sjl
24d83cfd39 Merge branch 'master' of http://192.168.1.22:3000/frontend/pqs-9100_client 2025-09-22 09:17:17 +08:00
sjl
b213b721bb 微调 2025-09-22 09:17:10 +08:00
caozehui
4ae42408c3 微调 2025-09-22 09:16:35 +08:00
贾同学
a9156f0954 UPDATE: 异步导出检测数据逻辑。 2025-09-19 16:18:10 +08:00
sjl
3e7509cd44 预检测重复初始化拦截 2025-09-19 11:20:06 +08:00
sjl
24becb82e1 比对式误差切换和删除零时表 2025-09-19 09:31:02 +08:00
sjl
6608587edd 预检测处理错误 2025-09-18 14:11:50 +08:00
sjl
5ad8cdecba 重新计算 2025-09-18 10:06:24 +08:00
贾同学
6b4cca1ef7 Merge remote-tracking branch 'origin/master' 2025-09-17 16:28:17 +08:00
贾同学
dea0844829 UPDATE: 根据计划的状态调按钮显示还是隐藏。 2025-09-17 16:27:46 +08:00
sjl
bbd438d23f 修复比对式一键检测 2025-09-17 15:42:05 +08:00
sjl
c88128b63b 1.被检设备监测点新增是否参与检测
2.调整通道配对只显示绑定和参与检测的通道数
2025-09-17 14:08:58 +08:00
sjl
95c68942ed 数据查询树替换接口联调 2025-09-17 08:46:26 +08:00
贾同学
5db685baca UPDATE: 修改计划数据源选择逻辑。 2025-09-16 14:42:02 +08:00
贾同学
fa710efea4 Merge remote-tracking branch 'origin/master' 2025-09-16 14:37:53 +08:00
贾同学
d0c3e1d9bd UPDATE: 修改计划数据源选择逻辑。 2025-09-16 14:37:17 +08:00
caozehui
589ddd38f3 微调 2025-09-16 13:47:40 +08:00
sjl
47d1500296 模拟式报告生成不弹框 2025-09-16 13:34:22 +08:00
sjl
4a8f8bff6a 预检测新增录波 2025-09-15 14:59:54 +08:00
贾同学
2b9b87a3db UPDATE: 布局样式优化。 2025-09-15 14:58:12 +08:00
贾同学
b241128105 Merge remote-tracking branch 'origin/master' 2025-09-15 10:56:13 +08:00
贾同学
226e3271ee UPDATE: 及格改成符合,添加无法比较。 2025-09-15 10:55:38 +08:00
sjl
1c253fd713 Merge branch 'master' of http://192.168.1.22:3000/frontend/pqs-9100_client 2025-09-15 10:43:53 +08:00
sjl
ed81d3d398 比对式报告生成显示检测中设备 2025-09-15 10:43:42 +08:00
贾同学
09b54a29ab ADD: 报告生成选择检测数据源并更新监测点结果。 2025-09-15 10:38:14 +08:00
贾同学
b27615baaf ADD: 1、报告生成选择界面流程。 2025-09-12 16:34:27 +08:00
sjl
c735e7a5bb 数据查询检测次数默认最后一次 2025-09-12 16:09:41 +08:00
sjl
c78f591baf 实时数据过了录波状态处理和进度条 2025-09-12 13:35:29 +08:00
贾同学
cfd8b072dd ADD: 修改数据合并逻辑,新增 event-source-polyfill包,实现长连接通信,展示合并进度 2025-09-11 11:03:14 +08:00
sjl
d18e34d2c9 比对模式没有检测次数,数据操作可查看检测中设备 2025-09-10 14:20:42 +08:00
sjl
53813795db 调整正式检测录波状态 2025-09-10 09:28:04 +08:00
sjl
8c3098e19a Merge branch 'master' of http://192.168.1.22:3000/frontend/pqs-9100_client 2025-09-09 20:54:32 +08:00
sjl
780a446aed 正式检测录波数据查询 2025-09-09 20:54:22 +08:00
贾同学
375f01a6ab UPDATE: 完善检修计划项目负责人和项目成员逻辑。 2025-09-09 10:25:53 +08:00
sjl
48aab7c1e9 Merge branch 'master' of http://192.168.1.22:3000/frontend/pqs-9100_client 2025-09-09 09:14:52 +08:00
sjl
d7cfe665e2 录波数据查询 2025-09-09 09:14:42 +08:00
贾同学
237c23bb70 UPDATE: 完善检修计划项目负责人和项目成员逻辑。 2025-09-08 16:23:19 +08:00
guanj
4cd6302ee0 修改 通道配对样式 2025-09-08 16:01:04 +08:00
caozehui
6a75709774 微调 2025-09-08 14:55:25 +08:00
贾同学
629dff1256 ADD: 检修计划添加项目负责人和项目成员 2025-09-08 10:21:54 +08:00
贾同学
6d6d03c03c UPDATE: 优化高度 2025-09-05 09:09:18 +08:00
贾同学
6122f53c8e UPDATE: 检测计划选择数据源逻辑改成主计划默认全选,子计划勾选校验 2025-09-05 08:57:39 +08:00
贾同学
5a7eea1052 UPDATE: 优化选择被检设备组件 2025-09-04 19:39:20 +08:00
贾同学
25f3570c18 UPDATE: 替换新增比对检测计划时,可选择的标准设备接口;优化选择被检设备组件 2025-09-04 11:12:29 +08:00
贾同学
74e015bd12 UPDATE: 重构检修计划选择被检设备组件,并处理对应数据逻辑 2025-09-03 20:44:32 +08:00
caozehui
da6a72807b 微调 2025-09-03 08:38:13 +08:00
贾同学
bb7ebaea45 UPDATE: 比对被检设备提交校验监测点信息 2025-09-02 11:03:39 +08:00
sjl
ae51b590af Merge branch 'master' of http://192.168.1.22:3000/frontend/pqs-9100_client 2025-09-01 14:53:44 +08:00
sjl
2ec3102eff 新建检测计划时,检测项下拉框要和误差体系里配置的要一致 2025-09-01 14:53:30 +08:00
贾同学
f5f7d259a9 ADD: 是否下载检测报告选择 2025-09-01 09:36:32 +08:00
guanj
4364f88526 Merge branch 'master' of http://192.168.1.22:3000/frontend/pqs-9100_client 2025-09-01 08:36:25 +08:00
guanj
0f5e21a06c 微调 2025-09-01 08:36:18 +08:00
贾同学
ddbaf5651a UPDATE: 勾选被检设备下载检测结果数据等 2025-08-29 15:09:05 +08:00
贾同学
a847419ab5 Merge remote-tracking branch 'origin/master' 2025-08-29 11:35:05 +08:00
贾同学
d5fb41cbab ADD: 检测计划添加导入标识字段以及逻辑 2025-08-29 11:34:55 +08:00
guanj
25e7b754b7 修改 正式检测结果 2025-08-29 11:12:57 +08:00
贾同学
a32ca3c849 ADD:数据合并按钮操作 2025-08-29 09:54:19 +08:00
caozehui
6e979c5dcb 微调 2025-08-28 16:29:00 +08:00
caozehui
8b578d4d8b 微调 2025-08-28 10:37:50 +08:00
贾同学
52fcdbfe1e ADD:导入子计划元数据二次确认提示 2025-08-27 20:21:55 +08:00
guanj
4559a7b5e2 修改检测查询页面空白问题 2025-08-27 16:32:29 +08:00
guanj
567201563d 修改预检测实时数据详情展示 2025-08-27 14:55:00 +08:00
guanj
772707ac42 修改比对式检测页面 2025-08-27 11:17:13 +08:00
guanj
4a6db824ba 修改通道配置页面 2025-08-26 20:52:24 +08:00
guanj
8b4c22e959 微调 2025-08-26 18:29:14 +08:00
caozehui
d7f1224df4 微调 2025-08-26 15:39:58 +08:00
贾同学
ac4e0e2077 UPDATE:优化子计划增删tab展示数据刷新问题 2025-08-26 15:28:01 +08:00
guanj
56a6f199c0 Merge branch 'master' of http://192.168.1.22:3000/frontend/pqs-9100_client 2025-08-26 15:16:25 +08:00
guanj
0abb765b32 修改查看 模拟式 页面展示 2025-08-26 15:16:18 +08:00
贾同学
4f8fdb83d1 ADD:检测计划添加检测配置相关 2025-08-26 11:13:23 +08:00
caozehui
300b220de2 微调 2025-08-26 10:58:29 +08:00
guanj
825d2cc46a Merge branch 'master' of http://192.168.1.22:3000/frontend/pqs-9100_client 2025-08-26 10:37:24 +08:00
guanj
95b602e6d4 修改检测数据查询展示页面 2025-08-26 10:37:13 +08:00
贾同学
a7b5bbf0bf Merge remote-tracking branch 'origin/master' 2025-08-25 14:53:06 +08:00
贾同学
dfbba11aae ADD:ICD添加检测是否两个开关字段 2025-08-25 14:39:59 +08:00
guanj
5cf39e8aa8 联调检测结果查询弹框 2025-08-25 11:35:38 +08:00
贾同学
a19a20ddd8 ADD:导入检测计划检测结果; 2025-08-25 11:02:07 +08:00
guanj
0985cc5d7c 修改正式监测返回结果 2025-08-22 16:21:57 +08:00
guanj
2be0be681e Merge branch 'master' of http://192.168.1.22:3000/frontend/pqs-9100_client 2025-08-22 15:34:07 +08:00
guanj
dd9ca8f956 联调 正式检测结果页面 2025-08-22 15:33:57 +08:00
贾同学
5cd60d9a32 ADD:导出检测计划检测结果; 2025-08-22 09:04:09 +08:00
guanj
959ae1dee9 Merge branch 'master' of http://192.168.1.22:3000/frontend/pqs-9100_client 2025-08-21 13:15:14 +08:00
guanj
d2d1490e9b 修改正式检测推送逻辑 2025-08-21 13:14:59 +08:00
caozehui
7bcd88c3a7 微调 2025-08-21 10:42:09 +08:00
guanj
8e3368bd29 修改正式检测逻辑 2025-08-21 09:33:13 +08:00
guanj
bc03ba88f0 修改样式 2025-08-20 20:05:07 +08:00
guanj
2aee4b281d 联调正式检测 2025-08-20 20:02:22 +08:00
贾同学
26647222e2 UPDATE:优化v-auth指令逻辑 2025-08-20 10:15:09 +08:00
贾同学
a2db45cace ADD:添加按钮权限 2025-08-20 08:51:01 +08:00
caozehui
d761c0449b 微调 2025-08-19 19:16:20 +08:00
caozehui
dc6a346fd4 微调 2025-08-19 09:37:42 +08:00
贾同学
e938c6b3d9 UPDATE:修改编辑计划按钮判断是否为主计划逻辑 2025-08-18 16:28:29 +08:00
caozehui
c9fef2a9d7 微调 2025-08-18 08:30:03 +08:00
sjl
9319dd06c5 首页判断被检设备下绑定监测点 2025-08-15 16:22:58 +08:00
贾同学
7b96ce84fc ADD:添加导出子计划元信息和导入检修计划按钮逻辑 2025-08-15 16:03:42 +08:00
sjl
b105ff890c 标准 2025-08-15 08:43:42 +08:00
sjl
61b87304e6 数模没有标准设备 2025-08-15 08:37:35 +08:00
sjl
83c8dc5f19 调整开始检测接口 2025-08-14 15:02:58 +08:00
贾同学
b1ddf540ca UPDATE:1.完善主计划导入被检设备逻辑;2.完善新增编辑子计划逻辑。 2025-08-13 20:34:08 +08:00
sjl
0025895696 预检测描述 2025-08-13 15:52:48 +08:00
sjl
1ec8cce63e 实时数据对齐,修改成标准对应被检 2025-08-13 10:06:55 +08:00
贾同学
865d52c135 Merge remote-tracking branch 'origin/master' 2025-08-12 20:43:11 +08:00
贾同学
ce8607af36 UPDATE:优化完善新增编辑检测计划绑定被检设备 2025-08-12 20:42:48 +08:00
sjl
4e8a6300dd 预检测 2025-08-12 20:17:37 +08:00
919e81da8b 前端优化调整 2025-08-12 10:26:30 +08:00
caozehui
18cb6dbde8 微调 2025-08-12 08:34:09 +08:00
6d405d16ed 前端优化调整 2025-08-11 16:30:53 +08:00
sjl
77d2176812 接口调整 2025-08-11 15:59:29 +08:00
c85eac3888 前端优化调整 2025-08-11 11:14:20 +08:00
sjl
27d2d82fcd 去除协议校验 2025-08-08 15:27:17 +08:00
sjl
ecbc3c30c8 1 2025-08-08 13:24:19 +08:00
sjl
0a65efd235 Merge branch 'master' of http://192.168.1.22:3000/frontend/pqs-9100_client
# Conflicts:
#	frontend/src/views/home/components/compareTestPopup.vue
#	frontend/src/views/home/components/testPopup.vue
2025-08-08 13:21:42 +08:00
sjl
5cd8fea60c 预见测 2025-08-08 13:18:01 +08:00
caozehui
3d1b4eb7c6 微调 2025-08-07 16:08:36 +08:00
ec1330bdb8 前端优化调整 2025-08-07 15:44:17 +08:00
caozehui
e66bcdb293 正式检测userPageId参数 2025-08-07 15:29:49 +08:00
sjl
88f1876ef0 通道配对 2025-08-07 15:17:04 +08:00
sjl
fdc1fd6fbd Merge branch 'master' of http://192.168.1.22:3000/frontend/pqs-9100_client 2025-08-07 14:44:04 +08:00
sjl
022d80f30e 实时数据对齐 2025-08-07 14:43:56 +08:00
f59f287b63 前端优化调整 2025-08-07 08:55:28 +08:00
6e573cc597 前端优化调整 2025-08-07 08:47:56 +08:00
d2f92ecde4 前端优化调整 2025-08-06 19:53:22 +08:00
sjl
b2a6a1de4e websocket 2025-08-06 15:28:17 +08:00
sjl
f374df79a6 Merge branch 'master' of http://192.168.1.22:3000/frontend/pqs-9100_client
# Conflicts:
#	frontend/src/utils/webSocketClient.ts
2025-08-06 15:21:17 +08:00
sjl
154eb9f79c 1 2025-08-06 15:19:27 +08:00
sjl
d0724cb7f6 预检测 2025-08-06 15:18:27 +08:00
d6d63523a3 websocket优化 2025-08-06 13:33:09 +08:00
sjl
83998f88ac Merge branch 'master' of http://192.168.1.22:3000/frontend/pqs-9100_client 2025-08-05 10:37:48 +08:00
sjl
9c5e54507b 通道配对,预检测界面 2025-08-05 10:37:40 +08:00
caozehui
15689b5284 Merge branch 'qr_branch'
# Conflicts:
#	frontend/src/views/machine/device/index.vue
2025-07-31 09:47:40 +08:00
caozehui
00893d2d1f 微调 2025-07-31 09:40:12 +08:00
sjl
0079f7415e 检测计划列表编辑 2025-07-29 18:35:46 +08:00
327801d040 出厂检测报告改造 2025-07-24 16:30:19 +08:00
sjl
6e22c01dd8 通道配对 2025-07-24 08:29:03 +08:00
sjl
bccb4b1f17 被检设备监测点 2025-07-22 16:12:08 +08:00
sjl
1f37cc567c 首页隐藏子计划,计划列表子计划设备管理 2025-07-22 14:56:58 +08:00
sjl
f81503091d Merge branch 'master' of http://192.168.1.22:3000/frontend/pqs-9100_client 2025-07-21 13:48:07 +08:00
sjl
e29f25653e 比对检测计划 2025-07-21 13:47:56 +08:00
caozehui
9e8e44b886 二楼检测项为空问题 2025-07-18 14:33:12 +08:00
caozehui
6e10b0c645 二楼检测项为空问题 2025-07-14 15:02:07 +08:00
sjl
c8f3b4eddc tab监测点框高度 2025-07-09 20:58:52 +08:00
sjl
cc848b1ffb 标准设备,比对模式被检设备 2025-07-09 20:12:40 +08:00
sjl
d2f0382bd9 新增场景切换 2025-07-01 14:58:11 +08:00
sjl
9fecf0ce3f 用户管理去除时间查询 2025-07-01 10:47:59 +08:00
sjl
290586d0ff 全局误差小数位4-6位 2025-06-30 10:06:19 +08:00
sjl
ddc45af223 优化计划弹框 2025-06-27 10:57:57 +08:00
sjl
0e6f123306 新增计划初始化 2025-06-27 10:53:56 +08:00
sjl
7b93363b23 通用配置误差保留小数位 2025-06-27 09:01:06 +08:00
caozehui
2fd0dcb8a3 微调 2025-06-27 08:40:27 +08:00
caozehui
86ee05f8af 微调 2025-06-26 14:01:04 +08:00
caozehui
38814b9f44 手动检测根据检测内容不同动态决定是否进行初始化操作 2025-06-26 13:17:11 +08:00
caozehui
fb5d13671d Merge remote-tracking branch 'origin/master' 2025-06-25 09:22:14 +08:00
caozehui
310a769092 微调 2025-06-25 09:21:58 +08:00
sjl
8c63edabdc 楼下场景不用报告模版 2025-06-24 11:03:55 +08:00
caozehui
5363625a2f 微调 2025-06-24 09:22:36 +08:00
efcd6e1cfe 临时提交(自动检测步骤) 2025-06-23 13:19:38 +08:00
acc4d0ca67 被检设备表格增加IP字段 2025-06-20 14:58:28 +08:00
772f38feca 代码提交 2025-06-20 12:11:57 +08:00
sjl
1eb141e559 新增检测计划默认显示,为空判断 2025-06-11 16:01:23 +08:00
caozehui
16b446bf20 微调 2025-06-04 22:15:37 +08:00
sjl
bc0de34c15 预检测不显示温度框 2025-06-04 16:38:02 +08:00
sjl
15f2c1ee41 用户,角色超级管理员不可编辑 2025-05-29 10:49:26 +08:00
sjl
81a944062a Merge branch 'master' of http://192.168.1.22:3000/frontend/pqs-9100_client 2025-05-29 10:15:10 +08:00
sjl
3c19b05f4b 间谐波次数,IP地址 2025-05-29 10:15:03 +08:00
caozehui
c1f53cdc69 微调 2025-05-27 09:07:59 +08:00
sjl
6ef2a6049b 系数校准二次确认,输入框长度限值 2025-05-26 15:36:06 +08:00
caozehui
9989cc98cb 微调 2025-05-26 14:41:06 +08:00
caozehui
e192158deb 微调 2025-05-23 10:15:05 +08:00
caozehui
2e5d551e5d 添加脚本与ICD检验时出现10500错误 2025-05-22 18:43:33 +08:00
caozehui
4622eb36d9 ABC三项可以分开单独展示部分相的数据 2025-05-22 08:45:55 +08:00
caozehui
9c53b7c18e 微调 2025-05-21 15:29:39 +08:00
caozehui
7b3805060f 微调 2025-05-19 09:15:48 +08:00
caozehui
629600bc00 归档前如果设备没有生成报告则自动生成报告 2025-05-16 10:33:43 +08:00
caozehui
8b144b63fc 批量生成报告 2025-05-15 16:08:35 +08:00
caozehui
098ab3a41d 省级平台在正式检测时增加温度、相对湿度参数 2025-05-15 08:57:24 +08:00
caozehui
2a53f577aa 检测计划新增数据处理原则字段、全局配置中移除数据处理原则字段 2025-05-14 08:58:21 +08:00
caozehui
f8b7c224b7 检测计划数据源字段显示bug、脚本大项启用/禁用功能 2025-05-12 15:57:19 +08:00
caozehui
f0b3bdd37c 预检测出现连接超时错误提示 2025-05-12 14:38:25 +08:00
caozehui
bb35eb749b 不合格项复检bug 2025-05-09 08:51:54 +08:00
caozehui
b3750d6a7f 微调 2025-05-06 15:22:26 +08:00
caozehui
33ebb91ab6 微调 2025-05-06 15:14:58 +08:00
sjl
3e00f2fee7 Merge branch 'master' of http://192.168.1.22:3000/frontend/pqs-9100_client 2025-05-06 11:00:23 +08:00
sjl
68e9856641 省级平台不展示系数校准 2025-05-06 11:00:12 +08:00
caozehui
51b2e80493 微调 2025-05-06 10:19:41 +08:00
sjl
cf77572f41 设备类型报告模版只在楼下厂家展示 2025-05-06 10:08:57 +08:00
GGJ
fe6524404c 微调 2025-04-29 10:47:25 +08:00
caozehui
ccda7218f4 检测计划数据源字段调整、绑定设备样式调整 2025-04-28 09:18:52 +08:00
caozehui
ddd0e6a22b (间)谐波含有率可以为小数 2025-04-27 16:11:48 +08:00
caozehui
ed85453de1 误差体系切换时-生成报告 2025-04-25 14:32:17 +08:00
caozehui
04939606fc 间谐波电流、间谐波电流复制bug,tooltip位置调整 2025-04-24 16:16:04 +08:00
caozehui
2df455b667 暂态设定幅值最大为180% 2025-04-24 10:18:27 +08:00
caozehui
8fb214d7a7 微调 2025-04-23 15:28:35 +08:00
caozehui
4f2c8a8016 微调 2025-04-23 10:45:46 +08:00
caozehui
c2974dbe0d 微调 2025-04-22 15:52:03 +08:00
caozehui
3891e5459a 误差体系修改 2025-04-22 14:21:24 +08:00
caozehui
9ed47ac5bc 检测结果数据显示问题 2025-04-22 14:12:07 +08:00
caozehui
114fcd140d 微调 2025-04-22 13:49:59 +08:00
caozehui
19a30894f8 微调 2025-04-21 09:11:31 +08:00
caozehui
2134fea518 微调 2025-04-21 09:10:24 +08:00
caozehui
82f495ce3b 调整窗口大小 2025-04-18 13:42:48 +08:00
caozehui
f2ecff54a3 微调 2025-04-18 11:07:04 +08:00
caozehui
f56004ac15 微调 2025-04-17 14:47:32 +08:00
caozehui
5303c47248 微调 2025-04-17 10:21:38 +08:00
GGJ
37731592f7 微调 2025-04-16 10:17:37 +08:00
caozehui
c9e41e0c82 日志导出csv、报告模板编辑框样式调整 2025-04-15 09:45:00 +08:00
caozehui
4a80a88e6e 微调 2025-04-14 19:51:06 +08:00
caozehui
30b219e14d 微调 2025-04-14 14:43:29 +08:00
caozehui
ecec9adeea icd校验 2025-04-14 13:22:46 +08:00
caozehui
1853152138 日志分页查询及展示 2025-04-11 14:34:56 +08:00
GGJ
5619413f37 修改点击使能默认够选通道使能,修改暂态使能持续时间保持一致 2025-04-10 11:11:23 +08:00
caozehui
5b736bc475 微调 2025-04-10 10:50:53 +08:00
caozehui
2aa37c3fe3 微调 2025-04-09 20:42:17 +08:00
caozehui
4e0cb72f32 微调 2025-04-09 20:06:08 +08:00
caozehui
ce74ec4db7 bug修复 2025-04-09 19:30:00 +08:00
caozehui
9938306884 微调 2025-04-09 14:30:00 +08:00
caozehui
ef63fcf807 bug修复 2025-04-08 09:51:35 +08:00
caozehui
08cffa4256 bug修复 2025-04-08 09:43:12 +08:00
caozehui
e1a740f8bf 文件大小提示 2025-03-31 18:54:13 +08:00
caozehui
56d035253b 微调 2025-03-31 13:22:57 +08:00
caozehui
e7348107d0 解决forge库在桌面端无法使用的问题 2025-03-28 09:38:59 +08:00
caozehui
ea38b4eb21 微调 2025-03-28 09:31:46 +08:00
GGJ
67ef976739 Merge branch 'master' of http://192.168.1.22:3000/frontend/pqs-9100_client 2025-03-27 18:34:51 +08:00
GGJ
60145ec4f7 修改参考设定值添加单位 2025-03-27 18:34:40 +08:00
caozehui
81600ddc69 引入获取是否自动生成报告接口 2025-03-27 16:20:16 +08:00
caozehui
e0941d628b 引入获取是否自动生成报告接口 2025-03-27 15:54:27 +08:00
caozehui
760dcbf723 微调 2025-03-27 14:59:01 +08:00
caozehui
b75ecb8c66 微调 2025-03-26 15:42:40 +08:00
caozehui
39f4cfa670 微调 2025-03-25 15:49:32 +08:00
caozehui
862251b83c 微调 2025-03-25 15:08:25 +08:00
caozehui
312490ce59 微调 2025-03-24 19:56:11 +08:00
caozehui
d7df999cf2 登录接口加密传输登录信息 2025-03-24 19:22:56 +08:00
caozehui
e0b1b5907b 微调 2025-03-24 15:41:53 +08:00
sjl
f06ca03881 微调 2025-03-24 14:59:45 +08:00
caozehui
a9b80ed33c 微调 2025-03-24 10:46:16 +08:00
sjl
834230b26a 报告模版 2025-03-24 08:56:08 +08:00
caozehui
403ddbfb6e 微调 2025-03-20 19:42:46 +08:00
caozehui
ffb44ea909 微调 2025-03-20 16:31:15 +08:00
sjl
b729e4efa1 微调 2025-03-19 13:30:11 +08:00
sjl
962c286fd5 主题切换 2025-03-19 10:26:41 +08:00
caozehui
c215f51554 微调 2025-03-18 19:38:27 +08:00
GGJ
859c85b427 检测脚本添加 额定电压 额定电流字段 2025-03-18 16:28:22 +08:00
sjl
5fe637e84f 微调 2025-03-17 16:03:53 +08:00
sjl
5f68071c77 微调颜色 2025-03-17 15:55:30 +08:00
caozehui
1675a5af31 微调 2025-03-17 14:33:19 +08:00
caozehui
ec1d09dbd0 微调 2025-03-17 13:30:00 +08:00
caozehui
eda7b516f6 微调 2025-03-17 13:24:35 +08:00
sjl
630eb48d28 微调 2025-03-17 13:17:12 +08:00
caozehui
0d2f8a7788 微调 2025-03-14 16:30:45 +08:00
GGJ
230c68f454 修改他不删除后编辑检测监本还存在问题 2025-03-14 16:23:27 +08:00
GGJ
68934060e6 修改参考设定值0不显示 2025-03-14 16:10:00 +08:00
GGJ
846e7514b7 Merge branch 'master' of http://192.168.1.22:3000/frontend/pqs-9100_client 2025-03-14 15:03:45 +08:00
GGJ
410cfb0f7a 检测脚本添加额定电压、额定电流 修改bug 2025-03-14 15:03:36 +08:00
caozehui
c892410b07 微调 2025-03-14 14:45:39 +08:00
sjl
7caa4a5303 微调 2025-03-14 12:27:04 +08:00
sjl
c17efbf9f6 微调 2025-03-14 12:13:34 +08:00
c46abeecf5 websocket代码提交 2025-03-14 11:35:55 +08:00
caozehui
0947097932 微调 2025-03-14 11:23:49 +08:00
caozehui
50fc02d4eb 微调 2025-03-13 14:40:33 +08:00
caozehui
a122e9e1b3 微调 2025-03-12 15:01:58 +08:00
sjl
d7cfcc7855 微调 2025-03-12 11:17:12 +08:00
sjl
05948aebf5 Merge branch 'master' of http://192.168.1.22:3000/frontend/pqs-9100_client 2025-03-12 09:53:01 +08:00
sjl
cca214aaf7 程控源 2025-03-12 09:52:52 +08:00
caozehui
0111954c5e 微调 2025-03-11 19:07:47 +08:00
sjl
ce31499964 Merge branch 'master' of http://192.168.1.22:3000/frontend/pqs-9100_client 2025-03-11 18:58:39 +08:00
sjl
059864d193 微调 2025-03-11 18:58:26 +08:00
caozehui
6d2fb4dde8 微调 2025-03-11 18:29:09 +08:00
caozehui
49059d9c59 微调 2025-03-11 18:19:18 +08:00
caozehui
d2f7041988 微调 2025-03-11 18:10:10 +08:00
caozehui
f94372d20b 微调 2025-03-11 16:13:03 +08:00
sjl
9af98b034f 微调 2025-03-11 14:16:21 +08:00
sjl
341b8df5a9 Merge branch 'master' of http://192.168.1.22:3000/frontend/pqs-9100_client 2025-03-11 11:31:02 +08:00
sjl
7470f697bd 微调 2025-03-11 11:30:52 +08:00
caozehui
c058b99a0d 微调 2025-03-11 11:30:10 +08:00
caozehui
d5eed13f38 测试项为1条时也展示出来 2025-03-11 11:04:02 +08:00
caozehui
c27ee88182 微调 2025-03-10 20:14:35 +08:00
sjl
e2559ffa65 Merge branch 'master' of http://192.168.1.22:3000/frontend/pqs-9100_client 2025-03-10 20:00:03 +08:00
sjl
a0bd32453c 微调 2025-03-10 19:59:55 +08:00
sjl
54e7a91841 微调 2025-03-10 19:58:53 +08:00
caozehui
042f6eef51 脚本切换 2025-03-10 19:53:52 +08:00
GGJ
c9857326c6 修改 检测脚本参考值 2025-03-10 16:11:08 +08:00
GGJ
9c4e0094f9 Merge branch 'master' of http://192.168.1.22:3000/frontend/pqs-9100_client 2025-03-10 16:00:15 +08:00
GGJ
781b27af51 修改间谐波值 2025-03-10 16:00:07 +08:00
caozehui
37e257afc8 Loading加载效果 2025-03-10 11:39:10 +08:00
GGJ
4d4fdd6190 修改测试bug 2025-03-10 11:18:32 +08:00
caozehui
0fd2d8252d 微调 2025-03-10 10:50:53 +08:00
caozehui
3cf5019880 源程控-时间展示 2025-03-10 10:10:32 +08:00
caozehui
258d3d692f 源参数默认展开 2025-03-10 10:09:15 +08:00
caozehui
667ed3bcc0 微调 2025-03-08 18:27:10 +08:00
caozehui
a882189022 微调 2025-03-08 18:13:16 +08:00
caozehui
686e009243 微调 2025-03-08 18:07:47 +08:00
caozehui
30d5f3390c Merge branch 'release' 2025-03-08 18:06:50 +08:00
caozehui
3398b414ca 微调 2025-03-08 16:56:57 +08:00
caozehui
b6a2c42feb Merge remote-tracking branch 'origin/release' into release
# Conflicts:
#	frontend/src/views/machine/controlSource/index.vue
2025-03-08 16:05:56 +08:00
caozehui
cb5a9050f2 微调 2025-03-08 16:04:43 +08:00
caozehui
661b27abd0 微调 2025-03-08 16:04:08 +08:00
caozehui
59a24c9ea2 Merge branch 'release' 2025-03-08 16:02:09 +08:00
caozehui
6cf5da5d48 微调 2025-03-08 16:01:41 +08:00
caozehui
537f5e2f4c Merge branch 'release'
# Conflicts:
#	frontend/src/views/machine/controlSource/index.vue
2025-03-08 14:39:20 +08:00
caozehui
70531a0729 微调 2025-03-08 14:36:48 +08:00
caozehui
ded15e5c8b 微调 2025-03-08 10:50:39 +08:00
caozehui
3021f7e4d3 微调 2025-03-08 10:49:42 +08:00
caozehui
876d9cfa00 微调 2025-03-08 10:48:25 +08:00
caozehui
8f28825162 微调 2025-03-08 10:47:19 +08:00
caozehui
0e6612f6bd 微调 2025-03-08 10:15:11 +08:00
caozehui
39183fde5d 微调 2025-03-07 16:26:14 +08:00
caozehui
b967419cb1 微调 2025-03-07 15:43:36 +08:00
sjl
0712698638 微调 2025-03-07 14:44:12 +08:00
sjl
d3a74e830f 微调 2025-03-07 14:00:20 +08:00
sjl
f68da773ab 微调 2025-03-07 13:17:11 +08:00
sjl
8db814dba4 Merge branch 'master' of http://192.168.1.22:3000/frontend/pqs-9100_client 2025-03-07 10:17:14 +08:00
sjl
7dcff99de5 微调 2025-03-07 10:17:06 +08:00
caozehui
97100dfa63 复制-二次弹窗确认 2025-03-07 09:02:19 +08:00
caozehui
d41707fcf9 微调 2025-03-06 15:50:45 +08:00
sjl
2377916f29 微调 2025-03-05 11:19:16 +08:00
caozehui
be2f40715a 微调 2025-03-05 10:42:25 +08:00
sjl
489c0882ac 微调 2025-03-05 09:32:13 +08:00
sjl
492db88b45 检测脚本查看隐藏显示 2025-03-04 16:01:34 +08:00
caozehui
3b198eb4ed 微调 2025-03-04 14:12:50 +08:00
caozehui
5efc27d4b6 微调 2025-03-04 10:28:09 +08:00
sjl
ec9f270d04 检测脚本详情查看 2025-03-04 09:35:41 +08:00
caozehui
afd2d467f3 微调 2025-03-03 13:33:17 +08:00
caozehui
ff0798450d 微调 2025-03-03 13:29:48 +08:00
caozehui
fbefc70368 微调 2025-03-03 13:25:19 +08:00
caozehui
faa0bc54a6 不合格项复检 2025-03-03 11:35:12 +08:00
caozehui
bf5f4eab77 Merge branch 'master' into dev 2025-02-28 15:38:08 +08:00
GGJ
188456c970 添加查看页面 2025-02-28 13:58:15 +08:00
caozehui
c06334e96f Merge branch 'master' into dev
# Conflicts:
#	frontend/src/views/home/components/dataCheckSingleChannelSingleTestPopup.vue
2025-02-28 09:03:05 +08:00
GGJ
82e8bf2e0b 优化检测脚本样式 2025-02-28 09:00:15 +08:00
GGJ
30382fb34d 添加一键重算功能 2025-02-27 16:24:13 +08:00
GGJ
c7e5ee0862 优化检测脚本页面 2025-02-27 15:09:09 +08:00
caozehui
d6ac63586b 合并master分支 2025-02-27 14:45:22 +08:00
caozehui
e0fd42199f 微调 2025-02-27 14:34:47 +08:00
caozehui
d9e3168484 Merge branch 'master' into dev
# Conflicts:
#	frontend/src/views/home/components/test.vue
#	frontend/src/views/home/components/testPopup.vue
2025-02-27 13:52:17 +08:00
caozehui
dc7e91ca66 微调 2025-02-27 10:07:41 +08:00
caozehui
692358d278 微调 2025-02-27 08:48:13 +08:00
GGJ
3750031d6b Merge branch 'master' of http://192.168.1.22:3000/frontend/pqs-9100_client 2025-02-27 08:42:37 +08:00
GGJ
0bae200241 优化检测脚本页面
录入检测脚本
2025-02-27 08:41:33 +08:00
sjl
61bd1a0d6a 微调 2025-02-26 19:49:23 +08:00
sjl
37cf3dfdd7 测试 2025-02-26 19:44:27 +08:00
caozehui
8be96ca64e 微调 2025-02-26 19:31:34 +08:00
sjl
d225391248 日志测试 2025-02-26 19:20:02 +08:00
sjl
f2fd0b078e 日志测试 2025-02-26 19:05:56 +08:00
caozehui
f71963ea3b 微调 2025-02-26 19:02:43 +08:00
sjl
6a635246bb 日志测试 2025-02-26 19:00:37 +08:00
caozehui
d338ca6b25 微调 2025-02-26 18:55:50 +08:00
caozehui
193c3b963c 微调 2025-02-26 18:07:57 +08:00
sjl
cad4965b1b 微调 2025-02-26 16:23:27 +08:00
caozehui
79306d846f 微调 2025-02-26 15:58:16 +08:00
caozehui
f4ea510c5d 测试1 2025-02-26 15:56:03 +08:00
sjl
07cc26fa8d 微调 2025-02-26 13:58:56 +08:00
sjl
f87e268242 微调 2025-02-26 13:57:42 +08:00
caozehui
1aeceffe0b 测试1 2025-02-26 13:57:07 +08:00
caozehui
fc97087767 测试1 2025-02-26 13:22:01 +08:00
sjl
61be97b04b Merge branch 'dev' of http://192.168.1.22:3000/frontend/pqs-9100_client into dev 2025-02-26 11:40:41 +08:00
sjl
934162875c 微调 2025-02-26 11:40:31 +08:00
caozehui
4b3991099a 微调 2025-02-26 11:25:20 +08:00
caozehui
b14a961c1a 微调 2025-02-26 11:13:28 +08:00
sjl
cbe41d80ce 合并frontend/src/views/home/components/testPopup.vue 2025-02-26 10:54:43 +08:00
sjl
28203f2621 微调 2025-02-26 10:52:39 +08:00
caozehui
b8492dfbe6 微调 2025-02-26 10:42:01 +08:00
caozehui
15faac8079 正式检测-移除重新检测按钮 2025-02-26 10:05:43 +08:00
caozehui
ad850ca4ee 微调 2025-02-26 10:00:44 +08:00
caozehui
6c05c6e06e 微调 2025-02-26 09:14:27 +08:00
caozehui
8b2cda80b1 微调 2025-02-25 16:37:59 +08:00
caozehui
3f7756b417 微调 2025-02-25 16:18:22 +08:00
caozehui
5004e319b6 微调 2025-02-25 15:11:23 +08:00
caozehui
19035f2dc9 微调 2025-02-25 15:03:45 +08:00
caozehui
9d8a5c76b2 微调 2025-02-25 14:52:06 +08:00
caozehui
6300eb2f43 微调 2025-02-25 14:13:59 +08:00
sjl
da58ffb621 微调 2025-02-25 14:01:07 +08:00
sjl
1500fafb92 微调 2025-02-25 14:00:40 +08:00
sjl
df53eea432 微调 2025-02-25 13:53:47 +08:00
sjl
cc1771c208 微调 2025-02-25 13:52:57 +08:00
caozehui
5e881c0804 微调 2025-02-25 13:48:44 +08:00
caozehui
5666e01ae0 微调 2025-02-25 13:47:33 +08:00
caozehui
4acec8a2d6 微调 2025-02-25 13:46:01 +08:00
sjl
426a511099 微调 2025-02-25 11:20:19 +08:00
caozehui
89303b44ae 微调 2025-02-25 11:00:04 +08:00
caozehui
ffb82066bb 正式检测设备连接超时,停止检测 2025-02-25 10:46:12 +08:00
caozehui
f46b8c0a56 微调 2025-02-25 10:29:29 +08:00
caozehui
280289af37 手动检测功能修改、一键检测功能修改(均未完成) 2025-02-25 10:17:33 +08:00
GGJ
a0c7f79302 修改检测脚本闪变编辑逻辑 2025-02-25 08:58:03 +08:00
GGJ
bd1eecc106 优化检测脚本页面 2025-02-24 16:45:39 +08:00
sjl
1a23bcb510 检测计划未检下拉框不可编辑 2025-02-24 16:30:52 +08:00
sjl
942c28d3bd 微调 2025-02-24 08:54:22 +08:00
GGJ
034b31ba47 联调检测脚本 2025-02-24 08:38:54 +08:00
sjl
7a76c2da8a 微调 2025-02-21 14:57:52 +08:00
caozehui
1a9d8e8606 检测通道数量微调 2025-02-21 09:05:06 +08:00
GGJ
14583d919d 联调检测脚本修改功能 2025-02-20 16:39:15 +08:00
caozehui
629fd174e4 微调 2025-02-20 15:01:15 +08:00
sjl
9080fe06a4 角标对齐 2025-02-20 10:21:36 +08:00
sjl
0e183bb5c1 检测源删除 2025-02-20 10:10:28 +08:00
GGJ
8caf856d2d 联调检测脚本页面 2025-02-19 16:54:54 +08:00
caozehui
d28669e6b0 正式检测-时间显示调整 2025-02-19 13:17:19 +08:00
700884d01a websocket代码提交 2025-02-19 11:28:52 +08:00
c06bd9fa24 websocket代码提交 2025-02-19 11:00:09 +08:00
GGJ
ce92a1d645 联调 新增检测脚本 2025-02-18 16:36:54 +08:00
caozehui
4f622da52c 调整检测计划导入、导出功能适应二楼装备 2025-02-18 15:58:38 +08:00
7d3e86a5fc websocket代码提交 2025-02-18 14:59:13 +08:00
sjl
c66d181b98 检测计划取消勾选 2025-02-18 09:00:23 +08:00
GGJ
f860630bfb 修改检测脚本新增页面路由权限 2025-02-18 08:51:20 +08:00
caozehui
6db4d79839 调整 2025-02-18 08:48:59 +08:00
GGJ
e2924d5e57 Merge branch 'master' of http://192.168.1.22:3000/frontend/pqs-9100_client
# Conflicts:
#	frontend/src/views/machine/testScript/components/testScriptPopup.vue
#	frontend/src/views/machine/testScript/index.vue
2025-02-17 16:46:20 +08:00
GGJ
92b9a82f21 修改检测脚本页面 2025-02-17 16:44:02 +08:00
sjl
c3f89ca1f8 微调 2025-02-17 16:32:02 +08:00
sjl
8c435c91dd Merge branch 'master' of http://192.168.1.22:3000/frontend/pqs-9100_client 2025-02-17 16:29:06 +08:00
sjl
6e6cfa5be0 微调 2025-02-17 16:28:59 +08:00
caozehui
ea203d0ccc 调整统计分析功能 2025-02-17 16:26:23 +08:00
caozehui
370cd52774 调整静态路由 2025-02-17 13:37:27 +08:00
caozehui
942bae3045 微调 2025-02-17 11:30:56 +08:00
caozehui
356548c8fb 显示时间 2025-02-17 10:16:48 +08:00
GGJ
5c012c2bc9 修改 检测脚本新增页面 2025-02-17 09:00:27 +08:00
GGJ
9c9b8015a0 Merge branch 'master' of http://192.168.1.22:3000/frontend/pqs-9100_client 2025-02-17 08:40:19 +08:00
GGJ
bba0ced7f9 修改 测试脚本页面 2025-02-17 08:39:18 +08:00
caozehui
e8de7b56d9 微调 2025-02-14 16:22:45 +08:00
caozehui
95022f62a8 数据分析 2025-02-14 13:30:50 +08:00
sjl
1d918f3335 微调 2025-02-14 11:44:19 +08:00
caozehui
f992637ad2 微调 2025-02-14 10:59:21 +08:00
caozehui
40f18bbdf8 微调 2025-02-14 10:39:00 +08:00
caozehui
3bb9cec08a 微调 2025-02-14 10:31:16 +08:00
caozehui
6842260f59 微调 2025-02-14 09:57:26 +08:00
sjl
305c30d725 微调 2025-02-14 09:16:07 +08:00
caozehui
e799dd2f08 移除headers中的Refresh-Token,添加Is-Refresh-Token 2025-02-13 21:02:34 +08:00
GGJ
1e83172e9a 修改检测脚本页面 2025-02-13 16:15:26 +08:00
sjl
475d236b7e 微调 2025-02-13 15:40:13 +08:00
249 changed files with 33247 additions and 18338 deletions

3
.gitignore vendored
View File

@@ -7,5 +7,6 @@ package-lock.json
data/
.vscode/launch.json
public/electron/
public/dist/
pnpm-lock.yaml
CLAUDE.md
/public/dist/

View File

@@ -1,112 +0,0 @@
import axios, { AxiosInstance, AxiosError, AxiosRequestConfig, InternalAxiosRequestConfig, AxiosResponse } from "axios";
import { showFullScreenLoading, tryHideFullScreenLoading } from "@/components/Loading/fullScreen";
import { LOGIN_URL } from "@/config";
import { ElMessage } from "element-plus";
import { ResultData } from "@/api/interface";
import { ResultEnum } from "@/enums/httpEnum";
import { checkStatus } from "./helper/checkStatus";
import { useUserStore } from "@/stores/modules/user";
import router from "@/routers";
export interface CustomAxiosRequestConfig extends InternalAxiosRequestConfig {
loading?: boolean;
}
const config = {
// 默认地址请求地址,可在 .env.** 文件中修改
baseURL: import.meta.env.VITE_API_URL as string,
// 设置超时时间
timeout: ResultEnum.TIMEOUT as number,
// 跨域时候允许携带凭证
withCredentials: true,
// post请求指定数据类型以及编码
headers: { 'Content-Type': 'application/json;charset=utf-8' }
};
class RequestHttp {
service: AxiosInstance;
public constructor(config: AxiosRequestConfig) {
// 创建实例
this.service = axios.create(config);
/**
* @description 请求拦截器
* 客户端发送请求 -> [请求拦截器] -> 服务器
* token校验(JWT) : 接受服务器返回的 token,存储到 vuex/pinia/本地储存当中
*/
this.service.interceptors.request.use(
(config: CustomAxiosRequestConfig) => {
const userStore = useUserStore();
// 当前请求不需要显示 loading在 api 服务中通过指定的第三个参数: { loading: false } 来控制
config.loading ?? (config.loading = true);
config.loading && showFullScreenLoading();
if (config.headers && typeof config.headers.set === "function") {
config.headers.set("x-access-token", userStore.token);
}
return config;
},
(error: AxiosError) => {
return Promise.reject(error);
}
);
/**
* @description 响应拦截器
* 服务器换返回信息 -> [拦截统一处理] -> 客户端JS获取到信息
*/
this.service.interceptors.response.use(
(response: AxiosResponse) => {
const { data } = response;
const userStore = useUserStore();
tryHideFullScreenLoading();
// 登陆失效
if (data.code == ResultEnum.OVERDUE) {
userStore.setToken("");
router.replace(LOGIN_URL);
ElMessage.error(data.message);
return Promise.reject(data);
}
// 全局错误信息拦截(防止下载文件的时候返回数据流,没有 code 直接报错)
if (data.code && data.code !== ResultEnum.SUCCESS) {
ElMessage.error(data.message);
return Promise.reject(data);
}
// 成功请求(在页面上除非特殊情况,否则不用处理失败逻辑)
return data;
},
async (error: AxiosError) => {
const { response } = error;
tryHideFullScreenLoading();
// 请求超时 && 网络错误单独判断,没有 response
if (error.message.indexOf("timeout") !== -1) ElMessage.error("请求超时!请您稍后重试");
if (error.message.indexOf("Network Error") !== -1) ElMessage.error("网络错误!请您稍后重试");
// 根据服务器响应的错误状态码,做不同的处理
if (response) checkStatus(response.status);
// 服务器结果都没有返回(可能服务器错误可能客户端断网),断网处理:可以跳转到断网页面
if (!window.navigator.onLine) router.replace("/500");
return Promise.reject(error);
}
);
}
/**
* @description 常用请求方法封装
*/
get<T>(url: string, params?: object, _object = {}): Promise<ResultData<T>> {
return this.service.get(url, { params, ..._object });
}
post<T>(url: string, params?: object | string, _object = {}): Promise<ResultData<T>> {
return this.service.post(url, params, _object);
}
put<T>(url: string, params?: object, _object = {}): Promise<ResultData<T>> {
return this.service.put(url, params, _object);
}
delete<T>(url: string, params?: any, _object = {}): Promise<ResultData<T>> {
return this.service.delete(url, { params, ..._object });
}
download(url: string, params?: object, _object = {}): Promise<BlobPart> {
return this.service.post(url, params, { ..._object, responseType: "blob" });
}
}
export default new RequestHttp(config);

6
.npmrc
View File

@@ -2,3 +2,9 @@ registry=https://registry.npmmirror.com/
disturl=https://registry.npmmirror.com/-/binary/node
electron_mirror=https://npmmirror.com/mirrors/electron/
electron-builder-binaries_mirror=https://registry.npmmirror.com/-/binary/electron-builder-binaries/
# 屏蔽警告配置
legacy-peer-deps=true
audit=false
fund=false
loglevel=error

View File

@@ -1,5 +1,6 @@
{
"cSpell.words": [
"Analyse",
"CHNFACTOR",
"CHNPACTOR",
"Chns",

View File

@@ -1,61 +0,0 @@
#### 项目简介
#### 常用命令
```shell
# 运行项目
npm run dev
# 仅运行前端
npm run dev-frontend
# 仅运行后端
npm run dev-electron
# 预发布模式环境变量为prod请先移动资源
npm run start
# 移动前端静态资源
npm run rd
# 移动资源,可配置
npm run move
# 代码加密
npm run encrypt
# 清除加密的代码
npm run clean
# 生成logo
npm run icon
# 打包 windows版
npm run build-w (调整为64位)
npm run build-w-32 (32位)
npm run build-w-64 (64位)
npm run build-w-arm64 (arm64)
# 打包 windows 免安装版)
# ee > v2.2.1
npm run build-wz (调整为64位)
npm run build-wz-32 (32位)
npm run build-wz-64 (64位)
npm run build-wz-arm64 (arm64)
# 打包 mac版
npm run build-m
npm run build-m-arm64 (m1芯片架构)
# 打包 linux版
# ee > v2.2.1
npm run build-l (默认64位 deb包)
npm run build-l-32 (32位 deb包)
npm run build-l-64 (64位 deb包)
npm run build-l-arm64 (64位 deb包 arm64)
npm run build-l-armv7l (64位 deb包 armv7l)
npm run build-lr-64 (64位 rpm包)
npm run build-lp-64 (64位 pacman包)
```

View File

@@ -1 +0,0 @@
chrome应用商店ctx文件解压后放置在此目录中打包时会将资源加入安装包内。

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 745 B

After

Width:  |  Height:  |  Size: 930 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 119 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 119 KiB

After

Width:  |  Height:  |  Size: 26 KiB

205
cmd/bin.js Normal file
View File

@@ -0,0 +1,205 @@
/**
* ee-bin 配置
* 仅适用于开发环境
*/
module.exports = {
/**
* development serve ("frontend" "electron" )
* ee-bin dev
*/
dev: {
// frontend前端服务
// 说明:该配置的意思是,进入 frontend 目录,执行 npm run dev
// 运行后的服务为 http://localhost:8080
// 如果 protocol 属性为 'file://' 那么不会执行命令,项目直接加载 indexPath 对应的文件。
frontend: {
directory: './frontend',
cmd: 'npm',
args: ['run', 'dev'],
port: 18091,
},
// electron主进程服务
// 说明:该配置的意思是,在根目录,执行 electron . --env=local
electron: {
directory: './',
cmd: 'electron',
args: ['.', '--env=local'],
watch: false,
}
},
/**
* 构建
* ee-bin build
*/
build: {
frontend: {
directory: './frontend',
cmd: 'npm',
args: ['run', 'build'],
},
electron: {
type: 'javascript',
bundleType: 'copy'
},
win64: {
cmd: 'electron-builder',
directory: './',
args: ['--config=./cmd/builder.json', '-w=nsis', '--x64'],
},
win32: {
args: ['--config=./cmd/builder.json', '-w=nsis', '--ia32'],
},
win_e: {
args: ['--config=./cmd/builder.json', '-w=portable', '--x64'],
},
win_7z: {
args: ['--config=./cmd/builder.json', '-w=7z', '--x64'],
},
mac: {
args: ['--config=./cmd/builder-mac.json', '-m'],
},
mac_arm64: {
args: ['--config=./cmd/builder-mac-arm64.json', '-m', '--arm64'],
},
linux: {
args: ['--config=./cmd/builder-linux.json', '-l=deb', '--x64'],
},
linux_arm64: {
args: ['--config=./cmd/builder-linux.json', '-l=deb', '--arm64'],
},
go_w: {
directory: './go',
cmd: 'go',
args: ['build', '-o=../build/extraResources/goapp.exe'],
},
go_m: {
directory: './go',
cmd: 'go',
args: ['build', '-o=../build/extraResources/goapp'],
},
go_l: {
directory: './go',
cmd: 'go',
args: ['build', '-o=../build/extraResources/goapp'],
},
python: {
directory: './python',
cmd: 'python',
args: ['./setup.py', 'build'],
},
},
/**
* 移动资源
* ee-bin move
*/
move: {
frontend_dist: {
src: './frontend/dist',
dest: './public/dist'
},
go_static: {
src: './frontend/dist',
dest: './go/public/dist'
},
go_config: {
src: './go/config',
dest: './go/public/config'
},
go_package: {
src: './package.json',
dest: './go/public/package.json'
},
go_images: {
src: './public/images',
dest: './go/public/images'
},
python_dist: {
src: './python/dist',
dest: './build/extraResources/py'
},
},
/**
* 预发布模式prod
* ee-bin start
*/
start: {
directory: './',
cmd: 'electron',
args: ['.', '--env=prod']
},
/**
* 加密
*/
encrypt: {
frontend: {
type: 'none',
files: [
'./public/dist/**/*.(js|json)',
],
cleanFiles: ['./public/dist'],
confusionOptions: {
compact: true,
stringArray: true,
stringArrayEncoding: ['none'],
stringArrayCallsTransform: true,
numbersToExpressions: true,
target: 'browser',
}
},
electron: {
type: 'confusion',
files: [
'./public/electron/**/*.(js|json)',
],
cleanFiles: ['./public/electron'],
specificFiles: [
'./public/electron/main.js',
'./public/electron/preload/bridge.js',
],
confusionOptions: {
compact: true,
stringArray: true,
stringArrayEncoding: ['rc4'],
deadCodeInjection: false,
stringArrayCallsTransform: true,
numbersToExpressions: true,
target: 'node',
}
}
},
/**
* 执行自定义命令
* ee-bin exec
*/
exec: {
// 单独调试air 实现 go 热重载
go: {
directory: './go',
cmd: 'air',
args: ['-c=config/.air.toml' ],
},
// windows 单独调试air 实现 go 热重载
go_w: {
directory: './go',
cmd: 'air',
args: ['-c=config/.air.windows.toml' ],
},
// 单独调试,以基础方式启动 go
go2: {
directory: './go',
cmd: 'go',
args: ['run', './main.go', '--env=dev','--basedir=../', '--port=7073'],
},
python: {
directory: './python',
cmd: 'python',
args: ['./main.py', '--port=7074'],
stdio: "inherit", // ignore
},
},
};

40
cmd/builder-linux.json Normal file
View File

@@ -0,0 +1,40 @@
{
"productName": "ee",
"appId": "com.bilibili.ee",
"copyright": "© 2025 duola Technology Co., Ltd.",
"directories": {
"output": "out"
},
"asar": true,
"files": [
"**/*",
"!cmd/",
"!data/",
"!electron/",
"!frontend/",
"!logs/",
"!out/",
"!go/",
"!python/"
],
"extraResources": [
{
"from": "build/extraResources",
"to": "extraResources"
}
],
"publish": [
{
"provider": "generic",
"url": ""
}
],
"linux": {
"icon": "build/icons/icon.icns",
"artifactName": "${productName}-${os}-${version}-${arch}.${ext}",
"target": [
"deb"
],
"category": "Utility"
}
}

View File

@@ -0,0 +1,38 @@
{
"productName": "ee",
"appId": "com.bilibili.ee",
"copyright": "© 2025 duola Technology Co., Ltd.",
"directories": {
"output": "out"
},
"asar": true,
"files": [
"**/*",
"!cmd/",
"!data/",
"!electron/",
"!frontend/",
"!logs/",
"!out/",
"!go/",
"!python/"
],
"extraResources": [
{
"from": "build/extraResources",
"to": "extraResources"
}
],
"publish": [
{
"provider": "generic",
"url": ""
}
],
"mac": {
"icon": "build/icons/icon.icns",
"artifactName": "${productName}-${os}-${version}-${arch}.${ext}",
"darkModeSupport": true,
"hardenedRuntime": false
}
}

38
cmd/builder-mac.json Normal file
View File

@@ -0,0 +1,38 @@
{
"productName": "ee",
"appId": "com.bilibili.ee",
"copyright": "© 2025 duola Technology Co., Ltd.",
"directories": {
"output": "out"
},
"asar": true,
"files": [
"**/*",
"!cmd/",
"!data/",
"!electron/",
"!frontend/",
"!logs/",
"!out/",
"!go/",
"!python/"
],
"extraResources": [
{
"from": "build/extraResources",
"to": "extraResources"
}
],
"publish": [
{
"provider": "generic",
"url": ""
}
],
"mac": {
"icon": "build/icons/icon.icns",
"artifactName": "${productName}-${os}-${version}-${arch}.${ext}",
"darkModeSupport": true,
"hardenedRuntime": false
}
}

View File

@@ -1,17 +1,21 @@
{
"productName": "pqs9100",
"appId": "com.njcn.pqs9100",
"copyright": "hongawen.com",
"productName": "NPQS9100",
"appId": "NQPS9100",
"copyright": "© 2025 南京灿能",
"directories": {
"output": "out"
},
"asar": true,
"files": [
"**/*",
"!cmd/",
"!data/",
"!electron/",
"!frontend/",
"!run/",
"!logs/",
"!data/"
"!out/",
"!go/",
"!python/"
],
"extraResources": {
"from": "build/extraResources/",
@@ -26,35 +30,15 @@
"installerHeaderIcon": "build/icons/icon.ico",
"createDesktopShortcut": true,
"createStartMenuShortcut": true,
"shortcutName": "自动检测平台"
},
"publish": [
{
"provider": "generic",
"url": "http://www.shining-electric.com/"
}
],
"mac": {
"icon": "build/icons/icon.icns",
"artifactName": "${productName}-${os}-${version}-${arch}.${ext}",
"darkModeSupport": true,
"hardenedRuntime": false
"shortcutName": "灿能检测"
},
"win": {
"icon": "build/icons/icon.ico",
"artifactName": "${productName}-${os}-${version}-${arch}.${ext}",
"target": [
{
"target": "nsis"
"target": "portable"
}
]
},
"linux": {
"icon": "build/icons/icon.icns",
"artifactName": "${productName}-${os}-${version}-${arch}.${ext}",
"target": [
"deb"
],
"category": "Utility"
}
}

View File

@@ -0,0 +1,133 @@
## 便携式 JRE/JDK8 集成指南
本指南介绍如何将 JRE 8 以“便携式”(解压即用、无需安装)的方式随应用打包,并在 Electron 主进程中通过绝对路径调用,从而避免要求用户在系统中安装 JDK/JRE。
### 为什么选择便携式 JRE
- 无需管理员权限与系统环境变量配置,用户无感知。
- 不污染系统环境(不写入 JAVA_HOME/PATH
- 跨平台一致,可精简体积、可控版本。
### 适用场景
- 运行 Java 程序或 JAR 包(仅需运行时)。
- 不需要 javac/jcmd/jmap 等开发/诊断工具(若需要,请改为随包便携式 JDK
### 推荐的 JRE 8 发行版(可再分发)
- Azul Zulu 8 JRE可选 ZuluFX 含 JavaFX[下载页面](https://www.azul.com/downloads/?version=java-8-lts&package=jre)
- BellSoft Liberica 8 JREStandard/FullFull 含 JavaFX[下载页面](https://bell-sw.com/pages/downloads/#/java-8-lts)
- Eclipse Temurin 8 JREAdoptium[下载页面](https://adoptium.net/temurin/releases/?version=8)
选择要点:
- 需要 AWT/Swing/字体/打印 → 选择非 headless 包。
- 需要 JavaFX → 选择 Liberica Full 或 ZuluFX。
- 仅命令行/服务端 → 任意 JRE 8headless 也可)。
### 目录放置约定
将解压后的 JRE 放入项目的 `build/extraResources/jre`,保证内部存在 `bin/java(.exe)`
```
build/
extraResources/
jre/
bin/
java(.exe)
lib/
...
```
构建后在生产环境可通过 `process.resourcesPath` 访问:
`<app>/resources/extraResources/jre/bin/java(.exe)`
### 主进程调用示例
`electron/preload/lifecycle.js` 或你的业务模块中封装 Java 运行工具(开发/生产两种路径):
```js
const path = require('path');
const { spawn } = require('child_process');
function getExtraResourcesDir() {
// 开发态:使用项目目录;生产态:使用 asar/resources 目录
const isDev = !!process.env.EE_DEV || process.env.NODE_ENV === 'development';
return isDev
? path.join(process.cwd(), 'build', 'extraResources')
: path.join(process.resourcesPath, 'extraResources');
}
function getJavaBinPath() {
const extraDir = getExtraResourcesDir();
const javaBinName = process.platform === 'win32' ? 'java.exe' : 'java';
return path.join(extraDir, 'jre', 'bin', javaBinName);
}
function runJavaJar(jarAbsPath, args = [], options = {}) {
const javaPath = getJavaBinPath();
const child = spawn(javaPath, ['-jar', jarAbsPath, ...args], {
stdio: 'inherit',
...options,
});
return child;
}
async function ensureJavaVersion(logger) {
return new Promise((resolve) => {
const child = spawn(getJavaBinPath(), ['-version']);
let out = '';
let err = '';
child.stdout && child.stdout.on('data', (d) => (out += d.toString()))
child.stderr && child.stderr.on('data', (d) => (err += d.toString()))
child.on('close', () => {
const text = (out + '\n' + err).trim();
logger && logger.info('[java] version check:', text);
resolve(text.includes('1.8.0'));
});
});
}
module.exports = { getJavaBinPath, runJavaJar, ensureJavaVersion };
```
在生命周期中调用(示例):
```js
const { logger } = require('ee-core/log');
const path = require('path');
const { runJavaJar, ensureJavaVersion } = require('./java-runner');
class Lifecycle {
async ready() {
const ok = await ensureJavaVersion(logger);
if (!ok) {
logger.error('[java] 未检测到 JRE 8请检查 extraResources/jre 是否存在');
}
}
async windowReady() {
const jarPath = path.join(process.resourcesPath || process.cwd(), 'extraResources', 'tools', 'your-app.jar');
// 示例:延后在某业务时机再启动 Java 进程
// const proc = runJavaJar(jarPath, ['--arg1', 'value']);
}
}
```
注意:示例中的 `java-runner` 为上文工具函数文件,实际请按你的项目结构放置。
### 验证清单
- 运行 `jre/bin/java -version` 输出包含 `1.8.0_xxx`
- 若涉及 GUI/字体/打印,验证 AWT/Swing 中文渲染与打印。
- 若需 JavaFX验证 JavaFX Demo 启动。
- 若涉及 TLS/HTTPS验证 SSL 通信正常。
### 许可与合规
- Azul Zulu、Eclipse TemurinAdoptium、BellSoft Liberica 的 JRE/JDK 8 发行包均可免费再分发GPLv2+CE 或厂商许可证)。
- 建议在应用的“关于/许可证”中附上所选发行版的许可证链接与致谢。
### 常见问题
1) 是否“阉割”?
— 上述 JRE 8 发行版均为标准运行时通过兼容性测试JRE 不包含开发者工具属于正常区别,不是删减。
2) 何时需要 JDK 而不是 JRE
— 需要 `javac` 编译或 `jcmd/jmap` 等诊断工具,或你的 Java 组件依赖 `tools.jar` 时。
3) 体积如何优化?
— 选择 headless若无 GUI 需求)、去除无用语言/字体包;或改用 JDK 9+ 使用 jlink不适用于 8

436
doc/打包方案对比.md Normal file
View File

@@ -0,0 +1,436 @@
# 应用打包方案对比与实现
本文档详细说明 ElectronEgg 应用的两种打包方案:纯绿色版方案 和 双版本方案。
---
## 方案对比
| 特性 | 方案一:纯绿色版 | 方案二:双版本打包 |
|------|-----------------|-------------------|
| **打包产物** | 单个便携版 exe | 安装版 exe + 便携版 exe |
| **安装过程** | 无需安装 | 安装版需安装,便携版无需 |
| **桌面快捷方式** | 应用内自动创建 | 安装版自动创建,便携版手动或自动 |
| **开始菜单** | 无 | 安装版有 |
| **卸载程序** | 无(直接删除) | 安装版有 |
| **适用场景** | 临时使用、U盘携带 | 正式部署、企业分发 |
| **用户体验** | 灵活、轻量 | 专业、完整 |
| **打包时间** | 快 | 较慢(打包两次) |
| **分发复杂度** | 简单(单文件) | 中等(两个文件) |
---
## 方案一:纯绿色版 + 自动创建快捷方式
### 特点
- ✅ 单个 exe 文件,双击即用
- ✅ 首次启动时询问是否创建桌面快捷方式
- ✅ 无需安装,无需卸载
- ✅ 适合快速分发和临时使用
### 实现步骤
#### 1. 修改打包配置
**文件**[cmd/builder.json](../cmd/builder.json)
```json
{
"productName": "南京灿能工具",
"appId": "com.canneng.tool",
"copyright": "© 2025 hongawen",
"directories": {
"output": "out"
},
"asar": true,
"files": [
"**/*",
"!cmd/",
"!data/",
"!electron/",
"!frontend/",
"!logs/",
"!out/",
"!go/",
"!python/"
],
"extraResources": {
"from": "build/extraResources/",
"to": "extraResources"
},
"publish": [
{
"provider": "generic",
"url": "https://your-update-server.com"
}
],
"win": {
"icon": "build/icons/icon.ico",
"artifactName": "${productName}-${os}-${version}-${arch}.${ext}",
"target": [
{
"target": "portable"
}
]
}
}
```
#### 2. 添加自动创建快捷方式功能
**文件**[electron/preload/lifecycle.js](../electron/preload/lifecycle.js)
`windowReady()` 钩子中添加以下代码:
```javascript
const { logger } = require('ee-core/log');
const { getConfig } = require('ee-core/config');
const { getMainWindow } = require('ee-core/electron');
class Lifecycle {
async ready() {
logger.info('[lifecycle] ready');
}
async electronAppReady() {
logger.info('[lifecycle] electron-app-ready');
}
async windowReady() {
logger.info('[lifecycle] window-ready');
// 延迟加载,无白屏
const { windowsOption } = getConfig();
if (windowsOption.show == false) {
const win = getMainWindow();
win.once('ready-to-show', () => {
win.show();
win.focus();
})
}
// 绿色版自动创建桌面快捷方式
await this.createDesktopShortcut();
}
/**
* 为绿色版创建桌面快捷方式
*/
async createDesktopShortcut() {
const { app, dialog, shell } = require('electron');
const path = require('path');
const fs = require('fs');
// 判断是否为便携版(绿色版)
// 安装版通常在 C:\Program Files 或 AppData\Local\Programs
const isPortable = process.platform === 'win32' &&
!process.execPath.includes('Program Files') &&
!process.execPath.includes('AppData\\Local\\Programs');
if (!isPortable) {
logger.info('[lifecycle] 非便携版,跳过快捷方式创建');
return;
}
try {
const desktopPath = app.getPath('desktop');
const shortcutPath = path.join(desktopPath, '南京灿能工具.lnk');
// 如果快捷方式已存在,跳过
if (fs.existsSync(shortcutPath)) {
logger.info('[lifecycle] 桌面快捷方式已存在');
return;
}
// 询问用户是否创建快捷方式
const result = await dialog.showMessageBox({
type: 'question',
buttons: ['创建', '跳过'],
defaultId: 0,
title: '创建桌面快捷方式',
message: '是否在桌面创建快捷方式?',
detail: '方便您下次快速启动应用'
});
if (result.response === 0) {
// Windows 下创建快捷方式
const success = shell.writeShortcutLink(shortcutPath, {
target: process.execPath,
cwd: path.dirname(process.execPath),
description: '南京灿能C端工具',
icon: process.execPath,
iconIndex: 0
});
if (success) {
logger.info('[lifecycle] 桌面快捷方式创建成功');
await dialog.showMessageBox({
type: 'info',
title: '成功',
message: '桌面快捷方式已创建',
buttons: ['确定']
});
} else {
logger.error('[lifecycle] 桌面快捷方式创建失败');
}
} else {
logger.info('[lifecycle] 用户跳过创建快捷方式');
}
} catch (error) {
logger.error('[lifecycle] 创建快捷方式时出错:', error);
}
}
async beforeClose() {
logger.info('[lifecycle] before-close');
}
}
Lifecycle.toString = () => '[class Lifecycle]';
module.exports = {
Lifecycle
};
```
#### 3. 打包命令
```bash
npm run build # 完整构建
npm run build-w # 打包 Windows 便携版
```
#### 4. 产物说明
打包完成后,在 `out/` 目录下会生成:
```
out/
└── 南京灿能工具-win-4.0.0-x64.exe (便携版,约 150-200MB
```
---
## 方案二:双版本打包(安装版 + 便携版)
### 特点
- ✅ 提供两种版本供用户选择
- ✅ 安装版:专业、完整的安装体验
- ✅ 便携版:灵活、轻量,无需安装
- ✅ 适合正式产品发布
### 实现步骤
#### 1. 修改打包配置
**文件**[cmd/builder.json](../cmd/builder.json)
```json
{
"productName": "南京灿能工具",
"appId": "com.canneng.tool",
"copyright": "© 2025 hongawen",
"directories": {
"output": "out"
},
"asar": true,
"files": [
"**/*",
"!cmd/",
"!data/",
"!electron/",
"!frontend/",
"!logs/",
"!out/",
"!go/",
"!python/"
],
"extraResources": {
"from": "build/extraResources/",
"to": "extraResources"
},
"nsis": {
"oneClick": false,
"allowElevation": true,
"allowToChangeInstallationDirectory": true,
"installerIcon": "build/icons/icon.ico",
"uninstallerIcon": "build/icons/icon.ico",
"installerHeaderIcon": "build/icons/icon.ico",
"createDesktopShortcut": true,
"createStartMenuShortcut": true,
"shortcutName": "南京灿能工具",
"artifactName": "${productName}-Setup-${version}.${ext}"
},
"portable": {
"artifactName": "${productName}-Portable-${version}.${ext}"
},
"publish": [
{
"provider": "generic",
"url": "https://your-update-server.com"
}
],
"win": {
"icon": "build/icons/icon.ico",
"target": [
{
"target": "nsis",
"arch": ["x64"]
},
{
"target": "portable",
"arch": ["x64"]
}
]
}
}
```
#### 2. 便携版快捷方式功能(可选)
如果希望便携版也能自动创建快捷方式,使用**方案一**中的 `createDesktopShortcut()` 代码。
#### 3. 打包命令
```bash
npm run build # 完整构建
npm run build-w # 打包两个版本
```
#### 4. 产物说明
打包完成后,在 `out/` 目录下会生成:
```
out/
├── 南京灿能工具-Setup-4.0.0.exe (安装版,约 150MB
└── 南京灿能工具-Portable-4.0.0.exe (便携版,约 150-200MB
```
#### 5. 版本差异说明
**安装版 (NSIS)**
- 需要安装到系统(默认 C:\Program Files
- 自动创建桌面快捷方式
- 自动创建开始菜单项
- 提供卸载程序
- 支持自动更新
- 适合企业部署、长期使用
**便携版 (Portable)**
- 单个 exe 文件
- 双击直接运行(首次会自解压)
- 无需安装,无需卸载
- 可放在 U 盘随身携带
- 适合临时使用、测试环境
---
## 快捷方式创建原理(技术细节)
### Windows 快捷方式 (.lnk)
```javascript
shell.writeShortcutLink(shortcutPath, {
target: process.execPath, // 目标程序路径
cwd: path.dirname(process.execPath), // 工作目录
description: '应用描述', // 快捷方式描述
icon: process.execPath, // 图标路径
iconIndex: 0, // 图标索引
args: '', // 启动参数(可选)
appUserModelId: 'com.app.id' // Windows 应用 ID可选
})
```
### 判断是否为便携版
```javascript
const isPortable = process.platform === 'win32' &&
!process.execPath.includes('Program Files') &&
!process.execPath.includes('AppData\\Local\\Programs');
```
**原理**
- 安装版通常安装在 `C:\Program Files\YourApp\`
- 或者 `C:\Users\用户名\AppData\Local\Programs\YourApp\`
- 便携版可以在任意位置运行
---
## 推荐配置
### 企业级应用(推荐方案二)
```
✅ 提供两个版本
✅ 主推安装版(专业形象)
✅ 提供便携版作为备选
```
### 轻量工具(推荐方案一)
```
✅ 只提供便携版
✅ 应用内自动创建快捷方式
✅ 简化分发流程
```
---
## 常见问题
### Q1: 便携版首次启动为什么慢?
**A**: 便携版是自解压程序,首次运行需要解压资源到临时目录(约 3-5 秒)。后续启动会快很多。
### Q2: 便携版数据存储在哪里?
**A**:
- 用户数据:`C:\Users\用户名\AppData\Roaming\你的appId\`
- 临时文件:`C:\Users\用户名\AppData\Local\Temp\`
### Q3: 如何让便携版也支持自动更新?
**A**: 需要配置 `electron-updater`,但便携版更新体验不如安装版。建议:
- 安装版:使用自动更新
- 便携版:提示用户下载新版本
### Q4: 可以同时运行两个版本吗?
**A**: 不建议。虽然技术上可行,但会导致数据冲突(共享同一个 userData 目录)。
### Q5: 如何自定义快捷方式图标?
**A**: 在 `build/icons/` 目录放置 `.ico` 文件,并在 `builder.json` 中配置:
```json
"win": {
"icon": "build/icons/custom-icon.ico"
}
```
---
## 测试检查清单
打包完成后,请进行以下测试:
### 安装版测试
- [ ] 安装到默认路径成功
- [ ] 安装到自定义路径成功
- [ ] 桌面快捷方式正常
- [ ] 开始菜单项正常
- [ ] 应用启动正常
- [ ] 卸载程序正常
### 便携版测试
- [ ] 双击 exe 正常启动
- [ ] 首次启动自动创建快捷方式(如已实现)
- [ ] 桌面快捷方式可用
- [ ] 应用功能正常
- [ ] 关闭后再次启动正常
- [ ] 可移动到其他目录运行
---
## 参考资源
- electron-builder 官方文档: https://www.electron.build/
- NSIS 配置: https://www.electron.build/configuration/nsis
- Portable 配置: https://www.electron.build/configuration/portable
- Electron shell API: https://www.electronjs.org/docs/latest/api/shell
---
*文档创建时间: 2025-10-14*
*作者: hongawen*

459
doc/生命周期描述.md Normal file
View File

@@ -0,0 +1,459 @@
# ElectronEgg 生命周期详解
本文档详细说明 ElectronEgg 框架的应用生命周期机制及其在项目中的实现。
## 生命周期流程图
```
┌─────────────┐
│ new │ 创建 ElectronEgg 实例
└──────┬──────┘
┌──────▼──────┐
│ ready │ core app 加载完成ee-core 框架初始化)
└──────┬──────┘
┌──────▼─────────────┐
│ electronAppReady │ Electron app 加载完成
└──────┬─────────────┘
├─────────────────┐
│ │
┌──────▼──────┐ ┌─────▼────────┐
│ mainWindow │ │ windowReady │ 主窗口创建完成
└──────┬──────┘ └─────▲────────┘
│ │
└─────────────────┘
┌──────▼──────┐
│ running │ 应用运行中
└──────┬──────┘
┌──────▼──────┐
│beforeClose │ 退出之前触发
└──────┬──────┘
┌──────▼──────┐
│ quit │ 应用退出
└─────────────┘
```
## 生命周期钩子详解
### 1. new - 实例创建
**触发时机**:调用 `new ElectronEgg()`
**实现位置**[electron/main.js](electron/main.js#L6)
```javascript
const { ElectronEgg } = require('ee-core');
const app = new ElectronEgg();
```
**作用**
- 创建 ElectronEgg 应用实例
- 初始化框架核心模块
- 准备生命周期管理器
---
### 2. ready - 核心应用就绪
**触发时机**ee-core 框架加载完成Electron app 启动之前
**实现位置**
- 注册:[electron/main.js](electron/main.js#L10)
- 实现:[electron/preload/lifecycle.js](electron/preload/lifecycle.js#L12-L14)
```javascript
// 注册
app.register("ready", life.ready);
// 实现
async ready() {
logger.info('[lifecycle] ready');
// 在这里可以做:
// - 初始化数据库连接
// - 加载配置文件
// - 初始化全局变量
}
```
**适用场景**
- ✅ 初始化数据库连接
- ✅ 加载应用配置
- ✅ 注册全局服务
- ✅ 初始化日志系统
- ❌ 不能操作窗口(窗口还未创建)
---
### 3. electronAppReady - Electron 应用就绪
**触发时机**Electron 的 `app.ready` 事件触发后,主窗口创建之前
**实现位置**
- 注册:[electron/main.js](electron/main.js#L11)
- 实现:[electron/preload/lifecycle.js](electron/preload/lifecycle.js#L19-L21)
```javascript
// 注册
app.register("electron-app-ready", life.electronAppReady);
// 实现
async electronAppReady() {
logger.info('[lifecycle] electron-app-ready');
// 在这里可以做:
// - 注册全局快捷键
// - 设置应用菜单
// - 初始化托盘图标
// - 注册协议处理
}
```
**适用场景**
- ✅ 注册全局快捷键 (globalShortcut)
- ✅ 创建应用菜单 (Menu)
- ✅ 创建系统托盘 (Tray)
- ✅ 注册自定义协议 (protocol)
- ⚠️ 可以创建窗口,但通常在框架内部自动创建
---
### 4. mainWindow - 主窗口创建
**触发时机**:框架创建主窗口时(内部流程,不需要手动注册)
**说明**
- 这是框架内部自动执行的步骤
- 根据 `electron/config/config.*.js` 中的 `windowsOption` 配置创建窗口
- 窗口创建完成后会触发 `windowReady` 钩子
**配置示例**
```javascript
// electron/config/config.default.js
windowsOption: {
width: 1200,
height: 800,
show: false, // 设置为 false 可实现无白屏启动
webPreferences: {
contextIsolation: false,
nodeIntegration: true
}
}
```
---
### 5. windowReady - 窗口就绪
**触发时机**:主窗口创建完成,页面加载完毕
**实现位置**
- 注册:[electron/main.js](electron/main.js#L12)
- 实现:[electron/preload/lifecycle.js](electron/preload/lifecycle.js#L26-L37)
```javascript
// 注册
app.register("window-ready", life.windowReady);
// 实现
async windowReady() {
logger.info('[lifecycle] window-ready');
// 延迟显示窗口,避免白屏
const { windowsOption } = getConfig();
if (windowsOption.show == false) {
const win = getMainWindow();
win.once('ready-to-show', () => {
win.show(); // 显示窗口
win.focus(); // 聚焦窗口
})
}
// 在这里可以做:
// - 向渲染进程发送初始化数据
// - 检查更新
// - 加载用户配置
}
```
**适用场景**
- ✅ 操作主窗口 (show/hide/maximize 等)
- ✅ 向渲染进程发送消息
- ✅ 执行自动更新检查
- ✅ 加载用户数据并同步到前端
- ✅ 实现无白屏启动(配合 `show: false`
---
### 6. running - 应用运行中
**触发时机**:窗口显示后,应用正常运行期间
**说明**
- 这不是一个独立的生命周期钩子
- 表示应用的正常运行状态
- 此时所有功能都可用
**可用操作**
- IPC 通信(前后端交互)
- 业务逻辑处理
- 数据库操作
- 网络请求
- 文件系统操作
---
### 7. beforeClose - 关闭前钩子
**触发时机**:用户点击关闭按钮或调用 `app.quit()` 之前
**实现位置**
- 注册:[electron/main.js](electron/main.js#L13)
- 实现:[electron/preload/lifecycle.js](electron/preload/lifecycle.js#L42-L44)
```javascript
// 注册
app.register("before-close", life.beforeClose);
// 实现
async beforeClose() {
logger.info('[lifecycle] before-close');
// 在这里可以做:
// - 保存用户数据
// - 关闭数据库连接
// - 清理临时文件
// - 释放系统资源
// - 确认是否退出
}
```
**适用场景**
- ✅ 保存应用状态
- ✅ 关闭数据库连接
- ✅ 清理临时资源
- ✅ 询问用户是否确认退出
- ✅ 上传日志或统计数据
**阻止关闭示例**
```javascript
async beforeClose(args, event) {
const { dialog } = require('electron');
const result = await dialog.showMessageBox({
type: 'question',
buttons: ['取消', '退出'],
message: '确定要退出应用吗?'
});
if (result.response === 0) {
// 阻止关闭
return false;
}
// 允许关闭
return true;
}
```
---
### 8. quit - 应用退出
**触发时机**`beforeClose` 完成后,应用进程终止
**说明**
- 这是最终状态,不可逆
- 所有资源清理应在 `beforeClose` 中完成
- 退出后进程结束,无法执行代码
---
## 额外生命周期preload
### preload - 预加载模块
**触发时机**:应用启动时,在所有其他钩子之前
**实现位置**
- 注册:[electron/main.js](electron/main.js#L16)
- 实现:[electron/preload/index.js](electron/preload/index.js#L7-L9)
```javascript
// 注册
app.register("preload", preload);
// 实现
function preload() {
logger.info('[preload] load 1');
// 在这里可以做:
// - 加载环境变量
// - 注册原生模块
// - 设置全局异常处理
}
```
**适用场景**
- ✅ 加载环境变量
- ✅ 注册 Node.js 原生模块
- ✅ 设置全局错误处理
- ✅ 初始化第三方 SDK
---
## 实际开发示例
### 示例 1数据库初始化
```javascript
// electron/preload/lifecycle.js
const Database = require('better-sqlite3');
let db;
class Lifecycle {
async ready() {
// 在 ready 钩子中初始化数据库
const path = require('path');
const dbPath = path.join(app.getPath('userData'), 'app.db');
db = new Database(dbPath);
logger.info('[lifecycle] database initialized');
}
async beforeClose() {
// 在关闭前关闭数据库连接
if (db) {
db.close();
logger.info('[lifecycle] database closed');
}
}
}
```
### 示例 2自动更新检查
```javascript
// electron/preload/lifecycle.js
const { autoUpdater } = require('electron-updater');
class Lifecycle {
async windowReady() {
// 窗口就绪后检查更新
const { getMainWindow } = require('ee-core/electron');
const win = getMainWindow();
autoUpdater.checkForUpdates();
autoUpdater.on('update-available', () => {
win.webContents.send('update-available');
});
logger.info('[lifecycle] update check started');
}
}
```
### 示例 3托盘图标
```javascript
// electron/preload/lifecycle.js
const { Tray, Menu } = require('electron');
let tray;
class Lifecycle {
async electronAppReady() {
// 在 Electron 就绪后创建托盘
const path = require('path');
const iconPath = path.join(__dirname, '../../public/images/tray-icon.png');
tray = new Tray(iconPath);
const contextMenu = Menu.buildFromTemplate([
{ label: '打开主窗口', click: () => {
const { getMainWindow } = require('ee-core/electron');
getMainWindow().show();
}},
{ label: '退出', click: () => {
const { app } = require('electron');
app.quit();
}}
]);
tray.setContextMenu(contextMenu);
logger.info('[lifecycle] tray created');
}
}
```
### 示例 4无白屏启动
```javascript
// electron/config/config.default.js
windowsOption: {
width: 1200,
height: 800,
show: false, // 关键配置:初始不显示
backgroundColor: '#ffffff'
}
// electron/preload/lifecycle.js
class Lifecycle {
async windowReady() {
// 页面加载完成后再显示,避免白屏
const { getMainWindow } = require('ee-core/electron');
const win = getMainWindow();
win.once('ready-to-show', () => {
win.show();
win.focus();
});
}
}
```
---
## 生命周期执行顺序总结
```
1. preload() # 预加载模块
2. new ElectronEgg() # 创建应用实例
3. ready() # 核心框架就绪
4. electronAppReady() # Electron 就绪
5. [创建主窗口] # 框架内部创建窗口
6. windowReady() # 窗口就绪
7. [应用运行中] # 正常运行状态
8. beforeClose() # 关闭前钩子
9. [应用退出] # 进程结束
```
---
## 注意事项
1. **异步支持**:所有生命周期钩子都支持 `async/await`,可以执行异步操作
2. **错误处理**:建议在每个钩子中添加 try-catch 错误处理
3. **顺序依赖**:不要在早期钩子中访问尚未初始化的资源(如在 `ready` 中操作窗口)
4. **性能优化**:避免在钩子中执行耗时操作,会阻塞应用启动
5. **资源清理**:在 `beforeClose` 中务必清理所有资源,避免内存泄漏
6. **日志记录**:建议在每个钩子中记录日志,方便排查问题
---
## 相关文件
- 生命周期注册:[electron/main.js](electron/main.js)
- 生命周期实现:[electron/preload/lifecycle.js](electron/preload/lifecycle.js)
- 预加载模块:[electron/preload/index.js](electron/preload/index.js)
- 窗口配置:[electron/config/config.default.js](electron/config/config.default.js)
---
## 参考资源
- ElectronEgg 官方文档https://www.kaka996.com/pages/987b1c/
- Electron 官方文档https://www.electronjs.org/zh/docs/latest/

View File

@@ -1,11 +0,0 @@
#### 基础配置
主进程的配置文件在./config文件夹下
```shell
# 说明
bin.js // 开发配置
config.default.js // 默认配置文件,开发环境和生产环境都会加载
config.local.js // 开发环境配置文件追加和覆盖default配置文件
config.prod.js // 生产环境配置文件追加和覆盖default配置文件
nodemon.json // 开发环境,代码(监控)热加载
builder.json // 打包配置
```

View File

@@ -1,170 +0,0 @@
const { app: electronApp } = require('electron');
const { autoUpdater } = require("electron-updater");
const is = require('ee-core/utils/is');
const Log = require('ee-core/log');
const Conf = require('ee-core/config');
const CoreWindow = require('ee-core/electron/window');
const Electron = require('ee-core/electron');
/**
* 自动升级插件
* @class
*/
class AutoUpdaterAddon {
constructor() {
}
/**
* 创建
*/
create () {
Log.info('[addon:autoUpdater] load');
const cfg = Conf.getValue('addons.autoUpdater');
if ((is.windows() && cfg.windows)
|| (is.macOS() && cfg.macOS)
|| (is.linux() && cfg.linux))
{
// continue
} else {
return
}
// 是否检查更新
if (cfg.force) {
this.checkUpdate();
}
const status = {
error: -1,
available: 1,
noAvailable: 2,
downloading: 3,
downloaded: 4,
}
const version = electronApp.getVersion();
Log.info('[addon:autoUpdater] current version: ', version);
// 设置下载服务器地址
let server = cfg.options.url;
let lastChar = server.substring(server.length - 1);
server = lastChar === '/' ? server : server + "/";
//Log.info('[addon:autoUpdater] server: ', server);
cfg.options.url = server;
// 是否后台自动下载
autoUpdater.autoDownload = cfg.force ? true : false;
try {
autoUpdater.setFeedURL(cfg.options);
} catch (error) {
Log.error('[addon:autoUpdater] setFeedURL error : ', error);
}
autoUpdater.on('checking-for-update', () => {
//sendStatusToWindow('正在检查更新...');
})
autoUpdater.on('update-available', (info) => {
info.status = status.available;
info.desc = '有可用更新';
this.sendStatusToWindow(info);
})
autoUpdater.on('update-not-available', (info) => {
info.status = status.noAvailable;
info.desc = '没有可用更新';
this.sendStatusToWindow(info);
})
autoUpdater.on('error', (err) => {
let info = {
status: status.error,
desc: err
}
this.sendStatusToWindow(info);
})
autoUpdater.on('download-progress', (progressObj) => {
let percentNumber = parseInt(progressObj.percent);
let totalSize = this.bytesChange(progressObj.total);
let transferredSize = this.bytesChange(progressObj.transferred);
let text = '已下载 ' + percentNumber + '%';
text = text + ' (' + transferredSize + "/" + totalSize + ')';
let info = {
status: status.downloading,
desc: text,
percentNumber: percentNumber,
totalSize: totalSize,
transferredSize: transferredSize
}
Log.info('[addon:autoUpdater] progress: ', text);
this.sendStatusToWindow(info);
})
autoUpdater.on('update-downloaded', (info) => {
info.status = status.downloaded;
info.desc = '下载完成';
this.sendStatusToWindow(info);
// 托盘插件默认会阻止窗口关闭,这里设置允许关闭窗口
Electron.extra.closeWindow = true;
autoUpdater.quitAndInstall();
// const mainWindow = CoreWindow.getMainWindow();
// if (mainWindow) {
// mainWindow.destroy()
// }
// electronApp.appQuit()
});
}
/**
* 检查更新
*/
checkUpdate () {
autoUpdater.checkForUpdates();
}
/**
* 下载更新
*/
download () {
autoUpdater.downloadUpdate();
}
/**
* 向前端发消息
*/
sendStatusToWindow(content = {}) {
const textJson = JSON.stringify(content);
const channel = 'app.updater';
const win = CoreWindow.getMainWindow();
win.webContents.send(channel, textJson);
}
/**
* 单位转换
*/
bytesChange (limit) {
let size = "";
if(limit < 0.1 * 1024){
size = limit.toFixed(2) + "B";
}else if(limit < 0.1 * 1024 * 1024){
size = (limit/1024).toFixed(2) + "KB";
}else if(limit < 0.1 * 1024 * 1024 * 1024){
size = (limit/(1024 * 1024)).toFixed(2) + "MB";
}else{
size = (limit/(1024 * 1024 * 1024)).toFixed(2) + "GB";
}
let sizeStr = size + "";
let index = sizeStr.indexOf(".");
let dou = sizeStr.substring(index + 1 , index + 3);
if(dou == "00"){
return sizeStr.substring(0, index) + sizeStr.substring(index + 3, index + 5);
}
return size;
}
}
AutoUpdaterAddon.toString = () => '[class AutoUpdaterAddon]';
module.exports = AutoUpdaterAddon;

View File

@@ -1,67 +0,0 @@
const { app: electronApp } = require('electron');
const Log = require('ee-core/log');
const Conf = require('ee-core/config');
/**
* 唤醒插件
* @class
*/
class AwakenAddon {
constructor() {
this.protocol = '';
}
/**
* 创建
*/
create () {
Log.info('[addon:awaken] load');
const cfg = Conf.getValue('addons.awaken');
this.protocol = cfg.protocol;
electronApp.setAsDefaultProtocolClient(this.protocol);
this.handleArgv(process.argv);
electronApp.on('second-instance', (event, argv) => {
if (process.platform === 'win32') {
this.handleArgv(argv)
}
})
// 仅用于macOS
electronApp.on('open-url', (event, urlStr) => {
this.handleUrl(urlStr)
})
}
/**
* 参数处理
*/
handleArgv(argv) {
const offset = electronApp.isPackaged ? 1 : 2;
const url = argv.find((arg, i) => i >= offset && arg.startsWith(this.protocol));
this.handleUrl(url)
}
/**
* url解析
*/
handleUrl(awakeUrlStr) {
if (!awakeUrlStr || awakeUrlStr.length === 0) {
return
}
const {hostname, pathname, search} = new URL(awakeUrlStr);
let awakeUrlInfo = {
urlStr: awakeUrlStr,
urlHost: hostname,
urlPath: pathname,
urlParams: search && search.slice(1)
}
Log.info('[addon:awaken] awakeUrlInfo:', awakeUrlInfo);
}
}
AwakenAddon.toString = () => '[class AwakenAddon]';
module.exports = AwakenAddon;

View File

@@ -1,94 +0,0 @@
const { app, session } = require('electron');
const _ = require('lodash');
const fs = require('fs');
const path = require('path');
const Log = require('ee-core/log');
/**
* 扩展插件 electron自身对该功能并不完全支持官方也不建议使用
* @class
*/
class ChromeExtensionAddon {
constructor() {
}
/**
* 创建
*/
async create () {
Log.info('[addon:chromeExtension] load');
const extensionIds = this.getAllIds();
for (let i = 0; i < extensionIds.length; i++) {
await this.load(extensionIds[i]);
}
}
/**
* 获取扩展id列表crx解压后的目录名即是该扩展的id
*/
getAllIds () {
const extendsionDir = this.getDirectory();
const ids = this.getDirs(extendsionDir);
return ids;
}
/**
* 扩展所在目录
*/
getDirectory () {
let extensionDirPath = '';
let variablePath = 'build'; // 打包前路径
if (app.isPackaged) {
variablePath = '..'; // 打包后路径
}
extensionDirPath = path.join(app.getAppPath(), variablePath, "extraResources", "chromeExtension");
return extensionDirPath;
}
/**
* 加载扩展
*/
async load (extensionId = '') {
if (_.isEmpty(extensionId)) {
return false
}
try {
const extensionPath = path.join(this.getDirectory(), extensionId);
Log.info('[addon:chromeExtension] extensionPath:', extensionPath);
await session.defaultSession.loadExtension(extensionPath, { allowFileAccess: true });
} catch (e) {
Log.info('[addon:chromeExtension] load extension error extensionId:%s, errorInfo:%s', extensionId, e.toString());
return false
}
return true
}
/**
* 获取目录下所有文件夹
*/
getDirs(dir) {
if (!dir) {
return [];
}
const components = [];
const files = fs.readdirSync(dir);
files.forEach(function(item, index) {
const stat = fs.lstatSync(dir + '/' + item);
if (stat.isDirectory() === true) {
components.push(item);
}
});
return components;
};
}
ChromeExtensionAddon.toString = () => '[class ChromeExtensionAddon]';
module.exports = ChromeExtensionAddon;

View File

@@ -1,33 +0,0 @@
const Log = require('ee-core/log');
const EE = require('ee-core/ee');
/**
* 安全插件
* @class
*/
class SecurityAddon {
constructor() {
}
/**
* 创建
*/
create () {
Log.info('[addon:security] load');
const { CoreApp } = EE;
const runWithDebug = process.argv.find(function(e){
let isHasDebug = e.includes("--inspect") || e.includes("--inspect-brk") || e.includes("--remote-debugging-port");
return isHasDebug;
})
// 不允许远程调试
if (runWithDebug) {
Log.error('[error] Remote debugging is not allowed, runWithDebug:', runWithDebug);
CoreApp.appQuit();
}
}
}
SecurityAddon.toString = () => '[class SecurityAddon]';
module.exports = SecurityAddon;

View File

@@ -1,72 +0,0 @@
const { Tray, Menu } = require('electron');
const path = require('path');
const Ps = require('ee-core/ps');
const Log = require('ee-core/log');
const Electron = require('ee-core/electron');
const CoreWindow = require('ee-core/electron/window');
const Conf = require('ee-core/config');
const EE = require('ee-core/ee');
/**
* 托盘插件
* @class
*/
class TrayAddon {
constructor() {
this.tray = null;
}
/**
* 创建托盘
*/
create () {
// 开发环境,代码热更新开启时,会导致托盘中有残影
if (Ps.isDev() && Ps.isHotReload()) return;
Log.info('[addon:tray] load');
const { CoreApp } = EE;
const cfg = Conf.getValue('addons.tray');
const mainWindow = CoreWindow.getMainWindow();
// 托盘图标
let iconPath = path.join(Ps.getHomeDir(), cfg.icon);
// 托盘菜单功能列表
let trayMenuTemplate = [
{
label: '显示',
click: function () {
mainWindow.show();
}
},
{
label: '退出',
click: function () {
CoreApp.appQuit();
}
}
]
// 点击关闭,最小化到托盘
mainWindow.on('close', (event) => {
if (Electron.extra.closeWindow == true) {
return;
}
mainWindow.hide();
event.preventDefault();
});
// 实例化托盘
this.tray = new Tray(iconPath);
this.tray.setToolTip(cfg.title);
const contextMenu = Menu.buildFromTemplate(trayMenuTemplate);
this.tray.setContextMenu(contextMenu);
this.tray.on('double-click', () => {
mainWindow.show()
})
}
}
TrayAddon.toString = () => '[class TrayAddon]';
module.exports = TrayAddon;

View File

@@ -1,108 +0,0 @@
/**
* ee-bin 配置
* 仅适用于开发环境
*/
module.exports = {
/**
* development serve ("frontend" "electron" )
* ee-bin dev
*/
dev: {
frontend: {
directory: './frontend',
cmd: 'npm',
args: ['run', 'dev'],
protocol: 'http://',
hostname: 'localhost',
port: 18091,
indexPath: 'index.html'
},
electron: {
directory: './',
cmd: 'electron',
args: ['.', '--env=local', '--color=always'], // --env: local|prod; '--color=always' 控制台颜色
}
},
/**
* 构建
* ee-bin build
*/
build: {
frontend: {
directory: './frontend',
cmd: 'npm',
args: ['run', 'build'],
}
},
/**
* 移动资源
* ee-bin move
*/
move: {
frontend_dist: {
dist: './frontend/dist',
target: './public/dist'
}
},
/**
* 预发布模式prod
* ee-bin start
*/
start: {
directory: './',
cmd: 'electron',
args: ['.', '--env=prod']
},
/**
* 加密
*/
encrypt: {
// confusion - 压缩混淆加密
// bytecode - 字节码加密
// strict - 先混淆加密,然后字节码加密
type: 'confusion',
// 文件匹配
// ! 符号开头的意思是过滤
files: [
'electron/**/*.(js|json)',
'!electron/config/encrypt.js',
'!electron/config/nodemon.json',
'!electron/config/builder.json',
'!electron/config/bin.json',
],
// 需要加密的文件后缀暂时只支持js
fileExt: ['.js'],
// 混淆加密配置
confusionOptions: {
// 压缩成一行
compact: true,
// 删除字符串文字并将其放置在一个特殊数组中
stringArray: true,
// 对stringArray的所有字符串文字进行编码'none' | 'base64' | 'rc4'
stringArrayEncoding: ['none'],
// 注入死代码,注:影响性能
deadCodeInjection: false,
}
},
/**
* 执行自定义命令
* ee-bin exec
*/
exec: {
node_v: {
directory: './',
cmd: 'node',
args: ['-v'],
},
npm_v: {
directory: './',
cmd: 'npm',
args: ['-v'],
},
},
};

View File

@@ -1,185 +1,71 @@
'use strict';
const path = require('path');
const {getBaseDir} = require('ee-core/ps');
/**
* 默认配置
*/
module.exports = (appInfo) => {
const config = {};
/**
* 开发者工具
*/
config.openDevTools = false;
/**
* 应用程序顶部菜单
*/
config.openAppMenu = true;
/**
* 1.507
* 主窗口
*/
config.windowsOption = {
title: '自动检测平台',
width: 1600,
height: 950,
minWidth: 1600,
minHeight: 950,
webPreferences: {
//webSecurity: false,
contextIsolation: false, // false -> 可在渲染进程中使用electron的apitrue->需要bridge.js(contextBridge)
nodeIntegration: true,
//preload: path.join(appInfo.baseDir, 'preload', 'bridge.js'),
},
frame: true,
show: false,
icon: path.join(appInfo.home, 'public', 'images', 'logo-32.png'),
};
/**
* ee框架日志
*/
config.logger = {
encoding: 'utf8',
level: 'INFO',
outputJSON: false,
buffer: true,
enablePerformanceTimer: false,
rotator: 'day',
appLogName: 'pqs9100.log',
coreLogName: 'pqs9100-core.log',
errorLogName: 'pqs9100-error.log'
}
/**
* 远程模式-web地址
*/
config.remoteUrl = {
enable: false,
url: 'http://electron-egg.kaka996.com/'
};
/**
* 内置socket服务
*/
config.socketServer = {
enable: false, // 是否开启
port: 7070,// 默认端口
path: "/socket.io/", // 默认路径名称
connectTimeout: 45000, // 客户端连接超时时间
pingTimeout: 30000, // 心跳检测超时时间
pingInterval: 25000, // 心跳检测间隔时间
maxHttpBufferSize: 1e8, // 每条消息的数据最大值
transports: ["polling", "websocket"], // http轮询和websocket
cors: {
origin: true, // http协议时需要设置允许跨域
},
channel: 'c1' // 默认频道c1可以自定义
};
/**
* 内置http服务
*/
config.httpServer = {
enable: false,
https: {
enable: false,
key: '/public/ssl/localhost+1.key',
cert: '/public/ssl/localhost+1.pem'
},
host: '127.0.0.1',
port: 7071,
cors: {
origin: "*" // 默认允许跨域
},
body: {
multipart: true,
formidable: {
keepExtensions: true
}
},
filterRequest: {
uris: [
'favicon.ico' // 默认过滤的uri favicon.ico
],
returnData: ''
module.exports = () => {
return {
openDevTools: false,
singleLock: true,
windowsOption: {
title: 'NPQS9100-自动检测平台',
menuBarVisible: false,
width: 1920,
height: 1000,
minWidth: 1024,
minHeight: 640,
webPreferences: {
//webSecurity: false,
contextIsolation: false, // false -> 可在渲染进程中使用electron的apitrue->需要bridge.js(contextBridge)
nodeIntegration: true,
//preload: path.join(getElectronDir(), 'preload', 'bridge.js'),
},
frame: true,
show: true,
icon: path.join(getBaseDir(), 'public', 'images', 'logo-32.png'),
},
logger: {
level: 'INFO',
outputJSON: false,
appLogName: '9100.log',
coreLogName: '9100-core.log',
errorLogName: '9100-error.log'
},
// 远程web地址
remote: {
enable: false,
url: ''
},
socketServer: {
enable: false,
port: 7070,
path: "/socket.io/",
connectTimeout: 45000,
pingTimeout: 30000,
pingInterval: 25000,
maxHttpBufferSize: 1e8,
transports: ["polling", "websocket"],
cors: {
origin: true,
},
channel: 'socket-channel'
},
httpServer: {
enable: false,
https: {
enable: false,
key: '/public/ssl/localhost+1.key',
cert: '/public/ssl/localhost+1.pem'
},
host: '127.0.0.1',
port: 7071,
},
mainServer: {
indexPath: '/public/dist/index.html',
channelSeparator: '/',
}
}
};
/**
* 主进程
*/
config.mainServer = {
protocol: 'file://',
indexPath: '/public/dist/index.html',
};
/**
* 硬件加速
*/
config.hardGpu = {
enable: true
};
/**
* 异常捕获
*/
config.exception = {
mainExit: false, // 主进程退出时是否捕获异常
childExit: true,
rendererExit: true,
};
/**
* jobs
*/
config.jobs = {
messageLog: true // 是否打印进程间通信的消息log
};
/**
* 插件功能
* @param window 官方内置插件
* @param tray 托盘插件
* @param security 安全插件
* @param awaken 唤醒插件
* @param autoUpdater 自动升级插件
*/
config.addons = {
window: {
enable: true,
},
tray: {
enable: true,
title: '自动检测平台',
icon: '/public/images/tray.png'
},
security: {
enable: true,
},
awaken: {
enable: true,
protocol: 'ee',
args: []
},
autoUpdater: {
enable: true,
windows: false,
macOS: false,
linux: false,
options: {
provider: 'generic',
url: 'http://kodo.qiniu.com/'
},
force: false,
}
};
return {
...config
};
}

View File

@@ -1,31 +1,13 @@
'use strict';
/**
* 开发环境配置,覆盖 config.default.js
* Development environment configuration, coverage config.default.js
*/
module.exports = (appInfo) => {
const config = {};
/**
* 开发者工具
*/
config.openDevTools = {
mode: 'undocked'
};
/**
* 应用程序顶部菜单
*/
config.openAppMenu = false;
/**
* jobs
*/
config.jobs = {
messageLog: true
};
module.exports = () => {
return {
...config
openDevTools: false,
jobs: {
messageLog: false
}
};
};

View File

@@ -1,29 +1,10 @@
'use strict';
/**
* 生产环境配置,覆盖 config.default.js
* coverage config.default.js
*/
module.exports = (appInfo) => {
const config = {};
/**
* 开发者工具
*/
config.openDevTools = true;
/**
* 应用程序顶部菜单
*/
config.openAppMenu = false;
/**
* jobs
*/
config.jobs = {
messageLog: false
};
module.exports = () => {
return {
...config
openDevTools: false,
};
};

View File

@@ -1,13 +0,0 @@
{
"watch": [
"electron/",
"main.js"
],
"ignore": [],
"ext": "js,json",
"verbose": true,
"exec": "electron . --env=local --hot-reload=1",
"restartable": "hr",
"colours": true,
"events": {}
}

View File

@@ -1,19 +1,13 @@
'use strict';
const { Controller } = require('ee-core');
const Log = require('ee-core/log');
const Services = require('ee-core/services');
const { logger } = require('ee-core/log');
const { exampleService } = require('../service/example');
/**
* example
* @class
*/
class ExampleController extends Controller {
constructor(ctx) {
super(ctx);
}
class ExampleController {
/**
* 所有方法接收两个参数
@@ -25,12 +19,12 @@ class ExampleController extends Controller {
* test
*/
async test () {
const result = await Services.get('example').test('electron');
Log.info('service result:', result);
const result = await exampleService.test('electron');
logger.info('service result:', result);
return 'hello electron-egg';
}
}
ExampleController.toString = () => '[class ExampleController]';
module.exports = ExampleController;
module.exports = ExampleController;

View File

@@ -1,50 +0,0 @@
const { Application } = require('ee-core');
class Index extends Application {
constructor() {
super();
// this === eeApp;
}
/**
* core app have been loaded
*/
async ready () {
// do some things
}
/**
* electron app ready
*/
async electronAppReady () {
// do some things
}
/**
* main window have been loaded
*/
async windowReady () {
// do some things
// 延迟加载,无白屏
const winOpt = this.config.windowsOption;
if (winOpt.show == false) {
const win = this.electron.mainWindow;
win.once('ready-to-show', () => {
win.show();
win.focus();
})
}
}
/**
* before app close
*/
async beforeClose () {
// do some things
}
}
Index.toString = () => '[class Index]';
module.exports = Index;

View File

@@ -1,5 +0,0 @@
const Log = require('ee-core/log');
exports.welcome = function () {
Log.info('[child-process] [jobs/example/hello] welcome ! ');
}

View File

@@ -1,29 +0,0 @@
const Job = require('ee-core/jobs/baseJobClass');
const Log = require('ee-core/log');
const Ps = require('ee-core/ps');
/**
* example - TimerJob
* @class
*/
class TimerJob extends Job {
constructor(params) {
super();
this.params = params;
}
/**
* handle()方法是必要的,且会被自动调用
*/
async handle () {
Log.info("[child-process] TimerJob params: ", this.params);
if (Ps.isChildJob()) {
Ps.exit();
}
}
}
TimerJob.toString = () => '[class TimerJob]';
module.exports = TimerJob;

19
electron/main.js Normal file
View File

@@ -0,0 +1,19 @@
const { ElectronEgg } = require('ee-core');
const { Lifecycle } = require('./preload/lifecycle');
const { preload } = require('./preload');
// new app
const app = new ElectronEgg();
// register lifecycle
const life = new Lifecycle();
app.register("ready", life.ready);
app.register("electron-app-ready", life.electronAppReady);
app.register("window-ready", life.windowReady);
app.register("before-close", life.beforeClose);
// register preload
app.register("preload", preload);
// run
app.run();

View File

@@ -1,14 +1,16 @@
/*************************************************
** preload为预加载模块该文件将会在程序启动时加载 **
*************************************************/
const Addon = require('ee-core/addon');
/**
* 预加载模块入口
*/
module.exports = async () => {
// 示例功能模块,可选择性使用和修改
Addon.get('tray').create();
Addon.get('security').create();
}
const { logger } = require('ee-core/log');
function preload() {
logger.info('[preload] load 1');
}
/**
* 预加载模块入口
*/
module.exports = {
preload
}

View File

@@ -0,0 +1,58 @@
'use strict';
const { logger } = require('ee-core/log');
const { getConfig } = require('ee-core/config');
const { getMainWindow } = require('ee-core/electron');
class Lifecycle {
/**
* core app have been loaded
*/
async ready() {
logger.info('[lifecycle] ready');
// 在这里可以做:
// - 初始化数据库连接
// - 加载配置文件
// - 初始化全局变量
}
/**
* electron app ready
*/
async electronAppReady() {
logger.info('[lifecycle] electron-app-ready');
}
/**
* main window have been loaded
*/
async windowReady() {
logger.info('[lifecycle] window-ready');
// 延迟加载,无白屏
const win = getMainWindow();
const { windowsOption } = getConfig();
if (windowsOption.show == false) {
win.once('ready-to-show', () => {
win.show();
win.focus();
})
} else {
win.show();
win.focus();
}
}
/**
* before app close
*/
async beforeClose() {
logger.info('[lifecycle] before-close');
}
}
Lifecycle.toString = () => '[class Lifecycle]';
module.exports = {
Lifecycle
};

View File

@@ -1,16 +1,12 @@
'use strict';
const { Service } = require('ee-core');
const { logger } = require('ee-core/log');
/**
* 示例服务service层为单例
* 示例服务
* @class
*/
class ExampleService extends Service {
constructor(ctx) {
super(ctx);
}
class ExampleService {
/**
* test
@@ -21,9 +17,14 @@ class ExampleService extends Service {
params: args
}
logger.info('ExampleService obj:', obj);
return obj;
}
}
ExampleService.toString = () => '[class ExampleService]';
module.exports = ExampleService;
module.exports = {
ExampleService,
exampleService: new ExampleService()
};

View File

@@ -19,6 +19,9 @@ VITE_API_URL=/api
# 开发环境跨域代理,支持配置多个
VITE_PROXY=[["/api","http://192.168.1.124:18092/"]]
#VITE_PROXY=[["/api","http://127.0.0.1:18092/"]]
VITE_PROXY=[["/api","http://192.168.1.125:18092/"]]
#VITE_PROXY=[["/api","http://192.168.1.125:18092/"]]
# VITE_PROXY=[["/api","http://192.168.1.138:8080/"]]张文
# 开启激活验证
VITE_ACTIVATE_OPEN=false

View File

@@ -22,4 +22,7 @@ VITE_DROP_CONSOLE=true
VITE_PWA=true
# 线上环境接口地址
#VITE_API_URL="/api" # 打包时用
VITE_API_URL="http://192.168.1.125:18092/"
# 开启激活验证
VITE_ACTIVATE_OPEN=false

11
frontend/.prettierrc Normal file
View File

@@ -0,0 +1,11 @@
{
"singleQuote": true,
"trailingComma": "none",
"tabWidth": 4,
"printWidth": 120,
"useTabs": false,
"semi": false,
"arrowParens": "avoid",
"endOfLine": "lf",
"htmlWhitespaceSensitivity": "ignore"
}

View File

@@ -14,7 +14,10 @@
},
"dependencies": {
"@element-plus/icons-vue": "^2.3.1",
"@types/event-source-polyfill": "^1.0.5",
"@vue-flow/core": "^1.45.0",
"@vueuse/core": "^10.4.1",
"autofit.js": "^3.2.8",
"axios": "^1.7.3",
"crypto-js": "^4.2.0",
"dayjs": "^1.11.9",
@@ -22,9 +25,12 @@
"echarts": "^5.4.3",
"echarts-liquidfill": "^3.1.0",
"element-plus": "^2.7.8",
"event-source-polyfill": "^1.0.31",
"html2canvas": "^1.4.1",
"md5": "^2.3.0",
"mitt": "^3.0.1",
"mkdirp": "^3.0.1",
"node-forge": "^1.3.1",
"nprogress": "^0.2.0",
"pinia": "^2.2.1",
"pinia-plugin-persistedstate": "^3.2.1",
@@ -41,11 +47,14 @@
"devDependencies": {
"@rushstack/eslint-patch": "^1.8.0",
"@tsconfig/node20": "^20.1.4",
"@types/html2canvas": "^1.0.0",
"@types/md5": "^2.3.2",
"@types/node": "^20.14.14",
"@types/node-forge": "^1.3.11",
"@types/nprogress": "^0.2.0",
"@types/qs": "^6.9.8",
"@types/sortablejs": "^1.15.2",
"@types/xlsx": "^0.0.36",
"@typescript-eslint/eslint-plugin": "^6.7.0",
"@typescript-eslint/parser": "^6.7.0",
"@vitejs/plugin-vue": "^5.0.5",
@@ -67,11 +76,11 @@
"unplugin-auto-import": "^0.18.3",
"unplugin-vue-components": "^0.27.4",
"unplugin-vue-setup-extend-plus": "^1.0.0",
"vite": "^5.3.1",
"vite": "^5.4.19",
"vite-plugin-compression": "^0.5.1",
"vite-plugin-eslint": "^1.8.1",
"vite-plugin-html": "^3.2.0",
"vite-plugin-node-polyfills": "^0.22.0",
"vite-plugin-node-polyfills": "^0.24.0",
"vite-plugin-pwa": "^0.16.5",
"vite-plugin-svg-icons": "^2.0.1",
"vue-tsc": "^2.0.21"

View File

@@ -1,27 +1,24 @@
<template>
<!--element-plus语言国际化全局修改为中文-->
<el-config-provider
:locale='locale'
:size='assemblySize'
:button='buttonConfig'
>
<router-view />
</el-config-provider>
<!--element-plus语言国际化全局修改为中文-->
<el-config-provider :locale="locale" :size="assemblySize" :button="buttonConfig">
<router-view />
</el-config-provider>
</template>
<script lang='ts' setup>
defineOptions({
name: 'App',
})
<script lang="ts" setup>
import { useI18n } from 'vue-i18n'
import { getBrowserLang } from '@/utils'
import { useTheme } from '@/hooks/useTheme'
import { ElConfigProvider } from 'element-plus'
import { LanguageType } from './stores/interface'
import { type LanguageType } from './stores/interface'
import { useGlobalStore } from '@/stores/modules/global'
import en from 'element-plus/es/locale/lang/en'
import zhCn from 'element-plus/es/locale/lang/zh-cn'
defineOptions({
name: 'App'
})
const globalStore = useGlobalStore()
// init theme
@@ -31,16 +28,24 @@ initTheme()
// init language
const i18n = useI18n()
onMounted(() => {
const language = globalStore.language ?? getBrowserLang()
i18n.locale.value = language
globalStore.setGlobalState('language', language as LanguageType)
const language = globalStore.language ?? getBrowserLang()
i18n.locale.value = language
globalStore.setGlobalState('language', language as LanguageType)
// 移除 autofit使用 CSS 自适应
// autofit.init({
// el: '#app',
// dw: 1440,
// dh: 900,
// resize: true,
// limit: 0.1
// })
})
// element language
const locale = computed(() => {
if (globalStore.language == 'zh') return zhCn
if (globalStore.language == 'en') return en
return getBrowserLang() == 'zh' ? zhCn : en
if (globalStore.language == 'zh') return zhCn
if (globalStore.language == 'en') return en
return getBrowserLang() == 'zh' ? zhCn : en
})
// element assemblySize
@@ -51,4 +56,10 @@ const buttonConfig = reactive({ autoInsertSpace: false })
document.getElementById('loadingPage')?.remove()
</script>
<style scoped></style>
<style scoped>
#app {
width: 100vw;
height: 100vh;
overflow: hidden;
}
</style>

View File

@@ -0,0 +1,14 @@
import http from '@/api'
import type { Activate } from '@/api/activate/interface'
export const generateApplicationCode = (params: Activate.ApplicationCodePlaintext) => {
return http.post(`/activate/generateApplicationCode`, params)
}
export const verifyActivationCode = (activationCode: string) => {
return http.post(`/activate/verifyActivationCode`, { activationCode })
}
export const getLicense = () => {
return http.post(`/activate/getLicense`)
}

View File

@@ -0,0 +1,55 @@
//激活模块
export namespace Activate {
export interface ApplicationModule {
/**
* 是否申请 1是 0否
*/
apply: number;
}
export interface ActivateModule extends ApplicationModule {
/**
* 是否永久 1是 0否
*/
permanently: number;
}
export interface ApplicationCodePlaintext {
/**
* 模拟式模块
*/
simulate: ApplicationModule;
/**
* 数字式模块
*/
digital: ApplicationModule;
/**
* 比对式模块
*/
contrast: ApplicationModule;
}
export interface ActivationCodePlaintext {
/**
* 模拟式模块
*/
simulate: ActivateModule;
/**
* 数字式模块
*/
digital: ActivateModule;
/**
* 比对式模块
*/
contrast: ActivateModule;
}
}

View File

@@ -1,112 +1,158 @@
export namespace CheckData {
export interface DataCheck {
scriptName: string,
errorSysName: string,
dataRule: string,
deviceName: string,
chnNum: string,
scriptName: string
errorSysId: string
dataRule: string
deviceName: string
chnNum: string
deviceId: string
num?: string | number
}
export interface PhaseCheckResult {
// 检测源定值-标准值
resultData: number,
resultData: number
// 装置原始数据-被检值
data: number,
data: number
// 误差值
errorData: number
// 第几次谐波
num?: number,
num?: number
//符合、不符合
isData?: number,
isData?: number
//最大误差值
radius?: string,
radius?: string
unit?: string
}
export interface DataItem {
num: number
isData: number
data: number
resultData: number
radius: string
errorData: number
unit: string
}
export interface TableRow {
isData: number
harmNum: number
radius: string
dataA: DataItem
dataB: DataItem
dataC: DataItem
dataT: DataItem | null
unit: string
timeDev?: string
uaDev?: string | number
ubDev?: string | number
ucDev?: string | number
utDev?: string | number
uaStdDev?: string | number
ubStdDev?: string | number
ucStdDev?: string | number
utStdDev?: string | number
}
/**
* 用于定义 查看(设备)通道检测结果响应数据 类型
*/
export interface ResCheckResult {
dataA?: PhaseCheckResult | null,
dataB?: PhaseCheckResult | null,
dataC?: PhaseCheckResult | null,
dataT?: PhaseCheckResult | null,
dataA?: PhaseCheckResult | null
dataB?: PhaseCheckResult | null
dataC?: PhaseCheckResult | null
dataT?: PhaseCheckResult | null
// 第几次谐波
//num: number | null,
//符合、不符合
isData?: number,
isData?: number
//最大误差值
radius?: string,
radius?: string
//单位
unit?: string,
unit?: string
}
/**
* 用于定义 查看(设备)通道检测结果表格展示数据 类型
*/
export interface CheckResult {
stdA?: string,
dataA?: string,
errorA?: string,
maxErrorA?: string,
isDataA?: number,
stdB?: string,
dataB?: string,
errorB?: string,
maxErrorB?: string,
isDataB?: number,
stdC?: string,
dataC?: string,
errorC?: string,
maxErrorC?: string,
isDataC?: number,
stdT?: string,
dataT?: string,
errorT?: string,
maxErrorT?: string,
isDataT?: number,
stdA?: string
dataA?: string
errorA?: string
maxErrorA?: string
isDataA?: number
unitA?: string
stdB?: string
dataB?: string
errorB?: string
maxErrorB?: string
isDataB?: number
unitB?: string
stdC?: string
dataC?: string
errorC?: string
maxErrorC?: string
isDataC?: number
unitC?: string
stdT?: string
dataT?: string
errorT?: string
maxErrorT?: string
isDataT?: number
unitT?: string
//最大误差值
maxError?: string,
unit?: string,
maxError?: string
unit?: string
//符合、不符合
result?: number,
result?: number
}
/**
* 用于定义 具体通道的原始数据类型
*/
export interface RawDataItem {
time?: string,
harmNum?: number | null,
dataA?: string,
dataB?: string,
dataC?: string,
dataT?: string,
time?: string
harmNum?: number | null
dataA?: string
dataB?: string
dataC?: string
dataT?: string
unit?: string | null
}
export interface Device {
deviceId: string; //装置序号Id
deviceName: string; //设备名称
chnNum: number; //设备通道数
deviceId: string //装置序号Id
deviceName: string //设备名称
chnNum: number //设备通道数
planId: string //计划Id
devType: string //设备类型
devVolt: number //设备电压
devCurr: number //设备电流
factorFlag: number //是否支持系数校准
checkResult: number //检测结果
chnNumList: string[] //连线存储数据
}
// 用来描述检测脚本类型
export interface ScriptItem {
id: string,
code: string,
scriptName: string,
id: string
code: string
scriptName: string
}
// 用来描述 检测数据-左侧树结构
export interface TreeItem {
scriptTypeName: string | null,
sourceDesc: string | null,
harmNum: number | null,
index: number | null,
fly: number | null,
children: TreeItem[] | null,
id: string | null
scriptTypeName: string | null
sourceDesc: string | null
harmNum: number | null
index: number | null
fly: number | null
children: TreeItem[] | null
}
// 用来描述 通道检测结果
@@ -116,30 +162,32 @@ export namespace CheckData {
SUCCESS = 1,
FAIL = 2,
TIMEOUT = 3,
ERRORDATA = 4
ERRORDATA = 4,
NOT_PART_IN_ERROR = 5
}
export interface DeviceCheckResult {
deviceId: string,
deviceName: string,
deviceId: string
deviceName: string
code?: string
chnResult: ChnCheckResultEnum[] //通道检测结果
}
//用来描述 某个脚本测试项对所有通道的检测结果
export interface ScriptChnItem {
scriptType: string
scriptName?: string //可以不要该属性,有点多余
scriptName?: string //可以不要该属性,有点多余
code?: string
// 设备
devices: Array<DeviceCheckResult>
}
export enum ButtonColorEnum {
INFO = '#909399',
LOADING = '#003078',
SUCCESS = '#67c23a',
LOADING = 'var(--el-color-primary)',
SUCCESS = '#91cc75',
WARNING = '#e6a23c',
DANGER = '#f56c6c',
DANGER = '#f56c6c'
}
/**
@@ -147,25 +195,24 @@ export namespace CheckData {
*/
export interface ButtonResult {
color: ButtonColorEnum
icon: 'Minus' | 'Loading' | 'Close' | 'CircleCheckFilled' | 'Link' | 'WarnTriangleFilled'
icon: 'More' | 'Loading' | 'Close' | 'CircleCheckFilled' | 'Link' | 'WarnTriangleFilled' | 'Minus'
}
/**
* 用于描述 脚本检测结果展示的按钮类型
*/
export interface ScriptChnViewItem {
scriptType: string,
scriptType: string
scriptName?: string //脚本项名称,可以不要该属性,有点多余
// 设备
devices: Array<{
deviceId: string,
deviceName: string,
chnResult: ButtonResult[],
deviceId: string
deviceName: string
chnResult: ButtonResult[]
}>
}
/**
* 定义检测日志类型
*/
@@ -173,8 +220,21 @@ export namespace CheckData {
type: 'info' | 'warning' | 'error'
log: string
}
/**
* 定义手动检测时,勾选的测试项
*/
export interface SelectTestItem {
preTest: boolean
timeTest: boolean
channelsTest: boolean
test: boolean
}
//描述比对式检测项描述
export interface CompareTestItem {
id: string
code: string
name: string
}
}

View File

@@ -1,8 +1,22 @@
import http from "@/api";
import {CheckData} from "@/api/check/interface";
import { pa } from 'element-plus/es/locale/index.mjs';
import http from '@/api'
import {CheckData} from '@/api/check/interface'
export const getBigTestItem = (planId: string) => {
return http.get(`/adPlan/getBigTestItem?planId=${planId}`, {loading: false});
export const getBigTestItem = (params: {
reCheckType: number
planId: string
devIds: string[]
patternId: string
}) => {
return http.post(`/adPlan/getBigTestItem`, params, {loading: false})
}
export const getScriptList = (params: {
devId:string,
chnNum:number,
num:number
}) => {
return http.post('/result/getCheckItem', params, {loading: false})
}
/**
@@ -10,12 +24,12 @@ export const getBigTestItem = (planId: string) => {
* @param params 当为scriptType为null时表示查询所有脚本类型否则只查询指定脚本类型。当为chnNum为-1时表示查询所有通道否则只查询指定通道。
*/
export const getFormData = (params: {
planId: string,
deviceId: string,
chnNum: string,
planId: string
deviceId: string
chnNum: string
scriptType: string | null
}) => {
return http.post("/result/formContent/", params, {loading: false});
return http.post('/result/formContent/', params, {loading: false})
}
/**
@@ -23,13 +37,13 @@ export const getFormData = (params: {
* @param params
*/
export const getTreeData = (params: {
scriptId: string,
devId: string,
devNum: string,
scriptType: string | null,
code: number,
scriptId?: string
devId?: string
devNum?: string
scriptType?: string | null
code?: string
}) => {
return http.post<CheckData.TreeItem[]>("/result/treeData/", params, {loading: true});
return http.post<CheckData.TreeItem[]>('/result/treeData/', params, {loading: false})
}
/**
@@ -37,23 +51,93 @@ export const getTreeData = (params: {
* @param params
*/
export const getTableData = (params: {
scriptType: string | null,
scriptId: string,
devId: string,
devNum: string,
code: number,
index: number,
scriptType: string | null
scriptId: string
devId: string
devNum: string
code: string
index: number
}) => {
return http.post("/result/resultData/", params, {loading: false});
return http.post('/result/resultData/', params, {loading: false})
}
export const exportRawData = (params: {
scriptType: string | null,
scriptId: string,
devId: string,
devNum: string,
code: number,
index: number,
scriptType: string | null
scriptId: string
devId: string
devNum: string
code: string
index: number
}) => {
return http.download("/result/exportRawData", params, {loading: false});
}
return http.download('/result/exportRawData', params, {loading: false})
}
/**
* 重新计算
* @param params
*/
export const reCalculate = (params: {
planId: string
scriptId: string
errorSysId: string
deviceId: string
code: string
patternId: string
}) => {
return http.post('/result/reCalculate', params, {loading: true})
}
/**
* 获取数据获取基本信息
* @param params
*/
export const getContrastFormContent = (params: {
planId: string
scriptType: string
deviceId: string
chnNum: string
num: number | null
patternId: string
}) => {
return http.post('/result/getContrastFormContent', params, {loading: false})
}
/**
* 获取检测结果
* @param params
*/
export const getContrastResult = (params: {
planId: string
scriptType: string
deviceId: string
chnNum: string | number
num: number | string | null
waveNum: number | null
isWave: boolean
patternId: string
}) => {
return http.post('/result/getContrastResult', params, {loading: true})
}
/**
* 更换误差体系
* @param params
*/
export const changeErrorSystem = (params: {
planId: string
scriptId: string
errorSysId: string
deviceId: string
code: string
patternId: string
}) => {
return http.post('/result/changeErrorSystem', params, {loading: true})
}
/**
* 删除(误差体系切换时产生的)临时表
* @param code
*/
export const deleteTempTable = (code: string) => {
return http.get(`/result/deleteTempTable?code=${code}`, null, {loading: false})
}

View File

@@ -0,0 +1,24 @@
import type { controlSource } from '@/api/device/interface/controlSource'
import http from '@/api'
/**
* @name 程控源管理模块
*/
//通讯校验
export const checkSimulate = (params: controlSource.ResControl) => {
return http.post(`/prepare/ytxCheckSimulate`,params)
}
//启动
export const startSimulateTest = (params: controlSource.ResControl) => {
return http.post(`prepare/startSimulateTest`,params,{loading:false})
}
//停止
export const closeSimulateTest = (params: controlSource.ResControl) => {
return http.post(`/prepare/closeSimulateTest`,params,{loading:false})
}

View File

@@ -266,172 +266,4 @@ const data = [
reCheck_Num: 0, //复检次数
},
]
// const plan_devicedata = [
// {
// id: '1', //装置序号ID
// name: '模拟装置1', //设备名称
// dev_Type: 'PQS882A',//设备类型
// dev_Chns: 1, //设备通道数
// check_Result: '合格', //检测结果
// report_State: '已生成', //报告状态
// document_State: '归档', //归档状态
// check_State:'检测完成',//检测状态
// reCheck_Num: 0, //复检次数
// },
// {
// id: '2', //装置序号ID
// name: '模拟装置2', //设备名称
// dev_Type: 'PQS882B4',//设备类型
// dev_Chns: 4, //设备通道数
// check_Result: '/', //检测结果
// report_State: '未生成', //报告状态
// document_State: '未归档', //归档状态
// check_State:'未检',//检测状态
// reCheck_Num: 0, //复检次数
// },
// {
// id: '3', //装置序号ID
// name: '模拟装置3', //设备名称
// dev_Type: 'PQS882B4',//设备类型
// dev_Chns: 4, //设备通道数
// check_Result: '/', //检测结果
// report_State: '未生成', //报告状态
// document_State: '未归档', //归档状态
// check_State:'检测中',//检测状态
// reCheck_Num: 0, //复检次数
// },
// {
// id: '4', //装置序号ID
// name: '模拟装置4', //设备名称
// dev_Type: 'PQS882B4',//设备类型
// dev_Chns: 4, //设备通道数
// check_Result: '不合格', //检测结果
// report_State: '未生成', //报告状态
// document_State: '未归档', //归档状态
// check_State:'检测完成',//检测状态
// reCheck_Num: 1, //复检次数
// },
// {
// id: '5', //装置序号ID
// name: '中电测试装置', //设备名称
// dev_Type: 'PMC-680M-22-22-00-115ANBC',//设备类型
// dev_Chns: 4, //设备通道数
// check_Result: '不合格', //检测结果
// report_State: '未生成', //报告状态
// document_State: '未归档', //归档状态
// check_State:'检测完成',//检测状态
// reCheck_Num: 1, //复检次数
// },
// {
// id: '6', //装置序号ID
// name: '易司拓测试装置1', //设备名称
// dev_Type: 'E703A',//设备类型
// dev_Chns: 1, //设备通道数
// check_Result: '不合格', //检测结果
// report_State: '已生成', //报告状态
// document_State: '未归档', //归档状态
// check_State:'检测完成',//检测状态
// reCheck_Num: 1, //复检次数
// },
// {
// id: '7', //装置序号ID
// name: '易司拓测试装置2', //设备名称
// dev_Type: 'E703A',//设备类型
// dev_Chns: 1, //设备通道数
// check_Result: '不合格', //检测结果
// report_State: '已生成', //报告状态
// document_State: '未归档', //归档状态
// check_State:'检测完成',//检测状态
// reCheck_Num: 1, //复检次数
// },
// {
// id: '8', //装置序号ID
// name: '山大电力测试装置1', //设备名称
// dev_Type: 'SDL-3002C',//设备类型
// dev_Chns: 1, //设备通道数
// check_Result: '不合格', //检测结果
// report_State: '已生成', //报告状态
// document_State: '未归档', //归档状态
// check_State:'检测完成',//检测状态
// reCheck_Num: 1, //复检次数
// },
// {
// id: '9', //装置序号ID
// name: '山大电力测试装置2', //设备名称
// dev_Type: 'SDL-3002C',//设备类型
// dev_Chns: 1, //设备通道数
// check_Result: '不合格', //检测结果
// report_State: '已生成', //报告状态
// document_State: '未归档', //归档状态
// check_State:'检测完成',//检测状态
// reCheck_Num: 2, //复检次数
// },
// {
// id: '10', //装置序号ID
// name: '山大电力测试装置2', //设备名称
// dev_Type: 'SDL-3002C',//设备类型
// dev_Chns: 1, //设备通道数
// check_Result: '不合格', //检测结果
// report_State: '已生成', //报告状态
// document_State: '未归档', //归档状态
// check_State:'检测完成',//检测状态
// reCheck_Num: 2, //复检次数
// },
// {
// id: '11', //装置序号ID
// name: '山大电力测试装置2', //设备名称
// dev_Type: 'SDL-3002C',//设备类型
// dev_Chns: 1, //设备通道数
// check_Result: '不合格', //检测结果
// report_State: '已生成', //报告状态
// document_State: '未归档', //归档状态
// check_State:'检测完成',//检测状态
// reCheck_Num: 2, //复检次数
// },
// {
// id: '12', //装置序号ID
// name: '山大电力测试装置2', //设备名称
// dev_Type: 'SDL-3002C',//设备类型
// dev_Chns: 1, //设备通道数
// check_Result: '不合格', //检测结果
// report_State: '已生成', //报告状态
// document_State: '未归档', //归档状态
// check_State:'检测完成',//检测状态
// reCheck_Num: 2, //复检次数
// },
// {
// id: '13', //装置序号ID
// name: '山大电力测试装置2', //设备名称
// dev_Type: 'SDL-3002C',//设备类型
// dev_Chns: 1, //设备通道数
// check_Result: '不合格', //检测结果
// report_State: '已生成', //报告状态
// document_State: '未归档', //归档状态
// check_State:'检测完成',//检测状态
// reCheck_Num: 2, //复检次数
// },
// {
// id: '14', //装置序号ID
// name: '山大电力测试装置3', //设备名称
// dev_Type: 'SDL-3002C',//设备类型
// dev_Chns: 1, //设备通道数
// check_Result: '不合格', //检测结果
// report_State: '已生成', //报告状态
// document_State: '未归档', //归档状态
// check_State:'检测完成',//检测状态
// reCheck_Num: 2, //复检次数
// },
// {
// id: '15', //装置序号ID
// name: '山大电力测试装置4', //设备名称
// dev_Type: 'SDL-3002C',//设备类型
// dev_Chns: 1, //设备通道数
// check_Result: '不合格', //检测结果
// report_State: '已生成', //报告状态
// document_State: '未归档', //归档状态
// check_State:'检测完成',//检测状态
// reCheck_Num: 2, //复检次数
// },
// ]
export default {data,plan_devicedata}

View File

@@ -1,4 +1,5 @@
import type { Device } from '@/api/device/interface/device'
import { pa } from 'element-plus/es/locale/index.mjs';
import type {Device} from '@/api/device/interface/device'
import http from '@/api'
/**
@@ -7,57 +8,67 @@ import http from '@/api'
//获取被检设备
export const getPqDevList = (params: Device.ReqPqDevParams) => {
return http.post(`/pqDev/list`, params)
return http.post(`/pqDev/list`, params)
}
//添加被检设备
export const addPqDev = (params: Device.ResPqDev) => {
return http.post(`/pqDev/add`, params)
return http.post(`/pqDev/add`, params)
}
//编辑被检设备
export const updatePqDev = (params: Device.ResPqDev) => {
return http.post(`/pqDev/update`, params)
return http.post(`/pqDev/update`, params)
}
//删除被检设备
export const deletePqDev = (params: string[]) => {
return http.post(`/pqDev/delete`, params)
return http.post(`/pqDev/delete`, params)
}
// export const downloadTemplate = () => {
// return http.download(`/pqDev/downloadTemplate`)
//导出被检设备
export const exportPqDev = (params: Device.ReqPqDevParams) => {
return http.download(`/pqDev/export`, params)
}
// 下载导入文件模板
export const downloadTemplate = (params: { pattern: string }) => {
return http.download(`/pqDev/downloadTemplate`,params)
}
//导入被检设备
export const importPqDev = (params: Device.ReqPqDevParams) => {
return http.uploadExcel(`/pqDev/import`, params)
}
//导入比对式被检设备
export const importContrastPqDev = (params: Device.ReqPqDevParams) => {
return http.upload(`/pqDev/importContrast`, params)
}
// //导出灿能二楼设备
// export const exportCNDev = (params: Device.ReqPqDevParams) => {
// return http.download(`/pqDev/exportCNDev`, params)
// }
//
// // 下载灿能二楼设备导入文件模板
// export const downloadCNDevTemplate = () => {
// return http.download(`/pqDev/downloadCNDevTemplate`)
// }
//
// //导入灿能二楼设备
// export const importCNDev = (params: Device.ReqPqDevParams) => {
// return http.uploadExcel(`/pqDev/importCNDev`, params)
// }
// //导入被检设备(比对)
// export const importPqDev=(params: Device.ReqPqDevParams)=>{
// return http.upload(`/pqDev/import`, params)
// }
// //导入比对式被检设备
// export const importContrastPqDev=(params: Device.ReqPqDevParams)=>{
// return http.upload(`/pqDev/importContrast`, params)
// }
//导出灿能二楼设备
export const exportCNDev = (params: Device.ReqPqDevParams) => {
return http.download(`/pqDev/exportCNDev`, params)
export const getPqDevById = (params: Device.ReqPqDevParams) => {
return http.get(`/pqDev/getById?id=${params.id}`)
}
// 下载灿能二楼设备导入文件模板
export const downloadCNDevTemplate = () => {
return http.download(`/pqDev/downloadCNDevTemplate`)
}
//导入灿能二楼设备
export const importCNDev = (params: Device.ReqPqDevParams) => {
return http.uploadExcel(`/pqDev/importCNDev`, params)
}
//根据设备类型决定电源、icd、模板、通道数、额定电压、额定电流
export const getPqDev = () => {
return http.get(`/devType/listAll`)
return http.get(`/devType/listAll`)
}
//被检设备归档
export const documentedPqDev = (ids: string[]) => {
return http.post(`/pqDev/documented`, ids)
export const getSelectOptions = (params:{ pattern: string }) => {
return http.get(`/pqDev/getSelectOptions`, params)
}

View File

@@ -30,5 +30,7 @@ export const deletePqErrSys = (params: string[]) => {
return http.post(`/pqErrSys/delete`, params)
}
//复制误差体系
export const copyPqErrSys = (params: ErrorSystem.ErrorSystemList) => {
return http.get(`/pqErrSys/copy?id=${params.id}`)
}

View File

@@ -0,0 +1,15 @@
import type { ReqPage,ResPage } from '@/api/interface'
// 被检设备模块
export namespace controlSource {
/**
* 被检设备新增、修改、根据id查询返回的对象
*/
export interface ResControl {
userPageId: string;
scriptId: string;
scriptIndex: number;
sourceId: string;
}
}

View File

@@ -25,6 +25,7 @@ export namespace DevType {
devChns: number; //设备通道数
reportName: string| null;//报告模版名称
state: number;
waveCmd:string| null;//录波指令
createBy?: string| null; //创建用户
createTime?: string| null; //创建时间
updateBy?: string| null; //更新用户

View File

@@ -1,91 +1,119 @@
import type { ReqPage,ResPage } from '@/api/interface'
import type { ReqPage, ResPage } from '@/api/interface'
import type { Monitor } from './monitor'
// 被检设备模块
export namespace Device {
/**
* 被检设备表格分页查询参数
*/
export interface ReqPqDevParams extends ReqPage{
id: string; // 装置序号id 必填
name: string; //设备名称
devType?: string; // 设备名称
createTime?:string; //创建时间
pattern:string;
}
/**
* 被检设备表格分页查询参数
*/
export interface ReqDevReportParams extends ReqPage{
planId?:string; // 计划id
devId?:string; // 装置id
}
/**
* 被检设备表格分页查询参数
*/
export interface ReqPqDevParams extends ReqPage {
id: string // 装置序号id 必填
name: string //设备名称
devType?: string // 设备名称
createTime?: string //创建时间
pattern: string
}
/**
* 被检设备新增、修改、根据id查询返回的对象
*/
* 被检设备表格分页查询参数
*/
export interface ReqDevReportParams extends ReqPage {
planId?: string // 计划id
devId?: string // 装置id
scriptId?: string // 脚本id
planCode?: string
devIdList?: string[] // 装置id列表
}
/**
* 被检设备新增、修改、根据id查询返回的对象
*/
export interface ResPqDev {
id: string; //装置序号ID
name: string; //设备名称
pattern: string; //设备模式 模拟 数字 比对
devType: string;//设备类型
devChns: number; //设备通道数
devVolt: number; //额定电压V
devCurr: number; //额定电流A
manufacturer?: string | null;//生产厂家
createDate: string; //生产日期
createId: string; //出厂编号
hardwareVersion: string; //固件版本
softwareVersion: string; //软件版本
protocol:string; //通讯协议
ip: string; //IP地址
port: number; //端口号
encryptionFlag: number; //装置是否为加密版本
series?: string| null; //装置识别码3ds加密
devKey?: string| null; //装置秘钥3ds加密
sampleID?: string| null; //样品编号
arrivedDate?: string; //送样日期
cityName?: string| null; //所属地市名称
gDName?: string| null; //所属供电公司名称
subName?: string| null; //所属电站名称
checkState?: number| null; //检测状态
checkResult?: number| null; //检测结果
reportState?: number| null; //报告状态
reportPath?: string| null; //报告路径
qRCode?: string| null; //设备关键信息二维码
reCheckNum: number; //复检次数
planId?:string;//检测计划Id
timeCheckResult?:number;//守时检测结果(0:不符合1:符合)
factorFlag?:number;//是否支持系数校准(0:不支持,1:支持)
factorCheckResult?: number;//系数校准结果(0:不合格1:合格2/表示没有做系数校准)
state: number; //状态
createBy?: string| null; //创建用户
createTime?: string| null; //创建时间
updateBy?: string| null; //更新用户
updateTime?: string| null; //更新时间
icdId: string| null;
power: string| null;//工作电源
preinvestmentPlan: string| null;
id: string //装置序号ID
name: string //设备名称
pattern: string //设备模式 模拟 数字 比对
devType: string //设备类型
manufacturer?: string | null //生产厂家
createDate: string //生产日期
createId: string //出厂编号
hardwareVersion: string //固件版本
softwareVersion: string //软件版本
protocol: string //通讯协议
ip: string //IP地址
port: number //端口号
encryptionFlag: number //装置是否为加密版本
series?: string | null //装置识别码3ds加密
devKey?: string | null //装置秘钥3ds加密
sampleId?: string | null //样品编号
arrivedDate?: string //送样日期
cityName?: string | null //所属地市名称
gdName?: string | null //所属供电公司名称
subName?: string | null //所属电站名称
reportPath?: string | null //报告路径
planId?: string //检测计划Id
factorFlag?: number //是否支持系数校准(0:不支持,1:支持)
preinvestmentPlan: string | null //预投计划
delegate: string | null //委托方
inspectChannel?: string[] | string //被检通道
inspectDate?: string | null //定检日期
harmSysId?: string | null //谐波系统设备id
importFlag?: number //是否为导入设备 0否 1是
state: number //状态
createBy?: string | null //创建用户
createTime?: string | null //创建时间
updateBy?: string | null //更新用户
updateTime?: string | null //更新时间
devChns: number //设备通道数
devVolt: number //额定电压V
devCurr: number //额定电流A
icdId: string | null
power: string | null //工作电源
devId?: number
checkState?: number | null //检测状态(0:未检1:检测中2:检测完成 3:归档)
checkResult?: number | null //检测结果(0:不符合1:符合2:未检)
reportState?: number | null //报告状态(0:未生成1:已生成2:未检)
recheckNum: number //复检次数
timeCheckResult?: number //守时检测结果(0:不符合1:符合)
factorCheckResult?: number //系数校准结果(0:不合格1:合格2:未检)
realtimeResult?: number //实时数据结论(0:不符合1:符合2:未检)
statisticsResult?: number //统计数据结论(0:不符合1:符合2:未检)
recordedResult?: number //录波数据结论(0:不符合1:符合2:未检)
checkBy?: string | null //检测人
checkTime?: string | null //检测时间
preDetectTime?: number //预检测耗时
coefficientTime?: number //系数校准耗时
formalCheckTime?: number //正式检测耗时
boundPlanName?: string | null
assign?: number ////是否分配给检测人员 0否 1是
monitorList: Monitor.ResPqMon[]
checked: boolean // 是否已选择
disabled: boolean // 是否禁用
}
export interface ResDev{
id: string;
name: string ,
icd:string ,
power:string,
devVolt:number,
devCurr:number,
devChns:number,
export interface SelectOption {
label: string
value: string | number
}
export interface ResDev {
id: string
name: string
icd: string
power: string
devVolt: number
devCurr: number
devChns: number
}
/**
* 被检设备表格查询分页返回的对象;
*/
export interface ResPqDevPage extends ResPage<ResPqDev> {
}
}
export interface ResTH {
temperature: number | null //温度
humidity: number | null //湿度
}
/**
* 被检设备表格查询分页返回的对象;
*/
export interface ResPqDevPage extends ResPage<ResPqDev> {}
}

View File

@@ -33,7 +33,9 @@ export namespace ErrorSystem {
endFlag?:number;//是否包含结束值
conditionType?:string;//判断条件值类型(包括值类型,绝对值、相对值)
maxErrorValue:number;//误差最大值
errorValueType:string;//误差值类型(包括值类型绝对值、相对值1、相对值2
errorValueType:any;//误差值类型(0标称值、1标准值、2值类型
valueType:number;//值类型1绝对值、2相对值
errorUnit:string;//误差单位
}
//查看详细误差体系

View File

@@ -24,6 +24,8 @@ export namespace ICD {
createTime?: string| null; //创建时间
updateBy?: string| null; //更新用户
updateTime?: string| null; //更新时间
angle: number; // 是否支持电压相角、电流相角指标
usePhaseIndex: number; // 角型接线时是否使用相别的指标来进行检测
}
/**

View File

@@ -1,33 +1,36 @@
import type { ReqPage, ResPage } from '@/api/interface'
// 被检设备模块
// 监测点模块
export namespace Monitor {
/**
* 电能质量指标字典数据表格分页查询参数
* 监测点表格分页查询参数
*/
export interface ReqPqMonParams extends ReqPage {
id: string; // 装置序号id 必填
devType?: string; // 设备名称
createTime?: string; //创建时间
name?: string; // 设备名称
}
/**
* 被检设备新增、修改、根据id查询返回的对象
* 监测点新增、修改、根据id查询返回的对象
*/
export interface ResPqMon {
id: string; //监测点ID
code: string; //默认与谐波系统监测点ID相同
devId: string; //所属设备ID
name: string; //所属母线
num: number; //监测点序号
pt: number; //PT变比
ct: number; //CT变比
ptType: string; //接线方式,字典表
busbar: string;//所属母线
name: string; //监测点名称
num: number; //线路号,监测点序号
pt: string; //PT变比
ct: string; //CT变比
connection: string; //接线方式,字典表
statInterval: number; //统计间隔
harmSysId: string; //默认与谐波系统监测点ID相同
checkFlag: number;//是否参与检测0否1是
}
/**
* 被检设备表格查询分页返回的对象;
* 监测点表格查询分页返回的对象;
*/
export interface ResPqMonPage extends ResPage<ResPqMon> {

View File

@@ -0,0 +1,39 @@
import type { ReqPage, ResPage } from '@/api/interface'
import type { UploadFile } from 'element-plus';
// 报告模版接口
export namespace PqReport {
/**
* 报告模版表格分页查询参数
*/
export interface ReqReportParams extends ReqPage {
id: string; // 装置序号id 必填
name?: string; // 设备名称
createTime?: string; //创建时间
}
/**
* 报告模版新增、修改、根据id查询返回的对象
*/
export interface ResReport {
id: string; //报告模板id
name: string;//报告模板名称
version:string;//版本号
baseFile?:string;//基础模板文件路径
detailFile?:string;//检测项模版文件路径
description:string;//描述信息
state:number;//状态:8-删除 1-正常
createBy?: string| null; //创建用户
createTime?: string| null; //创建时间
updateBy?: string| null; //更新用户
updateTime?: string| null; //更新时间
}
/**
* 报告模版表格查询分页返回的对象;
*/
export interface ResReportPage extends ResPage<ResReport> {
}
}

View File

@@ -0,0 +1,47 @@
import type { ReqPage, ResPage } from '@/api/interface'
// 标准设备模块
export namespace StandardDevice {
/**
* 标准设备表格分页查询参数
*/
export interface ReqPqStandardDeviceParams extends ReqPage {
id: string; // 装置序号id 必填
name: string; //设备名称
devType?: string; // 设备名称
createTime?: string; //创建时间
}
/**
* 标准设备新增、修改、根据id查询返回的对象
*/
export interface ResPqStandardDevice {
id: string; //装置序号ID
name: string; //设备名称
devType: string;//设备类型
manufacturer?: string | null;//生产厂家
protocol: string;//通讯协议
ip: string; //IP地址
port: number; //端口号
inspectChannel:string[] |string;//可检通道数
encryptionFlag: number; //装置是否为加密版本
series?: string | null; //装置识别码3ds加密
devKey?: string | null; //装置秘钥3ds加密
state: number; //状态
createBy?: string | null; //创建用户
createTime?: string | null; //创建时间
updateBy?: string | null; //更新用户
updateTime?: string | null; //更新时间
disabled?: boolean;
}
/**
* 标准设备表格查询分页返回的对象;
*/
export interface ResPqStandardDevicePage extends ResPage<ResPqStandardDevice> {
}
}

View File

@@ -2,38 +2,37 @@ import type { ReqPage, ResPage } from '@/api/interface'
// 检测脚本模块
export namespace TestScript {
/**
* 检测脚本表格分页查询参数
*/
export interface ReqTestScriptParams extends ReqPage{
id: string; // 装置序号id 必填
name: string;
type: string;
pattern:string;
}
/**
* 检测脚本表格分页查询参数
*/
export interface ReqTestScriptParams extends ReqPage {
id: string // 装置序号id 必填
name: string
type: string
pattern: string
}
// 检测脚本接口
export interface ResTestScript {
id?: string; //检测脚本ID
name: string; //检测脚本名称
type: string; //设定0为脚本1为模板
pattern: string;//检测脚本模式(字典表Code字段数字、模拟、比对)
valueType?: string;//脚本值类型(字典表Code字段相对值脚本、绝对值脚本、无)
standardName: string;//参照标准名称
standardTime: string;//标准推行时间
state:number;//
createBy?: string;
createTime?: string;
updateBy?: string;
updateTime?: string;
id?: string //检测脚本ID
name: string //检测脚本名称
type: string //设定0为脚本1为模板
pattern: string //检测脚本模式(字典表Code字段数字、模拟、比对)
valueType?: string //脚本值类型(字典表Code字段相对值脚本、绝对值脚本、无)
standardName: string //参照标准名称
standardTime: string //标准推行时间
state?: number //
createBy?: string
createTime?: string
updateBy?: string
updateTime?: string
selectedValue?: string
ratedCurr?: number
ratedVolt?: number
}
/**
* 检测脚本查询分页返回的对象;
*/
export interface ResTestScriptPage extends ResPage<ResTestScript> {
}
}
/**
* 检测脚本查询分页返回的对象;
*/
export interface ResTestScriptPage extends ResPage<ResTestScript> {}
}

View File

@@ -1,4 +1,3 @@
import type { Monitor } from '@/api/device/interface/monitor'
import http from '@/api'
/**
@@ -6,23 +5,9 @@ import http from '@/api'
*/
//获取监测点
export const getPqMonList = (params: Monitor.ReqPqMonParams) => {
//return http.post(`/pqMon/list`, params)
}
//添加监测点
export const addPqMon = (params: Monitor.ResPqMon) => {
//return http.post(`/pqMon/add`, params)
}
//编辑监测点
export const updatePqMon = (params: Monitor.ResPqMon) => {
//return http.post(`/pqMon/update`, params)
}
//删除监测点
export const deletePqMon = (params: string[]) => {
//return http.post(`/pqMon/delete`, params)
export const getPqMonList = (param:any) => {
return http.post(`/pqMonitor/list`, param)
}

View File

@@ -0,0 +1,51 @@
import type {PqReport} from '@/api/device/interface/report'
import http from '@/api'
/**
* @name 报告模版模块
*/
//获取报告模版
export const getPqReportList = (params: PqReport.ReqReportParams) => {
return http.post(`/report/list`, params)
}
//新增报告模版
export const addPqReport = (params: PqReport.ResReport) => {
return http.upload(`/report/add`, params)
}
//删除报告模版
export const deletePqReport = (params: string[]) => {
return http.post(`/report/delete`, params)
}
//查询报告模板详情
export const getPqReportById = (params: PqReport.ResReport) => {
return http.get(`/report/getById?id=${params.id}`)
}
//修改报告模板
export const updatePqReport = (params: PqReport.ResReport) => {
return http.upload(`/report/update`, params)
}
//查询所有报告模板名称
export const getPqReportAllName = () => {
return http.get(`/report/listAllName`)
}
//根据名称查询指定报告模板的所有版本
export const getPqReportAllVersion = (params:any) => {
return http.get(`/report/listAllVersion?name=${params.name}`)
}
//被检设备归档
export const documentedPqDev = (ids: string[]) => {
return http.post(`/report/documented`, ids)
}
//上传报告到云端
export const uploadReportToCloud = (deviceIds: string[]) => {
return http.post(`/report/uploadReportToCloud`, deviceIds)
}

View File

@@ -0,0 +1,55 @@
import type { StandardDevice } from '@/api/device/interface/standardDevice'
import http from '@/api'
/**
* @name 标准设备管理模块
*/
//获取标准设备
export const getPqStandardDevList = (params: StandardDevice.ReqPqStandardDeviceParams) => {
return http.post(`/pqStandardDev/list`, params)
}
//根据id查询标准设备
export const getPqStandardDevById = (params: StandardDevice.ReqPqStandardDeviceParams) => {
return http.get(`/pqStandardDev/getById?id=${params.id}`)
}
//添加标准设备
export const addPqStandardDev = (params: StandardDevice.ResPqStandardDevice) => {
return http.post(`/pqStandardDev/add`, params)
}
//编辑标准设备
export const updatePqStandardDev = (params: StandardDevice.ResPqStandardDevice) => {
return http.post(`/pqStandardDev/update`, params)
}
//删除标准设备
export const deletePqStandardDev = (params: string[]) => {
return http.post(`/pqStandardDev/delete`, params)
}
//导出标准设备
export const exportPqStandardDev = (params: StandardDevice.ReqPqStandardDeviceParams) => {
return http.download(`/pqStandardDev/export`, params)
}
// 下载导入文件模板
export const downloadTemplate = () => {
return http.download(`/pqStandardDev/downloadTemplate`)
}
//导入标准设备
export const importPqStandardDev = (params: StandardDevice.ReqPqStandardDeviceParams) => {
return http.uploadExcel(`/pqStandardDev/import`, params)
}
//获取所有标准设备
export const getAllPqStandardDev = () => {
return http.get(`/pqStandardDev/getAll`)
}
//获取可以绑定的标准设备
export const canBindingList = () => {
return http.get(`/pqStandardDev/canBindingList`)
}

View File

@@ -1,21 +1,53 @@
import type { TestScript } from '@/api/device/interface/testScript'
import http from '@/api'
import { Loading } from '@element-plus/icons-vue'
/**
* @name 检测脚本管理模块
*/
// 新增脚本
export const pqScriptAdd = (params: TestScript.ResTestScript) => {
return http.post(`/pqScript/add`, params)
}
// 修改脚本
export const pqScriptUpdate = (params: TestScript.ResTestScript) => {
return http.post(`/pqScript/update`, params)
}
// 获取检测脚本列表
export const getPqScriptList = (params: TestScript.ReqTestScriptParams) => {
return http.post(`/pqScript/list`, params)
return http.post(`/pqScript/list`, params)
}
//升级为模版
export const updatePqScript = (params: TestScript.ResTestScript) => {
return http.get(`/pqScript/upgradeToTemplate?id=${params.id}`)
return http.get(`/pqScript/upgradeToTemplate?id=${params.id}`)
}
//删除检测脚本
export const deletePqScript = (params: string[]) => {
return http.post(`/pqScript/delete`, params)
return http.post(`/pqScript/delete`, params)
}
//添加检测脚本
export const addScriptDtls = (params: any) => {
return http.post(`/pqScript/addScriptDtls`, params)
}
//根据脚本id查询检测脚本详情
export const dlsDetails = (params: any) => {
return http.post(`/pqScript/dlsDetails`, params)
}
// 删除脚本
export const deleteDtls = (params: any) => {
return http.post(`/pqScript/deleteDtls`, params)
}
// 启用关闭脚本
export const updateDtls = (params: any) => {
return http.post(`/pqScript/updateDtls`, params)
}
// 根据通讯参数生成装置下发原始数据公式
export const scriptDtlsCheckDataList = (params: any) => {
return http.post(`/pqScript/scriptDtlsCheckDataList`, params,{loading: false})
}
// 通讯脚本回显
export const checkDataList = (params: any) => {
return http.post(`/pqScript/checkDataList`, params, { loading: true })
}

View File

@@ -2,5 +2,5 @@ import http from "@/api";
//系数校准发送基本信息
export const getCoefficientCheck = (params: any) => {
return http.post(`/prepare/coefficientCheck`, params)
return http.post(`/prepare/coefficientCheck`, params,{loading: false})
}

View File

@@ -1,170 +1,242 @@
import { ElMessage, ElTreeSelect } from 'element-plus';
import axios, { AxiosInstance, AxiosError, AxiosRequestConfig, InternalAxiosRequestConfig, AxiosResponse } from 'axios'
import { ElMessage } from 'element-plus'
import axios, {
AxiosError,
type AxiosInstance,
type AxiosRequestConfig,
type AxiosResponse,
type InternalAxiosRequestConfig
} from 'axios'
import { showFullScreenLoading, tryHideFullScreenLoading } from '@/components/Loading/fullScreen'
import { LOGIN_URL } from '@/config'
import { ElMessage } from 'element-plus'
import { ResultData } from '@/api/interface'
import { type ResultData } from '@/api/interface'
import { ResultEnum } from '@/enums/httpEnum'
import { checkStatus } from './helper/checkStatus'
import { useUserStore } from '@/stores/modules/user'
import router from '@/routers'
import {refreshToken} from '@/api/user/login'
import { refreshToken } from '@/api/user/login'
import { EventSourcePolyfill } from 'event-source-polyfill'
export interface CustomAxiosRequestConfig extends InternalAxiosRequestConfig {
loading?: boolean;
loading?: boolean
}
const config = {
// 默认地址请求地址,可在 .env.** 文件中修改
baseURL: import.meta.env.VITE_API_URL as string,
// 设置超时时间
timeout: ResultEnum.TIMEOUT as number,
// 跨域时候允许携带凭证
withCredentials: true,
// post请求指定数据类型以及编码
headers: { 'Content-Type': 'application/json;charset=utf-8' },
// 默认地址请求地址,可在 .env.** 文件中修改
baseURL: import.meta.env.VITE_API_URL as string,
// 设置超时时间
timeout: ResultEnum.TIMEOUT as number,
// 跨域时候允许携带凭证
withCredentials: true,
// post请求指定数据类型以及编码
headers: { 'Content-Type': 'application/json;charset=utf-8' }
}
class RequestHttp {
service: AxiosInstance
service: AxiosInstance
public constructor(config: AxiosRequestConfig) {
// 创建实例
this.service = axios.create(config)
public constructor(config: AxiosRequestConfig) {
// 创建实例
this.service = axios.create(config)
/**
* @description 请求拦截器
* 客户端发送请求 -> [请求拦截器] -> 服务器
* token校验(JWT) : 接受服务器返回的 token,存储到 vuex/pinia/本地储存当中
*/
this.service.interceptors.request.use(
(config: CustomAxiosRequestConfig) => {
const userStore = useUserStore()
// 当前请求不需要显示 loading在 api 服务中通过指定的第三个参数: { loading: false } 来控制
config.loading ?? (config.loading = true)
config.loading && showFullScreenLoading()
if (config.headers && typeof config.headers.set === 'function') {
config.headers.set('Authorization', 'Bearer ' + userStore.accessToken)
config.headers.set('Refresh-Token', userStore.refreshToken)
}
return config
},
(error: AxiosError) => {
return Promise.reject(error)
},
)
let isFirst = true
/**
* @description 响应拦截器
* 服务器换返回信息 -> [拦截统一处理] -> 客户端JS获取到信息
*/
this.service.interceptors.response.use(
async (response: AxiosResponse) => {
const { data } = response
const userStore = useUserStore()
tryHideFullScreenLoading()
if(data.code === ResultEnum.ACCESSTOKEN_EXPIRED){
// 用长token去换短token
const result = await refreshToken()
if (result) { //获取新token成功的话
// 有新的token后重新请求
userStore.setAccessToken(result.data.accessToken)
userStore.setRefreshToken(result.data.refreshToken)
response.config.headers.Authorization = `Bearer ${result.data.accessToken}`//重新请求前需要将更新后的新token更换掉之前无效的token,不然会死循环
const resp = await this.service.request(response.config)
return resp
} else {
// 刷新失效,跳转登录页
/**
* @description 请求拦截器
* 客户端发送请求 -> [请求拦截器] -> 服务器
* token校验(JWT) : 接受服务器返回的 token,存储到 vuex/pinia/本地储存当中
*/
this.service.interceptors.request.use(
(config: CustomAxiosRequestConfig) => {
isFirst = true
const userStore = useUserStore()
// 当前请求不需要显示 loading在 api 服务中通过指定的第三个参数: { loading: false } 来控制
config.loading ?? (config.loading = true)
config.loading && showFullScreenLoading()
if (config.headers && typeof config.headers.set === 'function') {
config.headers.set('Authorization', 'Bearer ' + userStore.accessToken)
config.headers.set('Is-Refresh-Token', userStore.isRefreshToken + '')
}
return config
},
(error: AxiosError) => {
return Promise.reject(error)
}
}
// 登陆失效
if (data.code == ResultEnum.OVERDUE) {
console.log("登陆失效")
userStore.setAccessToken('')
userStore.setRefreshToken('')
userStore.setUserInfo({ name: '' })
router.replace(LOGIN_URL)
if(data.code != ResultEnum.OVERDUE){//临时处理token失效弹窗多次
ElMessage.error(data.message)
}else if(isFirst){
ElMessage.error(data.message)
isFirst = false
}
return Promise.reject(data)
)
let isFirst = true
/**
* @description 响应拦截器
* 服务器换返回信息 -> [拦截统一处理] -> 客户端JS获取到信息
*/
this.service.interceptors.response.use(
async (response: AxiosResponse) => {
const { data } = response
const userStore = useUserStore()
tryHideFullScreenLoading()
if (data.code === ResultEnum.ACCESSTOKEN_EXPIRED) {
// 用长token去换短token
userStore.setAccessToken(userStore.refreshToken)
userStore.setIsRefreshToken(true)
const result = await refreshToken()
if (result) {
//获取新token成功的话
// 有新的token后重新请求
userStore.setAccessToken(result.data.accessToken)
userStore.setRefreshToken(result.data.refreshToken)
userStore.setIsRefreshToken(false)
userStore.setExp(1000 * 60 * 60 * 24 * 30)
response.config.headers.Authorization = `Bearer ${result.data.accessToken}` //重新请求前需要将更新后的新token更换掉之前无效的token,不然会死循环
const resp = await this.service.request(response.config)
return resp
} else {
// 刷新失效,跳转登录页
}
}
// 登陆失效
if (data.code === ResultEnum.OVERDUE) {
//console.log('登陆失效')
userStore.setAccessToken('')
userStore.setRefreshToken('')
userStore.setIsRefreshToken(false)
userStore.setUserInfo({ id: '', name: '' })
userStore.setExp(0)
await router.replace(LOGIN_URL)
if (isFirst) {
//临时处理token失效弹窗多次
ElMessage.error(data.message)
isFirst = false
}
return Promise.reject(data)
}
// 全局错误信息拦截(防止下载文件的时候返回数据流,没有 code 直接报错)
if (data.code && data.code !== ResultEnum.SUCCESS) {
if (data.message.includes('&')) {
let formattedMessage = data.message.split('&').join('<br>')
if (data.message.includes(':')) {
formattedMessage = formattedMessage.replace(':', '')
}
ElMessage.error({ message: formattedMessage, dangerouslyUseHTMLString: true })
return Promise.reject(data)
}
ElMessage.error(data.message)
return Promise.reject(data)
}
// 成功请求(在页面上除非特殊情况,否则不用处理失败逻辑)
if (userStore.exp <= Date.now() && userStore.exp !== 0) {
userStore.setAccessToken('')
userStore.setRefreshToken('')
userStore.setIsRefreshToken(false)
userStore.setUserInfo({ id: '', name: '' })
userStore.setExp(0)
ElMessage.error('登录已过期,请重新登录!')
await router.replace(LOGIN_URL)
return Promise.reject(data)
}
// 对于blob类型的响应返回完整的response对象以保留响应头
if (response.config.responseType === 'blob') {
return response
}
return data
},
async (error: AxiosError) => {
const { response } = error
tryHideFullScreenLoading()
//console.log('error', error.message)
// 请求超时 && 网络错误单独判断,没有 response
if (error.message.indexOf('timeout') !== -1) ElMessage.error('请求超时!请您稍后重试')
if (error.message.indexOf('Network Error') !== -1) ElMessage.error('网络错误!请您稍后重试')
// 根据服务器响应的错误状态码,做不同的处理
if (response) checkStatus(response.status)
// 服务器结果都没有返回(可能服务器错误可能客户端断网),断网处理:可以跳转到断网页面
if (!window.navigator.onLine) router.replace('/500')
return Promise.reject(error)
}
)
}
/**
* @description 常用请求方法封装
*/
get<T>(url: string, params?: object, _object = {}): Promise<ResultData<T>> {
return this.service.get(url, { params, ..._object })
}
post<T>(url: string, params?: object | string, _object = {}): Promise<ResultData<T>> {
return this.service.post(url, params, _object)
}
put<T>(url: string, params?: object, _object = {}): Promise<ResultData<T>> {
return this.service.put(url, params, _object)
}
delete<T>(url: string, params?: any, _object = {}): Promise<ResultData<T>> {
return this.service.delete(url, { params, ..._object })
}
download(url: string, params?: object, _object = {}): Promise<BlobPart> {
return this.service.post(url, params, { ..._object, responseType: 'blob' }).then(res => res.data)
}
downloadWithHeaders(url: string, params?: object, _object = {}): Promise<AxiosResponse<Blob>> {
return this.service.post(url, params, { ..._object, responseType: 'blob' })
}
upload(url: string, params?: object, _object = {}): Promise<BlobPart> {
return this.service.post(url, params, {
..._object,
headers: { 'Content-Type': 'multipart/form-data' }
})
}
/**
* 针对excel的上传默认返回的是blob类型Excel没问题时返回json特殊处理
*/
uploadExcel(url: string, params?: object, _object = {}): Promise<BlobPart> {
return this.service
.post(url, params, {
..._object,
headers: { 'Content-Type': 'multipart/form-data' },
responseType: 'blob'
})
.then(res => res.data)
}
// 添加SSE连接方法
sse(url: string, params?: any): EventSource {
const userStore = useUserStore()
// 构造带参数的URL
let requestUrl = config.baseURL + url
if (params) {
const searchParams = new URLSearchParams()
for (const key in params) {
if (Object.prototype.hasOwnProperty.call(params, key)) {
searchParams.append(key, String(params[key]))
}
}
requestUrl += '?' + searchParams.toString()
}
// 全局错误信息拦截(防止下载文件的时候返回数据流,没有 code 直接报错)
if (data.code && data.code !== ResultEnum.SUCCESS) {
if(data.message.includes('&')){
const formattedMessage = data.message.split('&').join('<br>');
ElMessage.error({ message: formattedMessage, dangerouslyUseHTMLString: true });
return Promise.reject(data)
}
ElMessage.error(data.message)
return Promise.reject(data)
}
// 成功请求(在页面上除非特殊情况,否则不用处理失败逻辑)
return data
},
async (error: AxiosError) => {
const { response } = error
tryHideFullScreenLoading()
console.log('error', error.message)
// 请求超时 && 网络错误单独判断,没有 response
if (error.message.indexOf('timeout') !== -1) ElMessage.error('请求超时!请您稍后重试')
if (error.message.indexOf('Network Error') !== -1) ElMessage.error('网络错误!请您稍后重试')
// 根据服务器响应的错误状态码,做不同的处理
if (response) checkStatus(response.status)
// 服务器结果都没有返回(可能服务器错误可能客户端断网),断网处理:可以跳转到断网页面
if (!window.navigator.onLine) router.replace('/500')
return Promise.reject(error)
},
)
}
/**
* @description 常用请求方法封装
*/
get<T>(url: string, params?: object, _object = {}): Promise<ResultData<T>> {
return this.service.get(url, { params, ..._object })
}
post<T>(url: string, params?: object | string, _object = {}): Promise<ResultData<T>> {
return this.service.post(url, params, _object)
}
put<T>(url: string, params?: object, _object = {}): Promise<ResultData<T>> {
return this.service.put(url, params, _object)
}
delete<T>(url: string, params?: any, _object = {}): Promise<ResultData<T>> {
return this.service.delete(url, { params, ..._object })
}
download(url: string, params?: object, _object = {}): Promise<BlobPart> {
return this.service.post(url, params, { ..._object, responseType: 'blob' })
}
upload(url: string, params?: object, _object = {}): Promise<BlobPart> {
return this.service.post(url, params, {
..._object,
headers: { 'Content-Type': 'multipart/form-data' }
})
}
/**
* 针对excel的上传默认返回的是blob类型Excel没问题时返回json特殊处理
*/
uploadExcel(url: string, params?: object, _object = {}): Promise<BlobPart> {
return this.service.post(url, params, {
..._object,
headers: { 'Content-Type': 'multipart/form-data' },
responseType: 'blob',
})
}
// 创建EventSource连接
const eventSource = new EventSourcePolyfill(requestUrl, {
headers: {
Authorization: 'Bearer ' + userStore.accessToken
},
// 增加超时时间到1200秒
heartbeatTimeout: 1200000
})
// 设置默认的Authorization头部
eventSource.addEventListener('open', function () {
//console.log('SSE连接已建立')
})
// 添加错误处理
eventSource.addEventListener('error', function (err) {
console.error('SSE连接错误:', err)
})
return eventSource
}
}
export default new RequestHttp(config)

View File

@@ -15,6 +15,7 @@ export interface Result {
* 请求响应参数包含data
*/
export interface ResultData<T = any> extends Result {
map(arg0: (item: any) => { label: any; value: any; }): { label: string; value: string; }[] | { label: string; value: string; }[];
data: T;
}

View File

@@ -7,7 +7,7 @@ export namespace Plan {
export interface ResPlan {
id: string; //检测计划ID
name: string; //检测计划名称
pattern: string; //模式,字典表(数字、模拟、比对)
pattern?: string; //模式,字典表(数字、模拟、比对)
fatherPlanId?: string; //父计划ID
dataSourceId: string; //数据源ID
scriptId: string; //检测脚本ID
@@ -22,6 +22,23 @@ export namespace Plan {
createTime?:string; //创建时间
updateBy?:string; //更新用户
updateTime?:string; //更新时间
associateReport:number;//是否关联报告模板 0否 1是
reportTemplateName:string;
reportTemplateVersion:string;
dataRule:string;//数据处理原则
standardDevIds:string[];
standardDevMap:Map<string,number>;//标准设备
testItems:string[];//测试项
Check_By?:string;//计划检测人
progress?: number; // 进度百分比,例如 75
children?: ResPlan[];
testConfig?: PlanTestConfig;
importFlag?: number; // 导入标识0-否1-是
leader?: string; // 负责人
memberIds?: string | string[]; //成员
members?: string; //成员字符串
}
// 检测计划 + 分页
@@ -30,15 +47,27 @@ export namespace Plan {
}
export interface ReqPlan extends ResPlan {
datasourceIds:string;
sourceIds: string;
datasourceIds:string | string[];
sourceIds: string | null;
planId:string;
scriptName: string ;
errorSysName: string;
sourceName: string ;
standardDevNameStr: string;
testItemNameStr:string;
devIds: string[];
}
export interface PlanTestConfig {
planId: string;
waveRecord: number;
realTime: number;
statistics: number;
flicker: number;
maxTime: number;
}
}

View File

@@ -2,54 +2,53 @@ import type { Plan } from './interface'
import http from '@/api'
import type { ErrorSystem } from '../device/interface/error'
import type { Device } from '../device/interface/device'
import { ReqDevReportParams } from '@/api/device/interface/device'
/**
* @name 检测计划管理模块
*/
// 获取检测计划列表
export const getPlanList = (params: Plan.ReqPlanParams) => {
return http.post(`/adPlan/list`, params)
return http.post(`/adPlan/list`, params)
}
// 新增检测计划
export const addPlan = (params: any) => {
return http.post(`/adPlan/add`, params)
return http.post(`/adPlan/add`, params)
}
// 编辑检测计划
export const updatePlan = (params: any) => {
return http.post(`/adPlan/update`, params)
return http.post(`/adPlan/update`, params)
}
// 删除检测计划
export const deletePlan = (params: { id: string[] }) => {
return http.post(`/adPlan/delete`, params)
export const deletePlan = (params: { id: string[]; pattern: string }) => {
return http.post(`/adPlan/delete?pattern=${params.pattern}`, params.id)
}
// 获取指定模式下所有检测源
// 获取指定模式下所有检测源
export const getTestSourceList = (params: Plan.ReqPlan) => {
return http.get(`/pqSource/getAll?patternId=${params.pattern}`)
return http.get(`/pqSource/getAll?patternId=${params.pattern}`)
}
// 获取指定模式下所有检测脚本
export const getPqScriptList = (params: Plan.ReqPlan) => {
return http.get(`/pqScript/getAll?patternId=${params.pattern}`)
return http.get(`/pqScript/getAll?patternId=${params.pattern}`)
}
//获取所有误差体系
export const getPqErrSysList = () => {
return http.get<ErrorSystem.ErrorSystemList>(`/pqErrSys/getAll`)
return http.get<ErrorSystem.ErrorSystemList>(`/pqErrSys/getAll`)
}
//获取指定模式下所有未绑定的设备
export const getUnboundPqDevList = (params: Plan.ReqPlan) => {
return http.get(`/pqDev/listUnbound?pattern=${params.pattern}`)
export const getUnboundPqDevList = (params: { pattern: string}) => {
return http.get(`/pqDev/listUnbound?pattern=${params.pattern}`)
}
//根据检测计划id查询出所有已绑定的设备
export const getBoundPqDevList = (params: any) => {
return http.post(`/pqDev/listByPlanId`, params)
return http.post(`/adPlan/listByPlanId`, params)
}
//检测计划绑定设备
@@ -59,29 +58,105 @@ export const getBoundPqDevList = (params: any) => {
// 按照模式查询检测计划(用于首页展示)
export const getPlanListByPattern = (params: Plan.ReqPlan) => {
return http.get(`/adPlan/listByPattern?pattern=${params.pattern}`)
return http.get(`/adPlan/listByPattern?pattern=${params.pattern}`)
}
// 导出检测计划
export const exportPlan = (params: Device.ReqPqDevParams) => {
return http.download(`/adPlan/export`, params)
return http.download(`/adPlan/export`, params)
}
// 下载模板
export const downloadTemplate = () => {
return http.download(`/adPlan/downloadTemplate`)
export const downloadTemplate = (params: { patternId: string }) => {
return http.download(`/adPlan/downloadTemplate`, params)
}
// 导入检测计划
export const importPlan = (params: Device.ReqPqDevParams) => {
return http.upload(`/adPlan/import`, params)
return http.uploadExcel(`/adPlan/import`, params)
}
// 装置检测报告生成
export const generateDevReport = (params: Device.ReqDevReportParams) => {
return http.post(`/report/generateReport`, params)
return http.post(`/report/generateReport`, params)
}
// 装置检测报告下载
export const downloadDevData = (params: Device.ReqDevReportParams) => {
return http.download(`/report/downloadReport`, params)
return http.download(`/report/downloadReport`, params)
}
// 装置检测报告下载(带响应头)
export const downloadDevDataWithHeaders = (params: Device.ReqDevReportParams) => {
return http.downloadWithHeaders(`/report/downloadReport`, params)
}
export const staticsAnalyse = (params: { id: string[] }) => {
return http.download('/adPlan/analyse', params)
}
//根据计划id分页查询被检设
export const getDevListByPlanId = (params: any) => {
return http.post(`/adPlan/listDevByPlanId`, params)
}
//修改子计划名称
export const updateSubPlanName = (params: Plan.ReqPlan) => {
return http.get(`/adPlan/updateSubPlanName?planId=${params.id}&name=${params.name}`)
}
//子计划绑定/解绑标准设备
export const subPlanBindStandardDevList = (params: Plan.ReqPlan) => {
return http.post(`/adPlan/updateBindStandardDev`, params)
}
//子计划绑定/解绑被检设备
export const subPlanBindDev = (params: Plan.ReqPlan) => {
return http.post(`/adPlan/updateBindDev`, params)
}
//根据父计划ID获取未被子计划绑定的标准设备
export const getUnboundStandardDevList = (params: Plan.ResPlan) => {
return http.get(`/adPlan/getUnBoundStandardDev?fatherPlanId=${params.fatherPlanId}`)
}
//根据计划ID获取已绑定的标准设备
export const getBoundStandardDevList = (params: Plan.ResPlan) => {
return http.get(`/adPlan/getBoundStandardDev?planId=${params.id}`)
}
//根据计划ID获取已绑定的所有标准设备
export const getBoundStandardDevAllList = (params: { id: string }) => {
return http.get(`/adPlan/getBoundStandardDev?planId=${params.id}&all=1`)
}
// 导出子计划
export const exportSubPlan = (params: Plan.ResPlan) => {
return http.download(`/adPlan/exportSubPlan?planId=${params.id}`)
}
// 导入子检测计划
export const importSubPlan = (params: Plan.ResPlan) => {
return http.upload(`/adPlan/importSubPlan`, params)
}
// 导出计划检测结果数据
export const exportPlanCheckData = (params: any) => {
return http.post(
`/adPlan/exportPlanCheckData?planId=${params.id}&devIds=${params.devIds}&report=${params.report}`
)
}
//根据误差体系id获取测试项
export const getPqErrSysTestItemList = (params: {errorSysId : string}) => {
return http.get(`/pqErrSys/getTestItems?id=${params.errorSysId}`)
}
// 获取计划项目成员
export const getMemberList = (params: {id : string}) => {
return http.get(`/adPlan/getMemberList?planId=${params.id}`)
}
// 导入并合并子检测计划检测结果数据
export const importAndMergePlanCheckData = (params: Plan.ResPlan) => {
return http.upload(`/adPlan/importAndMergePlanCheckData`, params)
}

View File

@@ -0,0 +1,51 @@
export interface MonitorResult {
/**
* 监测点id
*/
monitorId: string;
/**
* 监测点序号
*/
monitorNum: number;
/**
* 总检测次数
*/
totalNum: number;
/**
* 合格检测次数
*/
qualifiedNum: number;
/**
* 不合格检测次数
*/
unQualifiedNum: number;
/**
* 误差体系名称
*/
errorSysName: string;
/**
* 检测结果
*/
checkResult: number;
/**
* 哪次
*/
whichTime: string;
/**
* 结论来源
*/
resultOrigin: string;
/**
* 来源类型
*/
resultType: string;
}

View File

@@ -0,0 +1,7 @@
import http from '@/api'
export const getMonitorResult = (devId: string) => http.post(`/result/getMonitorResult?devId=${devId}`)
export const getMonitorDataSourceResult = (monitorId: string) =>
http.get(`/result/getMonitorDataSourceResult?monitorId=${monitorId}`)
export const updateMonitorResult = (data: any) => http.post('/result/updateMonitorResult', data)

View File

@@ -13,9 +13,9 @@ export const closePreTest = (params) => {
* 开始正式检测
* @param params
*/
export const startTest = (params) => {
return http.post(`/prepare/startTest`, params, {loading: false})
}
// export const startTest = (params) => {
// return http.post(`/prepare/startTest`, params, {loading: false})
// }
/**
* 暂停正式检测
@@ -32,3 +32,15 @@ export const pauseTest = () => {
export const resumeTest = (params) => {
return http.post(`/prepare/restartTemTest/`, params, {loading: false})
}
/**
* 比对式通道配对
* @param params
*/
export const contrastTest = (params: any) => {
return http.post(`/prepare/startContrastTest`,params)
}
export const exportAlignData= () => {
return http.download(`/prepare/exportAlignData`)
}

View File

@@ -1,3 +1,4 @@
import http from '@/api'
import {type Base} from '@/api/system/base/interface'
@@ -12,6 +13,10 @@ export const updateTestConfig = (params: Base.ResTestConfig) => {
return http.post(`/sysTestConfig/update`, params)
}
//场景切换
export const updateScene = (params: any) => {
return http.post(`/sysTestConfig/update`,params)
}

View File

@@ -8,8 +8,8 @@ export namespace Base {
id: string; //系统配置表Id
autoGenerate:number;//检测报告是否自动生成0 否1是
maxTime:number;//最大复检次数默认3次
dataRule:string;//数据处理原则,关联字典所有值、部分值、cp95值、平均值、任意值默认任意值
state: number; //状态
scale:number;//小数位
createBy?: string| null; //创建用户
createTime?: string| null; //创建时间
updateBy?: string| null; //更新用户

View File

@@ -1,10 +1,16 @@
import http from '@/api'
import { type Dict } from '@/api/system/dictionary/interface'
import { c } from 'vite/dist/node/types.d-aGj9QkWt'
//获取字典类型
export const getDictTreeList = (params: Dict.ResDictTree) => {
const name = params.name || '';
return http.get(`/dictTree/getTree?keyword=${name}`, params)
export const getDictTreeByCode = (params: Dict.ResDictTree) => {
const code = params.code || ''
return http.get(`/dictTree/getTreeByCode?code=${code}`, { loading: true })
}
export const getDictTreeByName = (params: Dict.ResDictTree) => {
const name = params.name || ''
return http.get(`/dictTree/getTreeByName?name=${name}`)
}
//添加字典类型
@@ -21,4 +27,3 @@ export const updateDictTree = (params: Dict.ResDictTree) => {
export const deleteDictTree = (params: Dict.ResDictTree) => {
return http.post(`/dictTree/delete?id=${params.id}`)
}

View File

@@ -0,0 +1,16 @@
import type {AuditLog } from '@/api/system/log/interface/log.ts'
import http from '@/api'
/**
* @name 审计日志管理模块
*/
//获取审计日志
export const getAuditLog = (params: AuditLog.ReqAuditLogParams) => {
return http.post(`/sysLog/list`, params)
}
export const exportCsv = (params: AuditLog.ReqAuditLogParams) => {
return http.download(`/sysLog/exportCSV`, params)
}

View File

@@ -1,15 +1,36 @@
// 审计日志管理模块
export namespace Sys_Log_Audit {
// 日志列表
export interface Audit_LogList {
id: string;//日志表Id
operate_Type:string;//日志类型
ip:string;//操作IP
result: string;//事件结果
remark: string;//事件描述
warn:number;//告警标志
create_By:string;//创建用户
create_Time:string;//创建时间
}
import type { ReqPage, ResPage } from '@/api/interface'
}
// 审计日志管理模块
export namespace AuditLog {
/**
* 设备类型数据表格分页查询参数
*/
export interface ReqAuditLogParams extends ReqPage {
id: string; //审计日志Id 必填
createTime?: string; //创建时间
}
/**
* 设备类型新增、修改、根据id查询返回的对象
*/
export interface ResAuditLog {
id: string;//审计日志Id
operate_Type:string;//操作类型
ip:string;//操作IP
result: string;//事件结果
remark: string;//事件描述
level:number;//告警等级
warn:number;//告警标志
create_By:string;//创建用户
create_Time:string;//创建时间
sort:number;//排序
}
/**
* 设备类型表格查询分页返回的对象;
*/
export interface ResAuditLogPage extends ResPage<ResAuditLog> {
}
}

View File

@@ -1,12 +1,11 @@
import http from '@/api'
import {type VersionRegister} from '@/api/system/versionRegister/interface'
import { type VersionRegister } from '@/api/system/versionRegister/interface'
//获取有效数据配置
export const getRegRes = (params: VersionRegister.ResSys_Reg_Res) => {
export const getRegRes = (params: { type: string }) => {
return http.get(`/sysRegRes/getRegResByType?id=${params.type}`)
}
//编辑有效数据配置
export const updateRegRes = (params: VersionRegister.Sys_Reg_Res) => {
return http.post(`/sysRegRes/update`, params)

View File

@@ -1,21 +1,23 @@
// 登录模块
import type { ReqPage,ResPage } from '@/api/interface'
import type { ReqPage, ResPage } from '@/api/interface'
export namespace Login {
export interface ReqLoginForm {
username: string;
password: string;
}
export interface ResLogin {
accessToken: string;
refreshToken: string;
userInfo:{
name: string;
export interface ReqLoginForm {
username: string
password: string
checked: boolean
}
export interface ResLogin {
accessToken: string
refreshToken: string
userInfo: {
id: string
name: string
}
}
export interface ResAuthButtons {
[key: string]: string[]
}
}
export interface ResAuthButtons {
[key: string]: string[];
}
}
@@ -50,6 +52,8 @@ export namespace User {
updateTime?: string;//更新时间
roleIds?: string[]; //
roleNames?:string[]; //
roleCodes?:string[]; //
disabled?: boolean;
}
// 用户接口

View File

@@ -1,40 +1,60 @@
import type { Login } from '@/api/user/interface/user'
import { ADMIN as rePrefix } from '@/api/system/config/serviceName'
import { pa } from 'element-plus/es/locale/index.mjs';
import type {Login} from '@/api/user/interface/user'
import {ADMIN as rePrefix} from '@/api/system/config/serviceName'
import http from '@/api'
import type { Dict } from '@/api/interface'
import type {Dict} from '@/api/interface'
/**
* @name 登录模块
*/
// 用户登录
export const loginApi = (params: Login.ReqLoginForm) => {
return http.post<Login.ResLogin>(`${rePrefix}/login`, params, { loading: false })
// return http.post<Login.ResLogin>(`/Register1`, params, { loading: false })
export const loginApi = (params: { username: string; password: string}) => {
return http.post<Login.ResLogin>(`${rePrefix}/login`, params, {loading: false})
// return http.post<Login.ResLogin>(`/Register1`, params, { loading: false })
}
// 获取菜单列表
export const getAuthMenuListApi = () => {
return http.get<Menu.MenuOptions[]>(`/sysFunction/getMenu`, {}, { loading: false })
// return http.post<Menu.MenuOptions[]>(`/Register2`, {}, { loading: false })
return http.get<Menu.MenuOptions[]>(`/sysFunction/getMenu`, {}, {loading: false})
// return http.post<Menu.MenuOptions[]>(`/Register2`, {}, { loading: false })
}
// 获取按钮权限
export const getAuthButtonListApi = () => {
return http.get<Login.ResAuthButtons>(`/sysFunction/getButton`, {}, { loading: false })
// return http.post<Login.ResAuthButtons>(`/Register3`, {}, { loading: false })
return http.get<Login.ResAuthButtons>(`/sysFunction/getButton`, {}, {loading: false})
// return http.post<Login.ResAuthButtons>(`/Register3`, {}, { loading: false })
}
// 用户退出登录
export const logoutApi = () => {
return http.post(`${rePrefix}/logout`)
return http.post(`${rePrefix}/logout`)
}
//获取下拉框列表
export const getDictList = () =>{
return http.get<Dict>('/dictData/dictDataCache')
export const getDictList = () => {
return http.get<Dict>('/dictData/dictDataCache')
}
//token刷新
export const refreshToken = () => {
return http.get<Login.ResLogin>(`${rePrefix}/refreshToken`,
{},
{ loading: false }
return http.get<Login.ResLogin>(`${rePrefix}/refreshToken`,
{},
{loading: false}
)
}
//获取场景
export const getCurrentScene = () => {
return http.get('/sysTestConfig/getCurrentScene', {}, {loading: false})
}
/**
* 获取RSA公钥
*/
export const getPublicKey = (username: string) => {
return http.get(`/admin/getPublicKey?username=${username}`, {}, {loading: false})
}
/**
* 获取是否在检测中自动生成报告
*/
export const getAutoGenerate = () => {
return http.get('/sysTestConfig/getAutoGenerate', {}, {loading: false})
}

View File

@@ -37,3 +37,7 @@ export const updatePassWord = (params: User.ResPassWordUser) => {
return http.post(`/sysUser/updatePassword`,params)
}
// 获取所有用户
export const getAllUser= () => {
return http.get(`/sysUser/getAll`)
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

View File

@@ -1,19 +1,20 @@
<template>
<div class="not-container">
<img src="@/assets/images/403.png" class="not-img" alt="403" />
<div class="not-detail">
<h2>403</h2>
<h4>抱歉您无权访问该页面~🙅🙅</h4>
<el-button type="primary" @click="router.back"> 返回上一页 </el-button>
<div class="not-container">
<img src="@/assets/images/403.png" class="not-img" alt="403" />
<div class="not-detail">
<h2>403</h2>
<h4>抱歉您无权访问该页面~🙅🙅</h4>
<el-button type="primary" @click="router.back">返回上一页</el-button>
</div>
</div>
</div>
</template>
<script setup lang="ts" name="403">
import { useRouter } from "vue-router";
const router = useRouter();
import { useRouter } from 'vue-router'
const router = useRouter()
</script>
<style scoped lang="scss">
@import "./index.scss";
@use './index.scss';
</style>

View File

@@ -1,19 +1,20 @@
<template>
<div class="not-container">
<img src="@/assets/images/404.png" class="not-img" alt="404" />
<div class="not-detail">
<h2>404</h2>
<h4>抱歉您访问的页面不存在~🤷🤷</h4>
<el-button type="primary" @click="router.back"> 返回上一页 </el-button>
<div class="not-container">
<img src="@/assets/images/404.png" class="not-img" alt="404" />
<div class="not-detail">
<h2>404</h2>
<h4>抱歉您访问的页面不存在~🤷🤷</h4>
<el-button type="primary" @click="router.back">返回上一页</el-button>
</div>
</div>
</div>
</template>
<script setup lang="ts" name="404">
import { useRouter } from "vue-router";
const router = useRouter();
import { useRouter } from 'vue-router'
const router = useRouter()
</script>
<style scoped lang="scss">
@import "./index.scss";
@use './index.scss';
</style>

View File

@@ -1,19 +1,20 @@
<template>
<div class="not-container">
<img src="@/assets/images/500.png" class="not-img" alt="500" />
<div class="not-detail">
<h2>500</h2>
<h4>抱歉您的网络不见了~🤦🤦</h4>
<el-button type="primary" @click="router.back"> 返回上一页 </el-button>
<div class="not-container">
<img src="@/assets/images/500.png" class="not-img" alt="500" />
<div class="not-detail">
<h2>500</h2>
<h4>抱歉您的网络不见了~🤦🤦</h4>
<el-button type="primary" @click="router.back">返回上一页</el-button>
</div>
</div>
</div>
</template>
<script setup lang="ts" name="500">
import { useRouter } from "vue-router";
const router = useRouter();
import { useRouter } from 'vue-router'
const router = useRouter()
</script>
<style scoped lang="scss">
@import "./index.scss";
@use './index.scss';
</style>

View File

@@ -1,58 +1,69 @@
<template>
<el-dialog v-model='dialogVisible' :title='`批量添加${parameter.title}`' :destroy-on-close='true' width='580px'
draggable>
<el-form class='drawer-multiColumn-form' label-width='100px'>
<el-form-item label='模板下载 :'>
<el-button type='primary' :icon='Download' @click='downloadTemp'> 点击下载</el-button>
</el-form-item>
<el-form-item label='文件上传 :'>
<el-upload
action='#'
class='upload'
:drag='true'
:limit='excelLimit'
:multiple='true'
:show-file-list='true'
:http-request='uploadExcel'
:before-upload='beforeExcelUpload'
:on-exceed='handleExceed'
:accept="parameter.fileType!.join(',')"
>
<slot name='empty'>
<el-icon class='el-icon--upload'>
<upload-filled />
</el-icon>
<div class='el-upload__text'>将文件拖到此处<em>点击上传</em></div>
</slot>
<template #tip>
<slot name='tip'>
<div class='el-upload__tip'>请上传 .xls , .xlsx 标准格式文件文件最大为 {{ parameter.fileSize }}M</div>
</slot>
</template>
</el-upload>
</el-form-item>
<el-form-item v-if='parameter.showCover' label='数据覆盖 :'>
<el-switch v-model='isCover' />
</el-form-item>
</el-form>
</el-dialog>
<el-dialog
v-model="dialogVisible"
:title="`批量添加${parameter.title}`"
:destroy-on-close="true"
width="580px"
draggable
>
<el-form class="drawer-multiColumn-form" label-width="100px">
<el-form-item label="模板下载 :">
<el-button type="primary" :icon="Download" @click="downloadTemp">点击下载</el-button>
</el-form-item>
<el-form-item label="文件上传 :">
<el-upload
action="#"
class="upload"
:drag="true"
:limit="excelLimit"
:multiple="true"
:show-file-list="true"
:http-request="uploadExcel"
:before-upload="beforeExcelUpload"
:on-exceed="handleExceed"
:accept="parameter.fileType!.join(',')"
>
<slot name="empty">
<el-icon class="el-icon--upload">
<upload-filled />
</el-icon>
<div class="el-upload__text">
将文件拖到此处
<em>点击上传</em>
</div>
</slot>
<template #tip>
<slot name="tip">
<div class="el-upload__tip">
请上传 .xls , .xlsx 标准格式文件文件最大为 {{ parameter.fileSize }}M
</div>
</slot>
</template>
</el-upload>
</el-form-item>
<el-form-item v-if="parameter.showCover" label="数据覆盖 :">
<el-switch v-model="isCover" />
</el-form-item>
</el-form>
</el-dialog>
</template>
<script setup lang='ts' name='ImportExcel'>
<script setup lang="ts" name="ImportExcel">
import { ref } from 'vue'
import { useDownload } from '@/hooks/useDownload'
import { Download } from '@element-plus/icons-vue'
import { ElNotification, UploadRequestOptions, UploadRawFile, ElMessage } from 'element-plus'
import { ElMessage, ElNotification, UploadRawFile, UploadRequestOptions } from 'element-plus'
export interface ExcelParameterProps {
title: string; // 标题
showCover?: boolean; // 是否显示”数据覆盖“选项
patternId?: string; // 模式ID
fileSize?: number; // 上传文件的大小
fileType?: File.ExcelMimeType[]; // 上传文件的类型
tempApi?: (params: any) => Promise<any>; // 下载模板的Api
importApi?: (params: any) => Promise<any>; // 批量导入的Api
getTableList?: () => void; // 获取表格数据的Api
title: string // 标题
showCover?: boolean // 是否显示”数据覆盖“选项
patternId?: string // 模式ID
planId?: string | null //计划ID
fileSize?: number // 上传文件的大小
fileType?: File.ExcelMimeType[] // 上传文件的类型
tempApi?: (params: any) => Promise<any> // 下载模板的Api
importApi?: (params: any) => Promise<any> // 批量导入的Api
getTableList?: () => void // 获取表格数据的Api
}
// 是否覆盖数据
@@ -63,66 +74,77 @@ const excelLimit = ref(1)
const dialogVisible = ref(false)
// 父组件传过来的参数
const parameter = ref<ExcelParameterProps>({
title: '',
fileSize: 5,
fileType: ['application/vnd.ms-excel', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'],
title: '',
fileSize: 5,
fileType: ['application/vnd.ms-excel', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet']
})
const emit = defineEmits<{
(e: 'result', data: boolean): void
}>()
// 接收父组件参数
const acceptParams = (params: ExcelParameterProps) => {
parameter.value = { ...parameter.value, ...params }
dialogVisible.value = true
parameter.value = { ...parameter.value, ...params }
dialogVisible.value = true
}
// Excel 导入模板下载
const downloadTemp = () => {
if (!parameter.value.tempApi) return
useDownload(parameter.value.tempApi, `${parameter.value.title}模板`, {}, false)
if (!parameter.value.tempApi) return
useDownload(parameter.value.tempApi, `${parameter.value.title}模板`, { pattern: parameter.value.patternId }, false)
}
// 文件上传
const uploadExcel = async (param: UploadRequestOptions) => {
let excelFormData = new FormData()
excelFormData.append('file', param.file)
if (parameter.value.patternId) {
excelFormData.append('patternId', parameter.value.patternId)
}
isCover.value && excelFormData.append('isCover', isCover.value as unknown as Blob)
//await parameter.value.importApi!(excelFormData);
await parameter.value.importApi!(excelFormData)
.then(res => handleImportResponse(res))
parameter.value.getTableList && parameter.value.getTableList()
dialogVisible.value = false
let excelFormData = new FormData()
excelFormData.append('file', param.file)
if (parameter.value.patternId) {
excelFormData.append('patternId', parameter.value.patternId)
}
excelFormData.append('planId', parameter.value.planId)
isCover.value && excelFormData.append('isCover', isCover.value as unknown as Blob)
//await parameter.value.importApi!(excelFormData);
await parameter.value.importApi!(excelFormData).then(res => handleImportResponse(res))
parameter.value.getTableList && parameter.value.getTableList()
dialogVisible.value = false
}
async function handleImportResponse(res: any) {
if (res.type === 'application/json') {
const fileReader = new FileReader()
fileReader.onloadend = () => {
try {
const jsonData = JSON.parse(fileReader.result)
if (jsonData.code === 'A0000') {
ElMessage.success('导入成功')
} else {
ElMessage.error(jsonData.message)
if (res.type === 'application/json') {
const fileReader = new FileReader()
fileReader.onloadend = () => {
try {
const jsonData = JSON.parse(fileReader.result)
if (jsonData.code === 'A0000') {
ElMessage.success('导入成功')
} else {
ElMessageBox.alert(jsonData.message, {
title: '导入结果',
type: 'error'
})
}
emit('result', jsonData.data)
} catch (err) {
//console.log(err)
}
}
} catch (err) {
console.log(err)
}
fileReader.readAsText(res)
} else {
emit('result', false)
ElMessage.error('导入失败,请查看下载附件!')
let blob = new Blob([res], { type: 'application/vnd.ms-excel' })
const url = window.URL.createObjectURL(blob)
const link = document.createElement('a')
link.href = url
link.download = '导入失败数据'
document.body.appendChild(link)
link.click()
link.remove()
}
fileReader.readAsText(res)
} else {
ElMessage.error('导入失败,请查看下载附件!')
let blob = new Blob([res], { type: 'application/vnd.ms-excel' })
const url = window.URL.createObjectURL(blob)
const link = document.createElement('a')
link.href = url
link.download = '终端入网检测失败列表'
document.body.appendChild(link)
link.click()
link.remove()
}
}
/**
@@ -130,32 +152,32 @@ async function handleImportResponse(res: any) {
* @param file 上传的文件
* */
const beforeExcelUpload = (file: UploadRawFile) => {
const isExcel = parameter.value.fileType!.includes(file.type as File.ExcelMimeType)
const fileSize = file.size / 1024 / 1024 < parameter.value.fileSize!
if (!isExcel)
ElNotification({
title: '温馨提示',
message: '上传文件只能是 xls / xlsx 格式!',
type: 'warning',
})
if (!fileSize)
setTimeout(() => {
ElNotification({
title: '温馨提示',
message: `上传文件大小不能超过 ${parameter.value.fileSize}MB`,
type: 'warning',
})
}, 0)
return isExcel && fileSize
const isExcel = parameter.value.fileType!.includes(file.type as File.ExcelMimeType)
const fileSize = file.size / 1024 / 1024 < parameter.value.fileSize!
if (!isExcel)
ElNotification({
title: '温馨提示',
message: '上传文件只能是 xls / xlsx 格式!',
type: 'warning'
})
if (!fileSize)
setTimeout(() => {
ElNotification({
title: '温馨提示',
message: `上传文件大小不能超过 ${parameter.value.fileSize}MB`,
type: 'warning'
})
}, 0)
return isExcel && fileSize
}
// 文件数超出提示
const handleExceed = () => {
ElNotification({
title: '温馨提示',
message: '最多只能上传一个文件!',
type: 'warning',
})
ElNotification({
title: '温馨提示',
message: '最多只能上传一个文件!',
type: 'warning'
})
}
// 上传错误提示
@@ -177,9 +199,9 @@ const handleExceed = () => {
// }
defineExpose({
acceptParams,
acceptParams
})
</script>
<style lang='scss' scoped>
@import "./index.scss";
<style lang="scss" scoped>
@use './index.scss';
</style>

View File

@@ -0,0 +1,3 @@
.upload {
width: 80%;
}

View File

@@ -0,0 +1,241 @@
<template>
<el-dialog
v-model="dialogVisible"
:title="parameter.title"
:destroy-on-close="true"
width="450px"
:close-on-click-modal="!parameter.progressBar"
:show-close="!disable"
draggable
>
<el-upload
ref="uploadRef"
action="#"
class="upload"
:limit="1"
:http-request="uploadZip"
accept=".zip"
:auto-upload="!parameter.confirmMessage"
:on-change="handleChange"
:on-remove="handleRemove"
:disabled="fileDisabled"
>
<slot name="empty">
<el-button type="primary" :disabled="fileDisabled" icon="Upload">点击上传</el-button>
</slot>
<template #tip>
<slot name="tip">
<div class="el-upload__tip">请上传 .zip 标准格式文件</div>
</slot>
</template>
</el-upload>
<el-text v-if="parameter.progressBar && progressData.status === 'exception'" size="small" type="danger">
{{ progressData.message }}
</el-text>
<el-text v-if="parameter.progressBar && progressData.status === 'success'" size="small" type="success">
{{ progressData.message }}
</el-text>
<el-text v-if="parameter.progressBar && progressData.status === ''" size="small" type="info">
{{ progressData.message }}
</el-text>
<el-progress
style="margin-top: 10px; margin-bottom: 10px"
v-if="parameter.progressBar"
:status="progressData.status"
:percentage="progressData.percentage"
:stroke-width="10"
striped
striped-flow
></el-progress>
<template #footer v-if="parameter.confirmMessage">
<el-button :disabled="disable" type="primary" @click="uploadSubmit">开始导入</el-button>
</template>
</el-dialog>
</template>
<script setup lang="ts" name="ImportZip">
import { ref } from 'vue'
import type { UploadInstance, UploadProps, UploadRequestOptions } from 'element-plus'
import http from '@/api'
export interface ZipParameterProps {
title: string // 标题
patternId?: string // 模式ID
planId?: string // 计划ID
importApi?: (params: any) => Promise<any> // 批量导入的Api
confirmMessage?: string // 提示信息
progressBar?: boolean // 进度条
}
// dialog状态
const dialogVisible = ref(false)
const disable = ref(true)
const fileDisabled = ref(false)
const uploadRef = ref<UploadInstance>()
// 父组件传过来的参数
const parameter = ref<ZipParameterProps>({
title: ''
})
const emit = defineEmits<{
(e: 'result', data: boolean): void
}>()
// 接收父组件参数
const acceptParams = (params: ZipParameterProps) => {
parameter.value = { ...parameter.value, ...params }
dialogVisible.value = true
}
// 文件上传
const uploadZip = (param: UploadRequestOptions) => {
let zipFormData = new FormData()
zipFormData.append('file', param.file)
if (parameter.value.patternId) {
zipFormData.append('patternId', parameter.value.patternId)
}
if (parameter.value.planId) {
zipFormData.append('planId', parameter.value.planId)
}
if (parameter.value.progressBar) {
initSSE()
}
setTimeout(() => {
parameter.value.importApi!(zipFormData)
.then(res => handleImportResponse(res))
.catch(err => {
fileDisabled.value = false
disable.value = false
})
}, 1000)
}
const handleImportResponse = (res: any) => {
if (!parameter.value.progressBar) {
if (res.code === 'A0000') {
ElMessage.success('导入成功')
emit('result', true)
dialogVisible.value = false
} else {
ElMessage.error(res.message)
fileDisabled.value = false
disable.value = false
}
} else {
if (res.code !== 'A0000') {
ElMessage.error(res.message)
}
}
}
const uploadSubmit = () => {
if (!uploadRef.value) {
return ElMessage.warning('请选择文件!')
}
progressData.value = {
percentage: 0,
status: '',
message: ''
}
ElMessageBox.confirm(parameter.value.confirmMessage, '温馨提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
})
.then(() => {
disable.value = true
fileDisabled.value = true
uploadRef.value?.submit()
})
.catch(() => {
disable.value = false
fileDisabled.value = false
})
}
const handleChange: UploadProps['onChange'] = (uploadFile, uploadFiles) => {
disable.value = uploadFiles.length === 0
progressData.value = {
percentage: 0,
status: '',
message: ''
}
}
const handleRemove: UploadProps['onRemove'] = (uploadFile, uploadFiles) => {
disable.value = uploadFiles.length === 0
progressData.value = {
percentage: 0,
status: '',
message: ''
}
}
const progressData = ref({
percentage: 0,
status: '',
message: ''
})
const eventSource = ref<EventSource | null>(null)
const initSSE = () => {
eventSource.value = http.sse('/sse/createSse')
eventSource.value.onmessage = event => {
// console.log('收到消息内容是:', event.data)
const res = JSON.parse(event.data)
progressData.value.percentage = res.data
progressData.value.message = res.message
if (res.code === 'A0002') {
fileDisabled.value = false
disable.value = false
progressData.value.status = 'exception'
ElMessage.error(res.message)
}
if (progressData.value.percentage === 100) {
progressData.value.status = 'success'
eventSource.value!.close()
ElMessage.success('导入成功')
emit('result', true)
dialogVisible.value = false
}
}
eventSource.value.onerror = error => {
console.warn('SSE 连接出错:', error)
eventSource.value!.close()
}
}
// 添加一个手动关闭EventSource的函数
const closeEventSource = () => {
if (eventSource.value) {
eventSource.value.close()
eventSource.value = null
// console.log('SSE连接已关闭')
}
}
// 监听 dialogVisible 的变化,确保在对话框关闭时清理资源
watch(dialogVisible, newVal => {
if (!newVal) {
// 延迟执行,确保在组件完全关闭后清理
setTimeout(() => {
closeEventSource()
fileDisabled.value = false
disable.value = false
progressData.value = {
percentage: 0,
status: '',
message: ''
}
}, 100)
}
})
onUnmounted(() => {
closeEventSource()
})
defineExpose({
acceptParams
})
</script>
<style lang="scss" scoped>
@use './index.scss';
</style>

View File

@@ -9,5 +9,5 @@
<script setup lang="ts" name="Loading"></script>
<style scoped lang="scss">
@import "./index.scss";
@use "./index.scss";;
</style>

View File

@@ -119,6 +119,7 @@ import TableColumn from './components/TableColumn.vue'
import Sortable from 'sortablejs'
export interface ProTableProps {
columns: ColumnProps[]; // 列配置项 ==> 必传
data?: any[]; // 静态 table data 数据,若存在则不会使用 requestApi 返回的 data ==> 非必传
requestApi?: (params: any) => Promise<any>; // 请求表格数据的 api ==> 非必传

View File

@@ -1,49 +1,49 @@
<template>
<div class='icon-box' >
<el-input
ref='inputRef'
v-model='valueIcon'
v-bind='$attrs'
:placeholder='placeholder'
:clearable='clearable'
@clear='clearIcon'
@click='openDialog'
>
<template #append>
<el-button :icon='customIcons[iconValue]' />
</template>
</el-input>
<el-dialog v-model='dialogVisible' :title='placeholder' top='5%' width='30%' >
<el-input v-model='inputValue' placeholder='搜索图标' size='large' :prefix-icon='Icons.Search' />
<el-scrollbar v-if='Object.keys(iconsList).length'>
<div class='icon-list'>
<div v-for='item in iconsList' :key='item' class='icon-item' @click='selectIcon(item)'>
<component :is='item'></component>
<span>{{ item.name }}</span>
</div>
</div>
</el-scrollbar>
<el-empty v-else description='未搜索到您要找的图标~' />
</el-dialog>
</div>
<div class="icon-box">
<el-input
ref="inputRef"
v-model="valueIcon"
v-bind="$attrs"
:placeholder="placeholder"
:clearable="clearable"
@clear="clearIcon"
@click="openDialog"
>
<template #append>
<el-button :icon="customIcons[iconValue]" />
</template>
</el-input>
<el-dialog v-model="dialogVisible" :title="placeholder" top="5%" width="30%">
<el-input v-model="inputValue" placeholder="搜索图标" size="large" :prefix-icon="Icons.Search" />
<el-scrollbar v-if="Object.keys(iconsList).length">
<div class="icon-list">
<div v-for="item in iconsList" :key="item" class="icon-item" @click="selectIcon(item)">
<component :is="item"></component>
<span>{{ item.name }}</span>
</div>
</div>
</el-scrollbar>
<el-empty v-else description="未搜索到您要找的图标~" />
</el-dialog>
</div>
</template>
<script lang='ts' setup name='SelectIcon'>
<script lang="ts" setup name="SelectIcon">
import * as Icons from '@element-plus/icons-vue'
import { computed, ref } from 'vue';
import { computed, ref } from 'vue'
interface SelectIconProps {
iconValue: string| undefined;
title?: string;
clearable?: boolean;
placeholder?: string;
iconValue: string | undefined
title?: string
clearable?: boolean
placeholder?: string
}
const props = withDefaults(defineProps<SelectIconProps>(), {
iconValue: '',
title: '请选择图标',
clearable: true,
placeholder: '请选择图标',
iconValue: '',
title: '请选择图标',
clearable: true,
placeholder: '请选择图标'
})
// 重新接收一下,防止打包后 clearable 报错
@@ -55,37 +55,36 @@ const openDialog = () => (dialogVisible.value = true)
// 选择图标(触发更新父组件数据)
const emit = defineEmits<{
'update:iconValue': [value: string];
'update:iconValue': [value: string]
}>()
const selectIcon = (item: any) => {
dialogVisible.value = false
valueIcon.value = item.name
emit('update:iconValue', item.name)
setTimeout(() => inputRef.value.blur(), 0)
dialogVisible.value = false
valueIcon.value = item.name
emit('update:iconValue', item.name)
setTimeout(() => inputRef.value.blur(), 0)
}
// 清空图标
const inputRef = ref()
const clearIcon = () => {
valueIcon.value = ''
emit('update:iconValue', '')
setTimeout(() => inputRef.value.blur(), 0)
valueIcon.value = ''
emit('update:iconValue', '')
setTimeout(() => inputRef.value.blur(), 0)
}
// 监听搜索框值
const inputValue = ref('')
const customIcons: { [key: string]: any } = Icons
const iconsList = computed((): { [key: string]: any } => {
if (!inputValue.value) return Icons
let result: { [key: string]: any } = {}
for (const key in customIcons) {
if (key.toLowerCase().indexOf(inputValue.value.toLowerCase()) > -1) result[key] = customIcons[key]
}
return result
if (!inputValue.value) return Icons
let result: { [key: string]: any } = {}
for (const key in customIcons) {
if (key.toLowerCase().indexOf(inputValue.value.toLowerCase()) > -1) result[key] = customIcons[key]
}
return result
})
</script>
<style scoped lang='scss'>
@import "./index.scss";
<style scoped lang="scss">
@use './index.scss';
</style>

View File

@@ -1,97 +1,84 @@
<template>
<div class='time-control'>
<el-select
class='select'
v-model='timeUnit'
placeholder='选择时间单位'
@change='handleChange'
>
<!-- 采用 v-for 动态渲染 -->
<el-option
v-for='unit in timeUnits'
:key='unit.value'
:label='unit.label'
:value='unit.value'
></el-option>
</el-select>
<div class="time-control">
<el-select class="select" v-model="timeUnit" placeholder="选择时间单位" @change="handleChange">
<!-- 采用 v-for 动态渲染 -->
<el-option v-for="unit in timeUnits" :key="unit.value" :label="unit.label" :value="unit.value"></el-option>
</el-select>
<!-- 禁用时间选择器 -->
<div class='date-display'>
<el-date-picker
class='date-picker'
v-model='startDate'
type='date'
placeholder='起始时间'
@change='emitDateChange'
:disabled-date='disableStartDate'
:readonly="timeUnit != '自定义'"
></el-date-picker>
<el-text>~</el-text>
<el-date-picker
class='date-picker'
v-model='endDate'
type='date'
placeholder='结束时间'
@change='emitDateChange'
:disabled-date='disableEndDate'
:readonly="timeUnit !== '自定义'"
></el-date-picker>
<!-- 禁用时间选择器 -->
<div class="date-display">
<el-date-picker
class="date-picker"
v-model="startDate"
type="date"
placeholder="起始时间"
@change="emitDateChange"
:disabled-date="disableStartDate"
:readonly="timeUnit != '自定义'"
></el-date-picker>
<el-text>~</el-text>
<el-date-picker
class="date-picker"
v-model="endDate"
type="date"
placeholder="结束时间"
@change="emitDateChange"
:disabled-date="disableEndDate"
:readonly="timeUnit !== '自定义'"
></el-date-picker>
</div>
<div class="date-display" v-if="timeUnit !== '自定义'">
<el-button
style="width: 10px"
class="triangle-button"
type="primary"
@click="prevPeriod"
@change="emitDateChange"
>
<div class="left_triangle"></div>
</el-button>
<el-button class="triangle-button" type="primary" @click="goToCurrent">当前</el-button>
<el-button
style="width: 10px"
class="triangle-button"
type="primary"
@click="nextPeriod"
:disabled="isNextDisabled"
>
<div class="right_triangle"></div>
</el-button>
</div>
</div>
<div class='date-display' v-if="timeUnit !== '自定义'">
<el-button
style='width: 10px;'
class='triangle-button'
type='primary'
@click='prevPeriod'
@change='emitDateChange'
>
<div class='left_triangle'></div>
</el-button>
<el-button class='triangle-button' type='primary' @click='goToCurrent'>
当前
</el-button>
<el-button
style='width: 10px;'
class='triangle-button'
type='primary'
@click='nextPeriod'
:disabled='isNextDisabled'
>
<div class='right_triangle'></div>
</el-button>
</div>
</div>
</template>
<script setup lang='ts'>
import { ref, onMounted, defineProps, defineEmits } from 'vue'
<script setup lang="ts">
import { onMounted, ref } from 'vue'
// 定义时间单位的类型
interface TimeUnit {
label: string;
value: string;
label: string
value: string
}
// 定义组件的props包含包括和排除的时间单位
const props = defineProps({
include: {
type: Array as () => string[],
default: () => ['日', '周', '月', '季度', '年', '自定义'],
},
exclude: {
type: Array as () => string[],
default: () => [],
},
default: {
type: String,
default: '月',
},
include: {
type: Array as () => string[],
default: () => ['日', '周', '月', '季度', '年', '自定义']
},
exclude: {
type: Array as () => string[],
default: () => []
},
default: {
type: String,
default: '月'
}
})
// 定义事件
const emit = defineEmits<{
(e: 'update-dates', startDate: string, endDate: string): void;
(e: 'update-dates', startDate: string, endDate: string): void
}>()
const timeUnit = ref<string>(props.default) // 默认选择
const startDate = ref<Date>(new Date()) // 起始日期
@@ -100,219 +87,206 @@ const isNextDisabled = ref<boolean>(false) // 控制下一周期按钮的禁用
const today = ref<Date>(new Date()) // 当前日期
// 过滤出可用的时间单位
const timeUnits = ref<TimeUnit[]>(
props.include.filter(unit => !props.exclude.includes(unit)).map(unit => ({
label: unit,
value: unit,
})),
props.include
.filter(unit => !props.exclude.includes(unit))
.map(unit => ({
label: unit,
value: unit
}))
)
// 发出日期变化事件
const emitDateChange = () => {
emit('update-dates', formatDate(startDate.value), formatDate(endDate.value))
emit('update-dates', formatDate(startDate.value), formatDate(endDate.value))
}
// 在组件挂载时更新日期范围
onMounted(() => {
updateDateRange()
updateDateRange()
})
const handleChange = (unit: string) => {
// 根据选择的时间单位处理日期变化
if (unit !== '自定义') {
updateDateRange()
} else {
// 自定义选项逻辑
startDate.value = new Date(new Date().setDate(new Date().getDate() - 1))
endDate.value = new Date()
}
timeUnit.value = unit
// 根据选择的时间单位处理日期变化
if (unit !== '自定义') {
updateDateRange()
} else {
// 自定义选项逻辑
startDate.value = new Date(new Date().setDate(new Date().getDate() - 1))
endDate.value = new Date()
}
timeUnit.value = unit
// 确保开始时间和结束时间不为空
if (!startDate.value) {
startDate.value = new Date()
}
if (!endDate.value) {
endDate.value = new Date()
}
startDate.value = new Date()
}
if (!endDate.value) {
endDate.value = new Date()
}
emitDateChange() // 变化时也发出更新事件
updateNextButtonStatus()
emitDateChange() // 变化时也发出更新事件
updateNextButtonStatus()
}
const updateDateRange = () => {
// 根据选择的时间单位计算起始和结束日期
if (timeUnit.value === '日') {
startDate.value = today.value
endDate.value = today.value
} else if (timeUnit.value === '周') {
startDate.value = getStartOfWeek(today.value)
endDate.value = getEndOfWeek(today.value)
} else if (timeUnit.value === '月') {
// 获取本月的开始和结束日期
startDate.value = new Date(today.value.getFullYear(), today.value.getMonth(), 1)
endDate.value = new Date(today.value.getFullYear(), today.value.getMonth() + 1, 0)
// 根据选择的时间单位计算起始和结束日期
if (timeUnit.value === '日') {
startDate.value = today.value
endDate.value = today.value
} else if (timeUnit.value === '周') {
startDate.value = getStartOfWeek(today.value)
endDate.value = getEndOfWeek(today.value)
} else if (timeUnit.value === '月') {
// 获取本月的开始和结束日期
startDate.value = new Date(today.value.getFullYear(), today.value.getMonth(), 1);
endDate.value = new Date(today.value.getFullYear(), today.value.getMonth() + 1, 0);
// // 确保结束日期不超过今天
// if (endDate.value > today.value) {
// endDate.value = new Date(today.value);
// endDate.value.setHours(23, 59, 59, 999); // 设置结束时间为今天的23:59:59.999
// }
// // 确保结束日期不超过今天
// if (endDate.value > today.value) {
// endDate.value = new Date(today.value);
// endDate.value.setHours(23, 59, 59, 999); // 设置结束时间为今天的23:59:59.999
// }
} else if (timeUnit.value === '季度') {
const quarter = Math.floor(today.value.getMonth() / 3)
startDate.value = new Date(today.value.getFullYear(), quarter * 3, 1)
endDate.value = new Date(today.value.getFullYear(), quarter * 3 + 3, 0)
} else if (timeUnit.value === '季度') {
const quarter = Math.floor(today.value.getMonth() / 3);
startDate.value = new Date(today.value.getFullYear(), quarter * 3, 1);
endDate.value = new Date(today.value.getFullYear(), quarter * 3 + 3, 0);
// // 确保结束日期不超过今天
// if (endDate.value > today.value) {
// endDate.value = new Date(today.value);
// endDate.value.setHours(23, 59, 59, 999); // 设置结束时间为今天的23:59:59.999
// }
} else if (timeUnit.value === '年') {
startDate.value = new Date(today.value.getFullYear(), 0, 1)
endDate.value = new Date(today.value.getFullYear(), 11, 31)
// // 确保结束日期不超过今天
// if (endDate.value > today.value) {
// endDate.value = new Date(today.value);
// endDate.value.setHours(23, 59, 59, 999); // 设置结束时间为今天的23:59:59.999
// }
// // 确保结束日期不超过今天
// if (endDate.value > today.value) {
} else if (timeUnit.value === '年') {
startDate.value = new Date(today.value.getFullYear(), 0, 1);
endDate.value = new Date(today.value.getFullYear(), 11, 31);
// endDate.value = new Date(today.value);
// endDate.value.setHours(23, 59, 59, 999); // 设置结束时间为今天的23:59:59.999
// }
}
// 确保开始时间和结束时间不为空
if (!startDate.value) {
startDate.value = new Date()
}
if (!endDate.value) {
endDate.value = new Date()
}
// // 确保结束日期不超过今天
// if (endDate.value > today.value) {
// endDate.value = new Date(today.value);
// endDate.value.setHours(23, 59, 59, 999); // 设置结束时间为今天的23:59:59.999
// }
}
// 确保开始时间和结束时间不为空
if (!startDate.value) {
startDate.value = new Date()
}
if (!endDate.value) {
endDate.value = new Date()
}
updateNextButtonStatus()
updateNextButtonStatus()
}
const getStartOfWeek = (date: Date) => {
const startOfWeek = new Date(date)
const day = startOfWeek.getDay()
const diff = day === 0 ? -6 : 1 - day // 星期天的情况
startOfWeek.setDate(startOfWeek.getDate() + diff)
return startOfWeek
const startOfWeek = new Date(date)
const day = startOfWeek.getDay()
const diff = day === 0 ? -6 : 1 - day // 星期天的情况
startOfWeek.setDate(startOfWeek.getDate() + diff)
return startOfWeek
}
const getEndOfWeek = (date: Date) => {
const endOfWeek = new Date(date)
const day = endOfWeek.getDay()
const diff = day === 0 ? 0 : 7 - day // 星期天的情况
endOfWeek.setDate(endOfWeek.getDate() + diff)
const endOfWeek = new Date(date)
const day = endOfWeek.getDay()
const diff = day === 0 ? 0 : 7 - day // 星期天的情况
endOfWeek.setDate(endOfWeek.getDate() + diff)
// 获取今天的日期
const today = new Date();
today.setHours(23, 59, 59, 999); // 设置今天的结束时间23:59:59.999
// 获取今天的日期
const today = new Date()
today.setHours(23, 59, 59, 999) // 设置今天的结束时间23:59:59.999
// 返回不超过今天的结束时间
//return endOfWeek > today ? today : endOfWeek;
return endOfWeek
// 返回不超过今天的结束时间
//return endOfWeek > today ? today : endOfWeek;
return endOfWeek
}
const prevPeriod = () => {
const prevStartDate = new Date(startDate.value)
const prevEndDate = new Date(endDate.value)
const prevStartDate = new Date(startDate.value)
const prevEndDate = new Date(endDate.value)
if (timeUnit.value === '日') {
prevStartDate.setDate(prevStartDate.getDate() - 1)
prevEndDate.setDate(prevEndDate.getDate() - 1)
} else if (timeUnit.value === '周') {
prevStartDate.setDate(prevStartDate.getDate() - 7)
prevEndDate.setDate(prevEndDate.getDate() - 7)
} else if (timeUnit.value === '月') {
if (timeUnit.value === '日') {
prevStartDate.setDate(prevStartDate.getDate() - 1)
prevEndDate.setDate(prevEndDate.getDate() - 1)
} else if (timeUnit.value === '周') {
prevStartDate.setDate(prevStartDate.getDate() - 7)
prevEndDate.setDate(prevEndDate.getDate() - 7)
} else if (timeUnit.value === '月') {
prevStartDate.setMonth(prevStartDate.getMonth() - 1)
prevEndDate.setMonth(prevEndDate.getMonth() - 1)
} else if (timeUnit.value === '季度') {
prevStartDate.setMonth(prevStartDate.getMonth() - 3)
prevEndDate.setMonth(prevEndDate.getMonth() - 3)
} else if (timeUnit.value === '年') {
prevStartDate.setFullYear(prevStartDate.getFullYear() - 1)
prevEndDate.setFullYear(prevEndDate.getFullYear() - 1)
}
prevStartDate.setMonth(prevStartDate.getMonth() - 1)
prevEndDate.setMonth(prevEndDate.getMonth() - 1)
} else if (timeUnit.value === '季度') {
prevStartDate.setMonth(prevStartDate.getMonth() - 3)
prevEndDate.setMonth(prevEndDate.getMonth() - 3)
} else if (timeUnit.value === '年') {
prevStartDate.setFullYear(prevStartDate.getFullYear() - 1)
prevEndDate.setFullYear(prevEndDate.getFullYear() - 1)
}
startDate.value = prevStartDate
endDate.value = prevEndDate
updateNextButtonStatus()
startDate.value = prevStartDate
endDate.value = prevEndDate
updateNextButtonStatus()
}
const goToCurrent = () => {
if (timeUnit.value !== '自定义') {
updateDateRange() // 更新为当前选择时间单位的时间范围
}
if (timeUnit.value !== '自定义') {
updateDateRange() // 更新为当前选择时间单位的时间范围
}
}
const nextPeriod = () => {
const nextStartDate = new Date(startDate.value)
const nextEndDate = new Date(endDate.value)
const nextStartDate = new Date(startDate.value)
const nextEndDate = new Date(endDate.value)
if (timeUnit.value === '日') {
nextStartDate.setDate(nextStartDate.getDate() + 1)
nextEndDate.setDate(nextEndDate.getDate() + 1)
} else if (timeUnit.value === '周') {
nextStartDate.setDate(nextStartDate.getDate() + 7)
nextEndDate.setDate(nextEndDate.getDate() + 7)
} else if (timeUnit.value === '月') {
nextStartDate.setMonth(nextStartDate.getMonth() + 1)
nextEndDate.setMonth(nextEndDate.getMonth() + 1)
} else if (timeUnit.value === '季度') {
nextStartDate.setMonth(nextStartDate.getMonth() + 3)
nextEndDate.setMonth(nextStartDate.getMonth() + 3)
} else if (timeUnit.value === '年') {
nextStartDate.setFullYear(nextStartDate.getFullYear() + 1)
nextEndDate.setFullYear(nextEndDate.getFullYear() + 1)
}
if (timeUnit.value === '日') {
nextStartDate.setDate(nextStartDate.getDate() + 1)
nextEndDate.setDate(nextEndDate.getDate() + 1)
} else if (timeUnit.value === '周') {
nextStartDate.setDate(nextStartDate.getDate() + 7)
nextEndDate.setDate(nextEndDate.getDate() + 7)
} else if (timeUnit.value === '月') {
nextStartDate.setMonth(nextStartDate.getMonth() + 1)
nextEndDate.setMonth(nextEndDate.getMonth() + 1)
} else if (timeUnit.value === '季度') {
nextStartDate.setMonth(nextStartDate.getMonth() + 3)
nextEndDate.setMonth(nextStartDate.getMonth() + 3)
} else if (timeUnit.value === '年') {
nextStartDate.setFullYear(nextStartDate.getFullYear() + 1)
nextEndDate.setFullYear(nextEndDate.getFullYear() + 1)
}
startDate.value = nextStartDate
endDate.value = nextEndDate
updateNextButtonStatus()
startDate.value = nextStartDate
endDate.value = nextEndDate
updateNextButtonStatus()
}
const updateNextButtonStatus = () => {
// 更新下一个按钮的禁用状态
const maxDate = new Date() // 假设最新日期为今天
// 将 maxDate 设置为当天的开始时间
maxDate.setHours(0, 0, 0, 0)
// 将 endDate 设置为当天的开始时间并进行比较
const endDateAdjusted = new Date(endDate.value)
endDateAdjusted.setHours(0, 0, 0, 0)
// 仅比较日期部分
isNextDisabled.value = endDateAdjusted >= maxDate
emitDateChange() // 变化时也发出更新事件
// 更新下一个按钮的禁用状态
const maxDate = new Date() // 假设最新日期为今天
// 将 maxDate 设置为当天的开始时间
maxDate.setHours(0, 0, 0, 0)
// 将 endDate 设置为当天的开始时间并进行比较
const endDateAdjusted = new Date(endDate.value)
endDateAdjusted.setHours(0, 0, 0, 0)
// 仅比较日期部分
isNextDisabled.value = endDateAdjusted >= maxDate
emitDateChange() // 变化时也发出更新事件
}
// 限制开始日期不能选择超过当前日期
const disableStartDate = (date: Date) => {
return date > today.value
return date > today.value
}
// 限制结束日期不能超过当前日期且必须大于开始日期
const disableEndDate = (date: Date) => {
if (timeUnit.value !== '自定义') return false // 如果不是自定义时间单位,则不限制
const start = new Date(startDate.value)
return date > today.value || (start && date <= start)
if (timeUnit.value !== '自定义') return false // 如果不是自定义时间单位,则不限制
const start = new Date(startDate.value)
return date > today.value || (start && date <= start)
}
// 格式化日期yyyy-mm-dd
function formatDate(date: Date | null): string {
if (!date) {
return '';
}
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
return `${year}-${month}-${day}`;
if (!date) {
return ''
}
const year = date.getFullYear()
const month = String(date.getMonth() + 1).padStart(2, '0')
const day = String(date.getDate()).padStart(2, '0')
return `${year}-${month}-${day}`
}
</script>
<style scoped lang='scss'>
@import "./index.scss";
<style scoped lang="scss">
@use './index.scss';
</style>

View File

@@ -0,0 +1,282 @@
<template>
<div class="chart">
<div ref="chartRef" class="my-chart" />
</div>
</template>
<script setup lang="ts">
import { onBeforeUnmount, onMounted, ref, watch } from 'vue'
// import echarts from './echarts'
import * as echarts from 'echarts' // 全引入
// import 'echarts/lib/component/dataZoom'
const color = [
'var(--el-color-primary)',
'#07CCCA',
'#00BFF5',
'#FFBF00',
'#77DA63',
'#D5FF6B',
'#Ff6600',
'#FF9100',
'#5B6E96',
'#66FFCC',
'#B3B3B3'
]
const chartRef = ref<HTMLDivElement>()
const props = defineProps(['options', 'isInterVal', 'pieInterVal'])
let chart: echarts.ECharts | any = null
const resizeHandler = () => {
// 不在视野中的时候不进行resize
if (!chartRef.value) return
if (chartRef.value.offsetHeight == 0) return
chart.getZr().painter.getViewportRoot().style.display = 'none'
requestAnimationFrame(() => {
chart.resize()
chart.getZr().painter.getViewportRoot().style.display = ''
})
}
const initChart = () => {
if (!props.isInterVal && !props.pieInterVal) {
chart?.dispose()
}
// chart?.dispose()
chart = echarts.init(chartRef.value as HTMLDivElement)
const options = {
title: {
left: 'center',
// textStyle: {
color: '#000',
fontSize: 18,
// },
...(props.options?.title || null)
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow',
label: {
color: '#fff',
fontSize: 16
}
},
textStyle: {
color: '#fff',
fontStyle: 'normal',
opacity: 0.35,
fontSize: 14
},
backgroundColor: 'rgba(0,0,0,0.55)',
borderWidth: 0,
confine: true,
...(props.options?.tooltip || null)
},
legend: {
right: 20,
top: 0,
itemGap: 10,
itemStyle: {},
// textStyle: {
fontSize: 12,
padding: [2, 0, 0, 0], //[上、右、下、左]
// },
itemWidth: 15,
itemHeight: 10,
...(props.options?.legend || null)
},
grid: {
top: '60px',
left: '30px',
right: '70px',
bottom: props.options?.options?.dataZoom === null ? '10px' : '40px',
containLabel: true,
...(props.options?.grid || null)
},
xAxis: props.options?.xAxis ? handlerXAxis() : null,
yAxis: props.options?.yAxis ? handlerYAxis() : null,
dataZoom: props.options?.dataZoom || [
{
type: 'inside',
height: 13,
start: 0,
bottom: '20px',
end: 100
},
{
start: 0,
height: 13,
bottom: '20px',
end: 100
}
],
color: props.options?.color || color,
series: props.options?.series,
...props.options?.options
}
// console.log(options.series,"获取x轴");
handlerBar(options)
// 处理柱状图
chart.setOption(options, true)
setTimeout(() => {
chart.resize()
}, 0)
}
const handlerBar = (options: any) => {
if (Array.isArray(options.series)) {
options.series.forEach((item: any) => {
if (item.type === 'bar') {
item.barMinHeight = 10
item.barMaxWidth = 20
item.itemStyle = Object.assign(
{
color: (params: any) => {
if (params.value == 0 || params.value == 3.14159) {
return '#ccc'
} else {
return props.options?.color
? props.options?.color[params.seriesIndex]
: color[params.seriesIndex]
}
}
},
item.itemStyle
)
}
})
}
}
const handlerYAxis = () => {
let temp = {
type: 'value',
nameGap: 15,
nameTextStyle: {
color: '#000'
},
splitNumber: 5,
minInterval: 1,
axisLine: {
show: true,
lineStyle: {
color: '#000'
}
},
axisLabel: {
color: '#000',
fontSize: 14,
formatter: function (value) {
return value.toFixed(0) // 格式化显示为一位小数
}
},
splitLine: {
lineStyle: {
// 使用深浅的间隔色
color: ['#ccc'],
type: 'dashed',
opacity: 0.5
}
}
}
// props.options?.xAxis 是数组还是对象
if (Array.isArray(props.options?.yAxis)) {
return props.options?.yAxis.map((item: any) => {
return {
...temp,
...item
}
})
} else {
return {
...temp,
...props.options?.yAxis
}
}
}
const handlerXAxis = () => {
let temp = {
type: 'category',
axisTick: { show: false },
nameTextStyle: {
color: '#000'
},
axisLine: {
// lineStyle: {
color: '#000'
// }
},
axisLabel: {
// textStyle: {
fontFamily: 'dinproRegular',
color: '#000',
fontSize: '12'
// }
}
// boundaryGap: false,
}
// props.options?.xAxis 是数组还是对象
if (Array.isArray(props.options?.xAxis)) {
return props.options?.xAxis.map((item: any) => {
return {
...temp,
...item
}
})
} else {
return {
...temp,
...props.options?.xAxis
}
}
}
let throttle: ReturnType<typeof setTimeout>
// 动态计算table高度
const resizeObserver = new ResizeObserver(entries => {
for (const entry of entries) {
if (throttle) {
clearTimeout(throttle)
}
throttle = setTimeout(() => {
resizeHandler()
}, 100)
}
})
onMounted(() => {
initChart()
resizeObserver.observe(chartRef.value!)
})
defineExpose({ initChart })
onBeforeUnmount(() => {
resizeObserver.unobserve(chartRef.value!)
chart?.dispose()
})
watch(
() => props.options,
(newVal, oldVal) => {
initChart()
}
)
</script>
<style lang="scss" scoped>
.chart {
width: 100%;
height: 100%;
position: relative;
.el-button {
position: absolute;
right: 0px;
top: -60px;
}
.my-chart {
height: 100%;
width: 100%;
}
}
</style>

View File

@@ -3,11 +3,13 @@
// 首页地址(默认)
export const HOME_URL: string = "/home/index";
// export const HOME_URL: string = "/machine/controlSource";
// 登录页地址(默认)
export const LOGIN_URL: string = "/login";
// 默认主题颜色
export const DEFAULT_PRIMARY: string = "#003078";
export const DEFAULT_PRIMARY: string = "#526ADE";
// 路由白名单地址(本地存在的路由 staticRouter.ts 中)
export const ROUTER_WHITE_LIST: string[] = ["/500"];
@@ -17,3 +19,5 @@ export const AMAP_MAP_KEY: string = "";
// 百度地图 key
export const BAIDU_MAP_KEY: string = "";

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