refactor(steady): 重构稳态校验功能的合同验证逻辑
- 优化测量点对话框路径解析格式化 - 添加稳态校验重启API端点验证功能 - 更新创建参数类型定义支持可选lineId和lineIds数组 - 修复任务表格组件模板缩进格式问题 - 扩展任务表格列配置验证数组格式 - 添加任务表格仅对失败行显示重启操作功能 - 集成共享台账树组件发送选中叶节点监测点功能 - 添加多监测点创建参数构建支持 - 实现指标选择为空时全指标校验功能 - 添加预期项目数量计算功能 - 优化创建对话框指标选择标签防止输入框重置 - 添加任务表格树形选择滚动下拉菜单样式 - 实现页面创建流程调用创建API并轮询任务状态 - 添加创建结果面板加载状态和进度显示 - 实现页面重启流程确认调用重启API功能 - 优化汇总表格异常字段持久化支持 - 添加汇总表格监测点名称显示功能 - 优化详情面板按需加载项目详情 - 更新接口类型支持分页字段定义 - 优化ICD路径检查对话框标准映射参数传递 - 添加ICD记录导出SQL和JSON功能 - 扩展ICD路径API端点和类型定义 - 添加ICD路径参考选项和映射详情功能 - 重构ICD表格列显示移除激活和创建时间列 - 添加ICD映射详情对话框三个标签页功能 - 优化ICD映射详情JSON树形视图显示 - 更新ICD类型选项覆盖手动和上游标准状态
This commit is contained in:
@@ -1,417 +0,0 @@
|
||||
# check-square API 调试文档
|
||||
|
||||
## 1. 模块说明
|
||||
|
||||
- 模块路径:`steady/check-square`
|
||||
- 接口基础路径:`/steady/checksquare`
|
||||
- 返回包装:接口统一返回 `HttpResult<T>`,调试时重点查看响应体中的业务数据字段 `data`。
|
||||
- 时间格式:`yyyy-MM-dd HH:mm:ss`
|
||||
|
||||
本模块用于按监测点、时间范围和指标执行稳态数据校验,并提供任务列表、任务详情、检测项明细查询和任务删除能力。
|
||||
|
||||
## 2. 通用约定
|
||||
|
||||
### 2.1 请求头
|
||||
|
||||
| 名称 | 示例 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `Content-Type` | `application/json` | `POST` 请求使用 JSON 请求体 |
|
||||
| `Authorization` | 登录态 Token | 如当前环境开启认证,需携带现有登录接口返回的认证信息 |
|
||||
|
||||
### 2.2 任务状态
|
||||
|
||||
| 值 | 说明 |
|
||||
| --- | --- |
|
||||
| `RUNNING` | 执行中 |
|
||||
| `SUCCESS` | 执行成功 |
|
||||
| `FAIL` | 执行失败 |
|
||||
|
||||
### 2.3 明细类型
|
||||
|
||||
| 值 | 说明 |
|
||||
| --- | --- |
|
||||
| `SEGMENT` | 缺失区间 |
|
||||
| `VALUE_ORDER` | 指标值大小关系异常明细 |
|
||||
| `HARMONIC_PARITY` | 谐波奇偶关系异常明细 |
|
||||
|
||||
## 3. 调试顺序建议
|
||||
|
||||
1. 调用 `POST /steady/checksquare/create`:按监测点和时间范围创建或获取任务。
|
||||
2. 读取 `/create` 返回的 `data.taskId`:该返回值是任务列表中的行信息。
|
||||
3. 调用 `GET /steady/checksquare/detail`:用 `taskId` 查询任务下的检测项。
|
||||
4. 读取 `/detail` 返回的 `items[].itemId`:按检测项继续查询缺失区间或异常明细。
|
||||
5. 调用 `GET /steady/checksquare/item-detail`:按 `itemId + detailType` 查询具体明细。
|
||||
6. 调用 `POST /steady/checksquare/query`:按条件分页查询历史任务列表。
|
||||
7. 需要清理任务时调用 `POST /steady/checksquare/delete`。
|
||||
|
||||
注意:旧的获取或创建兼容接口已移除。创建或复用任务的逻辑已经合并到 `/create` 中。
|
||||
|
||||
## 4. 创建或获取任务
|
||||
|
||||
### 4.1 接口
|
||||
|
||||
`POST /steady/checksquare/create`
|
||||
|
||||
### 4.2 行为说明
|
||||
|
||||
接口开始执行前,会先按 `lineId + timeStart + timeEnd` 查询是否存在未删除的任务:
|
||||
|
||||
- 已存在:直接返回该任务的任务列表行信息。
|
||||
- 不存在:创建任务,执行校验,任务执行完成后返回任务列表行信息。
|
||||
|
||||
返回对象为 `SteadyChecksquareTaskVO`,用于页面任务列表展示。若要查看检测项明细,需要再调用 `GET /steady/checksquare/detail`。
|
||||
|
||||
### 4.3 请求字段
|
||||
|
||||
| 字段 | 类型 | 必填 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `lineId` | `String` | 是 | 监测点 ID |
|
||||
| `indicatorCodes` | `Array<String>` | 是 | 指标编码列表 |
|
||||
| `timeStart` | `String` | 是 | 开始时间,格式 `yyyy-MM-dd HH:mm:ss` |
|
||||
| `timeEnd` | `String` | 是 | 结束时间,格式 `yyyy-MM-dd HH:mm:ss` |
|
||||
|
||||
`indicatorCodes` 是数组;不要传成单个字符串。
|
||||
|
||||
### 4.4 请求示例
|
||||
|
||||
```http
|
||||
POST /steady/checksquare/create
|
||||
Content-Type: application/json
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"lineId": "LINE_001",
|
||||
"indicatorCodes": ["VOLTAGE_A", "CURRENT_A"],
|
||||
"timeStart": "2026-06-13 00:00:00",
|
||||
"timeEnd": "2026-06-13 01:00:00"
|
||||
}
|
||||
```
|
||||
|
||||
### 4.5 响应字段 `data`
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `taskId` | `String` | 任务 ID |
|
||||
| `taskNo` | `String` | 任务编号 |
|
||||
| `lineId` | `String` | 监测点 ID |
|
||||
| `lineName` | `String` | 监测点名称 |
|
||||
| `timeStart` | `String` | 开始时间 |
|
||||
| `timeEnd` | `String` | 结束时间 |
|
||||
| `intervalMinutes` | `Integer` | 统计间隔,单位分钟 |
|
||||
| `taskStatus` | `String` | 任务状态:`RUNNING`、`SUCCESS`、`FAIL` |
|
||||
| `itemCount` | `Integer` | 检测项数量 |
|
||||
| `abnormalItemCount` | `Integer` | 异常检测项数量 |
|
||||
| `minDataIntegrity` | `BigDecimal` | 最低数据完整率 |
|
||||
| `createTime` | `String` | 创建时间 |
|
||||
|
||||
### 4.6 响应示例
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"msg": "success",
|
||||
"data": {
|
||||
"taskId": "1812345678901234567",
|
||||
"taskNo": "CS202606130001",
|
||||
"lineId": "LINE_001",
|
||||
"lineName": "1号监测点",
|
||||
"timeStart": "2026-06-13 00:00:00",
|
||||
"timeEnd": "2026-06-13 01:00:00",
|
||||
"intervalMinutes": 1,
|
||||
"taskStatus": "SUCCESS",
|
||||
"itemCount": 2,
|
||||
"abnormalItemCount": 1,
|
||||
"minDataIntegrity": 98.50,
|
||||
"createTime": "2026-06-13 09:30:00"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 5. 查询任务列表
|
||||
|
||||
### 5.1 接口
|
||||
|
||||
`POST /steady/checksquare/query`
|
||||
|
||||
### 5.2 请求字段
|
||||
|
||||
| 字段 | 类型 | 必填 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `lineId` | `String` | 否 | 监测点 ID |
|
||||
| `indicatorCode` | `String` | 否 | 指标编码 |
|
||||
| `timeStart` | `String` | 否 | 检测开始时间,格式 `yyyy-MM-dd HH:mm:ss` |
|
||||
| `timeEnd` | `String` | 否 | 检测结束时间,格式 `yyyy-MM-dd HH:mm:ss` |
|
||||
| `hasAbnormal` | `Boolean` | 否 | 是否存在异常 |
|
||||
| `pageNum` | `Integer` | 否 | 页码 |
|
||||
| `pageSize` | `Integer` | 否 | 每页条数 |
|
||||
|
||||
`indicatorCode` 是单个字符串,用于历史任务筛选;和 `/create` 的 `indicatorCodes` 数组不同。
|
||||
|
||||
### 5.3 请求示例
|
||||
|
||||
```http
|
||||
POST /steady/checksquare/query
|
||||
Content-Type: application/json
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"lineId": "LINE_001",
|
||||
"indicatorCode": "VOLTAGE_A",
|
||||
"timeStart": "2026-06-13 00:00:00",
|
||||
"timeEnd": "2026-06-13 23:59:59",
|
||||
"hasAbnormal": true,
|
||||
"pageNum": 1,
|
||||
"pageSize": 10
|
||||
}
|
||||
```
|
||||
|
||||
### 5.4 响应说明
|
||||
|
||||
`data` 为 MyBatis-Plus `Page<SteadyChecksquareTaskVO>` 分页对象,常用字段如下:
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `records` | `Array<Object>` | 任务列表,每项字段同 `/create` 返回的 `SteadyChecksquareTaskVO` |
|
||||
| `total` | `Long` | 总记录数 |
|
||||
| `size` | `Long` | 每页条数 |
|
||||
| `current` | `Long` | 当前页码 |
|
||||
| `pages` | `Long` | 总页数 |
|
||||
|
||||
### 5.5 响应示例
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"msg": "success",
|
||||
"data": {
|
||||
"records": [
|
||||
{
|
||||
"taskId": "1812345678901234567",
|
||||
"taskNo": "CS202606130001",
|
||||
"lineId": "LINE_001",
|
||||
"lineName": "1号监测点",
|
||||
"timeStart": "2026-06-13 00:00:00",
|
||||
"timeEnd": "2026-06-13 01:00:00",
|
||||
"intervalMinutes": 1,
|
||||
"taskStatus": "SUCCESS",
|
||||
"itemCount": 2,
|
||||
"abnormalItemCount": 1,
|
||||
"minDataIntegrity": 98.50,
|
||||
"createTime": "2026-06-13 09:30:00"
|
||||
}
|
||||
],
|
||||
"total": 1,
|
||||
"size": 10,
|
||||
"current": 1,
|
||||
"pages": 1
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 6. 查询任务详情
|
||||
|
||||
### 6.1 接口
|
||||
|
||||
`GET /steady/checksquare/detail?taskId={taskId}`
|
||||
|
||||
### 6.2 请求参数
|
||||
|
||||
| 字段 | 类型 | 必填 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `taskId` | `String` | 是 | 任务 ID |
|
||||
|
||||
### 6.3 请求示例
|
||||
|
||||
```http
|
||||
GET /steady/checksquare/detail?taskId=1812345678901234567
|
||||
```
|
||||
|
||||
### 6.4 响应字段 `data`
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `taskId` | `String` | 任务 ID |
|
||||
| `taskNo` | `String` | 任务编号 |
|
||||
| `lineId` | `String` | 监测点 ID |
|
||||
| `lineName` | `String` | 监测点名称 |
|
||||
| `timeStart` | `String` | 开始时间 |
|
||||
| `timeEnd` | `String` | 结束时间 |
|
||||
| `intervalMinutes` | `Integer` | 统计间隔,单位分钟 |
|
||||
| `items` | `Array<Object>` | 检测项列表 |
|
||||
|
||||
### 6.5 检测项字段 `items[]`
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `itemId` | `String` | 检测项 ID,用于查询明细 |
|
||||
| `itemKey` | `String` | 检测项唯一键 |
|
||||
| `indicatorCode` | `String` | 指标编码 |
|
||||
| `indicatorName` | `String` | 指标名称 |
|
||||
| `harmonicOrder` | `Integer` | 谐波次数 |
|
||||
| `intervalMinutes` | `Integer` | 当前检测项统计间隔,单位分钟 |
|
||||
| `hasData` | `Boolean` | 时间范围内是否存在任意数据 |
|
||||
| `expectedPointCount` | `Integer` | 期望点数 |
|
||||
| `actualPointCount` | `Integer` | 实际点数 |
|
||||
| `missingPointCount` | `Integer` | 缺失点数 |
|
||||
| `dataIntegrity` | `BigDecimal` | 数据完整率 |
|
||||
| `dataIntegrityText` | `String` | 数据完整率文本 |
|
||||
| `abnormal` | `Boolean` | 指标值大小关系是否异常 |
|
||||
| `abnormalPointCount` | `Integer` | 指标值大小关系异常点数 |
|
||||
| `abnormalDetails` | `Array<Object>` | 指标值大小关系异常明细摘要 |
|
||||
| `harmonicParityAbnormal` | `Boolean` | 谐波奇偶关系是否异常 |
|
||||
| `harmonicParityAbnormalPointCount` | `Integer` | 谐波奇偶关系异常点数 |
|
||||
| `harmonicParityAbnormalDetails` | `Array<Object>` | 谐波奇偶关系异常明细摘要 |
|
||||
| `statSummaries` | `Array<Object>` | 统计类型摘要 |
|
||||
| `statDetails` | `Array<Object>` | 统计类型明细 |
|
||||
|
||||
### 6.6 响应示例
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"msg": "success",
|
||||
"data": {
|
||||
"taskId": "1812345678901234567",
|
||||
"taskNo": "CS202606130001",
|
||||
"lineId": "LINE_001",
|
||||
"lineName": "1号监测点",
|
||||
"timeStart": "2026-06-13 00:00:00",
|
||||
"timeEnd": "2026-06-13 01:00:00",
|
||||
"intervalMinutes": 1,
|
||||
"items": [
|
||||
{
|
||||
"itemId": "1812345678901234568",
|
||||
"itemKey": "LINE_001:VOLTAGE_A",
|
||||
"indicatorCode": "VOLTAGE_A",
|
||||
"indicatorName": "A相电压",
|
||||
"harmonicOrder": null,
|
||||
"intervalMinutes": 1,
|
||||
"hasData": true,
|
||||
"expectedPointCount": 60,
|
||||
"actualPointCount": 59,
|
||||
"missingPointCount": 1,
|
||||
"dataIntegrity": 98.33,
|
||||
"dataIntegrityText": "98.33%",
|
||||
"abnormal": true,
|
||||
"abnormalPointCount": 1,
|
||||
"abnormalDetails": [],
|
||||
"harmonicParityAbnormal": false,
|
||||
"harmonicParityAbnormalPointCount": 0,
|
||||
"harmonicParityAbnormalDetails": [],
|
||||
"statSummaries": [],
|
||||
"statDetails": []
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 7. 查询检测项明细
|
||||
|
||||
### 7.1 接口
|
||||
|
||||
`GET /steady/checksquare/item-detail`
|
||||
|
||||
### 7.2 请求参数
|
||||
|
||||
| 字段 | 类型 | 必填 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `itemId` | `String` | 是 | 检测项 ID |
|
||||
| `detailType` | `String` | 是 | 明细类型:`SEGMENT`、`VALUE_ORDER`、`HARMONIC_PARITY` |
|
||||
| `statType` | `String` | 否 | 统计类型;查询统计明细时使用 |
|
||||
| `pageNum` | `Integer` | 否 | 页码 |
|
||||
| `pageSize` | `Integer` | 否 | 每页条数 |
|
||||
|
||||
### 7.3 请求示例
|
||||
|
||||
```http
|
||||
GET /steady/checksquare/item-detail?itemId=1812345678901234568&detailType=SEGMENT&pageNum=1&pageSize=10
|
||||
```
|
||||
|
||||
### 7.4 响应字段 `data`
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `itemId` | `String` | 检测项 ID |
|
||||
| `detailType` | `String` | 明细类型 |
|
||||
| `statType` | `String` | 统计类型 |
|
||||
| `pageNum` | `Integer` | 当前页码;未分页查询时为空 |
|
||||
| `pageSize` | `Integer` | 每页条数;未分页查询时为空 |
|
||||
| `total` | `Long` | 总记录数;未分页查询时为空 |
|
||||
| `segments` | `Array<Object>` | 缺失区间,`detailType=SEGMENT` 时查看 |
|
||||
| `valueOrderDetails` | `Array<Object>` | 指标值大小关系异常明细,`detailType=VALUE_ORDER` 时查看 |
|
||||
| `harmonicParityDetails` | `Array<Object>` | 谐波奇偶关系异常明细,`detailType=HARMONIC_PARITY` 时查看 |
|
||||
|
||||
### 7.5 响应示例
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"msg": "success",
|
||||
"data": {
|
||||
"itemId": "1812345678901234568",
|
||||
"detailType": "SEGMENT",
|
||||
"statType": null,
|
||||
"pageNum": 1,
|
||||
"pageSize": 10,
|
||||
"total": 1,
|
||||
"segments": [
|
||||
{
|
||||
"segmentStart": "2026-06-13 00:10:00",
|
||||
"segmentEnd": "2026-06-13 00:10:00",
|
||||
"pointCount": 1
|
||||
}
|
||||
],
|
||||
"valueOrderDetails": [],
|
||||
"harmonicParityDetails": []
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 8. 删除任务
|
||||
|
||||
### 8.1 接口
|
||||
|
||||
`POST /steady/checksquare/delete`
|
||||
|
||||
### 8.2 请求字段
|
||||
|
||||
请求体直接传任务 ID 数组。
|
||||
|
||||
| 字段 | 类型 | 必填 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `taskIds` | `Array<String>` | 是 | 任务 ID 数组 |
|
||||
|
||||
### 8.3 请求示例
|
||||
|
||||
```http
|
||||
POST /steady/checksquare/delete
|
||||
Content-Type: application/json
|
||||
```
|
||||
|
||||
```json
|
||||
[
|
||||
"1812345678901234567"
|
||||
]
|
||||
```
|
||||
|
||||
### 8.4 响应示例
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"msg": "success",
|
||||
"data": true
|
||||
}
|
||||
```
|
||||
|
||||
## 9. 常见调试注意事项
|
||||
|
||||
- `/create` 返回的是任务列表行信息,不是详情页完整数据。
|
||||
- `/create` 如果命中已存在任务,会直接返回该任务;不会生成重复任务。
|
||||
- `/detail` 需要使用 `/create` 或 `/query` 返回的 `taskId`。
|
||||
- `/item-detail` 需要使用 `/detail` 返回的 `items[].itemId`。
|
||||
- `/query` 使用 `indicatorCode` 单值筛选;`/create` 使用 `indicatorCodes` 数组创建检测项。
|
||||
- 当前文档只覆盖现有有效接口,不包含旧的任务获取或创建兼容接口。
|
||||
@@ -0,0 +1,621 @@
|
||||
# check-square API 调试文档
|
||||
|
||||
## 1. 模块说明
|
||||
|
||||
- 模块路径:`steady/check-square`
|
||||
- 接口基础路径:`/steady/checksquare`
|
||||
- 返回包装:接口统一返回 `HttpResult<T>`,调试时重点查看响应体中的业务数据字段 `data`。
|
||||
- 时间格式:`yyyy-MM-dd HH:mm:ss`
|
||||
|
||||
本模块用于按监测点、时间范围和指标执行稳态数据校验,并提供任务列表、任务详情、检测项明细查询、失败任务重启和任务删除能力。当前标准支持单监测点和多监测点任务:单监测点可继续传 `lineId`,多监测点推荐传 `lineIds`。
|
||||
|
||||
## 2. 通用约定
|
||||
|
||||
### 2.1 请求头
|
||||
|
||||
| 名称 | 示例 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `Content-Type` | `application/json` | `POST` 请求使用 JSON 请求体 |
|
||||
| `Authorization` | 登录态 Token | 如当前环境开启认证,需携带现有登录接口返回的认证信息 |
|
||||
|
||||
### 2.2 任务状态
|
||||
|
||||
| 值 | 说明 |
|
||||
| --- | --- |
|
||||
| `RUNNING` | 执行中 |
|
||||
| `SUCCESS` | 执行成功 |
|
||||
| `FAIL` | 执行失败 |
|
||||
|
||||
### 2.3 明细类型
|
||||
|
||||
| 值 | 说明 |
|
||||
| --- | --- |
|
||||
| `SEGMENT` | 连续性区间,包含正常区间和缺失区间 |
|
||||
| `VALUE_ORDER` | 指标值大小关系异常明细 |
|
||||
| `HARMONIC_PARITY` | 谐波奇偶关系异常明细 |
|
||||
|
||||
### 2.4 统计类型
|
||||
|
||||
| 值 | 说明 |
|
||||
| --- | --- |
|
||||
| `AVG` | 平均值 |
|
||||
| `MAX` | 最大值 |
|
||||
| `MIN` | 最小值 |
|
||||
| `CP95` | CP95 值 |
|
||||
|
||||
## 3. 调试顺序建议
|
||||
|
||||
1. 调用 `POST /steady/checksquare/create`:按监测点列表、时间范围和指标列表创建或复用任务。
|
||||
2. 读取 `/create` 返回的 `data.taskId`:该返回值是任务列表中的行信息。
|
||||
3. 如果 `taskStatus=RUNNING`,轮询 `POST /steady/checksquare/query`,或稍后调用 `GET /steady/checksquare/detail` 查看任务详情。
|
||||
4. 调用 `GET /steady/checksquare/detail`:用 `taskId` 查询任务下的检测项。
|
||||
5. 读取 `/detail` 返回的 `items[].itemId`:按检测项继续查询连续性区间或异常明细。
|
||||
6. 调用 `GET /steady/checksquare/item-detail`:按 `itemId + detailType` 查询具体明细。
|
||||
7. 任务失败后如需重新执行,调用 `POST /steady/checksquare/restart`。
|
||||
8. 需要清理任务时调用 `POST /steady/checksquare/delete`。
|
||||
|
||||
注意:旧的获取或创建兼容接口已移除。创建或复用任务的逻辑已经合并到 `/create` 中。
|
||||
|
||||
## 4. 创建或复用任务
|
||||
|
||||
### 4.1 接口
|
||||
|
||||
`POST /steady/checksquare/create`
|
||||
|
||||
### 4.2 行为说明
|
||||
|
||||
接口开始执行前,会先按 `lineIds + timeStart + timeEnd` 查询是否存在未删除任务:
|
||||
|
||||
- 已存在:直接返回该任务的任务列表行信息。
|
||||
- 不存在:创建 `RUNNING` 任务并立即返回任务列表行信息,后台继续执行校验;任务完成后状态更新为 `SUCCESS`,失败时更新为 `FAIL`。
|
||||
|
||||
`lineIds` 会去重并清理空字符串。若未传 `lineIds`,后端会兼容读取 `lineId` 并转换为单元素监测点列表。返回对象为 `SteadyChecksquareTaskVO`。
|
||||
|
||||
### 4.3 请求字段
|
||||
|
||||
| 字段 | 类型 | 必填 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `lineId` | `String` | 条件必填 | 单监测点 ID;当 `lineIds` 为空时使用 |
|
||||
| `lineIds` | `Array<String>` | 条件必填 | 监测点 ID 列表;多监测点推荐使用该字段 |
|
||||
| `indicatorCodes` | `Array<String>` | 是 | 指标编码列表 |
|
||||
| `timeStart` | `String` | 是 | 开始时间,格式 `yyyy-MM-dd HH:mm:ss` |
|
||||
| `timeEnd` | `String` | 是 | 结束时间,格式 `yyyy-MM-dd HH:mm:ss` |
|
||||
|
||||
`lineId` 与 `lineIds` 至少传一个;`indicatorCodes` 是数组,不要传成单个字符串。
|
||||
|
||||
### 4.4 请求示例
|
||||
|
||||
单监测点兼容写法:
|
||||
|
||||
```http
|
||||
POST /steady/checksquare/create
|
||||
Content-Type: application/json
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"lineId": "LINE_001",
|
||||
"indicatorCodes": ["VOLTAGE_A", "CURRENT_A"],
|
||||
"timeStart": "2026-06-18 00:00:00",
|
||||
"timeEnd": "2026-06-18 01:00:00"
|
||||
}
|
||||
```
|
||||
|
||||
多监测点标准写法:
|
||||
|
||||
```json
|
||||
{
|
||||
"lineIds": ["LINE_001", "LINE_002"],
|
||||
"indicatorCodes": ["VOLTAGE_A", "CURRENT_A"],
|
||||
"timeStart": "2026-06-18 00:00:00",
|
||||
"timeEnd": "2026-06-18 01:00:00"
|
||||
}
|
||||
```
|
||||
|
||||
### 4.5 响应字段 `data`
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `taskId` | `String` | 任务 ID |
|
||||
| `taskNo` | `String` | 任务编号 |
|
||||
| `lineId` | `String` | 任务首个监测点 ID;兼容旧页面字段 |
|
||||
| `lineIds` | `Array<String>` | 本次任务的监测点 ID 列表 |
|
||||
| `lineName` | `String` | 监测点名称;多监测点时为多个名称拼接结果 |
|
||||
| `timeStart` | `String` | 开始时间 |
|
||||
| `timeEnd` | `String` | 结束时间 |
|
||||
| `intervalMinutes` | `Integer` | 统计间隔,单位分钟 |
|
||||
| `taskStatus` | `String` | 任务状态:`RUNNING`、`SUCCESS`、`FAIL` |
|
||||
| `itemCount` | `Integer` | 检测项数量 |
|
||||
| `abnormalItemCount` | `Integer` | 异常检测项数量 |
|
||||
| `minDataIntegrity` | `BigDecimal` | 最低数据完整性,0 到 1 的小数 |
|
||||
| `createTime` | `String` | 创建时间 |
|
||||
|
||||
### 4.6 响应示例
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"msg": "success",
|
||||
"data": {
|
||||
"taskId": "1812345678901234567",
|
||||
"taskNo": "CS202606180001",
|
||||
"lineId": "LINE_001",
|
||||
"lineIds": ["LINE_001", "LINE_002"],
|
||||
"lineName": "1号监测点,2号监测点",
|
||||
"timeStart": "2026-06-18 00:00:00",
|
||||
"timeEnd": "2026-06-18 01:00:00",
|
||||
"intervalMinutes": 1,
|
||||
"taskStatus": "RUNNING",
|
||||
"itemCount": 0,
|
||||
"abnormalItemCount": 0,
|
||||
"minDataIntegrity": 0.000000,
|
||||
"createTime": "2026-06-18 09:30:00"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 5. 查询任务列表
|
||||
|
||||
### 5.1 接口
|
||||
|
||||
`POST /steady/checksquare/query`
|
||||
|
||||
### 5.2 请求字段
|
||||
|
||||
| 字段 | 类型 | 必填 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `lineId` | `String` | 否 | 监测点 ID;后端按任务的 `lineIds` 检索文本匹配 |
|
||||
| `indicatorCode` | `String` | 否 | 指标编码;后端按任务的指标编码检索文本匹配 |
|
||||
| `timeStart` | `String` | 否 | 检测开始时间,格式 `yyyy-MM-dd HH:mm:ss` |
|
||||
| `timeEnd` | `String` | 否 | 检测结束时间,格式 `yyyy-MM-dd HH:mm:ss` |
|
||||
| `hasAbnormal` | `Boolean` | 否 | 是否存在异常;传 `true` 时仅查询异常检测项数量大于 0 的任务 |
|
||||
| `pageNum` | `Integer` | 否 | 页码 |
|
||||
| `pageSize` | `Integer` | 否 | 每页条数 |
|
||||
|
||||
`indicatorCode` 是单个字符串,用于历史任务筛选;和 `/create` 的 `indicatorCodes` 数组不同。
|
||||
|
||||
### 5.3 请求示例
|
||||
|
||||
```http
|
||||
POST /steady/checksquare/query
|
||||
Content-Type: application/json
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"lineId": "LINE_001",
|
||||
"indicatorCode": "VOLTAGE_A",
|
||||
"timeStart": "2026-06-18 00:00:00",
|
||||
"timeEnd": "2026-06-18 23:59:59",
|
||||
"hasAbnormal": true,
|
||||
"pageNum": 1,
|
||||
"pageSize": 10
|
||||
}
|
||||
```
|
||||
|
||||
### 5.4 响应说明
|
||||
|
||||
`data` 为 MyBatis-Plus `Page<SteadyChecksquareTaskVO>` 分页对象,常用字段如下:
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `records` | `Array<Object>` | 任务列表,每项字段同 `/create` 返回的 `SteadyChecksquareTaskVO` |
|
||||
| `total` | `Long` | 总记录数 |
|
||||
| `size` | `Long` | 每页条数 |
|
||||
| `current` | `Long` | 当前页码 |
|
||||
| `pages` | `Long` | 总页数 |
|
||||
|
||||
### 5.5 响应示例
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"msg": "success",
|
||||
"data": {
|
||||
"records": [
|
||||
{
|
||||
"taskId": "1812345678901234567",
|
||||
"taskNo": "CS202606180001",
|
||||
"lineId": "LINE_001",
|
||||
"lineIds": ["LINE_001", "LINE_002"],
|
||||
"lineName": "1号监测点,2号监测点",
|
||||
"timeStart": "2026-06-18 00:00:00",
|
||||
"timeEnd": "2026-06-18 01:00:00",
|
||||
"intervalMinutes": 1,
|
||||
"taskStatus": "SUCCESS",
|
||||
"itemCount": 4,
|
||||
"abnormalItemCount": 1,
|
||||
"minDataIntegrity": 0.985000,
|
||||
"createTime": "2026-06-18 09:30:00"
|
||||
}
|
||||
],
|
||||
"total": 1,
|
||||
"size": 10,
|
||||
"current": 1,
|
||||
"pages": 1
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 6. 查询任务详情
|
||||
|
||||
### 6.1 接口
|
||||
|
||||
`GET /steady/checksquare/detail?taskId={taskId}`
|
||||
|
||||
### 6.2 请求参数
|
||||
|
||||
| 字段 | 类型 | 必填 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `taskId` | `String` | 是 | 任务 ID |
|
||||
|
||||
### 6.3 请求示例
|
||||
|
||||
```http
|
||||
GET /steady/checksquare/detail?taskId=1812345678901234567
|
||||
```
|
||||
|
||||
### 6.4 响应字段 `data`
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `taskId` | `String` | 任务 ID |
|
||||
| `taskNo` | `String` | 任务编号 |
|
||||
| `lineId` | `String` | 任务首个监测点 ID;兼容旧页面字段 |
|
||||
| `lineIds` | `Array<String>` | 本次任务的监测点 ID 列表 |
|
||||
| `lineName` | `String` | 监测点名称 |
|
||||
| `timeStart` | `String` | 开始时间 |
|
||||
| `timeEnd` | `String` | 结束时间 |
|
||||
| `intervalMinutes` | `Integer` | 统计间隔,单位分钟 |
|
||||
| `items` | `Array<Object>` | 检测项列表 |
|
||||
|
||||
### 6.5 检测项字段 `items[]`
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `itemId` | `String` | 检测项 ID,用于查询明细 |
|
||||
| `itemKey` | `String` | 检测项唯一键 |
|
||||
| `lineId` | `String` | 当前检测项所属监测点 ID |
|
||||
| `lineName` | `String` | 当前检测项所属监测点名称 |
|
||||
| `indicatorCode` | `String` | 指标编码 |
|
||||
| `indicatorName` | `String` | 指标名称 |
|
||||
| `harmonicOrder` | `Integer` | 谐波次数;非谐波或聚合项可为空 |
|
||||
| `intervalMinutes` | `Integer` | 当前检测项统计间隔,单位分钟 |
|
||||
| `hasData` | `Boolean` | 时间范围内是否存在任意数据 |
|
||||
| `expectedPointCount` | `Integer` | 期望点数 |
|
||||
| `actualPointCount` | `Integer` | 实际点数 |
|
||||
| `missingPointCount` | `Integer` | 缺失点数 |
|
||||
| `dataIntegrity` | `BigDecimal` | 数据完整性,0 到 1 的小数 |
|
||||
| `dataIntegrityText` | `String` | 数据完整性文本 |
|
||||
| `abnormal` | `Boolean` | 指标值大小关系是否异常 |
|
||||
| `abnormalPointCount` | `Integer` | 指标值大小关系异常点数 |
|
||||
| `abnormalDetails` | `Array<Object>` | 指标值大小关系异常明细摘要 |
|
||||
| `harmonicParityAbnormal` | `Boolean` | 谐波奇偶关系是否异常 |
|
||||
| `harmonicParityAbnormalPointCount` | `Integer` | 谐波奇偶关系异常点数 |
|
||||
| `harmonicParityAbnormalDetails` | `Array<Object>` | 谐波奇偶关系异常明细摘要 |
|
||||
| `statSummaries` | `Array<Object>` | 统计类型摘要 |
|
||||
| `statDetails` | `Array<Object>` | 统计类型明细 |
|
||||
|
||||
### 6.6 统计摘要字段 `statSummaries[]`
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `statType` | `String` | 统计类型:`AVG`、`MAX`、`MIN`、`CP95` |
|
||||
| `supported` | `Boolean` | 是否支持该统计类型 |
|
||||
| `hasData` | `Boolean` | 是否存在数据 |
|
||||
| `expectedPointCount` | `Integer` | 期望点数 |
|
||||
| `actualPointCount` | `Integer` | 实际点数 |
|
||||
| `missingPointCount` | `Integer` | 缺失点数 |
|
||||
| `dataIntegrity` | `BigDecimal` | 数据完整性 |
|
||||
| `dataIntegrityText` | `String` | 数据完整性文本 |
|
||||
|
||||
### 6.7 响应示例
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"msg": "success",
|
||||
"data": {
|
||||
"taskId": "1812345678901234567",
|
||||
"taskNo": "CS202606180001",
|
||||
"lineId": "LINE_001",
|
||||
"lineIds": ["LINE_001", "LINE_002"],
|
||||
"lineName": "1号监测点,2号监测点",
|
||||
"timeStart": "2026-06-18 00:00:00",
|
||||
"timeEnd": "2026-06-18 01:00:00",
|
||||
"intervalMinutes": 1,
|
||||
"items": [
|
||||
{
|
||||
"itemId": "1812345678901234568",
|
||||
"itemKey": "LINE_001:VOLTAGE_A",
|
||||
"lineId": "LINE_001",
|
||||
"lineName": "1号监测点",
|
||||
"indicatorCode": "VOLTAGE_A",
|
||||
"indicatorName": "A相电压",
|
||||
"harmonicOrder": null,
|
||||
"intervalMinutes": 1,
|
||||
"hasData": true,
|
||||
"expectedPointCount": 60,
|
||||
"actualPointCount": 59,
|
||||
"missingPointCount": 1,
|
||||
"dataIntegrity": 0.983333,
|
||||
"dataIntegrityText": "98.33%",
|
||||
"abnormal": true,
|
||||
"abnormalPointCount": 1,
|
||||
"abnormalDetails": [],
|
||||
"harmonicParityAbnormal": false,
|
||||
"harmonicParityAbnormalPointCount": 0,
|
||||
"harmonicParityAbnormalDetails": [],
|
||||
"statSummaries": [
|
||||
{
|
||||
"statType": "AVG",
|
||||
"supported": true,
|
||||
"hasData": true,
|
||||
"expectedPointCount": 60,
|
||||
"actualPointCount": 59,
|
||||
"missingPointCount": 1,
|
||||
"dataIntegrity": 0.983333,
|
||||
"dataIntegrityText": "98.33%"
|
||||
}
|
||||
],
|
||||
"statDetails": []
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 7. 查询检测项明细
|
||||
|
||||
### 7.1 接口
|
||||
|
||||
`GET /steady/checksquare/item-detail`
|
||||
|
||||
### 7.2 请求参数
|
||||
|
||||
| 字段 | 类型 | 必填 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `itemId` | `String` | 是 | 检测项 ID |
|
||||
| `detailType` | `String` | 是 | 明细类型:`SEGMENT`、`VALUE_ORDER`、`HARMONIC_PARITY` |
|
||||
| `statType` | `String` | 否 | 统计类型;查询统计类型连续性或谐波奇偶明细时使用 |
|
||||
| `pageNum` | `Integer` | 否 | 页码;和 `pageSize` 同时为正数时启用分页 |
|
||||
| `pageSize` | `Integer` | 否 | 每页条数;和 `pageNum` 同时为正数时启用分页 |
|
||||
|
||||
### 7.3 请求示例
|
||||
|
||||
查询连续性区间:
|
||||
|
||||
```http
|
||||
GET /steady/checksquare/item-detail?itemId=1812345678901234568&detailType=SEGMENT&pageNum=1&pageSize=10
|
||||
```
|
||||
|
||||
查询大小关系异常:
|
||||
|
||||
```http
|
||||
GET /steady/checksquare/item-detail?itemId=1812345678901234568&detailType=VALUE_ORDER&pageNum=1&pageSize=10
|
||||
```
|
||||
|
||||
查询谐波奇偶关系异常:
|
||||
|
||||
```http
|
||||
GET /steady/checksquare/item-detail?itemId=1812345678901234568&detailType=HARMONIC_PARITY&statType=AVG&pageNum=1&pageSize=10
|
||||
```
|
||||
|
||||
### 7.4 响应字段 `data`
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `itemId` | `String` | 检测项 ID |
|
||||
| `lineId` | `String` | 当前检测项所属监测点 ID |
|
||||
| `lineName` | `String` | 当前检测项所属监测点名称 |
|
||||
| `detailType` | `String` | 明细类型 |
|
||||
| `statType` | `String` | 统计类型 |
|
||||
| `pageNum` | `Integer` | 当前页码;未分页查询时为空 |
|
||||
| `pageSize` | `Integer` | 每页条数;未分页查询时为空 |
|
||||
| `total` | `Long` | 总记录数;未分页查询时为空 |
|
||||
| `segments` | `Array<Object>` | 连续性区间,`detailType=SEGMENT` 时查看 |
|
||||
| `valueOrderDetails` | `Array<Object>` | 指标值大小关系异常明细,`detailType=VALUE_ORDER` 时查看 |
|
||||
| `harmonicParityDetails` | `Array<Object>` | 谐波奇偶关系异常明细,`detailType=HARMONIC_PARITY` 时查看 |
|
||||
|
||||
### 7.5 连续性区间字段 `segments[]`
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `startTime` | `String` | 区间开始时间 |
|
||||
| `endTime` | `String` | 区间结束时间 |
|
||||
| `status` | `String` | 区间状态:`NORMAL`、`MISSING` |
|
||||
| `harmonicOrder` | `Integer` | 谐波次数 |
|
||||
| `missingPointCount` | `Integer` | 缺失点数 |
|
||||
| `durationMinutes` | `Integer` | 持续时长,单位分钟 |
|
||||
|
||||
### 7.6 大小关系异常字段 `valueOrderDetails[]`
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `time` | `String` | 异常点时间 |
|
||||
| `phase` | `String` | 相别 |
|
||||
| `harmonicOrder` | `Integer` | 谐波次数 |
|
||||
| `maxValue` | `BigDecimal` | 最大值 |
|
||||
| `minValue` | `BigDecimal` | 最小值 |
|
||||
| `avgValue` | `BigDecimal` | 平均值 |
|
||||
| `cp95Value` | `BigDecimal` | CP95 值 |
|
||||
|
||||
### 7.7 谐波奇偶关系异常字段 `harmonicParityDetails[]`
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `time` | `String` | 异常点时间 |
|
||||
| `phase` | `String` | 相别 |
|
||||
| `statType` | `String` | 统计类型 |
|
||||
| `evenHarmonicOrder` | `Integer` | 偶次谐波次数 |
|
||||
| `evenValue` | `BigDecimal` | 偶次谐波值 |
|
||||
| `oddHarmonicOrders` | `Array<Integer>` | 参与比较的奇次谐波次数 |
|
||||
| `oddValues` | `Array<BigDecimal>` | 参与比较的奇次谐波值 |
|
||||
| `oddMedianValue` | `BigDecimal` | 奇次谐波中位数 |
|
||||
| `thresholdMultiplier` | `BigDecimal` | 异常阈值倍数 |
|
||||
|
||||
### 7.8 响应示例
|
||||
|
||||
连续性区间:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"msg": "success",
|
||||
"data": {
|
||||
"itemId": "1812345678901234568",
|
||||
"lineId": "LINE_001",
|
||||
"lineName": "1号监测点",
|
||||
"detailType": "SEGMENT",
|
||||
"statType": null,
|
||||
"pageNum": 1,
|
||||
"pageSize": 10,
|
||||
"total": 1,
|
||||
"segments": [
|
||||
{
|
||||
"startTime": "2026-06-18 00:10:00",
|
||||
"endTime": "2026-06-18 00:10:00",
|
||||
"status": "MISSING",
|
||||
"harmonicOrder": null,
|
||||
"missingPointCount": 1,
|
||||
"durationMinutes": 1
|
||||
}
|
||||
],
|
||||
"valueOrderDetails": [],
|
||||
"harmonicParityDetails": []
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
大小关系异常:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"msg": "success",
|
||||
"data": {
|
||||
"itemId": "1812345678901234568",
|
||||
"lineId": "LINE_001",
|
||||
"lineName": "1号监测点",
|
||||
"detailType": "VALUE_ORDER",
|
||||
"statType": null,
|
||||
"pageNum": 1,
|
||||
"pageSize": 10,
|
||||
"total": 1,
|
||||
"segments": [],
|
||||
"valueOrderDetails": [
|
||||
{
|
||||
"time": "2026-06-18 00:20:00",
|
||||
"phase": "A",
|
||||
"harmonicOrder": null,
|
||||
"maxValue": 231.12000000,
|
||||
"minValue": 228.45000000,
|
||||
"avgValue": 229.64000000,
|
||||
"cp95Value": 230.98000000
|
||||
}
|
||||
],
|
||||
"harmonicParityDetails": []
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 8. 重启失败任务
|
||||
|
||||
### 8.1 接口
|
||||
|
||||
`POST /steady/checksquare/restart?taskId={taskId}`
|
||||
|
||||
### 8.2 行为说明
|
||||
|
||||
- 仅允许重启 `taskStatus=FAIL` 的任务。
|
||||
- 重启复用原任务记录,`taskId` 不变。
|
||||
- 重启前会清理该任务旧的检测项、统计摘要和明细数据,避免重复结果键。
|
||||
- 接口返回时任务状态已更新为 `RUNNING`;后台执行结束后更新为 `SUCCESS` 或 `FAIL`。
|
||||
|
||||
### 8.3 请求参数
|
||||
|
||||
| 字段 | 类型 | 必填 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `taskId` | `String` | 是 | 失败任务 ID |
|
||||
|
||||
### 8.4 请求示例
|
||||
|
||||
```http
|
||||
POST /steady/checksquare/restart?taskId=1812345678901234567
|
||||
```
|
||||
|
||||
### 8.5 响应说明
|
||||
|
||||
响应 `data` 与 `/create` 的 `SteadyChecksquareTaskVO` 字段一致。
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"msg": "success",
|
||||
"data": {
|
||||
"taskId": "1812345678901234567",
|
||||
"taskNo": "CS202606180001",
|
||||
"lineId": "LINE_001",
|
||||
"lineIds": ["LINE_001", "LINE_002"],
|
||||
"lineName": "1号监测点,2号监测点",
|
||||
"timeStart": "2026-06-18 00:00:00",
|
||||
"timeEnd": "2026-06-18 01:00:00",
|
||||
"intervalMinutes": 1,
|
||||
"taskStatus": "RUNNING",
|
||||
"itemCount": 0,
|
||||
"abnormalItemCount": 0,
|
||||
"minDataIntegrity": 0.000000,
|
||||
"createTime": "2026-06-18 09:30:00"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 9. 删除任务
|
||||
|
||||
### 9.1 接口
|
||||
|
||||
`POST /steady/checksquare/delete`
|
||||
|
||||
### 9.2 请求字段
|
||||
|
||||
请求体直接传任务 ID 数组。
|
||||
|
||||
| 字段 | 类型 | 必填 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `taskIds` | `Array<String>` | 是 | 任务 ID 数组 |
|
||||
|
||||
### 9.3 请求示例
|
||||
|
||||
```http
|
||||
POST /steady/checksquare/delete
|
||||
Content-Type: application/json
|
||||
```
|
||||
|
||||
```json
|
||||
[
|
||||
"1812345678901234567"
|
||||
]
|
||||
```
|
||||
|
||||
### 9.4 响应示例
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"msg": "success",
|
||||
"data": true
|
||||
}
|
||||
```
|
||||
|
||||
## 10. 常见调试注意事项
|
||||
|
||||
- `/create` 返回的是任务列表行信息,不是详情页完整数据。
|
||||
- `/create` 如果命中已存在任务,会直接返回该任务,不会生成重复任务。
|
||||
- 新标准推荐传 `lineIds`;`lineId` 仅作为单监测点兼容字段保留。
|
||||
- `/query` 的 `lineId` 会匹配任务内的 `lineIds`,可用于筛选多监测点任务。
|
||||
- `/detail` 需要使用 `/create`、`/restart` 或 `/query` 返回的 `taskId`。
|
||||
- `/detail` 的任务级 `lineId` 是首个监测点,检测项级 `items[].lineId` 才是该检测项实际所属监测点。
|
||||
- `/item-detail` 需要使用 `/detail` 返回的 `items[].itemId`。
|
||||
- `/item-detail` 只有 `pageNum` 和 `pageSize` 同时为正数时才分页;否则返回全部匹配明细,分页字段为空。
|
||||
- `/restart` 只允许重启失败任务,成功或执行中的任务调用会返回业务失败。
|
||||
- `dataIntegrity` 是 0 到 1 的小数,展示百分比优先使用 `dataIntegrityText`。
|
||||
@@ -6,56 +6,66 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-empty v-if="!task" class="empty-result" description="新增后在此查看任务摘要" />
|
||||
<div v-if="loading" class="create-loading">
|
||||
<el-icon class="create-loading__icon"><Loading /></el-icon>
|
||||
<span class="create-loading__title">正在创建校验任务</span>
|
||||
<span class="create-loading__desc">任务提交后将在此显示摘要</span>
|
||||
</div>
|
||||
|
||||
<el-empty v-else-if="!task" class="empty-result" description="新增后在此查看任务摘要" />
|
||||
|
||||
<template v-else>
|
||||
<div class="result-body">
|
||||
<div class="result-overview">
|
||||
<div class="task-card">
|
||||
<div class="task-title">
|
||||
<span>{{ task.taskNo || task.taskId || '-' }}</span>
|
||||
<el-tag :type="resolveChecksquareTaskStatusType(task.taskStatus)" effect="plain">
|
||||
{{ formatChecksquareTaskStatus(task.taskStatus) }}
|
||||
</el-tag>
|
||||
<div class="result-scroll">
|
||||
<div class="result-body">
|
||||
<div class="result-overview">
|
||||
<div class="task-card">
|
||||
<div class="task-title">
|
||||
<span>{{ task.taskNo || task.taskId || '-' }}</span>
|
||||
<el-tag :type="resolveChecksquareTaskStatusType(task.taskStatus)" effect="plain">
|
||||
{{ formatChecksquareTaskStatus(task.taskStatus) }}
|
||||
</el-tag>
|
||||
</div>
|
||||
<div class="task-meta">
|
||||
<span>{{ task.lineName || task.lineId || '-' }}</span>
|
||||
<span>{{ task.timeStart || '-' }} 至 {{ task.timeEnd || '-' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="task-meta">
|
||||
<span>{{ task.lineName || task.lineId || '-' }}</span>
|
||||
<span>{{ task.timeStart || '-' }} 至 {{ task.timeEnd || '-' }}</span>
|
||||
|
||||
<div class="result-metrics">
|
||||
<div class="metric-item">
|
||||
<span class="metric-label">进线/监测点</span>
|
||||
<span class="metric-value">{{ task.lineName || task.lineId || '-' }}</span>
|
||||
</div>
|
||||
<div class="metric-item">
|
||||
<span class="metric-label">检测项</span>
|
||||
<span class="metric-value">{{ displayItemCount }}</span>
|
||||
</div>
|
||||
<div class="metric-item">
|
||||
<span class="metric-label">异常项</span>
|
||||
<span class="metric-value is-danger">{{ task.abnormalItemCount ?? '-' }}</span>
|
||||
</div>
|
||||
<div class="metric-item">
|
||||
<span class="metric-label">最低完整率</span>
|
||||
<span class="metric-value">
|
||||
{{ formatChecksquareIntegrity(task.minDataIntegrity) }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="metric-item">
|
||||
<span class="metric-label">统计间隔</span>
|
||||
<span class="metric-value">{{ task.intervalMinutes ?? '-' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="result-metrics">
|
||||
<div class="metric-item">
|
||||
<span class="metric-label">进线/监测点</span>
|
||||
<span class="metric-value">{{ task.lineName || task.lineId || '-' }}</span>
|
||||
<div class="result-detail-grid">
|
||||
<div class="detail-block">
|
||||
<span class="detail-label">校验时间</span>
|
||||
<span class="detail-value">{{ task.timeStart || '-' }} 至 {{ task.timeEnd || '-' }}</span>
|
||||
</div>
|
||||
<div class="metric-item">
|
||||
<span class="metric-label">检测项</span>
|
||||
<span class="metric-value">{{ task.itemCount ?? '-' }}</span>
|
||||
<div class="detail-block">
|
||||
<span class="detail-label">创建时间</span>
|
||||
<span class="detail-value">{{ task.createTime || '-' }}</span>
|
||||
</div>
|
||||
<div class="metric-item">
|
||||
<span class="metric-label">异常项</span>
|
||||
<span class="metric-value is-danger">{{ task.abnormalItemCount ?? '-' }}</span>
|
||||
</div>
|
||||
<div class="metric-item">
|
||||
<span class="metric-label">最低完整率</span>
|
||||
<span class="metric-value">{{ formatChecksquareIntegrity(task.minDataIntegrity) }}</span>
|
||||
</div>
|
||||
<div class="metric-item">
|
||||
<span class="metric-label">统计间隔</span>
|
||||
<span class="metric-value">{{ task.intervalMinutes ?? '-' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="result-detail-grid">
|
||||
<div class="detail-block">
|
||||
<span class="detail-label">校验时间</span>
|
||||
<span class="detail-value">{{ task.timeStart || '-' }} 至 {{ task.timeEnd || '-' }}</span>
|
||||
</div>
|
||||
<div class="detail-block">
|
||||
<span class="detail-label">创建时间</span>
|
||||
<span class="detail-value">{{ task.createTime || '-' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -64,6 +74,8 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { Loading } from '@element-plus/icons-vue'
|
||||
import type { SteadyDataView } from '@/api/steady/steadyDataView/interface'
|
||||
import {
|
||||
formatChecksquareIntegrity,
|
||||
@@ -75,9 +87,18 @@ defineOptions({
|
||||
name: 'ChecksquareCreateResultPanel'
|
||||
})
|
||||
|
||||
defineProps<{
|
||||
const props = defineProps<{
|
||||
task: SteadyDataView.SteadyChecksquareTask | null
|
||||
expectedItemCount: number
|
||||
loading?: boolean
|
||||
}>()
|
||||
|
||||
const displayItemCount = computed(() => {
|
||||
if (Number(props.task?.itemCount || 0) > 0) return props.task?.itemCount
|
||||
if (props.expectedItemCount > 0) return props.expectedItemCount
|
||||
|
||||
return props.task?.itemCount ?? '-'
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@@ -99,6 +120,43 @@ defineProps<{
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.create-loading {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
color: var(--el-text-color-secondary);
|
||||
}
|
||||
|
||||
.create-loading__icon {
|
||||
color: var(--el-color-primary);
|
||||
font-size: 28px;
|
||||
animation: rotate-loading 1s linear infinite;
|
||||
}
|
||||
|
||||
.create-loading__title {
|
||||
color: var(--el-text-color-primary);
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.create-loading__desc {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
@keyframes rotate-loading {
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
.result-body {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
@@ -107,6 +165,13 @@ defineProps<{
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.result-scroll {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
overflow-y: auto;
|
||||
padding-right: 4px;
|
||||
}
|
||||
|
||||
.result-overview {
|
||||
display: grid;
|
||||
flex: none;
|
||||
|
||||
@@ -31,6 +31,13 @@
|
||||
highlight-current-row
|
||||
empty-text="暂无校验结果"
|
||||
>
|
||||
<el-table-column label="监测点名称" width="150">
|
||||
<template #default="{ row }">
|
||||
<span class="line-name" :title="resolveChecksquareLineName(row)">
|
||||
{{ resolveChecksquareLineName(row) }}
|
||||
</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="indicatorName" label="指标名称" width="160">
|
||||
<template #default="{ row }">
|
||||
<span class="indicator-name" :title="resolveChecksquareRowName(row)">
|
||||
@@ -105,21 +112,25 @@ defineOptions({
|
||||
name: 'ChecksquareSummaryTable'
|
||||
})
|
||||
|
||||
defineProps<{
|
||||
result: SteadyDataView.SteadyChecksquareQueryResult | null
|
||||
items: SteadyDataView.SteadyChecksquareItem[]
|
||||
loading: boolean
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
refresh: []
|
||||
detail: [row: SteadyDataView.SteadyChecksquareItem]
|
||||
}>()
|
||||
|
||||
const props = defineProps<{
|
||||
result: SteadyDataView.SteadyChecksquareQueryResult | null
|
||||
items: SteadyDataView.SteadyChecksquareItem[]
|
||||
loading: boolean
|
||||
}>()
|
||||
|
||||
const hasAbnormalCount = (value?: number | null) => Number(value || 0) > 0
|
||||
|
||||
const stripPercentUnit = (value: string) => value.replace(/%$/, '')
|
||||
|
||||
const resolveChecksquareLineName = (row: SteadyDataView.SteadyChecksquareItem) => {
|
||||
return row.lineName || row.lineId || props.result?.lineName || props.result?.lineId || '-'
|
||||
}
|
||||
|
||||
const formatSummaryDataIntegrity = (row: SteadyDataView.SteadyChecksquareItem) => {
|
||||
return stripPercentUnit(formatDataIntegrity(row.dataIntegrity, row.dataIntegrityText))
|
||||
}
|
||||
@@ -157,6 +168,7 @@ const formatSummaryStatIntegrity = (
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.line-name,
|
||||
.indicator-name {
|
||||
display: inline-block;
|
||||
max-width: 100%;
|
||||
|
||||
@@ -11,6 +11,15 @@
|
||||
</template>
|
||||
|
||||
<template #operation="{ row }">
|
||||
<el-button
|
||||
v-if="row.taskStatus === 'FAIL'"
|
||||
type="primary"
|
||||
link
|
||||
:icon="RefreshRight"
|
||||
@click="emit('restart', row)"
|
||||
>
|
||||
重启
|
||||
</el-button>
|
||||
<el-button type="danger" link :icon="Delete" @click="emit('delete', row)">删除</el-button>
|
||||
</template>
|
||||
</ProTable>
|
||||
@@ -19,7 +28,7 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, h, reactive, ref } from 'vue'
|
||||
import { ElButton, ElDatePicker, ElTag, ElTreeSelect } from 'element-plus'
|
||||
import { Delete, Plus } from '@element-plus/icons-vue'
|
||||
import { Delete, Plus, RefreshRight } from '@element-plus/icons-vue'
|
||||
import ProTable from '@/components/ProTable/index.vue'
|
||||
import type { ColumnProps, ProTableInstance } from '@/components/ProTable/interface'
|
||||
import type { SteadyDataView } from '@/api/steady/steadyDataView/interface'
|
||||
@@ -45,6 +54,7 @@ const props = defineProps<{
|
||||
const emit = defineEmits<{
|
||||
createTask: []
|
||||
detail: [row: SteadyDataView.SteadyChecksquareTask]
|
||||
restart: [row: SteadyDataView.SteadyChecksquareTask]
|
||||
delete: [row: SteadyDataView.SteadyChecksquareTask]
|
||||
viewMeasurementPoint: [row: SteadyDataView.SteadyChecksquareTask]
|
||||
}>()
|
||||
@@ -135,6 +145,7 @@ const renderLineSearch = ({ searchParam }: { searchParam: ChecksquareTaskSearchP
|
||||
collapseTags: true,
|
||||
collapseTagsTooltip: true,
|
||||
maxCollapseTags: 1,
|
||||
popperClass: 'checksquare-search-tree-popper',
|
||||
filterable: true,
|
||||
clearable: true,
|
||||
defaultExpandAll: true,
|
||||
@@ -159,6 +170,7 @@ const renderIndicatorSearch = ({ searchParam }: { searchParam: ChecksquareTaskSe
|
||||
collapseTags: true,
|
||||
collapseTagsTooltip: true,
|
||||
maxCollapseTags: 1,
|
||||
popperClass: 'checksquare-search-tree-popper',
|
||||
filterable: true,
|
||||
clearable: true,
|
||||
defaultExpandAll: true,
|
||||
@@ -292,7 +304,7 @@ const columns = reactive<ColumnProps<SteadyDataView.SteadyChecksquareTask>[]>([
|
||||
minWidth: 170,
|
||||
render: ({ row }) => resolveChecksquareText(row.createTime)
|
||||
},
|
||||
{ prop: 'operation', label: '操作', fixed: 'right', width: 150 }
|
||||
{ prop: 'operation', label: '操作', fixed: 'right', width: 180 }
|
||||
])
|
||||
|
||||
const getTableList = (params: ChecksquareTaskSearchParams) => {
|
||||
@@ -329,4 +341,8 @@ defineExpose({
|
||||
vertical-align: bottom;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
:global(.checksquare-search-tree-popper .el-select-dropdown__wrap) {
|
||||
max-height: 280px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -33,10 +33,10 @@
|
||||
/>
|
||||
</div>
|
||||
<div class="query-actions">
|
||||
<el-button type="primary" :icon="Plus" :loading="loading.query" @click="emit('create')">
|
||||
<el-button type="primary" :icon="Plus" :loading="loading.query" :disabled="loading.query" @click="emit('create')">
|
||||
新增
|
||||
</el-button>
|
||||
<el-button type="primary" plain :icon="RefreshLeft" @click="emit('reset')">重置</el-button>
|
||||
<el-button type="primary" plain :icon="RefreshLeft" :disabled="loading.query" @click="emit('reset')">重置</el-button>
|
||||
</div>
|
||||
<div class="toolbar-field indicator-form-item">
|
||||
<span class="toolbar-field__label">稳态指标:</span>
|
||||
@@ -52,6 +52,7 @@
|
||||
filterable
|
||||
clearable
|
||||
default-expand-all
|
||||
:disabled="loading.query"
|
||||
node-key="treeKey"
|
||||
value-key="treeKey"
|
||||
:props="{ label: 'name', children: 'children' }"
|
||||
@@ -65,7 +66,7 @@
|
||||
</div>
|
||||
</template>
|
||||
</el-tree-select>
|
||||
<el-button type="primary" plain @click="handleSelectAllIndicators">全选</el-button>
|
||||
<el-button type="primary" plain :disabled="loading.query" @click="handleSelectAllIndicators">全选</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
@@ -331,7 +332,65 @@ watch(
|
||||
|
||||
.indicator-tree-select {
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
max-width: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.indicator-tree-select :deep(.el-select__wrapper) {
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
max-width: 100%;
|
||||
min-height: 32px;
|
||||
height: 32px;
|
||||
align-items: center;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.indicator-tree-select :deep(.el-select__selection) {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
width: 0;
|
||||
max-width: 100%;
|
||||
min-height: 24px;
|
||||
height: 24px;
|
||||
max-height: 24px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.indicator-tree-select :deep(.el-select__selected-item) {
|
||||
flex: 0 1 auto;
|
||||
min-width: 0;
|
||||
max-width: 100%;
|
||||
height: 24px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.indicator-tree-select :deep(.el-select__tags) {
|
||||
min-width: 0;
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
min-height: 24px;
|
||||
height: 24px;
|
||||
max-height: 24px;
|
||||
overflow: hidden;
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
|
||||
.indicator-tree-select :deep(.el-tag) {
|
||||
min-width: 0;
|
||||
max-width: 100%;
|
||||
height: 22px;
|
||||
flex: 0 1 auto;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.indicator-tree-select :deep(.el-select__tags-text) {
|
||||
max-width: 150px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.indicator-select-node {
|
||||
|
||||
@@ -15,7 +15,10 @@ const files = {
|
||||
summaryTable: path.resolve(rootDir, 'views/steady/checksquare/components/ChecksquareSummaryTable.vue'),
|
||||
detailPanel: path.resolve(rootDir, 'views/steady/checksquare/components/ChecksquareDetailPanel.vue'),
|
||||
createResultPanel: path.resolve(rootDir, 'views/steady/checksquare/components/ChecksquareCreateResultPanel.vue'),
|
||||
measurementPointDialog: path.resolve(rootDir, 'views/steady/checksquare/components/ChecksquareMeasurementPointDialog.vue'),
|
||||
measurementPointDialog: path.resolve(
|
||||
rootDir,
|
||||
'views/steady/checksquare/components/ChecksquareMeasurementPointDialog.vue'
|
||||
),
|
||||
payload: path.resolve(rootDir, 'views/steady/checksquare/utils/checksquarePayload.ts'),
|
||||
ledgerUtils: path.resolve(rootDir, 'views/steady/checksquare/utils/checksquareLedger.ts'),
|
||||
taskTableUtils: path.resolve(rootDir, 'views/steady/checksquare/utils/checksquareTaskTable.ts'),
|
||||
@@ -39,11 +42,20 @@ const checks = [
|
||||
/\/steady\/checksquare\/create/.test(api) &&
|
||||
!/\/steady\/checksquare\/get-or-create/.test(api) &&
|
||||
/\/steady\/checksquare\/delete/.test(api) &&
|
||||
/\/steady\/checksquare\/restart\?taskId=/.test(api) &&
|
||||
/\/steady\/checksquare\/detail/.test(api) &&
|
||||
/\/steady\/checksquare\/item-detail/.test(api)
|
||||
)
|
||||
}
|
||||
],
|
||||
[
|
||||
'checksquare restart api uses documented task id query endpoint',
|
||||
() =>
|
||||
/export const restartSteadyChecksquareTask = \(taskId: string\)/.test(read(files.api)) &&
|
||||
/http\.post<SteadyDataView\.SteadyChecksquareTask>\([\s\S]*`\/steady\/checksquare\/restart\?taskId=\$\{encodeURIComponent\(taskId\)\}`/.test(
|
||||
read(files.api)
|
||||
)
|
||||
],
|
||||
[
|
||||
'checksquare delete api accepts documented task id array body',
|
||||
() =>
|
||||
@@ -66,7 +78,8 @@ const checks = [
|
||||
const typeBlock =
|
||||
read(files.apiTypes).match(/interface SteadyChecksquareCreateParams\s*\{[\s\S]*?\n {4}\}/)?.[0] || ''
|
||||
return (
|
||||
/lineId: string/.test(typeBlock) &&
|
||||
/lineId\?: string/.test(typeBlock) &&
|
||||
/lineIds\?: string\[\]/.test(typeBlock) &&
|
||||
/indicatorCodes: string\[\]/.test(typeBlock) &&
|
||||
/timeStart: string/.test(typeBlock) &&
|
||||
/timeEnd: string/.test(typeBlock) &&
|
||||
@@ -75,7 +88,10 @@ const checks = [
|
||||
}
|
||||
],
|
||||
['task table component exists', () => exists(files.taskTable)],
|
||||
['task table uses ProTable like event list', () => /<ProTable[\s\S]*row-key="taskId"[\s\S]*:columns="columns"/.test(read(files.taskTable))],
|
||||
[
|
||||
'task table uses ProTable like event list',
|
||||
() => /<ProTable[\s\S]*row-key="taskId"[\s\S]*:columns="columns"/.test(read(files.taskTable))
|
||||
],
|
||||
[
|
||||
'task table exposes create task header action',
|
||||
() =>
|
||||
@@ -87,9 +103,17 @@ const checks = [
|
||||
'task table has documented task columns',
|
||||
() => {
|
||||
const source = read(files.taskTable)
|
||||
return ['taskNo', 'lineName', 'timeStart', 'timeEnd', 'taskStatus', 'itemCount', 'abnormalItemCount', 'minDataIntegrity', 'createTime'].every(
|
||||
prop => new RegExp(`prop:\\s*'${prop}'`).test(source)
|
||||
)
|
||||
return [
|
||||
'taskNo',
|
||||
'lineName',
|
||||
'timeStart',
|
||||
'timeEnd',
|
||||
'taskStatus',
|
||||
'itemCount',
|
||||
'abnormalItemCount',
|
||||
'minDataIntegrity',
|
||||
'createTime'
|
||||
].every(prop => new RegExp(`prop:\\s*'${prop}'`).test(source))
|
||||
}
|
||||
],
|
||||
[
|
||||
@@ -105,6 +129,19 @@ const checks = [
|
||||
)
|
||||
}
|
||||
],
|
||||
[
|
||||
'task table exposes restart only for failed rows',
|
||||
() => {
|
||||
const taskTable = read(files.taskTable)
|
||||
const operationSlot = taskTable.match(/<template #operation="\{ row \}">[\s\S]*?<\/template>/)?.[0] || ''
|
||||
return (
|
||||
/v-if="row\.taskStatus === 'FAIL'"/.test(operationSlot) &&
|
||||
/emit\('restart', row\)/.test(operationSlot) &&
|
||||
/restart: \[row: SteadyDataView\.SteadyChecksquareTask\]/.test(taskTable) &&
|
||||
/RefreshRight/.test(taskTable)
|
||||
)
|
||||
}
|
||||
],
|
||||
[
|
||||
'task detail opens from abnormal item count value',
|
||||
() => {
|
||||
@@ -112,8 +149,7 @@ const checks = [
|
||||
return (
|
||||
/prop:\s*'abnormalItemCount'[\s\S]*ElButton[\s\S]*type:\s*'primary'[\s\S]*link:\s*true[\s\S]*emit\('detail', row\)/.test(
|
||||
taskTable
|
||||
) &&
|
||||
/resolveChecksquareText\(row\.abnormalItemCount\)/.test(taskTable)
|
||||
) && /resolveChecksquareText\(row\.abnormalItemCount\)/.test(taskTable)
|
||||
)
|
||||
}
|
||||
],
|
||||
@@ -124,8 +160,18 @@ const checks = [
|
||||
/taskTimeRange/.test(read(files.taskTableUtils)) &&
|
||||
/hasAbnormal/.test(read(files.taskTable))
|
||||
],
|
||||
['workbench remains create dialog selector body', () => /SteadyLedgerTree/.test(read(files.workbench)) && /TimePeriodSearch/.test(read(files.workbench))],
|
||||
['workbench emits create instead of old query action', () => /create: \[\]/.test(read(files.workbench)) && !/query: \[\]/.test(read(files.workbench))],
|
||||
[
|
||||
'workbench remains create dialog selector body',
|
||||
() => /SteadyLedgerTree/.test(read(files.workbench)) && /TimePeriodSearch/.test(read(files.workbench))
|
||||
],
|
||||
[
|
||||
'shared ledger tree emits checked leaf monitor points for checksquare create payload',
|
||||
() => /getCheckedNodes\(\s*true\s*,\s*false\s*\)/.test(read(path.resolve(rootDir, 'views/steady/steadyDataView/components/SteadyLedgerTree.vue')))
|
||||
],
|
||||
[
|
||||
'workbench emits create instead of old query action',
|
||||
() => /create: \[\]/.test(read(files.workbench)) && !/query: \[\]/.test(read(files.workbench))
|
||||
],
|
||||
['workbench no longer renders result table', () => !/ChecksquareSummaryTable/.test(read(files.workbench))],
|
||||
[
|
||||
'workbench create action uses short add label',
|
||||
@@ -148,7 +194,51 @@ const checks = [
|
||||
],
|
||||
[
|
||||
'payload builds create params without harmonic orders',
|
||||
() => /buildSteadyChecksquareCreatePayload/.test(read(files.payload)) && !/harmonicOrder/.test(read(files.payload))
|
||||
() =>
|
||||
/buildSteadyChecksquareCreatePayload/.test(read(files.payload)) &&
|
||||
!/harmonicOrder/.test(read(files.payload))
|
||||
],
|
||||
[
|
||||
'payload supports multi monitor point create params',
|
||||
() => {
|
||||
const payload = read(files.payload)
|
||||
const page = read(files.page)
|
||||
return (
|
||||
/buildSteadyChecksquareCreatePayload\s*=\s*\(\s*lineIds:\s*string\[\]/.test(payload) &&
|
||||
/lineIds,/.test(payload) &&
|
||||
/lineId:\s*lineIds\[0\]/.test(payload) &&
|
||||
!/lineIds\.length > 1/.test(payload) &&
|
||||
/buildSteadyChecksquareCreatePayload\(lineIds\.value,\s*selectedIndicators\.value,\s*formState\.value\)/.test(
|
||||
page
|
||||
) &&
|
||||
!/buildSteadyChecksquareCreatePayload\(lineIds\.value\[0\]/.test(page)
|
||||
)
|
||||
}
|
||||
],
|
||||
[
|
||||
'payload allows empty indicator selection for full indicator checks',
|
||||
() => {
|
||||
const payload = read(files.payload)
|
||||
const page = read(files.page)
|
||||
return (
|
||||
!/if\s*\(!indicators\.length\)\s*return\s*'请选择指标'/.test(payload) &&
|
||||
/indicatorCodes:\s*collectChecksquareIndicatorCodes\(indicators\)/.test(payload) &&
|
||||
/indicators:\s*selectedIndicators\.value/.test(page)
|
||||
)
|
||||
}
|
||||
],
|
||||
[
|
||||
'payload exposes expected item count calculation for selected or full indicators',
|
||||
() => {
|
||||
const payload = read(files.payload)
|
||||
return (
|
||||
/calculateChecksquareExpectedItemCount/.test(payload) &&
|
||||
/selectedIndicatorCount/.test(payload) &&
|
||||
/totalIndicatorCount/.test(payload) &&
|
||||
/lineCount/.test(payload) &&
|
||||
/selectedIndicatorCount > 0 \? selectedIndicatorCount : totalIndicatorCount/.test(payload)
|
||||
)
|
||||
}
|
||||
],
|
||||
[
|
||||
'page renders task table as first screen',
|
||||
@@ -211,6 +301,38 @@ const checks = [
|
||||
)
|
||||
}
|
||||
],
|
||||
[
|
||||
'create dialog indicator select keeps selected tags from resizing input',
|
||||
() => {
|
||||
const workbench = read(files.workbench)
|
||||
return (
|
||||
/class="indicator-tree-select"/.test(workbench) &&
|
||||
/collapse-tags/.test(workbench) &&
|
||||
/\.indicator-tree-select\s*:deep\(\.el-select__wrapper\)[\s\S]*height:\s*32px/.test(workbench) &&
|
||||
/\.indicator-tree-select\s*:deep\(\.el-select__selection\)[\s\S]*width:\s*0[\s\S]*height:\s*24px[\s\S]*overflow:\s*hidden/.test(
|
||||
workbench
|
||||
) &&
|
||||
/\.indicator-tree-select\s*:deep\(\.el-select__tags\)[\s\S]*height:\s*24px[\s\S]*overflow:\s*hidden[\s\S]*flex-wrap:\s*nowrap/.test(
|
||||
workbench
|
||||
) &&
|
||||
/\.indicator-tree-select\s*:deep\(\.el-tag\)[\s\S]*height:\s*22px[\s\S]*overflow:\s*hidden/.test(
|
||||
workbench
|
||||
)
|
||||
)
|
||||
}
|
||||
],
|
||||
[
|
||||
'task table tree select filters use scrollable dropdowns',
|
||||
() => {
|
||||
const taskTable = read(files.taskTable)
|
||||
return (
|
||||
/popperClass:\s*'checksquare-search-tree-popper'/.test(taskTable) &&
|
||||
/\.checksquare-search-tree-popper[\s\S]*\.el-select-dropdown__wrap[\s\S]*max-height:\s*280px/.test(
|
||||
taskTable
|
||||
)
|
||||
)
|
||||
}
|
||||
],
|
||||
[
|
||||
'task table monitor point name opens measurement point dialog like event list',
|
||||
() => {
|
||||
@@ -292,20 +414,23 @@ const checks = [
|
||||
[
|
||||
'page wraps old workbench in create dialog',
|
||||
() =>
|
||||
/<el-dialog[\s\S]*新增校验任务[\s\S]*width="960px"[\s\S]*<ChecksquareWorkbench/.test(
|
||||
read(files.page)
|
||||
) &&
|
||||
/<el-dialog[\s\S]*新增校验任务[\s\S]*width="960px"[\s\S]*<ChecksquareWorkbench/.test(read(files.page)) &&
|
||||
/\.checksquare-create-dialog\s*\{[\s\S]*height:\s*560px/.test(read(files.page))
|
||||
],
|
||||
[
|
||||
'page create flow calls create api, keeps summary only and refreshes task table',
|
||||
() =>
|
||||
/createSteadyChecksquareTask/.test(read(files.page)) &&
|
||||
!/getOrCreateSteadyChecksquareTask/.test(read(files.page)) &&
|
||||
!/refreshCreateTaskDetail/.test(read(files.page)) &&
|
||||
!/startCreateTaskPolling/.test(read(files.page)) &&
|
||||
/taskTableRef\.value\?\.refresh\(\)/.test(read(files.page)) &&
|
||||
/activeCreateTask\.value/.test(read(files.page))
|
||||
'page create flow calls create api, then polls task list status and refreshes task table',
|
||||
() => {
|
||||
const page = read(files.page)
|
||||
return (
|
||||
/createSteadyChecksquareTask/.test(page) &&
|
||||
/querySteadyChecksquareTasks\(buildCreateTaskStatusQuery\(activeCreateTask\.value\)\)/.test(page) &&
|
||||
!/getSteadyChecksquareDetail\(createdTask\.taskId\)/.test(page) &&
|
||||
!/getOrCreateSteadyChecksquareTask/.test(page) &&
|
||||
/startCreateTaskPolling\(createdTask\.taskId\)/.test(page) &&
|
||||
/taskTableRef\.value\?\.refresh\(\)/.test(page) &&
|
||||
/activeCreateTask\.value/.test(page)
|
||||
)
|
||||
}
|
||||
],
|
||||
[
|
||||
'create dialog shows create task summary without detail table',
|
||||
@@ -315,54 +440,75 @@ const checks = [
|
||||
const panel = read(files.createResultPanel)
|
||||
return (
|
||||
exists(files.createResultPanel) &&
|
||||
/<template #result>[\s\S]*<ChecksquareCreateResultPanel[\s\S]*:task="activeCreateTask"[\s\S]*<\/template>/.test(
|
||||
/<template #result>[\s\S]*<ChecksquareCreateResultPanel[\s\S]*:task="activeCreateTask"[\s\S]*:loading="loading\.query"[\s\S]*<\/template>/.test(
|
||||
page
|
||||
) &&
|
||||
/activeCreateTask\.value\s*=\s*null[\s\S]*loading\.query\s*=\s*true/.test(page) &&
|
||||
/:disabled="loading\.query"/.test(workbench) &&
|
||||
/\.checksquare-create-dialog\s*\{[\s\S]*display:\s*block/.test(page) &&
|
||||
/<slot name="result" \/>/.test(workbench) &&
|
||||
/\.checksquare-main\s*\{[\s\S]*display:\s*flex[\s\S]*flex-direction:\s*column/.test(workbench) &&
|
||||
/\.checksquare-layout\s*\{[\s\S]*grid-template-columns:\s*240px\s+minmax\(0,\s*1fr\)/.test(
|
||||
workbench
|
||||
) &&
|
||||
/\.checksquare-layout\s*\{[\s\S]*grid-template-columns:\s*240px\s+minmax\(0,\s*1fr\)/.test(workbench) &&
|
||||
/\.checksquare-result-slot\s*\{[\s\S]*width:\s*100%/.test(workbench) &&
|
||||
/\.checksquare-result-slot\s*\{[\s\S]*min-width:\s*0/.test(workbench) &&
|
||||
/\.checksquare-result-slot\s*:deep\(\.checksquare-result-panel\)\s*\{[\s\S]*height:\s*100%/.test(
|
||||
workbench
|
||||
) &&
|
||||
/name:\s*'ChecksquareCreateResultPanel'/.test(panel) &&
|
||||
/loading\?:\s*boolean/.test(panel) &&
|
||||
!/detail:\s*SteadyDataView\.SteadyChecksquareQueryResult \| null/.test(panel) &&
|
||||
/expectedItemCount:\s*number/.test(panel) &&
|
||||
/:expected-item-count="expectedCreateItemCount"/.test(page) &&
|
||||
/v-if="loading"/.test(panel) &&
|
||||
/正在创建校验任务/.test(panel) &&
|
||||
/class="result-scroll"/.test(panel) &&
|
||||
/class="result-overview"/.test(panel) &&
|
||||
/class="result-body"/.test(panel) &&
|
||||
!/class="result-items"/.test(panel) &&
|
||||
!/detailItems/.test(panel) &&
|
||||
/class="result-detail-grid"/.test(panel) &&
|
||||
!/detail-block--wide/.test(panel) &&
|
||||
/\.result-overview\s*\{[\s\S]*grid-template-columns:\s*minmax\(0,\s*1fr\)/.test(panel) &&
|
||||
/\.result-metrics\s*\{[\s\S]*grid-template-columns:\s*repeat\(2,\s*minmax\(0,\s*1fr\)\)/.test(panel) &&
|
||||
/\.result-detail-grid\s*\{[^}]*grid-template-columns:\s*repeat\(2,\s*minmax\(0,\s*1fr\)\)/.test(panel) &&
|
||||
/\.result-detail-grid\s*\{[^}]*grid-template-columns:\s*repeat\(2,\s*minmax\(0,\s*1fr\)\)/.test(
|
||||
panel
|
||||
) &&
|
||||
!/class="result-tips"/.test(panel) &&
|
||||
!/class="tips-title"/.test(panel) &&
|
||||
!/class="tips-list"/.test(panel) &&
|
||||
/class="result-metrics"[\s\S]*lineName[\s\S]*task\.itemCount/.test(panel) &&
|
||||
/class="result-metrics"[\s\S]*lineName[\s\S]*displayItemCount/.test(panel) &&
|
||||
/校验任务摘要/.test(panel) &&
|
||||
/task\.itemCount/.test(panel) &&
|
||||
/displayItemCount/.test(panel) &&
|
||||
/props\.expectedItemCount/.test(panel) &&
|
||||
/task\.abnormalItemCount/.test(panel) &&
|
||||
/task\.minDataIntegrity/.test(panel) &&
|
||||
!/class="detail-label">任务编号/.test(panel) &&
|
||||
/\.result-body\s*\{[\s\S]*flex:\s*1/.test(panel) &&
|
||||
/\.result-scroll\s*\{[\s\S]*overflow-y:\s*auto/.test(panel) &&
|
||||
!/class="result-detail-table"/.test(panel) &&
|
||||
!/resultItems/.test(panel) &&
|
||||
!/emit\('detail', row\)/.test(panel)
|
||||
)
|
||||
}
|
||||
],
|
||||
[
|
||||
'create dialog does not poll create task detail',
|
||||
'create dialog polls running task status and clears polling lifecycle',
|
||||
() => {
|
||||
const page = read(files.page)
|
||||
return (
|
||||
!/createTaskPollingTimer/.test(page) &&
|
||||
!/startCreateTaskPolling/.test(page) &&
|
||||
!/stopCreateTaskPolling/.test(page) &&
|
||||
!/setInterval/.test(page) &&
|
||||
!/onBeforeUnmount/.test(page)
|
||||
/createTaskPollingTimer/.test(page) &&
|
||||
/startCreateTaskPolling/.test(page) &&
|
||||
/stopCreateTaskPolling/.test(page) &&
|
||||
/window\.setTimeout/.test(page) &&
|
||||
/window\.clearTimeout/.test(page) &&
|
||||
/CREATE_TASK_POLLING_INTERVALS/.test(page) &&
|
||||
/getCreateTaskPollingDelay/.test(page) &&
|
||||
/buildCreateTaskStatusQuery/.test(page) &&
|
||||
/isChecksquareTaskFinished/.test(page) &&
|
||||
/onBeforeUnmount\(stopCreateTaskPolling\)/.test(page) &&
|
||||
/watch\(createDialogVisible/.test(page) &&
|
||||
/refreshCreateTaskStatus/.test(page) &&
|
||||
!/refreshCreateTaskDetail/.test(page) &&
|
||||
/activeCreateTask\.value\s*=\s*\{\s*\.\.\.activeCreateTask\.value/.test(page)
|
||||
)
|
||||
}
|
||||
],
|
||||
@@ -374,9 +520,20 @@ const checks = [
|
||||
/deleteSteadyChecksquareTasks\(\[row\.taskId\]\)/.test(read(files.page)) &&
|
||||
/taskTableRef\.value\?\.refresh\(\)/.test(read(files.page))
|
||||
],
|
||||
[
|
||||
'page restart flow confirms, calls restart api and refreshes task table',
|
||||
() =>
|
||||
/@restart="handleRestartTask"/.test(read(files.page)) &&
|
||||
/restartSteadyChecksquareTask/.test(read(files.page)) &&
|
||||
/ElMessageBox\.confirm/.test(read(files.page)) &&
|
||||
/restartSteadyChecksquareTask\(row\.taskId\)/.test(read(files.page)) &&
|
||||
/taskTableRef\.value\?\.refresh\(\)/.test(read(files.page))
|
||||
],
|
||||
[
|
||||
'page detail flow calls detail api',
|
||||
() => /getSteadyChecksquareDetail/.test(read(files.page)) && /detailDialogVisible\.value = true/.test(read(files.page))
|
||||
() =>
|
||||
/getSteadyChecksquareDetail/.test(read(files.page)) &&
|
||||
/detailDialogVisible\.value = true/.test(read(files.page))
|
||||
],
|
||||
[
|
||||
'task detail and item detail dialogs use the same size',
|
||||
@@ -393,7 +550,9 @@ const checks = [
|
||||
],
|
||||
[
|
||||
'summary table supports persisted abnormal fields',
|
||||
() => /abnormalPointCount/.test(read(files.summaryTable)) && /harmonicParityAbnormalPointCount/.test(read(files.summaryTable))
|
||||
() =>
|
||||
/abnormalPointCount/.test(read(files.summaryTable)) &&
|
||||
/harmonicParityAbnormalPointCount/.test(read(files.summaryTable))
|
||||
],
|
||||
[
|
||||
'summary table renders positive abnormal counts in danger color',
|
||||
@@ -401,7 +560,9 @@ const checks = [
|
||||
const summaryTable = read(files.summaryTable)
|
||||
return (
|
||||
/hasAbnormalCount/.test(summaryTable) &&
|
||||
/:class="\{\s*'is-abnormal-count': hasAbnormalCount\(row\.abnormalPointCount\)\s*\}"/.test(summaryTable) &&
|
||||
/:class="\{\s*'is-abnormal-count': hasAbnormalCount\(row\.abnormalPointCount\)\s*\}"/.test(
|
||||
summaryTable
|
||||
) &&
|
||||
/:class="\{\s*'is-abnormal-count': hasAbnormalCount\(row\.harmonicParityAbnormalPointCount\)\s*\}"/.test(
|
||||
summaryTable
|
||||
) &&
|
||||
@@ -414,8 +575,9 @@ const checks = [
|
||||
() => {
|
||||
const summaryTable = read(files.summaryTable)
|
||||
const dataIntegrityGroup =
|
||||
summaryTable.match(/<el-table-column label="数据完整性"[\s\S]*?<\/el-table-column>\s*<el-table-column label="操作"/)?.[0] ||
|
||||
''
|
||||
summaryTable.match(
|
||||
/<el-table-column label="数据完整性"[\s\S]*?<\/el-table-column>\s*<el-table-column label="操作"/
|
||||
)?.[0] || ''
|
||||
|
||||
return (
|
||||
/label="数据完整性"/.test(dataIntegrityGroup) &&
|
||||
@@ -459,6 +621,24 @@ const checks = [
|
||||
'summary table keeps indicator name column at configured width',
|
||||
() => /prop="indicatorName" label="指标名称" width="160"/.test(read(files.summaryTable))
|
||||
],
|
||||
[
|
||||
'summary table displays monitor point name before indicator name',
|
||||
() => {
|
||||
const summaryTable = read(files.summaryTable)
|
||||
const lineNameIndex = summaryTable.indexOf('label="监测点名称"')
|
||||
const indicatorIndex = summaryTable.indexOf('prop="indicatorName"')
|
||||
|
||||
return (
|
||||
/resolveChecksquareLineName/.test(summaryTable) &&
|
||||
/:title="resolveChecksquareLineName\(row\)"/.test(summaryTable) &&
|
||||
/row\.lineName\s*\|\|\s*row\.lineId\s*\|\|\s*props\.result\?\.lineName\s*\|\|\s*props\.result\?\.lineId/.test(
|
||||
summaryTable
|
||||
) &&
|
||||
lineNameIndex >= 0 &&
|
||||
indicatorIndex > lineNameIndex
|
||||
)
|
||||
}
|
||||
],
|
||||
[
|
||||
'summary table places abnormal fields after indicator name and hides max continuous missing column',
|
||||
() => {
|
||||
@@ -534,14 +714,17 @@ const checks = [
|
||||
],
|
||||
[
|
||||
'detail panel loads item details on demand',
|
||||
() => /getSteadyChecksquareItemDetail/.test(read(files.detailPanel)) && /detailType/.test(read(files.detailPanel))
|
||||
() =>
|
||||
/getSteadyChecksquareItemDetail/.test(read(files.detailPanel)) && /detailType/.test(read(files.detailPanel))
|
||||
],
|
||||
[
|
||||
'item detail api types support documented pagination fields',
|
||||
() => {
|
||||
const types = read(files.apiTypes)
|
||||
return (
|
||||
/interface SteadyChecksquareItemDetailParams[\s\S]*pageNum\?: number[\s\S]*pageSize\?: number/.test(types) &&
|
||||
/interface SteadyChecksquareItemDetailParams[\s\S]*pageNum\?: number[\s\S]*pageSize\?: number/.test(
|
||||
types
|
||||
) &&
|
||||
/interface SteadyChecksquareItemDetail[\s\S]*pageNum\?: number \| null[\s\S]*pageSize\?: number \| null[\s\S]*total\?: number \| null/.test(
|
||||
types
|
||||
)
|
||||
|
||||
@@ -7,22 +7,14 @@
|
||||
:request-api="querySteadyChecksquareTasks"
|
||||
@create-task="openCreateDialog"
|
||||
@detail="openTaskDetail"
|
||||
@restart="handleRestartTask"
|
||||
@delete="handleDeleteTask"
|
||||
@view-measurement-point="openMeasurementPointDialog"
|
||||
/>
|
||||
|
||||
<ChecksquareMeasurementPointDialog
|
||||
v-model:visible="measurementPointDialogVisible"
|
||||
:data="measurementPointData"
|
||||
/>
|
||||
<ChecksquareMeasurementPointDialog v-model:visible="measurementPointDialogVisible" :data="measurementPointData" />
|
||||
|
||||
<el-dialog
|
||||
v-model="createDialogVisible"
|
||||
title="新增校验任务"
|
||||
width="960px"
|
||||
append-to-body
|
||||
destroy-on-close
|
||||
>
|
||||
<el-dialog v-model="createDialogVisible" title="新增校验任务" width="960px" append-to-body destroy-on-close>
|
||||
<div class="checksquare-create-dialog">
|
||||
<ChecksquareWorkbench
|
||||
v-model:form="formState"
|
||||
@@ -44,6 +36,8 @@
|
||||
<template #result>
|
||||
<ChecksquareCreateResultPanel
|
||||
:task="activeCreateTask"
|
||||
:loading="loading.query"
|
||||
:expected-item-count="expectedCreateItemCount"
|
||||
/>
|
||||
</template>
|
||||
</ChecksquareWorkbench>
|
||||
@@ -71,7 +65,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, reactive, ref } from 'vue'
|
||||
import { computed, onBeforeUnmount, onMounted, reactive, ref, watch } from 'vue'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import {
|
||||
createSteadyChecksquareTask,
|
||||
@@ -79,7 +73,8 @@ import {
|
||||
getSteadyChecksquareDetail,
|
||||
getSteadyTrendIndicatorTree,
|
||||
getSteadyTrendLedgerTree,
|
||||
querySteadyChecksquareTasks
|
||||
querySteadyChecksquareTasks,
|
||||
restartSteadyChecksquareTask
|
||||
} from '@/api/steady/steadyDataView'
|
||||
import type { SteadyDataView } from '@/api/steady/steadyDataView/interface'
|
||||
import {
|
||||
@@ -102,6 +97,7 @@ import {
|
||||
} from './utils/checksquareLedger'
|
||||
import {
|
||||
buildSteadyChecksquareCreatePayload,
|
||||
calculateChecksquareExpectedItemCount,
|
||||
defaultChecksquareFormState,
|
||||
validateChecksquareSelection
|
||||
} from './utils/checksquarePayload'
|
||||
@@ -137,8 +133,20 @@ const loading = reactive({
|
||||
detail: false
|
||||
})
|
||||
let ledgerSearchTimer: ReturnType<typeof setTimeout> | null = null
|
||||
let createTaskPollingTimer: number | null = null
|
||||
let createTaskPollingRunning = false
|
||||
let createTaskPollingCount = 0
|
||||
const CREATE_TASK_POLLING_INTERVALS = [3000, 5000, 10000]
|
||||
|
||||
const lineIds = computed(() => collectSelectedLineIds(selectedLedgerNodes.value))
|
||||
const totalIndicatorCount = computed(() => collectLeafIndicators(indicatorTree.value).length)
|
||||
const expectedCreateItemCount = computed(() =>
|
||||
calculateChecksquareExpectedItemCount({
|
||||
lineCount: lineIds.value.length,
|
||||
selectedIndicatorCount: selectedIndicators.value.length,
|
||||
totalIndicatorCount: totalIndicatorCount.value
|
||||
})
|
||||
)
|
||||
|
||||
const unwrapData = <T,>(response: { data: T } | T): T => {
|
||||
if (response && typeof response === 'object' && 'data' in response) {
|
||||
@@ -148,6 +156,13 @@ const unwrapData = <T,>(response: { data: T } | T): T => {
|
||||
return response as T
|
||||
}
|
||||
|
||||
const isChecksquareTaskFinished = (status?: string) => status === 'SUCCESS' || status === 'FAIL'
|
||||
|
||||
const getCreateTaskPollingDelay = () => {
|
||||
const index = Math.min(createTaskPollingCount, CREATE_TASK_POLLING_INTERVALS.length - 1)
|
||||
return CREATE_TASK_POLLING_INTERVALS[index]
|
||||
}
|
||||
|
||||
const loadLedgerTree = async (keyword = ledgerKeyword.value) => {
|
||||
loading.ledger = true
|
||||
try {
|
||||
@@ -177,6 +192,7 @@ const loadIndicatorTree = async () => {
|
||||
}
|
||||
|
||||
const openCreateDialog = () => {
|
||||
stopCreateTaskPolling()
|
||||
activeCreateTask.value = null
|
||||
createDialogVisible.value = true
|
||||
}
|
||||
@@ -196,6 +212,7 @@ const handleIndicatorChange = (nodes: SteadyDataView.SteadyIndicatorNode[]) => {
|
||||
}
|
||||
|
||||
const handleReset = () => {
|
||||
stopCreateTaskPolling()
|
||||
formState.value = defaultChecksquareFormState()
|
||||
selectedLedgerNodes.value = []
|
||||
selectedIndicators.value = []
|
||||
@@ -205,7 +222,82 @@ const handleReset = () => {
|
||||
selectorResetKey.value += 1
|
||||
}
|
||||
|
||||
const normalizeCreateTask = (result: SteadyDataView.SteadyChecksquareTask): SteadyDataView.SteadyChecksquareTask | null => {
|
||||
const buildCreateTaskStatusQuery = (
|
||||
task: SteadyDataView.SteadyChecksquareTask | null
|
||||
): SteadyDataView.SteadyChecksquareTaskQueryParams => ({
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
lineId: task?.lineId,
|
||||
timeStart: task?.timeStart,
|
||||
timeEnd: task?.timeEnd
|
||||
})
|
||||
|
||||
const refreshCreateTaskStatus = async (taskId: string) => {
|
||||
if (createTaskPollingRunning) return
|
||||
|
||||
createTaskPollingRunning = true
|
||||
try {
|
||||
const taskResponse = await querySteadyChecksquareTasks(buildCreateTaskStatusQuery(activeCreateTask.value))
|
||||
const taskPage = unwrapData(taskResponse)
|
||||
const latestTask = taskPage.records?.find(task => task.taskId === taskId)
|
||||
|
||||
if (activeCreateTask.value && latestTask) {
|
||||
activeCreateTask.value = {
|
||||
...activeCreateTask.value,
|
||||
taskNo: latestTask.taskNo || activeCreateTask.value.taskNo,
|
||||
lineId: latestTask.lineId || activeCreateTask.value.lineId,
|
||||
lineName: latestTask.lineName || activeCreateTask.value.lineName,
|
||||
timeStart: latestTask.timeStart || activeCreateTask.value.timeStart,
|
||||
timeEnd: latestTask.timeEnd || activeCreateTask.value.timeEnd,
|
||||
intervalMinutes: latestTask.intervalMinutes ?? activeCreateTask.value.intervalMinutes,
|
||||
taskStatus: latestTask.taskStatus || activeCreateTask.value.taskStatus,
|
||||
itemCount: latestTask.itemCount ?? activeCreateTask.value.itemCount,
|
||||
abnormalItemCount: latestTask.abnormalItemCount ?? activeCreateTask.value.abnormalItemCount,
|
||||
minDataIntegrity: latestTask.minDataIntegrity ?? activeCreateTask.value.minDataIntegrity,
|
||||
createTime: latestTask.createTime || activeCreateTask.value.createTime
|
||||
}
|
||||
}
|
||||
|
||||
if (isChecksquareTaskFinished(latestTask?.taskStatus || activeCreateTask.value?.taskStatus)) {
|
||||
stopCreateTaskPolling()
|
||||
taskTableRef.value?.refresh()
|
||||
return
|
||||
}
|
||||
|
||||
scheduleCreateTaskPolling(taskId)
|
||||
} finally {
|
||||
createTaskPollingRunning = false
|
||||
}
|
||||
}
|
||||
|
||||
const stopCreateTaskPolling = () => {
|
||||
if (createTaskPollingTimer !== null) window.clearTimeout(createTaskPollingTimer)
|
||||
|
||||
createTaskPollingTimer = null
|
||||
createTaskPollingRunning = false
|
||||
createTaskPollingCount = 0
|
||||
}
|
||||
|
||||
const scheduleCreateTaskPolling = (taskId: string) => {
|
||||
if (createTaskPollingTimer !== null) window.clearTimeout(createTaskPollingTimer)
|
||||
|
||||
createTaskPollingTimer = window.setTimeout(() => {
|
||||
createTaskPollingTimer = null
|
||||
createTaskPollingCount += 1
|
||||
void refreshCreateTaskStatus(taskId).catch(() => scheduleCreateTaskPolling(taskId))
|
||||
}, getCreateTaskPollingDelay())
|
||||
}
|
||||
|
||||
const startCreateTaskPolling = (taskId: string) => {
|
||||
stopCreateTaskPolling()
|
||||
|
||||
// 校验任务只需要监控执行状态,轮询任务列表行信息,避免反复拉取检测项明细。
|
||||
scheduleCreateTaskPolling(taskId)
|
||||
}
|
||||
|
||||
const normalizeCreateTask = (
|
||||
result: SteadyDataView.SteadyChecksquareTask
|
||||
): SteadyDataView.SteadyChecksquareTask | null => {
|
||||
const taskId = result.taskId || result.taskNo
|
||||
if (!taskId) return null
|
||||
|
||||
@@ -236,14 +328,19 @@ const handleCreateTask = async () => {
|
||||
return
|
||||
}
|
||||
|
||||
activeCreateTask.value = null
|
||||
loading.query = true
|
||||
try {
|
||||
// /create 只返回任务行信息,检测项明细统一通过 /detail 拉取,避免把列表行误当成详情数据。
|
||||
// /create 返回任务行信息,新增弹窗只监控任务状态,不拉取检测项详情。
|
||||
const response = await createSteadyChecksquareTask(
|
||||
buildSteadyChecksquareCreatePayload(lineIds.value[0], selectedIndicators.value, formState.value)
|
||||
buildSteadyChecksquareCreatePayload(lineIds.value, selectedIndicators.value, formState.value)
|
||||
)
|
||||
const result = unwrapData(response)
|
||||
activeCreateTask.value = normalizeCreateTask(result)
|
||||
const createdTask = normalizeCreateTask(result)
|
||||
activeCreateTask.value = createdTask
|
||||
if (createdTask?.taskId && !isChecksquareTaskFinished(createdTask.taskStatus)) {
|
||||
startCreateTaskPolling(createdTask.taskId)
|
||||
}
|
||||
ElMessage.success('校验任务已获取')
|
||||
taskTableRef.value?.refresh()
|
||||
} finally {
|
||||
@@ -283,12 +380,31 @@ const handleDeleteTask = async (row: SteadyDataView.SteadyChecksquareTask) => {
|
||||
return
|
||||
}
|
||||
|
||||
// 删除接口按任务 ID 数组批量处理;列表行操作只传当前行任务 ID。
|
||||
// 删除接口按任务 ID 数组批量处理,行操作只传当前任务 ID。
|
||||
await deleteSteadyChecksquareTasks([row.taskId])
|
||||
ElMessage.success('删除校验任务成功')
|
||||
taskTableRef.value?.refresh()
|
||||
}
|
||||
|
||||
const handleRestartTask = async (row: SteadyDataView.SteadyChecksquareTask) => {
|
||||
if (!row.taskId) return
|
||||
|
||||
try {
|
||||
await ElMessageBox.confirm('确认重启该失败的校验任务吗?重启后会清理旧结果并重新执行。', '重启确认', {
|
||||
confirmButtonText: '重启',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
})
|
||||
} catch {
|
||||
return
|
||||
}
|
||||
|
||||
// 重启接口仅支持失败任务,成功后复用原任务 ID 并刷新列表展示最新运行状态。
|
||||
await restartSteadyChecksquareTask(row.taskId)
|
||||
ElMessage.success('校验任务已重启')
|
||||
taskTableRef.value?.refresh()
|
||||
}
|
||||
|
||||
const openItemDetail = (item: SteadyDataView.SteadyChecksquareItem) => {
|
||||
selectedItem.value = item
|
||||
itemDetailDialogVisible.value = true
|
||||
@@ -303,6 +419,12 @@ onMounted(() => {
|
||||
loadLedgerTree()
|
||||
loadIndicatorTree()
|
||||
})
|
||||
|
||||
watch(createDialogVisible, visible => {
|
||||
if (!visible) stopCreateTaskPolling()
|
||||
})
|
||||
|
||||
onBeforeUnmount(stopCreateTaskPolling)
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
@@ -29,13 +29,25 @@ export const collectChecksquareIndicatorCodes = (indicators: SteadyDataView.Stea
|
||||
return Array.from(new Set(indicators.map(item => item.indicatorCode).filter(Boolean))) as string[]
|
||||
}
|
||||
|
||||
export const calculateChecksquareExpectedItemCount = (params: {
|
||||
lineCount: number
|
||||
selectedIndicatorCount: number
|
||||
totalIndicatorCount: number
|
||||
}) => {
|
||||
const { lineCount, selectedIndicatorCount, totalIndicatorCount } = params
|
||||
const indicatorCount = selectedIndicatorCount > 0 ? selectedIndicatorCount : totalIndicatorCount
|
||||
|
||||
return lineCount * indicatorCount
|
||||
}
|
||||
|
||||
export const buildSteadyChecksquareCreatePayload = (
|
||||
lineId: string,
|
||||
lineIds: string[],
|
||||
indicators: SteadyDataView.SteadyIndicatorNode[],
|
||||
formState: ChecksquareFormState
|
||||
): SteadyDataView.SteadyChecksquareCreateParams => {
|
||||
return {
|
||||
lineId,
|
||||
lineId: lineIds[0],
|
||||
lineIds,
|
||||
indicatorCodes: collectChecksquareIndicatorCodes(indicators),
|
||||
timeStart: (formState.timeRange[0] || '').replace(/\.[^.]+$/, ''),
|
||||
timeEnd: (formState.timeRange[1] || '').replace(/\.[^.]+$/, '')
|
||||
@@ -47,11 +59,9 @@ export const validateChecksquareSelection = (params: {
|
||||
indicators: SteadyDataView.SteadyIndicatorNode[]
|
||||
timeRange: string[]
|
||||
}) => {
|
||||
const { lineIds, indicators, timeRange } = params
|
||||
const { lineIds, timeRange } = params
|
||||
|
||||
if (!lineIds.length) return '请选择监测点'
|
||||
if (lineIds.length > 1) return '数据校验一次只能选择一个监测点'
|
||||
if (!indicators.length) return '请选择指标'
|
||||
if (!timeRange[0]) return '请选择开始时间'
|
||||
if (!timeRange[1]) return '请选择结束时间'
|
||||
if (Date.parse(timeRange[0].replace(' ', 'T')) > Date.parse(timeRange[1].replace(' ', 'T'))) {
|
||||
|
||||
Reference in New Issue
Block a user