41 Commits
2026-05 ... dev

Author SHA1 Message Date
caozehui
8c7c34cda2 温度湿度弹窗中新增守时勾选框 2026-07-13 10:35:04 +08:00
caozehui
b0ee58a1b5 docs: add time check design spec 2026-07-10 16:02:28 +08:00
caozehui
c3915dc8f5 检测计划-开启守时检测功能 2026-07-10 10:19:47 +08:00
caozehui
ff4a197436 Merge branch 'dev' 2026-07-10 08:43:26 +08:00
caozehui
ad2d94a893 切换程控源页面保持页面缓存 2026-07-10 08:42:38 +08:00
caozehui
030e84ae16 被检设备上传图片功能 2026-07-09 16:35:53 +08:00
caozehui
60112e84e3 归档 2026-07-09 09:15:40 +08:00
caozehui
8f12d0e03e 暂态写入写出时间 2026-07-08 15:51:11 +08:00
caozehui
49ded8c568 微调 2026-07-08 08:33:38 +08:00
caozehui
a39ad579f9 微调 2026-07-06 20:06:31 +08:00
caozehui
657d357159 微调 2026-07-06 16:24:23 +08:00
caozehui
0d7d5722c1 微调 2026-07-06 10:44:38 +08:00
caozehui
36496e5825 脚本检测项单位调整 2026-07-06 08:46:48 +08:00
caozehui
5caffc9cc9 微调 2026-07-02 18:36:26 +08:00
caozehui
15bef7b17f 微调 2026-07-02 15:33:16 +08:00
caozehui
4ac32753f1 微调 2026-07-02 15:21:31 +08:00
caozehui
131adc81a7 三项不平衡补充序分量 2026-07-02 15:00:19 +08:00
caozehui
3135f76a2f 归档 2026-07-01 16:12:33 +08:00
caozehui
08d81f724f 微调 2026-07-01 13:59:28 +08:00
caozehui
29ef22c9b7 闪变调整 2026-06-26 13:57:17 +08:00
caozehui
7738d7d153 源控程序优化 2026-06-26 10:31:52 +08:00
caozehui
20bc6e3ab5 正式检测-过载测试显示结果 2026-06-26 08:46:23 +08:00
caozehui
2ea0557f4e 程控源调整 2026-06-25 10:52:19 +08:00
caozehui
1a35510801 修改配置文件 2026-06-23 11:42:45 +08:00
caozehui
5ad3029e0b pqdif 2026-06-23 10:21:56 +08:00
caozehui
cd51cfb052 程控源调整 2026-06-18 10:01:39 +08:00
caozehui
4415eb30b2 微调 2026-06-17 15:51:02 +08:00
caozehui
78d094e4a2 icd导入按钮及权限控制 2026-06-17 13:20:36 +08:00
caozehui
8f5642d0b5 icd调整 2026-06-16 19:26:47 +08:00
caozehui
6c191aa96f 多影响量 2026-06-16 13:24:21 +08:00
caozehui
f2d7156b4f 移除文件 2026-06-15 10:11:51 +08:00
caozehui
d44c79f7b8 报告模版新增pattern字段 2026-06-12 13:30:07 +08:00
caozehui
964ba77f8a Revert "docs: add report template pattern design"
This reverts commit 7c1f135e99.
2026-06-12 09:50:21 +08:00
caozehui
7c1f135e99 docs: add report template pattern design 2026-06-12 09:49:30 +08:00
caozehui
1982ccd7ff 闪变支持配置Pst 2026-06-12 09:22:50 +08:00
caozehui
5abaa743c0 修改常量 2026-06-11 10:35:11 +08:00
caozehui
52a8095334 移除文件 2026-06-11 09:57:01 +08:00
caozehui
654c997607 移除文件 2026-06-11 09:43:59 +08:00
caozehui
b31efb9ede 闪变编辑框微调 2026-06-11 09:28:26 +08:00
caozehui
195b58d798 比对检测计划默认值 2026-06-09 19:22:54 +08:00
caozehui
0423de2683 新增小工具页面、SNTP对时功能集成到小工具页面 2026-06-08 08:46:29 +08:00
152 changed files with 3980 additions and 3853 deletions

View File

@@ -54,13 +54,13 @@ webSocket:
port: 7778
#源参数下发,暂态数据默认值
Dip:
# 暂态前时间s
# fPreTime: 2f
#Dip:
# 暂态前时间s
# fPreTime: 2f
#写入时间s
fRampIn: 0.001f
# fRampIn: 0.001f
#写出时间s
fRampOut: 0.001f
# fRampOut: 0.001f
# 暂态后时间s
# fAfterTime: 3f
@@ -97,6 +97,8 @@ qr:
db:
type: mysql
icd:
txt-dir: ..\..\..\9100\DeviceControl\Config
# 比对录波需要的配置,晚点再做优化
# 系统配置
@@ -118,3 +120,5 @@ activate:
public-key: "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnFMmIVanMxsW5S/qP8Wcxf/J3/i4631BP3UtWkRzO7jAw9HIAgK4Y7X53hXj6zMbfme1vMjQc0mq7m/KrH4WlTYpFexLO6Gnk8oH40F04tp+ABZIq93zNOydPEaVoZeTPH/LlkwrrxVGAMNNIKuebcqapp25JiWtlSFMv4kH/nDAj+2m8+P4zYVM1Ed6gO01eKDEYE3SBA1Ket2BfHTgviR/F8WKwlXh11enywsJnrHTM5dJQdlUxCjHy214TpheYOz/cv9elQnDfFAbmZW8mH5/hgMSTkm3h4uR7ITin6Erg+yc/t1kGaTWrzloyBRMSiFN/Pwr5yQjj+1wQqqUkwIDAQAB"
dataCheck:
enable: false

