feat(tools): 新增数据补数功能模块

- 实现补数任务面板组件,支持监测点ID输入、时间范围选择和时间步长设置
- 添加任务状态卡片组件,实时展示任务执行进度和结果统计
- 集成参数规则表格组件,显示后端配置的模板规则信息
- 实现补数API接口服务,包括预估写入量、创建任务和查询状态功能
- 添加磁盘监控策略对话框组件,支持全局监控配置管理
- 完成补数功能页面布局设计,集成左右双栏界面结构
- 实现任务轮询机制,自动更新任务执行状态直到完成
- 添加表单验证逻辑,确保输入参数符合业务规则要求
This commit is contained in:
2026-04-30 09:04:52 +08:00
parent 398a2cf1dc
commit 0dc0e4ecdc
7 changed files with 1649 additions and 0 deletions

View File

@@ -0,0 +1,98 @@
import http from '@/api'
import type { ResultData } from '@/api/interface'
import type { AddData } from './interface'
type AddDataRequestMethod = 'get' | 'post'
const ADD_DATA_ROUTE_PATHS = ['/addData', '/api/addData'] as const
const ADD_DATA_BASE_URL = String(import.meta.env.VITE_API_URL || '').trim()
const resolveDevProxyTarget = () => {
const proxyConfig = import.meta.env.VITE_PROXY
if (!Array.isArray(proxyConfig)) return ''
const matchedProxy = proxyConfig.find(item => Array.isArray(item) && item[0] === '/api')
if (!matchedProxy?.[1]) return ''
return String(matchedProxy[1]).replace(/\/+$/, '')
}
const buildAddDataRequestPaths = (path: string) => {
const requestPaths = new Set<string>()
const devProxyTarget = resolveDevProxyTarget()
for (const routePath of ADD_DATA_ROUTE_PATHS) {
if (ADD_DATA_BASE_URL === '/api' && routePath.startsWith('/api/')) {
if (devProxyTarget) {
requestPaths.add(`${devProxyTarget}${routePath}${path}`)
}
requestPaths.add(`${window.location.origin}${routePath}${path}`)
continue
}
requestPaths.add(`${routePath}${path}`)
}
return Array.from(requestPaths)
}
const isFallbackableAddDataError = (error: unknown) => {
const responseCode = typeof error === 'object' && error !== null && 'code' in error ? String(error.code) : ''
const responseMessage = typeof error === 'object' && error !== null && 'message' in error ? String(error.message) : ''
const normalizedMessage = responseMessage.toLowerCase()
// 部分部署环境会把未命中的 addData 路由转到旧的操作分发入口,
// 前端在识别到“unknown operate”或典型路由错误时回退到备用前缀重试一次。
return (
responseCode === '404' ||
normalizedMessage.includes('unknown operate') ||
normalizedMessage.includes('not found') ||
normalizedMessage.includes('no handler found')
)
}
const requestAddData = async <T>(
method: AddDataRequestMethod,
path: string,
params?: object
): Promise<ResultData<T>> => {
let lastError: unknown
const requestPaths = buildAddDataRequestPaths(path)
for (let index = 0; index < requestPaths.length; index += 1) {
const requestPath = requestPaths[index]
try {
if (method === 'get') {
return await http.get<T>(requestPath)
}
return await http.post<T>(requestPath, params)
} catch (error) {
lastError = error
if (index === requestPaths.length - 1 || !isFallbackableAddDataError(error)) {
throw error
}
}
}
throw lastError
}
export const getAddDataPreview = (params: AddData.TaskRequestParams) => {
return requestAddData<AddData.PreviewResponse>('post', '/task/preview', params)
}
export const createAddDataTask = (params: AddData.TaskRequestParams) => {
return requestAddData<AddData.CreateTaskResponse>('post', '/task/create', params)
}
export const getAddDataTaskStatus = (taskId: string | number) => {
return requestAddData<AddData.TaskStatusResponse>('get', `/task/status/${taskId}`)
}
export const getAddDataTemplateList = () => {
return requestAddData<AddData.TemplateItem[]>('get', '/template/list')
}

View File

@@ -0,0 +1,109 @@
export namespace AddData {
export type LineMode = 'single' | 'multiple'
export type IntervalMinutes = 1 | 3 | 5 | 10
export type TaskStatus = 'WAITING' | 'RUNNING' | 'SUCCESS' | 'FAILED' | (string & {})
export interface TaskRequestParams {
lineIds: string[]
startTime: string
endTime: string
intervalMinutes: IntervalMinutes
}
export interface TaskFormModel {
lineMode: LineMode
lineIds: string[]
startTime: string
endTime: string
intervalMinutes: IntervalMinutes
}
export interface PreviewTableStat {
tableName?: string
timePointCount?: number | string
phaseCount?: number | string
rowCount?: number | string
}
export interface PreviewResponse {
lineCount?: number | string
intervalMinutes?: number | string
totalRowCount?: number | string
tableStats?: PreviewTableStat[]
}
export interface CreateTaskResponse {
taskId?: string | number
status?: TaskStatus
}
export interface TaskStatusResponse {
taskId?: string
status?: TaskStatus
currentTableName?: string
currentBatchInfo?: string
insertedCount?: number | string
skippedCount?: number | string
failedCount?: number | string
failureReason?: string
startTime?: string
endTime?: string
hourlyTimeResults?: string[]
}
export interface TemplateItem {
parameterName?: string
tableName?: string
phaseDisplay?: string
phaseCodes?: string[]
display?: boolean
showQualified?: boolean
maxValueRule?: string
minValueRule?: string
averageValueRule?: string
cp95ValueRule?: string
decimalScale?: number | string
}
export interface PreviewTableSummary {
tableName: string
timePointCount: number
phaseCount: number
rowCount: number
}
export interface NormalizedPreview {
lineCount: number
intervalMinutes: number
totalRowCount: number
tableStats: PreviewTableSummary[]
}
export interface NormalizedTaskStatus {
taskId: string
status: TaskStatus
currentTableName: string
currentBatchInfo: string
insertedCount: number
skippedCount: number
failedCount: number
failureReason: string
hourlyTimeResults: string[]
startTime: string
endTime: string
}
export interface NormalizedTemplateItem {
parameterName: string
tableName: string
phaseDisplay: string
phaseCodesText: string
displayText: string
showQualifiedText: string
maxValueRule: string
minValueRule: string
averageValueRule: string
cp95ValueRule: string
decimalScaleText: string
}
}