feat(steady): 重构稳态校验功能并优化界面布局
- 更新 API 接口路径从 /steady/data-view/checksquare/* 到 /steady/checksquare/* - 修改校验任务状态枚举值 FAILED 为 FAIL 并更新相关处理逻辑 - 移除缺失率和最大连续缺失分钟数字段,简化数据完整性计算 - 添加新的创建结果面板组件 ChecksquareCreateResultPanel.vue - 调整创建对话框布局,采用两行搜索控件设计 - 更新任务表头部按钮文字为"新增"并调整搜索列配置为5列 - 修改详情面板显示开始时间和结束时间字段 - 重构工作台界面布局,使用 flex 布局替代 grid 布局 - 更新设备类型相关 API 接口和数据结构定义 - 添加设备类型字典常量并更新路由配置 - 优化搜索表单展开收起逻辑的计算方式 - 调整创建流程不再轮询获取任务详情,改为直接显示摘要信息 - 更新数据完整性格式化函数参数和调用方式 - 修改创建对话框样式类名和尺寸配置 - 添加设备类型管理相关的接口定义和实现方法
This commit is contained in:
@@ -0,0 +1,417 @@
|
||||
# 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,215 @@
|
||||
<template>
|
||||
<aside class="table-main card checksquare-result-panel">
|
||||
<div class="table-header">
|
||||
<div class="header-button-lf">
|
||||
<span class="section-title">校验任务摘要</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-empty v-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>
|
||||
<div class="task-meta">
|
||||
<span>{{ task.lineName || task.lineId || '-' }}</span>
|
||||
<span>{{ task.timeStart || '-' }} 至 {{ task.timeEnd || '-' }}</span>
|
||||
</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>
|
||||
<div class="metric-item">
|
||||
<span class="metric-label">检测项</span>
|
||||
<span class="metric-value">{{ task.itemCount ?? '-' }}</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>
|
||||
</template>
|
||||
</aside>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { SteadyDataView } from '@/api/steady/steadyDataView/interface'
|
||||
import {
|
||||
formatChecksquareIntegrity,
|
||||
formatChecksquareTaskStatus,
|
||||
resolveChecksquareTaskStatusType
|
||||
} from '../utils/checksquareTaskTable'
|
||||
|
||||
defineOptions({
|
||||
name: 'ChecksquareCreateResultPanel'
|
||||
})
|
||||
|
||||
defineProps<{
|
||||
task: SteadyDataView.SteadyChecksquareTask | null
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.checksquare-result-panel {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
min-width: 0;
|
||||
min-height: 0;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
color: var(--el-text-color-primary);
|
||||
}
|
||||
|
||||
.empty-result {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.result-body {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.result-overview {
|
||||
display: grid;
|
||||
flex: none;
|
||||
grid-template-columns: minmax(0, 1fr);
|
||||
gap: 12px;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.task-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
padding: 10px;
|
||||
border: 1px solid var(--el-border-color-lighter);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.task-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 8px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.task-title span:first-child,
|
||||
.task-meta span {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.task-meta {
|
||||
display: flex;
|
||||
min-width: 0;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
font-size: 12px;
|
||||
color: var(--el-text-color-secondary);
|
||||
}
|
||||
|
||||
.result-metrics {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.metric-item {
|
||||
display: flex;
|
||||
min-width: 0;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
padding: 8px;
|
||||
border: 1px solid var(--el-border-color-lighter);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.metric-label,
|
||||
.detail-label {
|
||||
font-size: 12px;
|
||||
color: var(--el-text-color-secondary);
|
||||
}
|
||||
|
||||
.metric-value,
|
||||
.detail-value {
|
||||
overflow: hidden;
|
||||
font-weight: 600;
|
||||
color: var(--el-text-color-primary);
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.metric-value.is-danger {
|
||||
color: var(--el-color-danger);
|
||||
}
|
||||
|
||||
.result-detail-grid {
|
||||
display: grid;
|
||||
flex: none;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.detail-block {
|
||||
display: flex;
|
||||
min-width: 0;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
padding: 12px;
|
||||
border: 1px solid var(--el-border-color-lighter);
|
||||
border-radius: 4px;
|
||||
background: var(--el-fill-color-blank);
|
||||
}
|
||||
|
||||
.detail-value {
|
||||
font-size: 13px;
|
||||
white-space: normal;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
@media (max-width: 1280px) {
|
||||
.result-metrics {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -8,7 +8,7 @@
|
||||
<div class="overview-item">
|
||||
<span class="overview-label">数据完整性</span>
|
||||
<span class="overview-value">
|
||||
{{ formatDataIntegrity(selectedItem.dataIntegrity, selectedItem.dataIntegrityText, selectedItem.missingRate) }}
|
||||
{{ formatDataIntegrity(selectedItem.dataIntegrity, selectedItem.dataIntegrityText) }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="overview-item">
|
||||
@@ -45,7 +45,8 @@
|
||||
<el-table-column prop="status" label="状态" width="90">
|
||||
<template #default="{ row }">{{ formatSegmentStatus(row.status) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="startTime" label="统计时间" min-width="220" />
|
||||
<el-table-column prop="startTime" label="开始时间" min-width="170" />
|
||||
<el-table-column prop="endTime" label="结束时间" min-width="170" />
|
||||
<el-table-column prop="harmonicOrder" label="谐波次数" width="100" align="right">
|
||||
<template #default="{ row }">{{ row.harmonicOrder ?? '-' }}</template>
|
||||
</el-table-column>
|
||||
|
||||
@@ -121,7 +121,7 @@ const hasAbnormalCount = (value?: number | null) => Number(value || 0) > 0
|
||||
const stripPercentUnit = (value: string) => value.replace(/%$/, '')
|
||||
|
||||
const formatSummaryDataIntegrity = (row: SteadyDataView.SteadyChecksquareItem) => {
|
||||
return stripPercentUnit(formatDataIntegrity(row.dataIntegrity, row.dataIntegrityText, row.missingRate))
|
||||
return stripPercentUnit(formatDataIntegrity(row.dataIntegrity, row.dataIntegrityText))
|
||||
}
|
||||
|
||||
const formatSummaryStatIntegrity = (
|
||||
|
||||
@@ -4,10 +4,10 @@
|
||||
row-key="taskId"
|
||||
:columns="columns"
|
||||
:request-api="getTableList"
|
||||
:search-col="{ xs: 1, sm: 2, md: 2, lg: 4, xl: 4 }"
|
||||
:search-col="{ xs: 1, sm: 2, md: 2, lg: 5, xl: 5 }"
|
||||
>
|
||||
<template #tableHeader>
|
||||
<el-button type="primary" :icon="Plus" @click="emit('createTask')">新增校验任务</el-button>
|
||||
<el-button type="primary" :icon="Plus" @click="emit('createTask')">新增</el-button>
|
||||
</template>
|
||||
|
||||
<template #operation="{ row }">
|
||||
@@ -284,7 +284,7 @@ const columns = reactive<ColumnProps<SteadyDataView.SteadyChecksquareTask>[]>([
|
||||
label: '最低完整性',
|
||||
minWidth: 120,
|
||||
align: 'center',
|
||||
render: ({ row }) => formatChecksquareIntegrity(row.minDataIntegrity, row.maxMissingRate)
|
||||
render: ({ row }) => formatChecksquareIntegrity(row.minDataIntegrity)
|
||||
},
|
||||
{
|
||||
prop: 'createTime',
|
||||
|
||||
@@ -32,6 +32,12 @@
|
||||
@update:range-value="handleTimeRangeChange"
|
||||
/>
|
||||
</div>
|
||||
<div class="query-actions">
|
||||
<el-button type="primary" :icon="Plus" :loading="loading.query" @click="emit('create')">
|
||||
新增
|
||||
</el-button>
|
||||
<el-button type="primary" plain :icon="RefreshLeft" @click="emit('reset')">重置</el-button>
|
||||
</div>
|
||||
<div class="toolbar-field indicator-form-item">
|
||||
<span class="toolbar-field__label">稳态指标:</span>
|
||||
<div class="indicator-select-row">
|
||||
@@ -62,13 +68,11 @@
|
||||
<el-button type="primary" plain @click="handleSelectAllIndicators">全选</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="query-actions">
|
||||
<el-button type="primary" :icon="Plus" :loading="loading.query" @click="emit('create')">
|
||||
新增校验任务
|
||||
</el-button>
|
||||
<el-button type="primary" plain :icon="RefreshLeft" @click="emit('reset')">重置</el-button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="checksquare-result-slot">
|
||||
<slot name="result" />
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</template>
|
||||
@@ -210,7 +214,7 @@ watch(
|
||||
<style scoped lang="scss">
|
||||
.checksquare-layout {
|
||||
display: grid;
|
||||
grid-template-columns: 320px minmax(0, 1fr);
|
||||
grid-template-columns: 240px minmax(0, 1fr);
|
||||
gap: 12px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
@@ -249,17 +253,31 @@ watch(
|
||||
}
|
||||
|
||||
.checksquare-main {
|
||||
display: block;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
align-items: flex-end;
|
||||
min-width: 0;
|
||||
min-height: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.checksquare-result-slot {
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
min-height: 0;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.checksquare-result-slot :deep(.checksquare-result-panel) {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.query-card {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(360px, 1.2fr) minmax(280px, 1fr);
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
align-items: stretch;
|
||||
align-items: center;
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
@@ -271,6 +289,7 @@ watch(
|
||||
}
|
||||
|
||||
.toolbar-field--time {
|
||||
flex: 1 1 100%;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
@@ -297,6 +316,8 @@ watch(
|
||||
}
|
||||
|
||||
.indicator-form-item {
|
||||
order: 2;
|
||||
flex: 1 1 auto;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
@@ -330,20 +351,27 @@ watch(
|
||||
|
||||
.query-actions {
|
||||
display: flex;
|
||||
grid-column: 1 / -1;
|
||||
order: 3;
|
||||
flex: 0 0 auto;
|
||||
justify-content: flex-start;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
@media (max-width: 1360px) {
|
||||
.checksquare-layout:not(.is-ledger-collapsed) {
|
||||
grid-template-columns: 280px minmax(0, 1fr);
|
||||
grid-template-columns: 220px minmax(0, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 1280px) {
|
||||
.query-card {
|
||||
grid-template-columns: minmax(0, 1fr);
|
||||
.toolbar-field--time,
|
||||
.indicator-form-item {
|
||||
flex: 1 1 100%;
|
||||
}
|
||||
|
||||
.checksquare-result-slot {
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -14,6 +14,7 @@ const files = {
|
||||
taskTable: path.resolve(rootDir, 'views/steady/checksquare/components/ChecksquareTaskTable.vue'),
|
||||
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'),
|
||||
payload: path.resolve(rootDir, 'views/steady/checksquare/utils/checksquarePayload.ts'),
|
||||
ledgerUtils: path.resolve(rootDir, 'views/steady/checksquare/utils/checksquareLedger.ts'),
|
||||
@@ -34,11 +35,12 @@ const checks = [
|
||||
() => {
|
||||
const api = read(files.api)
|
||||
return (
|
||||
/\/steady\/data-view\/checksquare\/query/.test(api) &&
|
||||
/\/steady\/data-view\/checksquare\/create/.test(api) &&
|
||||
/\/steady\/data-view\/checksquare\/delete/.test(api) &&
|
||||
/\/steady\/data-view\/checksquare\/detail/.test(api) &&
|
||||
/\/steady\/data-view\/checksquare\/item-detail/.test(api)
|
||||
/\/steady\/checksquare\/query/.test(api) &&
|
||||
/\/steady\/checksquare\/create/.test(api) &&
|
||||
!/\/steady\/checksquare\/get-or-create/.test(api) &&
|
||||
/\/steady\/checksquare\/delete/.test(api) &&
|
||||
/\/steady\/checksquare\/detail/.test(api) &&
|
||||
/\/steady\/checksquare\/item-detail/.test(api)
|
||||
)
|
||||
}
|
||||
],
|
||||
@@ -48,7 +50,7 @@ const checks = [
|
||||
/export const deleteSteadyChecksquareTasks = \(taskIds: SteadyDataView\.SteadyChecksquareDeleteParams\)/.test(
|
||||
read(files.api)
|
||||
) &&
|
||||
/http\.post<boolean>\('\/steady\/data-view\/checksquare\/delete', taskIds/.test(read(files.api)) &&
|
||||
/http\.post<boolean>\('\/steady\/checksquare\/delete', taskIds/.test(read(files.api)) &&
|
||||
/export type SteadyChecksquareDeleteParams = string\[\]/.test(read(files.apiTypes))
|
||||
],
|
||||
[
|
||||
@@ -76,7 +78,10 @@ const checks = [
|
||||
['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',
|
||||
() => /<template #tableHeader>/.test(read(files.taskTable)) && /新增校验任务/.test(read(files.taskTable)) && /emit\('createTask'\)/.test(read(files.taskTable))
|
||||
() =>
|
||||
/<template #tableHeader>/.test(read(files.taskTable)) &&
|
||||
/>新增<\/el-button>/.test(read(files.taskTable)) &&
|
||||
/emit\('createTask'\)/.test(read(files.taskTable))
|
||||
],
|
||||
[
|
||||
'task table has documented task columns',
|
||||
@@ -123,14 +128,23 @@ const checks = [
|
||||
['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))],
|
||||
[
|
||||
'create dialog workbench keeps time and indicator on one row without compressing actions',
|
||||
() =>
|
||||
/\.query-card\s*\{[\s\S]*grid-template-columns:\s*minmax\(360px,\s*1\.2fr\)\s+minmax\(280px,\s*1fr\)/.test(
|
||||
read(files.workbench)
|
||||
) &&
|
||||
/\.query-actions\s*\{[\s\S]*grid-column:\s*1\s*\/\s*-1/.test(read(files.workbench)) &&
|
||||
/\.indicator-select-row\s*\{[\s\S]*align-items:\s*center/.test(read(files.workbench)) &&
|
||||
/\.query-actions\s*\{[\s\S]*justify-content:\s*flex-start/.test(read(files.workbench))
|
||||
'workbench create action uses short add label',
|
||||
() => /@click="emit\('create'\)"[\s\S]*>\s*新增\s*<\/el-button>/.test(read(files.workbench))
|
||||
],
|
||||
[
|
||||
'create dialog workbench places search controls in two rows with actions after indicator',
|
||||
() => {
|
||||
const workbench = read(files.workbench)
|
||||
return (
|
||||
/\.query-card\s*\{[\s\S]*display:\s*flex[\s\S]*flex-wrap:\s*wrap/.test(workbench) &&
|
||||
/\.toolbar-field--time\s*\{[\s\S]*flex:\s*1\s+1\s+100%/.test(workbench) &&
|
||||
/\.indicator-form-item\s*\{[\s\S]*order:\s*2[\s\S]*flex:\s*1\s+1\s+auto/.test(workbench) &&
|
||||
/\.query-actions\s*\{[\s\S]*order:\s*3[\s\S]*flex:\s*0\s+0\s+auto/.test(workbench) &&
|
||||
/<div class="query-actions">[\s\S]*emit\('create'\)[\s\S]*emit\('reset'\)[\s\S]*<\/div>\s*<div class="toolbar-field indicator-form-item">/.test(
|
||||
workbench
|
||||
)
|
||||
)
|
||||
}
|
||||
],
|
||||
[
|
||||
'payload builds create params without harmonic orders',
|
||||
@@ -260,7 +274,14 @@ const checks = [
|
||||
],
|
||||
[
|
||||
'search collapse toggle only appears when filters exceed available first row columns',
|
||||
() => /prev\s*>\s*props\.searchCol\[breakPoint\.value\]/.test(read(files.searchForm))
|
||||
() =>
|
||||
/const searchColCount[\s\S]*typeof props\.searchCol !== 'number'[\s\S]*props\.searchCol\[breakPoint\.value\][\s\S]*const firstRowSearchCols[\s\S]*Math\.max\(searchColCount - 1,\s*1\)[\s\S]*prev\s*>\s*firstRowSearchCols/.test(
|
||||
read(files.searchForm)
|
||||
)
|
||||
],
|
||||
[
|
||||
'checksquare task search grid follows event list five-column layout',
|
||||
() => /:search-col="\{\s*xs:\s*1,\s*sm:\s*2,\s*md:\s*2,\s*lg:\s*5,\s*xl:\s*5\s*\}"/.test(read(files.taskTable))
|
||||
],
|
||||
[
|
||||
'custom search render does not receive generic form item v-model',
|
||||
@@ -270,14 +291,80 @@ const checks = [
|
||||
],
|
||||
[
|
||||
'page wraps old workbench in create dialog',
|
||||
() => /<el-dialog[\s\S]*新增校验任务[\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 and refreshes task table',
|
||||
'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)) &&
|
||||
/createDialogVisible\.value = false/.test(read(files.page))
|
||||
/activeCreateTask\.value/.test(read(files.page))
|
||||
],
|
||||
[
|
||||
'create dialog shows create task summary without detail table',
|
||||
() => {
|
||||
const page = read(files.page)
|
||||
const workbench = read(files.workbench)
|
||||
const panel = read(files.createResultPanel)
|
||||
return (
|
||||
exists(files.createResultPanel) &&
|
||||
/<template #result>[\s\S]*<ChecksquareCreateResultPanel[\s\S]*:task="activeCreateTask"[\s\S]*<\/template>/.test(
|
||||
page
|
||||
) &&
|
||||
/\.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-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) &&
|
||||
/class="result-overview"/.test(panel) &&
|
||||
/class="result-body"/.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) &&
|
||||
!/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) &&
|
||||
/校验任务摘要/.test(panel) &&
|
||||
/task\.itemCount/.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) &&
|
||||
!/class="result-detail-table"/.test(panel) &&
|
||||
!/resultItems/.test(panel) &&
|
||||
!/emit\('detail', row\)/.test(panel)
|
||||
)
|
||||
}
|
||||
],
|
||||
[
|
||||
'create dialog does not poll create task detail',
|
||||
() => {
|
||||
const page = read(files.page)
|
||||
return (
|
||||
!/createTaskPollingTimer/.test(page) &&
|
||||
!/startCreateTaskPolling/.test(page) &&
|
||||
!/stopCreateTaskPolling/.test(page) &&
|
||||
!/setInterval/.test(page) &&
|
||||
!/onBeforeUnmount/.test(page)
|
||||
)
|
||||
}
|
||||
],
|
||||
[
|
||||
'page delete flow confirms, calls delete api and refreshes task table',
|
||||
@@ -407,8 +494,26 @@ const checks = [
|
||||
/formatChecksquareIntegrity/.test(taskTableUtils) &&
|
||||
/prop="dataIntegrity"/.test(summaryTable) &&
|
||||
/formatDataIntegrity/.test(tableUtils) &&
|
||||
!/prop:\s*'maxMissingRate'/.test(taskTable) &&
|
||||
!/prop="missingRate"/.test(summaryTable)
|
||||
!/maxMissingRate/.test(types) &&
|
||||
!/missingRate/.test(types) &&
|
||||
!/maxContinuousMissingMinutes/.test(types) &&
|
||||
!/maxMissingRate/.test(taskTable) &&
|
||||
!/missingRate/.test(summaryTable) &&
|
||||
!/missingRate/.test(tableUtils) &&
|
||||
!/missingRate/.test(taskTableUtils)
|
||||
)
|
||||
}
|
||||
],
|
||||
[
|
||||
'checksquare task status follows documented FAIL value',
|
||||
() => {
|
||||
const source = read(files.taskTableUtils)
|
||||
const types = read(files.apiTypes)
|
||||
|
||||
return (
|
||||
/taskStatus\?: 'RUNNING' \| 'SUCCESS' \| 'FAIL' \| string/.test(types) &&
|
||||
/status === 'FAIL'/.test(source) &&
|
||||
!/FAILED/.test(source)
|
||||
)
|
||||
}
|
||||
],
|
||||
@@ -467,7 +572,13 @@ const checks = [
|
||||
'detail panel renders documented detail fields',
|
||||
() => {
|
||||
const detailPanel = read(files.detailPanel)
|
||||
return /prop="status"[\s\S]*状态/.test(detailPanel) && /oddHarmonicOrders/.test(detailPanel) && /oddValues/.test(detailPanel)
|
||||
return (
|
||||
/prop="status"[\s\S]*状态/.test(detailPanel) &&
|
||||
/prop="startTime" label="开始时间"/.test(detailPanel) &&
|
||||
/prop="endTime" label="结束时间"/.test(detailPanel) &&
|
||||
/oddHarmonicOrders/.test(detailPanel) &&
|
||||
/oddValues/.test(detailPanel)
|
||||
)
|
||||
}
|
||||
],
|
||||
[
|
||||
|
||||
@@ -16,7 +16,13 @@
|
||||
:data="measurementPointData"
|
||||
/>
|
||||
|
||||
<el-dialog v-model="createDialogVisible" title="新增校验任务" width="1120px" 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"
|
||||
@@ -34,7 +40,13 @@
|
||||
@indicator-change="handleIndicatorChange"
|
||||
@create="handleCreateTask"
|
||||
@reset="handleReset"
|
||||
/>
|
||||
>
|
||||
<template #result>
|
||||
<ChecksquareCreateResultPanel
|
||||
:task="activeCreateTask"
|
||||
/>
|
||||
</template>
|
||||
</ChecksquareWorkbench>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
@@ -78,6 +90,7 @@ import {
|
||||
sortSteadyIndicatorTree
|
||||
} from '@/views/steady/steadyDataView/utils/selectionRules'
|
||||
import { normalizeSteadyLedgerTree } from '@/views/steady/steadyDataView/utils/ledgerTree'
|
||||
import ChecksquareCreateResultPanel from './components/ChecksquareCreateResultPanel.vue'
|
||||
import ChecksquareDetailPanel from './components/ChecksquareDetailPanel.vue'
|
||||
import ChecksquareMeasurementPointDialog from './components/ChecksquareMeasurementPointDialog.vue'
|
||||
import ChecksquareSummaryTable from './components/ChecksquareSummaryTable.vue'
|
||||
@@ -104,6 +117,7 @@ const selectedIndicators = ref<SteadyDataView.SteadyIndicatorNode[]>([])
|
||||
const taskDetail = ref<SteadyDataView.SteadyChecksquareQueryResult | null>(null)
|
||||
const selectedTask = ref<SteadyDataView.SteadyChecksquareTask | null>(null)
|
||||
const selectedItem = ref<SteadyDataView.SteadyChecksquareItem | null>(null)
|
||||
const activeCreateTask = ref<SteadyDataView.SteadyChecksquareTask | null>(null)
|
||||
const measurementPointData = ref<ChecksquareMeasurementPointDetail | null>(null)
|
||||
const formState = ref(defaultChecksquareFormState())
|
||||
const ledgerKeyword = ref('')
|
||||
@@ -163,6 +177,7 @@ const loadIndicatorTree = async () => {
|
||||
}
|
||||
|
||||
const openCreateDialog = () => {
|
||||
activeCreateTask.value = null
|
||||
createDialogVisible.value = true
|
||||
}
|
||||
|
||||
@@ -186,9 +201,30 @@ const handleReset = () => {
|
||||
selectedIndicators.value = []
|
||||
defaultLedgerCheckedKeys.value = []
|
||||
defaultIndicatorCheckedKeys.value = []
|
||||
activeCreateTask.value = null
|
||||
selectorResetKey.value += 1
|
||||
}
|
||||
|
||||
const normalizeCreateTask = (result: SteadyDataView.SteadyChecksquareTask): SteadyDataView.SteadyChecksquareTask | null => {
|
||||
const taskId = result.taskId || result.taskNo
|
||||
if (!taskId) return null
|
||||
|
||||
return {
|
||||
taskId,
|
||||
taskNo: result.taskNo,
|
||||
lineId: result.lineId,
|
||||
lineName: result.lineName,
|
||||
timeStart: result.timeStart,
|
||||
timeEnd: result.timeEnd,
|
||||
intervalMinutes: result.intervalMinutes,
|
||||
taskStatus: result.taskStatus,
|
||||
itemCount: result.itemCount,
|
||||
abnormalItemCount: result.abnormalItemCount,
|
||||
minDataIntegrity: result.minDataIntegrity,
|
||||
createTime: result.createTime
|
||||
}
|
||||
}
|
||||
|
||||
const handleCreateTask = async () => {
|
||||
const selectionError = validateChecksquareSelection({
|
||||
lineIds: lineIds.value,
|
||||
@@ -202,12 +238,13 @@ const handleCreateTask = async () => {
|
||||
|
||||
loading.query = true
|
||||
try {
|
||||
// 新增校验任务会写入结果表,成功后刷新历史任务列表展示落库记录。
|
||||
await createSteadyChecksquareTask(
|
||||
// /create 只返回任务行信息,检测项明细统一通过 /detail 拉取,避免把列表行误当成详情数据。
|
||||
const response = await createSteadyChecksquareTask(
|
||||
buildSteadyChecksquareCreatePayload(lineIds.value[0], selectedIndicators.value, formState.value)
|
||||
)
|
||||
ElMessage.success('新增校验任务成功')
|
||||
createDialogVisible.value = false
|
||||
const result = unwrapData(response)
|
||||
activeCreateTask.value = normalizeCreateTask(result)
|
||||
ElMessage.success('校验任务已获取')
|
||||
taskTableRef.value?.refresh()
|
||||
} finally {
|
||||
loading.query = false
|
||||
@@ -276,6 +313,7 @@ onMounted(() => {
|
||||
}
|
||||
|
||||
.checksquare-create-dialog {
|
||||
display: block;
|
||||
height: 560px;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
@@ -24,14 +24,9 @@ export const formatBooleanText = (value?: boolean | null) => {
|
||||
return value ? '是' : '否'
|
||||
}
|
||||
|
||||
export const formatDataIntegrity = (value?: number | null, text?: string | null, fallbackMissingRate?: number | null) => {
|
||||
export const formatDataIntegrity = (value?: number | null, text?: string | null) => {
|
||||
if (text) return text
|
||||
const integrityValue =
|
||||
value === null || value === undefined || !Number.isFinite(Number(value))
|
||||
? fallbackMissingRate === null || fallbackMissingRate === undefined || !Number.isFinite(Number(fallbackMissingRate))
|
||||
? null
|
||||
: 1 - Number(fallbackMissingRate)
|
||||
: Number(value)
|
||||
const integrityValue = value === null || value === undefined || !Number.isFinite(Number(value)) ? null : Number(value)
|
||||
|
||||
if (integrityValue === null) return '-'
|
||||
|
||||
@@ -52,7 +47,7 @@ export const formatStatMissingRate = (
|
||||
const summary = findStatSummary(item, statType)
|
||||
if (!summary || summary.supported === false) return '-'
|
||||
|
||||
return formatDataIntegrity(summary.dataIntegrity, summary.dataIntegrityText, summary.missingRate)
|
||||
return formatDataIntegrity(summary.dataIntegrity, summary.dataIntegrityText)
|
||||
}
|
||||
|
||||
export const resolveChecksquareRowName = (item: SteadyDataView.SteadyChecksquareItem) => {
|
||||
@@ -216,10 +211,6 @@ const summarizeStatType = (
|
||||
const expectedPointCount = supportedSummaries.reduce((total, summary) => total + (summary.expectedPointCount || 0), 0)
|
||||
const actualPointCount = supportedSummaries.reduce((total, summary) => total + (summary.actualPointCount || 0), 0)
|
||||
const missingPointCount = supportedSummaries.reduce((total, summary) => total + (summary.missingPointCount || 0), 0)
|
||||
const maxContinuousMissingMinutes = Math.max(
|
||||
0,
|
||||
...supportedSummaries.map(summary => summary.maxContinuousMissingMinutes || 0)
|
||||
)
|
||||
|
||||
return {
|
||||
statType,
|
||||
@@ -229,8 +220,7 @@ const summarizeStatType = (
|
||||
actualPointCount,
|
||||
missingPointCount,
|
||||
dataIntegrity: expectedPointCount ? actualPointCount / expectedPointCount : null,
|
||||
missingRate: expectedPointCount ? missingPointCount / expectedPointCount : null,
|
||||
maxContinuousMissingMinutes
|
||||
dataIntegrityText: expectedPointCount ? undefined : '-'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -252,7 +242,6 @@ export const buildHarmonicParentSummary = (
|
||||
const expectedPointCount = sumNumber(children, item => item.expectedPointCount)
|
||||
const actualPointCount = sumNumber(children, item => item.actualPointCount)
|
||||
const missingPointCount = sumNumber(children, item => item.missingPointCount)
|
||||
const maxContinuousMissingMinutes = Math.max(0, ...children.map(item => item.maxContinuousMissingMinutes || 0))
|
||||
const statSummaries = CHECKSQUARE_STAT_TYPES.map(statType => summarizeStatType(children, statType))
|
||||
|
||||
return {
|
||||
@@ -263,9 +252,6 @@ export const buildHarmonicParentSummary = (
|
||||
missingPointCount,
|
||||
dataIntegrity: expectedPointCount ? actualPointCount / expectedPointCount : null,
|
||||
dataIntegrityText: expectedPointCount ? undefined : '-',
|
||||
missingRate: expectedPointCount ? missingPointCount / expectedPointCount : null,
|
||||
missingRateText: expectedPointCount ? undefined : '-',
|
||||
maxContinuousMissingMinutes,
|
||||
statSummaries,
|
||||
statDetails: [],
|
||||
children
|
||||
|
||||
@@ -19,7 +19,7 @@ export const buildChecksquareTaskQueryParams = (
|
||||
export const formatChecksquareTaskStatus = (status?: string) => {
|
||||
if (!status) return '--'
|
||||
if (status === 'SUCCESS') return '成功'
|
||||
if (status === 'FAILED') return '失败'
|
||||
if (status === 'FAIL') return '失败'
|
||||
if (status === 'RUNNING') return '执行中'
|
||||
|
||||
return status
|
||||
@@ -27,19 +27,14 @@ export const formatChecksquareTaskStatus = (status?: string) => {
|
||||
|
||||
export const resolveChecksquareTaskStatusType = (status?: string) => {
|
||||
if (status === 'SUCCESS') return 'success'
|
||||
if (status === 'FAILED') return 'danger'
|
||||
if (status === 'FAIL') return 'danger'
|
||||
if (status === 'RUNNING') return 'warning'
|
||||
|
||||
return 'info'
|
||||
}
|
||||
|
||||
export const formatChecksquareIntegrity = (value?: number | null, fallbackMissingRate?: number | null) => {
|
||||
const integrityValue =
|
||||
value === null || value === undefined || !Number.isFinite(Number(value))
|
||||
? fallbackMissingRate === null || fallbackMissingRate === undefined || !Number.isFinite(Number(fallbackMissingRate))
|
||||
? null
|
||||
: 1 - Number(fallbackMissingRate)
|
||||
: Number(value)
|
||||
export const formatChecksquareIntegrity = (value?: number | null) => {
|
||||
const integrityValue = value === null || value === undefined || !Number.isFinite(Number(value)) ? null : Number(value)
|
||||
|
||||
if (integrityValue === null) return '--'
|
||||
|
||||
|
||||
Reference in New Issue
Block a user