View File

@@ -1 +1 @@
116212
51248

Binary file not shown.

Binary file not shown.

View File

@@ -16,3 +16,5 @@
.\binlog.000038
.\binlog.000039
.\binlog.000040
.\binlog.000041
.\binlog.000042

View File

@@ -0,0 +1,211 @@
# 守时检测设计
## 背景
现有守时页面 `frontend/src/views/home/components/timeTest.vue` 是演示式页面,包含假数据、步骤条和旧的“标准时间/上送时刻”展示方式,不符合新的业务流程。新的守时检测需要同时支持多台装置,动态展示每台装置的暂态触发时刻、守时误差和结果,并与现有检测弹窗、后端 `startPreTest` 流程联动。
后端项目位于 `D:\njcn\test\CN_Gather`。本设计覆盖前端与后端协同修改。
## 目标
- 守时页面只展示守时检测用时和设备结果表,不展示源连接、设备连接、协议校验、相序校验等前置阶段。
- 表格列固定为装置名称、暂态1触发时刻、暂态2触发时刻、守时误差、结果。
- 未收到对应数据时,单元格显示 loading 转圈状态。
- 守时检测计时格式为 `HH:mm:ss`
- 支持多台设备同时检测。
- 守时检测过程要像 `factorTest.vue` 一样监听并拦截 WebSocket 错误,错误时终止流程。
- 守时完成后,如果后续还有正式检测,前后端自动流向正式检测且保持组件状态;如果没有正式检测,则清理连接和 WebSocket 资源。
## 检测项契约
`PreDetectionParam.testItemList` 从 3 位改为 4 位:
1. 第 1 位:预检测
2. 第 2 位:系数校准
3. 第 3 位:守时校验
4. 第 4 位:正式检测
前端所有调用 `startPreTest` 的位置都必须发送 4 位数组,例如:
- 只做守时检测:`[false, false, true, false]`
- 预检测 + 守时检测:`[true, false, true, false]`
- 守时检测 + 正式检测:`[false, false, true, true]`
- 预检测 + 守时检测 + 正式检测:`[true, false, true, true]`
后端所有读取 `testItemList` 的逻辑同步使用新索引,不能继续把第三位解释为正式检测。
## 前端流程
### 启动规则
`testPopup.vue` 继续作为检测弹窗流程编排入口。
只勾选守时检测时,前端调用现有 `startPreTest`。接口成功返回后,认为守时检测开始,`timeTest.vue` 开始计时。后端随后执行源连接、设备连接、协议校验、相序校验,再进入守时暂态流程。
勾选预检测 + 守时检测时,守时检测不在 `startPreTest` 成功后立刻开始计时。预检测包含源连接、设备连接、协议校验、相序校验四个阶段;这四个阶段完成后,后端推送守时开始/暂态数据相关消息,前端开始守时计时并展示表格更新。
### 页面结构
`timeTest.vue` 改为轻量结果页:
- 顶部为紧凑计时区域,显示 `守时检测用时HH:mm:ss`
- 下方为结果表格,列为 `装置名称 / 暂态1触发时刻 / 暂态2触发时刻 / 守时误差 / 结果`
- 行数据初始化自 `checkStore.devices`
- 未收到数据的字段显示 Element Plus loading 转圈。
- `time_waiting_second` 消息只触发轻量提示,例如“已收到第一组暂态数据,等待第二组暂态数据”,不增加页面状态区和统计区。
### Step 切换与状态保活
守时检测完成且后续进入正式检测后,用户可以点击弹窗顶部 step 切回守时检测页。此时守时页只读展示已完成结果,不重启监听、不重置 loading、不重新计时。
用户再切回正式检测页时,正式检测组件的表格信息、计时信息、日志进度和当前检测步骤不能初始化。实现上应保持组件实例存活,只切换 `stepsActiveView` 的显示状态,避免因 step 切换触发正式检测组件重建或初始化。
### 错误处理
`timeTest.vue` 需要接收并监听 `webMsgSend`,与 `factorTest.vue` 保持一致的错误拦截思路:
- 通用错误码如 `10520``10521``10522``10523``10524``10525``10527``10528``10529``10550``10551``10552``-1`
- WebSocket 连接类错误,如 `socket_timeout``server_error``device_error``connect/Source``connect/Dev`
- 源连接、设备连接、协议校验、相序校验阶段虽然不展示在页面中,但这些阶段的错误必须被守时页捕获,弹窗提示后 `emit('update:testStatus', 'error')`,父流程终止。
## 后端流程
### 总体接入
后端采用复用现有检测链路的方案。`startPreTest` 仍作为统一入口,守时校验作为现有检测链路中的独立业务阶段插入。
单独勾选守时检测时,后端需要执行完整前置阶段:
1. 源连接
2. 设备连接
3. 协议校验
4. 相序校验
5. 守时检测暂态流程
勾选预检测 + 守时检测时,预检测已经包含前四个阶段。后端不重复执行、不关闭源连接和设备连接,直接进入守时检测暂态流程。
### 守时业务规则
守时检测使用后端内置的两段守时专用暂态脚本,不由前端传脚本编号。
业务过程:
1. 下发暂态脚本 1。
2. 收取每台装置的一组暂态数据。
3. 两次暂态触发时刻均从 `DevData.time` 读取。
4. 同一设备存在多个监测点数据时,以第一条数据为准。
5. 第一组数据收齐后,后端推送 `time_waiting_second` 事件。
6. 后端等待 `waitTime - 暂态持续时间1 - fRampOut - fPreTime - fRampIn`
7. 下发暂态脚本 2。
8. 收取每台装置第二组暂态数据。
9. 计算 `dtMs = 暂态1触发时刻 - 暂态2触发时刻 - waitTime`
10. `dtMs <= 10ms` 为合格,否则不合格。
结果判定完全由后端负责。前端只展示后端推送的守时误差和结果,不自行计算合格性。
### 守时结束后的资源处理
如果 `testItemList[3]``true`,守时检测完成后自动流向正式检测:
- 后端不关闭源连接。
- 后端不关闭设备连接。
- 后端不清理正式检测需要的流程上下文。
- 前端不关闭 WebSocket不重建正式检测组件。
- 前端收到守时成功后自动切到正式检测步骤。
如果 `testItemList[3]``false`,守时检测后没有正式检测:
- 后端在守时结束后断开设备连接和源连接。
- 后端清理检测流程上下文。
- 前端停止计时,展示最终结果,状态置为 `success``error`
- 弹窗关闭时清理 WebSocket 回调或连接。
如果守时检测失败,无论是否勾选正式检测,都终止流程,不进入正式检测,并按错误流程释放资源。
## WebSocket 消息
守时阶段使用独立的守时消息语义,避免前端混在正式检测或系数校准消息里猜状态。推荐 `requestId` 使用 `Time_Check`
### `time_first_data`
收到某台设备第一次暂态数据。
`data` 建议字段:
- `deviceId`
- `deviceName`
- `monitorId` 或监测点标识
- `firstTriggerTime`
### `time_waiting_second`
第一组暂态数据已收齐,后端已进入等待第二次暂态数据阶段。
该消息只作为事件触发标志,前端只做提示,不依赖业务字段。
### `time_second_data`
收到某台设备第二次暂态数据。
`data` 建议字段:
- `deviceId`
- `deviceName`
- `monitorId` 或监测点标识
- `secondTriggerTime`
### `time_result`
某台设备守时结果计算完成。
`data` 建议字段:
- `deviceId`
- `deviceName`
- `firstTriggerTime`
- `secondTriggerTime`
- `waitTimeMs`
- `dtMs`
- `result`
- `message`
`result` 可以使用后端现有结果约定,也可以明确为 `passed` / `failed`。前端需统一映射为“合格 / 不合格”。
### `time_finish`
全部守时检测完成。
`data` 建议字段:
- `allPassed`
- `results`
- `hasFormalTest`
前端收到后停止守时计时。如果 `hasFormalTest``true`,自动流向正式检测;否则结束当前检测流程。
## 涉及文件
前端:
- `frontend/src/views/home/components/timeTest.vue`
- `frontend/src/views/home/components/testPopup.vue`
- `frontend/src/api/socket/socket.ts`
- 必要时检查 `frontend/src/views/home/components/compareTestPopup.vue` 是否存在同类 `testItemList` 构造逻辑。
后端:
- `D:\njcn\test\CN_Gather\detection\src\main\java\com\njcn\gather\detection\pojo\param\PreDetectionParam.java`
- `D:\njcn\test\CN_Gather\detection\src\main\java\com\njcn\gather\detection\controller\PreDetectionController.java`
- `D:\njcn\test\CN_Gather\detection\src\main\java\com\njcn\gather\detection\service\impl\PreDetectionServiceImpl.java`
- `D:\njcn\test\CN_Gather\detection\src\main\java\com\njcn\gather\detection\handler\SocketDevResponseService.java`
- `D:\njcn\test\CN_Gather\detection\src\main\java\com\njcn\gather\detection\pojo\enums\SourceOperateCodeEnum.java`
## 测试建议
- 单独勾选守时检测:验证 `startPreTest` 成功后计时立即开始,前置四阶段错误可被前端拦截,最终无正式检测时资源被清理。
- 勾选预检测 + 守时检测:验证前置四阶段不重复执行、不关闭连接,守时计时从预检测四阶段完成后开始。
- 勾选守时检测 + 正式检测:验证守时成功后自动进入正式检测,连接和 WebSocket 不被清理。
- 正式检测中点击 step 切回守时页,再切回正式检测页:验证正式检测表格、计时、日志和进度不初始化。
- 多设备且多监测点:验证每台设备只取第一条监测点数据参与守时计算。
- 守时失败:验证不进入正式检测,并按错误流程释放资源。
- `time_waiting_second`:验证只提示,不改变表格结构。

