Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8c7c34cda2 | ||
|
|
b0ee58a1b5 | ||
|
|
c3915dc8f5 |
211
docs/superpowers/specs/2026-07-10-time-check-design.md
Normal file
211
docs/superpowers/specs/2026-07-10-time-check-design.md
Normal 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`:验证只提示,不改变表格结构。
|
||||||
@@ -36,6 +36,7 @@ export const useCheckStore = defineStore(CHECK_STORE_KEY, {
|
|||||||
initSelectTestItems() {
|
initSelectTestItems() {
|
||||||
const appSceneStore = useAppSceneStore()
|
const appSceneStore = useAppSceneStore()
|
||||||
this.selectTestItems.preTest = true
|
this.selectTestItems.preTest = true
|
||||||
|
this.selectTestItems.timeTest = false
|
||||||
if (appSceneStore.currentScene === '1') {
|
if (appSceneStore.currentScene === '1') {
|
||||||
this.selectTestItems.channelsTest = true
|
this.selectTestItems.channelsTest = true
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -355,7 +355,7 @@ const handleSubmitAgain = async () => {
|
|||||||
devIds: devIds.value,
|
devIds: devIds.value,
|
||||||
standardDevIds: standardDevIds.value,
|
standardDevIds: standardDevIds.value,
|
||||||
pairs: pairs.value,
|
pairs: pairs.value,
|
||||||
testItemList: [checkStore.selectTestItems.preTest, false, checkStore.selectTestItems.test],
|
testItemList: [checkStore.selectTestItems.preTest, false, false, checkStore.selectTestItems.test],
|
||||||
userId: userStore.userInfo.id,
|
userId: userStore.userInfo.id,
|
||||||
phaseCheck: xiangXuIsShow.value ? 1 : 0,
|
phaseCheck: xiangXuIsShow.value ? 1 : 0,
|
||||||
})
|
})
|
||||||
@@ -423,7 +423,7 @@ const handleSubmitFast = async () => {
|
|||||||
devIds: devIds.value,
|
devIds: devIds.value,
|
||||||
standardDevIds: standardDevIds.value,
|
standardDevIds: standardDevIds.value,
|
||||||
pairs: pairs.value,
|
pairs: pairs.value,
|
||||||
testItemList: [checkStore.selectTestItems.preTest, false, checkStore.selectTestItems.test],
|
testItemList: [checkStore.selectTestItems.preTest, false, false, checkStore.selectTestItems.test],
|
||||||
|
|
||||||
userId: userStore.userInfo.id,
|
userId: userStore.userInfo.id,
|
||||||
phaseCheck: xiangXuIsShow.value ? 1 : 0,
|
phaseCheck: xiangXuIsShow.value ? 1 : 0,
|
||||||
@@ -448,7 +448,7 @@ const handleSubmitFast = async () => {
|
|||||||
devIds: devIds.value,
|
devIds: devIds.value,
|
||||||
standardDevIds: standardDevIds.value,
|
standardDevIds: standardDevIds.value,
|
||||||
pairs: pairs.value,
|
pairs: pairs.value,
|
||||||
testItemList: [checkStore.selectTestItems.preTest, false, checkStore.selectTestItems.test],
|
testItemList: [checkStore.selectTestItems.preTest, false, false, checkStore.selectTestItems.test],
|
||||||
|
|
||||||
userId: userStore.userInfo.id,
|
userId: userStore.userInfo.id,
|
||||||
phaseCheck: xiangXuIsShow.value ? 1 : 0,
|
phaseCheck: xiangXuIsShow.value ? 1 : 0,
|
||||||
@@ -571,7 +571,8 @@ const sendReCheck = () => {
|
|||||||
humidity: checkStore.humidity,
|
humidity: checkStore.humidity,
|
||||||
testItemList: [
|
testItemList: [
|
||||||
checkStore.selectTestItems.preTest,
|
checkStore.selectTestItems.preTest,
|
||||||
checkStore.selectTestItems.channelsTest,
|
false,
|
||||||
|
false,
|
||||||
checkStore.selectTestItems.test
|
checkStore.selectTestItems.test
|
||||||
]
|
]
|
||||||
}).then(res => {
|
}).then(res => {
|
||||||
|
|||||||
@@ -1,18 +1,30 @@
|
|||||||
<template>
|
<template>
|
||||||
<el-dialog title="选择测试内容" v-model='dialogVisible' @close="handleClose" v-bind="dialogSmall" >
|
<el-dialog title="选择测试内容" v-model='dialogVisible' @close="handleClose" v-bind="dialogSmall" >
|
||||||
<div>
|
<div>
|
||||||
<el-form ref="dialogFormRef" :model="formContent" class="form-three">
|
<el-form ref="dialogFormRef" :model="formContent" class="form-three select-test-form">
|
||||||
<el-form-item prop="preTest" :label-width="100">
|
<el-form-item prop="preTest">
|
||||||
<el-checkbox v-model="formContent.preTest" label="预检测"/>
|
<div class="test-item-checkbox">
|
||||||
|
<span>预检测</span>
|
||||||
|
<el-checkbox v-model="formContent.preTest"/>
|
||||||
|
</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item v-if="checkStore.plan.timeCheck===1" prop="timeTest" :label-width="100">
|
<el-form-item v-if="checkStore.plan.timeCheck===1" prop="timeTest">
|
||||||
<el-checkbox v-model="formContent.timeTest" label="守时检测"/>
|
<div class="test-item-checkbox">
|
||||||
|
<span>守时检测</span>
|
||||||
|
<el-checkbox v-model="formContent.timeTest"/>
|
||||||
|
</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item v-if="channelsTestShow" prop="channelsTest" :label-width="100">
|
<el-form-item v-if="channelsTestShow" prop="channelsTest">
|
||||||
<el-checkbox v-model="formContent.channelsTest" label="系数校准"/>
|
<div class="test-item-checkbox">
|
||||||
|
<span>系数校准</span>
|
||||||
|
<el-checkbox v-model="formContent.channelsTest"/>
|
||||||
|
</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item prop="test" :label-width="100">
|
<el-form-item prop="test">
|
||||||
<el-checkbox v-model="formContent.test" label="正式检测"/>
|
<div class="test-item-checkbox">
|
||||||
|
<span>正式检测</span>
|
||||||
|
<el-checkbox v-model="formContent.test"/>
|
||||||
|
</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</div>
|
</div>
|
||||||
@@ -123,5 +135,30 @@ defineExpose({open})
|
|||||||
|
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
.test-item-checkbox {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
min-width: 92px;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select-test-form {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2, 120px);
|
||||||
|
justify-content: center;
|
||||||
|
column-gap: 70px;
|
||||||
|
row-gap: 22px;
|
||||||
|
padding: 12px 0 18px;
|
||||||
|
|
||||||
|
:deep(.el-form-item) {
|
||||||
|
width: auto !important;
|
||||||
|
margin: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-form-item__content) {
|
||||||
|
flex: none;
|
||||||
|
justify-content: flex-start;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -28,18 +28,18 @@
|
|||||||
:icon="stepsActive > 1 || stepsActiveIndex > stepsTotalNum - 1 ? SuccessFilled : Edit"
|
:icon="stepsActive > 1 || stepsActiveIndex > stepsTotalNum - 1 ? SuccessFilled : Edit"
|
||||||
@click="handleStepClick(1)"
|
@click="handleStepClick(1)"
|
||||||
/>
|
/>
|
||||||
<!-- 守时检测步骤 -->
|
|
||||||
<el-step
|
|
||||||
v-if="timeTestSelected"
|
|
||||||
title="守时检测"
|
|
||||||
:icon="stepsActive > 2 || stepsActiveIndex > stepsTotalNum - 1 ? SuccessFilled : UploadFilled"
|
|
||||||
@click="handleStepClick(2)"
|
|
||||||
/>
|
|
||||||
<!-- 系数校准步骤 -->
|
<!-- 系数校准步骤 -->
|
||||||
<el-step
|
<el-step
|
||||||
v-if="channelsTestSelected"
|
v-if="channelsTestSelected"
|
||||||
title="系数校准"
|
title="系数校准"
|
||||||
:icon="stepsActive > 3 || stepsActiveIndex > stepsTotalNum - 1 ? SuccessFilled : Odometer"
|
:icon="stepsActive > 2 || stepsActiveIndex > stepsTotalNum - 1 ? SuccessFilled : Odometer"
|
||||||
|
@click="handleStepClick(2)"
|
||||||
|
/>
|
||||||
|
<!-- 守时检测步骤 -->
|
||||||
|
<el-step
|
||||||
|
v-if="timeTestSelected"
|
||||||
|
title="守时检测"
|
||||||
|
:icon="stepsActive > 3 || stepsActiveIndex > stepsTotalNum - 1 ? SuccessFilled : UploadFilled"
|
||||||
@click="handleStepClick(3)"
|
@click="handleStepClick(3)"
|
||||||
/>
|
/>
|
||||||
<!-- 正式检测步骤 -->
|
<!-- 正式检测步骤 -->
|
||||||
@@ -66,13 +66,13 @@
|
|||||||
<!-- 守时检测组件 -->
|
<!-- 守时检测组件 -->
|
||||||
<timeTest
|
<timeTest
|
||||||
v-if="showComponent && timeTestSelected"
|
v-if="showComponent && timeTestSelected"
|
||||||
v-show="timeTestSelected && stepsActiveView == 2"
|
v-show="timeTestSelected && stepsActiveView == 3"
|
||||||
v-model:testStatus="timeTestStatus"
|
v-model:testStatus="timeTestStatus"
|
||||||
/>
|
/>
|
||||||
<!-- 系数校准检测组件 -->
|
<!-- 系数校准检测组件 -->
|
||||||
<factorTest
|
<factorTest
|
||||||
v-if="showComponent && channelsTestSelected"
|
v-if="showComponent && channelsTestSelected"
|
||||||
v-show="channelsTestSelected && stepsActiveView == 3"
|
v-show="channelsTestSelected && stepsActiveView == 2"
|
||||||
v-model:testStatus="channelsTestStatus"
|
v-model:testStatus="channelsTestStatus"
|
||||||
:webMsgSend="webMsgSend"
|
:webMsgSend="webMsgSend"
|
||||||
/>
|
/>
|
||||||
@@ -255,20 +255,20 @@ const initOperate = () => {
|
|||||||
}
|
}
|
||||||
stepsTotalNum.value = count + 1
|
stepsTotalNum.value = count + 1
|
||||||
|
|
||||||
// 根据选中的检测项,设置初始显示的步骤(按优先级:预检测 > 守时检测 > 系数校准 > 正式检测)
|
// 根据选中的检测项,设置初始显示的步骤(按优先级:预检测 > 系数校准 > 守时检测 > 正式检测)
|
||||||
if (preTestSelected.value) {
|
if (preTestSelected.value) {
|
||||||
stepsActiveView.value = 1 // 显示预检测组件
|
stepsActiveView.value = 1 // 显示预检测组件
|
||||||
stepsActive.value = 1 // 业务逻辑设为预检测
|
stepsActive.value = 1 // 业务逻辑设为预检测
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (timeTestSelected.value) {
|
if (channelsTestSelected.value) {
|
||||||
stepsActiveView.value = 2 // 显示守时检测组件
|
stepsActiveView.value = 2 // 显示系数校准组件
|
||||||
stepsActive.value = 2 // 业务逻辑设为守时检测
|
stepsActive.value = 2 // 业务逻辑设为系数校准
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (channelsTestSelected.value) {
|
if (timeTestSelected.value) {
|
||||||
stepsActiveView.value = 3 // 显示系数校准组件
|
stepsActiveView.value = 3 // 显示守时检测组件
|
||||||
stepsActive.value = 3 // 业务逻辑设为系数校准
|
stepsActive.value = 3 // 业务逻辑设为守时检测
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (testSelected.value) {
|
if (testSelected.value) {
|
||||||
@@ -354,6 +354,7 @@ const handleSubmitFast = () => {
|
|||||||
testItemList: [
|
testItemList: [
|
||||||
checkStore.selectTestItems.preTest,
|
checkStore.selectTestItems.preTest,
|
||||||
checkStore.selectTestItems.channelsTest,
|
checkStore.selectTestItems.channelsTest,
|
||||||
|
checkStore.selectTestItems.timeTest,
|
||||||
checkStore.selectTestItems.test
|
checkStore.selectTestItems.test
|
||||||
]
|
]
|
||||||
}).then(res => {
|
}).then(res => {
|
||||||
@@ -369,10 +370,7 @@ const handleSubmitFast = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
case 2: // 守时检测步骤
|
case 2: // 系数校准步骤
|
||||||
timeTestStatus.value = 'start'
|
|
||||||
break
|
|
||||||
case 3: // 系数校准步骤
|
|
||||||
if (channelsTestStatus.value == 'waiting') {
|
if (channelsTestStatus.value == 'waiting') {
|
||||||
// 如果没有预检测且有系数校准,需要先进行初始化
|
// 如果没有预检测且有系数校准,需要先进行初始化
|
||||||
if (!checkStore.selectTestItems.preTest && checkStore.selectTestItems.channelsTest) {
|
if (!checkStore.selectTestItems.preTest && checkStore.selectTestItems.channelsTest) {
|
||||||
@@ -387,6 +385,7 @@ const handleSubmitFast = () => {
|
|||||||
testItemList: [
|
testItemList: [
|
||||||
checkStore.selectTestItems.preTest,
|
checkStore.selectTestItems.preTest,
|
||||||
checkStore.selectTestItems.channelsTest,
|
checkStore.selectTestItems.channelsTest,
|
||||||
|
checkStore.selectTestItems.timeTest,
|
||||||
checkStore.selectTestItems.test
|
checkStore.selectTestItems.test
|
||||||
]
|
]
|
||||||
}).then(res => {
|
}).then(res => {
|
||||||
@@ -402,12 +401,47 @@ const handleSubmitFast = () => {
|
|||||||
channelsTestStatus.value = 'start'
|
channelsTestStatus.value = 'start'
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
case 4: // 正式检测步骤
|
case 3: // 守时检测步骤
|
||||||
if (TestStatus.value == 'waiting') {
|
if (timeTestStatus.value == 'waiting') {
|
||||||
// 如果没有预检测和系数校准,直接进行正式检测需要先初始化
|
|
||||||
if (
|
if (
|
||||||
!checkStore.selectTestItems.preTest &&
|
!checkStore.selectTestItems.preTest &&
|
||||||
!checkStore.selectTestItems.channelsTest &&
|
!checkStore.selectTestItems.channelsTest &&
|
||||||
|
checkStore.selectTestItems.timeTest
|
||||||
|
) {
|
||||||
|
startPreTest({
|
||||||
|
userPageId: JwtUtil.getLoginName(),
|
||||||
|
devIds: deviceIds,
|
||||||
|
planId: planId,
|
||||||
|
reCheckType: checkStore.reCheckType == 1 ? '1' : '2',
|
||||||
|
userId: userStore.userInfo.id,
|
||||||
|
temperature: checkStore.temperature,
|
||||||
|
humidity: checkStore.humidity,
|
||||||
|
testItemList: [
|
||||||
|
checkStore.selectTestItems.preTest,
|
||||||
|
checkStore.selectTestItems.channelsTest,
|
||||||
|
checkStore.selectTestItems.timeTest,
|
||||||
|
checkStore.selectTestItems.test
|
||||||
|
]
|
||||||
|
}).then(res => {
|
||||||
|
if (res.code !== 'A0000') {
|
||||||
|
ElMessageBox.alert('守时检测失败', '检测失败', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
type: 'error'
|
||||||
|
})
|
||||||
|
timeTestStatus.value = 'error'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
timeTestStatus.value = 'start'
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case 4: // 正式检测步骤
|
||||||
|
if (TestStatus.value == 'waiting') {
|
||||||
|
// 如果没有预检测、系数校准和守时检测,直接进行正式检测需要先初始化
|
||||||
|
if (
|
||||||
|
!checkStore.selectTestItems.preTest &&
|
||||||
|
!checkStore.selectTestItems.channelsTest &&
|
||||||
|
!checkStore.selectTestItems.timeTest &&
|
||||||
checkStore.selectTestItems.test
|
checkStore.selectTestItems.test
|
||||||
) {
|
) {
|
||||||
startPreTest({
|
startPreTest({
|
||||||
@@ -421,6 +455,7 @@ const handleSubmitFast = () => {
|
|||||||
testItemList: [
|
testItemList: [
|
||||||
checkStore.selectTestItems.preTest,
|
checkStore.selectTestItems.preTest,
|
||||||
checkStore.selectTestItems.channelsTest,
|
checkStore.selectTestItems.channelsTest,
|
||||||
|
checkStore.selectTestItems.timeTest,
|
||||||
checkStore.selectTestItems.test
|
checkStore.selectTestItems.test
|
||||||
]
|
]
|
||||||
}).then(res => {
|
}).then(res => {
|
||||||
@@ -629,27 +664,25 @@ const nextStep = () => {
|
|||||||
if (ActiveStatue.value != 'error') {
|
if (ActiveStatue.value != 'error') {
|
||||||
ActiveStatue.value = 'waiting' // 重置为等待状态
|
ActiveStatue.value = 'waiting' // 重置为等待状态
|
||||||
let tempStep = stepsActiveIndex.value
|
let tempStep = stepsActiveIndex.value
|
||||||
let idx = 1
|
const stepOrder = [
|
||||||
|
{ key: 'preTest', step: 1 },
|
||||||
|
{ key: 'channelsTest', step: 2 },
|
||||||
|
{ key: 'timeTest', step: 3 },
|
||||||
|
{ key: 'test', step: 4 }
|
||||||
|
] as const
|
||||||
stepsActiveIndex.value++ // 步骤索引递增
|
stepsActiveIndex.value++ // 步骤索引递增
|
||||||
|
|
||||||
// 遍历检测项,找到下一个需要执行的步骤
|
// 遍历检测项,找到下一个需要执行的步骤
|
||||||
for (let selectTestItemsKey in checkStore.selectTestItems) {
|
for (let item of stepOrder) {
|
||||||
if (
|
if (tempStep == 0 && checkStore.selectTestItems[item.key]) {
|
||||||
tempStep == 0 &&
|
|
||||||
checkStore.selectTestItems[selectTestItemsKey as keyof typeof checkStore.selectTestItems]
|
|
||||||
) {
|
|
||||||
// 找到下一个要执行的检测项
|
// 找到下一个要执行的检测项
|
||||||
stepsActiveView.value = idx // 设置显示的组件
|
stepsActiveView.value = item.step // 设置显示的组件
|
||||||
stepsActive.value = idx // 设置业务逻辑步骤
|
stepsActive.value = item.step // 设置业务逻辑步骤
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (
|
if (checkStore.selectTestItems[item.key] && tempStep != 0) {
|
||||||
checkStore.selectTestItems[selectTestItemsKey as keyof typeof checkStore.selectTestItems] &&
|
|
||||||
tempStep != 0
|
|
||||||
) {
|
|
||||||
tempStep-- // 跳过已选择的检测项
|
tempStep-- // 跳过已选择的检测项
|
||||||
}
|
}
|
||||||
idx++
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,9 @@
|
|||||||
<el-form-item label='相对湿度(%)' prop='humidity' :label-width='110'>
|
<el-form-item label='相对湿度(%)' prop='humidity' :label-width='110'>
|
||||||
<el-input v-model='formContent.humidity' placeholder='请输入湿度' maxlength='32' show-word-limit />
|
<el-input v-model='formContent.humidity' placeholder='请输入湿度' maxlength='32' show-word-limit />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<el-form-item v-if='checkStore.plan.timeCheck === 1' label='守时检测' prop='timeTest' :label-width='110'>
|
||||||
|
<el-checkbox v-model='timeTestChecked' @change='handleTimeTestChange' />
|
||||||
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -33,15 +36,30 @@ const dialogFormRef = ref()
|
|||||||
|
|
||||||
const dialogVisible = ref(false)
|
const dialogVisible = ref(false)
|
||||||
const formContent = reactive<Device.ResTH>({ temperature: 0, humidity: 0 })
|
const formContent = reactive<Device.ResTH>({ temperature: 0, humidity: 0 })
|
||||||
|
const timeTestChecked = ref(false)
|
||||||
const checkStore = useCheckStore()
|
const checkStore = useCheckStore()
|
||||||
const open = async () => {
|
const open = async () => {
|
||||||
resetFormContent()
|
resetFormContent()
|
||||||
|
syncTimeTestSelection(false)
|
||||||
dialogVisible.value = true
|
dialogVisible.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// 清空表单内容
|
// 清空表单内容
|
||||||
const resetFormContent = () => {
|
const resetFormContent = () => {
|
||||||
Object.assign(formContent, { temperature: '22', humidity: '50' })
|
Object.assign(formContent, { temperature: '22', humidity: '50' })
|
||||||
|
timeTestChecked.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
const syncTimeTestSelection = (selected: boolean) => {
|
||||||
|
timeTestChecked.value = selected
|
||||||
|
checkStore.setSelectTestItems({
|
||||||
|
...checkStore.selectTestItems,
|
||||||
|
timeTest: selected,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleTimeTestChange = (value: string | number | boolean) => {
|
||||||
|
syncTimeTestSelection(Boolean(value))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -95,5 +113,4 @@ defineExpose({ open })
|
|||||||
|
|
||||||
|
|
||||||
<style scoped lang='scss'>
|
<style scoped lang='scss'>
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
@@ -172,21 +172,6 @@
|
|||||||
/>
|
/>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<!-- <el-form-item :label-width="110" label="守时检测" prop="timeCheck">
|
|
||||||
<el-radio-group
|
|
||||||
v-model="formContent.timeCheck"
|
|
||||||
:disabled="planType != 0 || allDisabled || !canEdited"
|
|
||||||
>
|
|
||||||
<el-radio :value="1">是</el-radio>
|
|
||||||
<el-radio :value="0">否</el-radio>
|
|
||||||
</el-radio-group>
|
|
||||||
</el-form-item>-->
|
|
||||||
<!-- <el-form-item label="关联报告模版" :label-width="100" prop='associateReport'>
|
|
||||||
<el-radio-group v-model="formContent.associateReport">
|
|
||||||
<el-radio :value="1">是</el-radio>
|
|
||||||
<el-radio :value="0">否</el-radio>
|
|
||||||
</el-radio-group>
|
|
||||||
</el-form-item> -->
|
|
||||||
<el-form-item
|
<el-form-item
|
||||||
v-if="formContent.associateReport === 1"
|
v-if="formContent.associateReport === 1"
|
||||||
:label-width="110"
|
:label-width="110"
|
||||||
@@ -207,6 +192,9 @@
|
|||||||
/>
|
/>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<el-form-item v-if="!selectByMode" :label-width="110" label="守时检测" prop="timeCheck">
|
||||||
|
<el-switch v-model="formContent.timeCheck" :active-value="1" :inactive-value="0" />
|
||||||
|
</el-form-item>
|
||||||
<el-form-item v-if="selectByMode" :label-width="110" label="项目负责人" prop="leader">
|
<el-form-item v-if="selectByMode" :label-width="110" label="项目负责人" prop="leader">
|
||||||
<el-select
|
<el-select
|
||||||
v-model="formContent.leader"
|
v-model="formContent.leader"
|
||||||
|
|||||||
Reference in New Issue
Block a user