View File

@@ -12,3 +12,10 @@ VITE_REPORT=false
# 浏览器是否显示vue的告警信息
VUE_APP_SILENCE_WARNINGS=true
VITE_COMPANY_WEBSITE=http://www.shining-electric.com/
VITE_COMPANY_NAME=南京灿能电力自动化股份有限公司
VITE_IS_SHOW_RAW_DATA=true
# 开启激活验证
VITE_ACTIVATE_OPEN=true

View File

@@ -22,7 +22,3 @@ VITE_API_URL=/api
VITE_PROXY=[["/api","http://127.0.0.1:18092/"]]
#VITE_PROXY=[["/api","http://192.168.1.124:18092/"]]
#VITE_PROXY=[["/api","http://192.168.2.125:18092/"]]
# VITE_PROXY=[["/api","http://192.168.1.138:8080/"]]张文
VITE_IS_SHOW_RAW_DATA=true
# 开启激活验证
VITE_ACTIVATE_OPEN=false

View File

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

View File

@@ -1,269 +0,0 @@
const data = [
{
id: 'device1',
deviceName:"模拟装置1",
deviceType:"PQS882B4电能质量监测装置",
deviceChannels:"4",
planName: "沧州220kV留古等4座变电站电能质量检测",
deviceUn: "57.74",
deviceIn: "5",
deviceCompany: "南京灿能电力自动化股份有限公司",
deviceModel: "模拟式",
},
{
id: 'device2',
deviceName:"模拟装置2",
deviceType:"PQS882A电能质量监测装置",
deviceChannels:"1",
planName: "邯郸220kV团城站等4座站电能质量检测",
deviceUn: "57.74",
deviceIn: "5",
deviceCompany: "南京灿能电力自动化股份有限公司",
deviceModel: "模拟式",
},
{
id: 'device3',
deviceName:"模拟装置3",
deviceType:"PQS882A电能质量监测装置",
deviceChannels:"1",
planName: "衡水冀州光伏电站配套出口工程",
deviceUn: "57.74",
deviceIn: "1",
deviceCompany: "南京灿能电力自动化股份有限公司",
deviceModel: "模拟式",
},
{
id: 'device4',
deviceName:"模拟装置4",
deviceType:"PMC-680M-22-22-00-115ANBC电能质量监测装置",
deviceChannels:"4",
planName: "深圳市中电软件有限公司委托送检",
deviceUn: "57.74",
deviceIn: "5",
deviceCompany: "深圳中电电力技术股份有限公司",
deviceModel: "模拟式",
},
]
const plan_devicedata = [
{
id: '1', //装置序号ID
name: '240001', //设备名称
dev_Type: 'PQS-882B4',//设备类型
dev_Chns: 4, //设备通道数
check_Result: '未检', //检测结果
report_State: '未生成', //报告状态
document_State: '未归档', //归档状态
check_State:'未检',//检测状态
reCheck_Num: 0, //复检次数
},
{
id: '2', //装置序号ID
name: '240002', //设备名称
dev_Type: 'PQS-882B4',//设备类型
dev_Chns: 4, //设备通道数
check_Result: '未检', //检测结果
report_State: '未生成', //报告状态
document_State: '未归档', //归档状态
check_State:'未检',//检测状态
reCheck_Num: 0, //复检次数
},
{
id: '3', //装置序号ID
name: '240003', //设备名称
dev_Type: 'PQS-882B4',//设备类型
dev_Chns: 4, //设备通道数
check_Result: '未检', //检测结果
report_State: '未生成', //报告状态
document_State: '未归档', //归档状态
check_State:'未检',//检测状态
reCheck_Num: 0, //复检次数
},
{
id: '4', //装置序号ID
name: '240004', //设备名称
dev_Type: 'PQS-882B4',//设备类型
dev_Chns: 4, //设备通道数
check_Result: '未检', //检测结果
report_State: '未生成', //报告状态
document_State: '未归档', //归档状态
check_State:'未检',//检测状态
reCheck_Num: 0, //复检次数
},
{
id: '5', //装置序号ID
name: '240005', //设备名称
dev_Type: 'PQS-882A',//设备类型
dev_Chns: 1, //设备通道数
check_Result: '不符合', //检测结果
report_State: '未生成', //报告状态
document_State: '未归档', //归档状态
check_State:'检测完成',//检测状态
reCheck_Num: 1, //复检次数
},
{
id: '6', //装置序号ID
name: '240006', //设备名称
dev_Type: 'PQS-882B4',//设备类型
dev_Chns: 4, //设备通道数
check_Result: '不符合', //检测结果
report_State: '未生成', //报告状态
document_State: '未归档', //归档状态
check_State:'检测完成',//检测状态
reCheck_Num: 1, //复检次数
},
{
id: '7', //装置序号ID
name: '240007', //设备名称
dev_Type: 'PQS-882A',//设备类型
dev_Chns: 1, //设备通道数
check_Result: '符合', //检测结果
report_State: '未生成', //报告状态
document_State: '未归档', //归档状态
check_State:'检测完成',//检测状态
reCheck_Num: 1, //复检次数
},
{
id: '8', //装置序号ID
name: '240008', //设备名称
dev_Type: 'PQS-882B4',//设备类型
dev_Chns: 4, //设备通道数
check_Result: '符合', //检测结果
report_State: '未生成', //报告状态
document_State: '未归档', //归档状态
check_State:'检测完成',//检测状态
reCheck_Num: 1, //复检次数
},
{
id: '9', //装置序号ID
name: '240009', //设备名称
dev_Type: 'PQS-882B4',//设备类型
dev_Chns: 4, //设备通道数
check_Result: '不符合', //检测结果
report_State: '已生成', //报告状态
document_State: '未归档', //归档状态
check_State:'检测完成',//检测状态
reCheck_Num: 1, //复检次数
},
{
id: '10', //装置序号ID
name: '240010', //设备名称
dev_Type: 'PQS-882A',//设备类型
dev_Chns: 1, //设备通道数
check_Result: '符合', //检测结果
report_State: '已生成', //报告状态
document_State: '未归档', //归档状态
check_State:'检测完成',//检测状态
reCheck_Num: 2, //复检次数
},
{
id: '11', //装置序号ID
name: '240011', //设备名称
dev_Type: 'PQS-882A',//设备类型
dev_Chns: 1, //设备通道数
check_Result: '符合', //检测结果
report_State: '已生成', //报告状态
document_State: '已归档', //归档状态
check_State:'检测完成',//检测状态
reCheck_Num: 1, //复检次数
},
{
id: '12', //装置序号ID
name: '240012', //设备名称
dev_Type: 'PQS-882B4',//设备类型
dev_Chns: 4, //设备通道数
check_Result: '符合', //检测结果
report_State: '已生成', //报告状态
document_State: '已归档', //归档状态
check_State:'检测完成',//检测状态
reCheck_Num: 2, //复检次数
},
{
id: '13', //装置序号ID
name: '240013', //设备名称
dev_Type: 'PQS-882A',//设备类型
dev_Chns: 1, //设备通道数
check_Result: '未检', //检测结果
report_State: '未生成', //报告状态
document_State: '未归档', //归档状态
check_State:'未检',//检测状态
reCheck_Num: 0, //复检次数
},
{
id: '14', //装置序号ID
name: '240014', //设备名称
dev_Type: 'PQS-882A',//设备类型
dev_Chns: 1, //设备通道数
check_Result: '未检', //检测结果
report_State: '未生成', //报告状态
document_State: '未归档', //归档状态
check_State:'未检',//检测状态
reCheck_Num: 0, //复检次数
},
{
id: '15', //装置序号ID
name: '240015', //设备名称
dev_Type: 'PQS-882A',//设备类型
dev_Chns: 1, //设备通道数
check_Result: '未检', //检测结果
report_State: '未生成', //报告状态
document_State: '未归档', //归档状态
check_State:'未检',//检测状态
reCheck_Num: 0, //复检次数
},
{
id: '16', //装置序号ID
name: '240016', //设备名称
dev_Type: 'PQS-882A',//设备类型
dev_Chns: 1, //设备通道数
check_Result: '未检', //检测结果
report_State: '未生成', //报告状态
document_State: '未归档', //归档状态
check_State:'未检',//检测状态
reCheck_Num: 0, //复检次数
},
{
id: '17', //装置序号ID
name: '240017', //设备名称
dev_Type: 'PQS-882A',//设备类型
dev_Chns: 1, //设备通道数
check_Result: '未检', //检测结果
report_State: '未生成', //报告状态
document_State: '未归档', //归档状态
check_State:'未检',//检测状态
reCheck_Num: 0, //复检次数
},
{
id: '18', //装置序号ID
name: '240018', //设备名称
dev_Type: 'PQS-882A',//设备类型
dev_Chns: 1, //设备通道数
check_Result: '未检', //检测结果
report_State: '未生成', //报告状态
document_State: '未归档', //归档状态
check_State:'未检',//检测状态
reCheck_Num: 0, //复检次数
},
{
id: '19', //装置序号ID
name: '240019', //设备名称
dev_Type: 'PQS-882A',//设备类型
dev_Chns: 1, //设备通道数
check_Result: '未检', //检测结果
report_State: '未生成', //报告状态
document_State: '未归档', //归档状态
check_State:'未检',//检测状态
reCheck_Num: 0, //复检次数
},
{
id: '20', //装置序号ID
name: '240020', //设备名称
dev_Type: 'PQS-882A',//设备类型
dev_Chns: 1, //设备通道数
check_Result: '未检', //检测结果
report_State: '未生成', //报告状态
document_State: '未归档', //归档状态
check_State:'未检',//检测状态
reCheck_Num: 0, //复检次数
},
]
export default {data,plan_devicedata}

View File

@@ -0,0 +1,91 @@
import type { Device } from '@/api/device/interface/device'
import type { Monitor } from '@/api/device/interface/monitor'
const DEVICE_FIELD_NAMES = [
'id',
'name',
'pattern',
'devType',
'manufacturer',
'createDate',
'createId',
'hardwareVersion',
'softwareVersion',
'protocol',
'ip',
'port',
'encryptionFlag',
'series',
'devKey',
'sampleId',
'arrivedDate',
'cityName',
'gdName',
'subName',
'reportPath',
'icdId',
'planId',
'factorFlag',
'preinvestmentPlan',
'delegate',
'inspectChannel',
'inspectDate',
'harmSysId',
'importFlag'
] as const
const MONITOR_FIELD_NAMES = [
'id',
'devId',
'busbar',
'name',
'num',
'pt',
'ct',
'connection',
'statInterval',
'harmSysId',
'checkFlag'
] as const
function appendIfPresent(formData: FormData, key: string, value: unknown) {
if (value === null || value === undefined || value === '') {
return
}
formData.append(key, String(value))
}
function appendMonitorFields(formData: FormData, monitorList: Monitor.ResPqMon[]) {
monitorList.forEach((monitor, index) => {
MONITOR_FIELD_NAMES.forEach(fieldName => {
appendIfPresent(formData, `monitorList[${index}].${fieldName}`, monitor[fieldName])
})
})
}
export function buildPqDevFormData(device: Device.ResPqDev, imageFile?: File | null) {
const formData = new FormData()
DEVICE_FIELD_NAMES.forEach(fieldName => {
appendIfPresent(formData, fieldName, device[fieldName])
})
appendMonitorFields(formData, device.monitorList ?? [])
if (imageFile) {
formData.append('image', imageFile)
}
return formData
}
export function buildDeviceImagePreviewSrc(
hasImage?: boolean,
imageBase64?: string | null,
imageContentType?: string | null
) {
if (!hasImage || !imageBase64 || !imageContentType) {
return null
}
return `data:${imageContentType};base64,${imageBase64}`
}

View File

@@ -1,5 +1,6 @@
import type {Device} from '@/api/device/interface/device'
import http from '@/api'
import { buildPqDevFormData } from './formData'
/**
* @name 被检设备管理模块
@@ -11,13 +12,13 @@ export const getPqDevList = (params: Device.ReqPqDevParams) => {
}
//添加被检设备
export const addPqDev = (params: Device.ResPqDev) => {
return http.post(`/pqDev/add`, params)
export const addPqDev = (params: Device.ResPqDev, imageFile?: File | null) => {
return http.upload(`/pqDev/add`, buildPqDevFormData(params, imageFile))
}
//编辑被检设备
export const updatePqDev = (params: Device.ResPqDev) => {
return http.post(`/pqDev/update`, params)
export const updatePqDev = (params: Device.ResPqDev, imageFile?: File | null) => {
return http.upload(`/pqDev/update`, buildPqDevFormData(params, imageFile))
}
//删除被检设备
@@ -71,3 +72,7 @@ export const getPqDev = () => {
export const getSelectOptions = (params:{ pattern: string }) => {
return http.get(`/pqDev/getSelectOptions`, params)
}
export const downloadDeviceImage = (id: string) => {
return http.downloadGetWithHeaders(`/pqDev/image/download?id=${id}`)
}

View File

@@ -4,34 +4,38 @@ import http from '@/api'
/**
* @name ICD管理模块
*/
//获取ICD分页
export const getICDList = (params: ICD.ReqICDParams) => {
return http.post(`/icd/list`,params)
return http.post<ICD.ResICDPage>(`/icd/list`, params)
}
//获取ICD
export const getICDAllList = (params: ICD.ResICD) => {
return http.get(`/icd/listAll`,params)
export const getICDAllList = () => {
return http.get<ICD.ResICD[]>(`/icd/listAll`)
}
//添加ICD
export const addICD = (params: ICD.ResICD) => {
export const getStandardICDList = () => {
return http.get<ICD.StandardOption[]>(`/icd/standard-list`)
}
export const getICDById = (id: string) => {
return http.get<ICD.ResICD>(`/icd/getById`, { id })
}
export const addICD = (params: FormData) => {
return http.upload(`/icd/add`, params)
}
//编辑ICD
export const updateICD = (params: ICD.ResICD) => {
}
export const updateICD = (params: FormData) => {
return http.upload(`/icd/update`, params)
}
//删除ICD
export const deleteICD = (params: string[]) => {
}
export const importICDJson = (params: FormData) => {
return http.upload(`/icd/import/json`, params, { loading: false })
}
export const deleteICD = (params: string[]) => {
return http.post(`/icd/delete`, params)
}
}
export const exportICD = (id: string) => {
return http.downloadGetWithHeaders(`/icd/export/${id}`)
}

View File

@@ -19,6 +19,7 @@ export namespace DevType {
id: string; //设备类型ID
name: string;//设备类型名称
icd: string| null;//设备关联的ICD
pqdif: string| null;//设备关联的PQDIF
power: string| null;//工作电源
devVolt: number; //额定电压V
devCurr: number; //额定电流A
@@ -38,4 +39,4 @@ export namespace DevType {
export interface ResPqDevTypePage extends ResPage<ResPqDevType> {
}
}
}

View File

@@ -91,6 +91,9 @@ export namespace Device {
monitorList: Monitor.ResPqMon[]
checked?: boolean // 是否已选择
disabled?: boolean // 是否禁用
hasImage?: boolean // 是否存在设备图片
imageBase64?: string | null // 设备图片 Base64用于详情回显
imageContentType?: string | null // 设备图片 MIME 类型
}
export interface SelectOption {
@@ -116,4 +119,4 @@ export namespace Device {
* 被检设备表格查询分页返回的对象;
*/
export interface ResPqDevPage extends ResPage<ResPqDev> {}
}
}

View File

@@ -1,38 +1,36 @@
import type { ReqPage, ResPage } from '@/api/interface'
// ICD模块
export namespace ICD {
export interface ReqICDParams extends ReqPage {
name?: string
type?: number
}
/**
* ICD表格分页查询参数
*/
export interface ReqICDParams extends ReqPage {
id: string; // 装置序号id 必填
devType?: string; // 设备名称
createTime?: string; //创建时间
}
export interface StandardOption {
id: string
name: string
type?: number
}
/**
* ICD新增、修改、根据id查询返回的对象
*/
export interface ResICD {
id: string; //icdID
name: string;//icd名称
path: string;//icd存储地址
state: number;
createBy?: string| null; //创建用户
createTime?: string| null; //创建时间
updateBy?: string| null; //更新用户
updateTime?: string| null; //更新时间
angle: number; // 是否支持电压相角、电流相角指标
usePhaseIndex: number; // 角型接线时是否使用相别的指标来进行检测
mappingFile: string | null;//映射文件
}
export interface ResICD {
id: string
name: string
state: number
createBy?: string | null
createTime?: string | null
updateBy?: string | null
updateTime?: string | null
angle: number
usePhaseIndex: number
jsonStr: string
xmlStr: string
result: number
msg: string | null
type: number
referenceIcdId: string | null
referenceIcdName?: string | null
hasIcdFile?: boolean | null
}
/**
* ICD表格查询分页返回的对象
*/
export interface ResICDPage extends ResPage<ResICD> {
}
}
export interface ResICDPage extends ResPage<ResICD> {}
}

View File

@@ -0,0 +1,26 @@
import type { ReqPage, ResPage } from '@/api/interface'
export namespace PQDIF {
export interface ReqPQDIFParams extends ReqPage {
name?: string
result?: number
}
export interface ResPQDIF {
id: string
name: string
filePath?: string | null
recordCount?: number | null
observationCount?: number | null
sampleValueCount?: number | null
result?: number | null
msg?: string | null
state: number
createBy?: string | null
createTime?: string | null
updateBy?: string | null
updateTime?: string | null
}
export interface ResPQDIFPage extends ResPage<ResPQDIF> {}
}

View File

@@ -1,39 +1,34 @@
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: string
name?: string
createTime?: string
pattern: 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; //更新时间
id: string
name: string
version: string
pattern: string
baseFile?: string
detailFile?: string
description: string
state: number
createBy?: string | null
createTime?: string | null
updateBy?: string | null
updateTime?: string | null
}
/**
* 报告模版表格查询分页返回的对象;
*/
export interface ResReportPage extends ResPage<ResReport> {
export interface ResReportOption {
id: string
name: string
version: string
displayName: string
}
}
export interface ResReportPage extends ResPage<ResReport> {}
}

View File

@@ -0,0 +1,14 @@
import type { PQDIF } from '@/api/device/interface/pqdif'
import http from '@/api'
export const getPQDIFList = (params: PQDIF.ReqPQDIFParams) => {
return http.post<PQDIF.ResPQDIFPage>('/pqdif/list', params)
}
export const getPQDIFAllList = () => {
return http.get<PQDIF.ResPQDIF[]>('/pqdif/listAll')
}
export const importPQDIFJson = (params: FormData) => {
return http.upload('/pqdif/import/json', params, { loading: false })
}

View File

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

View File

@@ -214,6 +214,10 @@ class RequestHttp {
return this.service.post(url, params, { ..._object, responseType: 'blob' })
}
downloadGetWithHeaders(url: string, _object = {}): Promise<AxiosResponse<Blob>> {
return this.service.get(url, { ..._object, responseType: 'blob' })
}
upload(url: string, params?: object, _object = {}): Promise<BlobPart> {
return this.service.post(url, params, {
..._object,

View File

@@ -1,5 +1,15 @@
import http from '@/api'
export interface SntpTimeMessage {
type: string
deviceIp?: string
computerTime?: string
deviceTime?: string
computerTimestampMs?: number
deviceTimestampMs?: number
errorMs?: number
}
export const startSntpService = () => {
return http.post('/sntp/start', {})
}

View File

Before

Width:  |  Height:  |  Size: 9.0 KiB

After

Width:  |  Height:  |  Size: 9.0 KiB

View File

@@ -0,0 +1,171 @@
<template>
<el-dialog
v-model="dialogVisible"
:title="title"
width="460px"
destroy-on-close
:close-on-click-modal="!loading"
:show-close="!loading"
>
<div v-loading="loading" :element-loading-text="loadingText" class="json-import-content">
<el-upload
ref="uploadRef"
action="#"
class="upload"
:auto-upload="false"
:limit="1"
accept=".json,application/json"
:on-exceed="handleExceed"
:on-change="handleChange"
:on-remove="handleRemove"
:disabled="loading"
>
<el-button type="primary" :icon="Upload" :disabled="loading">选择文件</el-button>
<template #tip>
<div class="el-upload__tip">{{ tip }}</div>
</template>
</el-upload>
<div v-if="loading" class="json-import-loading-text">{{ loadingText }}</div>
</div>
<template #footer>
<div class="dialog-footer">
<el-button :disabled="loading" @click="close">取消</el-button>
<el-button type="primary" :loading="loading" @click="submit">开始导入</el-button>
</div>
</template>
</el-dialog>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { Upload } from '@element-plus/icons-vue'
import {
ElMessage,
ElNotification,
genFileId,
type UploadFile,
type UploadInstance,
type UploadProps,
type UploadRawFile
} from 'element-plus'
const props = withDefaults(
defineProps<{
title: string
requestApi: (params: FormData) => Promise<unknown>
tip?: string
loadingText?: string
successMessage?: string
}>(),
{
tip: '请上传 .json 文件',
loadingText: '导入中,请稍候...',
successMessage: '导入成功'
}
)
const emit = defineEmits<{
(e: 'success'): void
}>()
const dialogVisible = ref(false)
const loading = ref(false)
const uploadRef = ref<UploadInstance | null>(null)
const selectedFile = ref<File | null>(null)
const open = () => {
dialogVisible.value = true
}
const close = () => {
if (loading.value) {
return
}
dialogVisible.value = false
selectedFile.value = null
uploadRef.value?.clearFiles()
}
const isJsonFile = (file: UploadRawFile | File) => {
return file.name.toLowerCase().endsWith('.json') || file.type === 'application/json'
}
const notifyInvalidFile = () => {
ElNotification({
title: '温馨提示',
message: '上传文件只能是 json 格式!',
type: 'warning'
})
}
const setSelectedFile = (file: UploadRawFile | File | undefined) => {
if (!file) {
selectedFile.value = null
return
}
if (!isJsonFile(file)) {
notifyInvalidFile()
selectedFile.value = null
uploadRef.value?.clearFiles()
return
}
selectedFile.value = file
}
const handleExceed: UploadProps['onExceed'] = files => {
uploadRef.value?.clearFiles()
const file = files[0] as UploadRawFile
file.uid = genFileId()
uploadRef.value?.handleStart(file)
setSelectedFile(file)
}
const handleChange = (uploadFile: UploadFile) => {
setSelectedFile(uploadFile.raw as File | undefined)
}
const handleRemove = () => {
selectedFile.value = null
}
const submit = async () => {
if (loading.value) {
return
}
if (!selectedFile.value) {
ElMessage.warning('请选择文件!')
return
}
const formData = new FormData()
formData.append('file', selectedFile.value)
loading.value = true
try {
await props.requestApi(formData)
ElMessage.success(props.successMessage)
dialogVisible.value = false
selectedFile.value = null
uploadRef.value?.clearFiles()
emit('success')
} finally {
loading.value = false
}
}
defineExpose({
open,
close
})
</script>
<style scoped>
.json-import-content {
min-height: 120px;
}
.json-import-loading-text {
margin-top: 12px;
color: var(--el-text-color-secondary);
font-size: 13px;
}
</style>

View File

@@ -4,7 +4,7 @@
<el-header>
<div class="logo flx-center">
<!-- <img class="logo-img" src="@/assets/images/logo.svg" alt="logo" /> -->
<img class="logo-img" src="@/assets/images/cn_pms9100_logo.png" alt="logo" />
<img class="logo-img" src="@/assets/images/logo.png" alt="logo" />
<span class="logo-text">{{ title }}</span>
</div>
<el-menu v-if="showMenuFlag" trigger="click" mode="horizontal" :router="false" :default-active="activeMenu">

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