- 更新 API 接口路径从 /steady/data-view/checksquare/* 到 /steady/checksquare/* - 修改校验任务状态枚举值 FAILED 为 FAIL 并更新相关处理逻辑 - 移除缺失率和最大连续缺失分钟数字段,简化数据完整性计算 - 添加新的创建结果面板组件 ChecksquareCreateResultPanel.vue - 调整创建对话框布局,采用两行搜索控件设计 - 更新任务表头部按钮文字为"新增"并调整搜索列配置为5列 - 修改详情面板显示开始时间和结束时间字段 - 重构工作台界面布局,使用 flex 布局替代 grid 布局 - 更新设备类型相关 API 接口和数据结构定义 - 添加设备类型字典常量并更新路由配置 - 优化搜索表单展开收起逻辑的计算方式 - 调整创建流程不再轮询获取任务详情,改为直接显示摘要信息 - 更新数据完整性格式化函数参数和调用方式 - 修改创建对话框样式类名和尺寸配置 - 添加设备类型管理相关的接口定义和实现方法
319 lines
13 KiB
TypeScript
319 lines
13 KiB
TypeScript
import type { SteadyDataView } from '@/api/steady/steadyDataView/interface'
|
||
|
||
export const CHECKSQUARE_STAT_TYPES: SteadyDataView.SteadyTrendStatType[] = ['AVG', 'MAX', 'MIN', 'CP95']
|
||
export const CHECKSQUARE_HARMONIC_ORDER_MIN = 2
|
||
export const CHECKSQUARE_HARMONIC_ORDER_MAX = 50
|
||
export const CHECKSQUARE_HARMONIC_ORDERS = Array.from(
|
||
{ length: CHECKSQUARE_HARMONIC_ORDER_MAX - CHECKSQUARE_HARMONIC_ORDER_MIN + 1 },
|
||
(_item, index) => index + CHECKSQUARE_HARMONIC_ORDER_MIN
|
||
)
|
||
const CHECKSQUARE_STAT_LABEL_MAP: Record<SteadyDataView.SteadyTrendStatType, string> = {
|
||
AVG: '平均值',
|
||
MAX: '最大值',
|
||
MIN: '最小值',
|
||
CP95: 'CP95'
|
||
}
|
||
|
||
export const formatChecksquareStatType = (statType: SteadyDataView.SteadyTrendStatType | string) => {
|
||
return CHECKSQUARE_STAT_LABEL_MAP[statType as SteadyDataView.SteadyTrendStatType] || statType
|
||
}
|
||
|
||
export const formatBooleanText = (value?: boolean | null) => {
|
||
if (value === null || value === undefined) return '-'
|
||
|
||
return value ? '是' : '否'
|
||
}
|
||
|
||
export const formatDataIntegrity = (value?: number | null, text?: string | null) => {
|
||
if (text) return text
|
||
const integrityValue = value === null || value === undefined || !Number.isFinite(Number(value)) ? null : Number(value)
|
||
|
||
if (integrityValue === null) return '-'
|
||
|
||
return `${(integrityValue * 100).toFixed(2)}%`
|
||
}
|
||
|
||
export const findStatSummary = (
|
||
item: SteadyDataView.SteadyChecksquareItem,
|
||
statType: SteadyDataView.SteadyTrendStatType
|
||
) => {
|
||
return item.statSummaries?.find(summary => summary.statType === statType)
|
||
}
|
||
|
||
export const formatStatMissingRate = (
|
||
item: SteadyDataView.SteadyChecksquareItem,
|
||
statType: SteadyDataView.SteadyTrendStatType
|
||
) => {
|
||
const summary = findStatSummary(item, statType)
|
||
if (!summary || summary.supported === false) return '-'
|
||
|
||
return formatDataIntegrity(summary.dataIntegrity, summary.dataIntegrityText)
|
||
}
|
||
|
||
export const resolveChecksquareRowName = (item: SteadyDataView.SteadyChecksquareItem) => {
|
||
const progressText = getHarmonicProgressText(item)
|
||
if (progressText) return `${item.indicatorName || item.indicatorCode}(${progressText})`
|
||
if (!item.harmonicOrder) return item.indicatorName || item.indicatorCode
|
||
|
||
return `${item.harmonicOrder}次`
|
||
}
|
||
|
||
export const getHarmonicProgressText = (item: SteadyDataView.SteadyChecksquareItem) => {
|
||
const children = item.children || []
|
||
if (!children.length || !children.some(child => child.harmonicOrder)) return ''
|
||
|
||
const totalCount = children.length
|
||
const resolvedCount = children.filter(child => isResolvedChecksquareItem(child)).length
|
||
if (resolvedCount >= totalCount) return ''
|
||
|
||
return `已完成 ${resolvedCount}/${totalCount}`
|
||
}
|
||
|
||
export const collectMissingSegments = (item: SteadyDataView.SteadyChecksquareItem | null) => {
|
||
if (!item) return []
|
||
|
||
return (item.statDetails || []).flatMap(detail =>
|
||
(detail.segments || [])
|
||
.filter(segment => segment.status === 'MISSING')
|
||
.map(segment => ({
|
||
...segment,
|
||
statType: detail.statType
|
||
}))
|
||
)
|
||
}
|
||
|
||
export const hasChecksquareDetail = (item: SteadyDataView.SteadyChecksquareItem) => {
|
||
return (
|
||
Boolean(item.itemId) ||
|
||
Boolean(item.abnormalPointCount) ||
|
||
Boolean(item.harmonicParityAbnormalPointCount) ||
|
||
Boolean(item.missingPointCount) ||
|
||
(item.statDetails || []).some(detail => (detail.segments || []).length)
|
||
)
|
||
}
|
||
|
||
const hasChecksquareHarmonicOrderRange = (indicator: SteadyDataView.SteadyIndicatorNode) => {
|
||
const start = Number(indicator.harmonicOrderStart)
|
||
const end = Number(indicator.harmonicOrderEnd)
|
||
if (!Number.isFinite(start) || !Number.isFinite(end)) return false
|
||
|
||
const rangeStart = Math.min(start, end)
|
||
const rangeEnd = Math.max(start, end)
|
||
|
||
return rangeStart <= CHECKSQUARE_HARMONIC_ORDER_MAX && rangeEnd >= CHECKSQUARE_HARMONIC_ORDER_MIN
|
||
}
|
||
|
||
export const isChecksquareHarmonicIndicator = (indicator: SteadyDataView.SteadyIndicatorNode) => {
|
||
// 只有指标目录明确包含 2-50 次谐波范围时,才预建谐波子行并执行逐次合并。
|
||
return hasChecksquareHarmonicOrderRange(indicator)
|
||
}
|
||
|
||
export const buildPendingChecksquareItem = (
|
||
indicator: SteadyDataView.SteadyIndicatorNode
|
||
): SteadyDataView.SteadyChecksquareItem => {
|
||
const indicatorCode = indicator.indicatorCode || indicator.id || indicator.treeKey || indicator.name
|
||
|
||
return {
|
||
itemKey: `pending|${indicatorCode}`,
|
||
indicatorCode,
|
||
indicatorName: indicator.name || indicatorCode,
|
||
children: isChecksquareHarmonicIndicator(indicator) ? buildPendingChecksquareHarmonicItems(indicator) : undefined,
|
||
statSummaries: [],
|
||
statDetails: []
|
||
}
|
||
}
|
||
|
||
export const buildPendingChecksquareHarmonicItems = (
|
||
indicator: SteadyDataView.SteadyIndicatorNode
|
||
): SteadyDataView.SteadyChecksquareItem[] => {
|
||
const indicatorCode = indicator.indicatorCode || indicator.id || indicator.treeKey || indicator.name
|
||
|
||
return CHECKSQUARE_HARMONIC_ORDERS.map(harmonicOrder => ({
|
||
itemKey: `pending|${indicatorCode}|${harmonicOrder}`,
|
||
indicatorCode,
|
||
indicatorName: indicator.name || indicatorCode,
|
||
harmonicOrder,
|
||
statSummaries: [],
|
||
statDetails: []
|
||
}))
|
||
}
|
||
|
||
export const buildPendingChecksquareResult = (
|
||
indicators: SteadyDataView.SteadyIndicatorNode[],
|
||
formState: { timeRange: string[] }
|
||
): SteadyDataView.SteadyChecksquareQueryResult => {
|
||
return {
|
||
lineId: '',
|
||
timeStart: formState.timeRange[0] || '',
|
||
timeEnd: formState.timeRange[1] || '',
|
||
items: indicators.map(buildPendingChecksquareItem)
|
||
}
|
||
}
|
||
|
||
const isResolvedChecksquareItem = (item: SteadyDataView.SteadyChecksquareItem) => {
|
||
return item.hasData !== undefined || item.expectedPointCount !== undefined || item.actualPointCount !== undefined
|
||
}
|
||
|
||
const normalizeChecksquareResultItemKey = (
|
||
item: SteadyDataView.SteadyChecksquareItem,
|
||
itemKey: string
|
||
): SteadyDataView.SteadyChecksquareItem => ({
|
||
...item,
|
||
itemKey
|
||
})
|
||
|
||
const isValidChecksquareHarmonicOrder = (value: number) => {
|
||
return Number.isInteger(value) && CHECKSQUARE_HARMONIC_ORDERS.includes(value)
|
||
}
|
||
|
||
const parseChecksquareHarmonicOrder = (value?: string | number | null) => {
|
||
const order = Number(value)
|
||
|
||
return isValidChecksquareHarmonicOrder(order) ? order : null
|
||
}
|
||
|
||
const parseChecksquareHarmonicOrderFromText = (value?: string | null) => {
|
||
if (!value) return null
|
||
|
||
const orderText = value.match(/(?:^|[^\d])([2-9]|[1-4]\d|50)(?:次|[^\d]|$)/)?.[1]
|
||
|
||
return parseChecksquareHarmonicOrder(orderText)
|
||
}
|
||
|
||
const resolveChecksquareHarmonicOrder = (item: SteadyDataView.SteadyChecksquareItem) => {
|
||
return (
|
||
parseChecksquareHarmonicOrder(item.harmonicOrder) ||
|
||
parseChecksquareHarmonicOrderFromText(item.itemKey) ||
|
||
parseChecksquareHarmonicOrderFromText(item.indicatorName) ||
|
||
parseChecksquareHarmonicOrderFromText(item.indicatorCode)
|
||
)
|
||
}
|
||
|
||
const sumNumber = (
|
||
items: SteadyDataView.SteadyChecksquareItem[],
|
||
getter: (item: SteadyDataView.SteadyChecksquareItem) => unknown
|
||
) => {
|
||
return items.reduce((total, item) => {
|
||
const value = Number(getter(item))
|
||
|
||
return Number.isFinite(value) ? total + value : total
|
||
}, 0)
|
||
}
|
||
|
||
const summarizeStatType = (
|
||
items: SteadyDataView.SteadyChecksquareItem[],
|
||
statType: SteadyDataView.SteadyTrendStatType
|
||
): SteadyDataView.SteadyChecksquareStatSummary => {
|
||
const summaries = items
|
||
.map(item => findStatSummary(item, statType))
|
||
.filter((summary): summary is SteadyDataView.SteadyChecksquareStatSummary => Boolean(summary))
|
||
const supportedSummaries = summaries.filter(summary => summary.supported !== false)
|
||
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)
|
||
|
||
return {
|
||
statType,
|
||
supported: supportedSummaries.length > 0,
|
||
hasData: supportedSummaries.length > 0 && supportedSummaries.every(summary => summary.hasData === true),
|
||
expectedPointCount,
|
||
actualPointCount,
|
||
missingPointCount,
|
||
dataIntegrity: expectedPointCount ? actualPointCount / expectedPointCount : null,
|
||
dataIntegrityText: expectedPointCount ? undefined : '-'
|
||
}
|
||
}
|
||
|
||
export const buildHarmonicParentSummary = (
|
||
parentItem: SteadyDataView.SteadyChecksquareItem,
|
||
children: SteadyDataView.SteadyChecksquareItem[]
|
||
): SteadyDataView.SteadyChecksquareItem => {
|
||
if (!children.length || !children.every(item => isResolvedChecksquareItem(item))) {
|
||
return {
|
||
itemKey: parentItem.itemKey,
|
||
indicatorCode: parentItem.indicatorCode,
|
||
indicatorName: parentItem.indicatorName,
|
||
statSummaries: [],
|
||
statDetails: [],
|
||
children
|
||
}
|
||
}
|
||
|
||
const expectedPointCount = sumNumber(children, item => item.expectedPointCount)
|
||
const actualPointCount = sumNumber(children, item => item.actualPointCount)
|
||
const missingPointCount = sumNumber(children, item => item.missingPointCount)
|
||
const statSummaries = CHECKSQUARE_STAT_TYPES.map(statType => summarizeStatType(children, statType))
|
||
|
||
return {
|
||
...parentItem,
|
||
hasData: children.every(item => item.hasData === true),
|
||
expectedPointCount,
|
||
actualPointCount,
|
||
missingPointCount,
|
||
dataIntegrity: expectedPointCount ? actualPointCount / expectedPointCount : null,
|
||
dataIntegrityText: expectedPointCount ? undefined : '-',
|
||
statSummaries,
|
||
statDetails: [],
|
||
children
|
||
}
|
||
}
|
||
|
||
export const mergeChecksquareIndicatorResult = (
|
||
currentResult: SteadyDataView.SteadyChecksquareQueryResult | null,
|
||
indicator: SteadyDataView.SteadyIndicatorNode,
|
||
indicatorResult: SteadyDataView.SteadyChecksquareQueryResult
|
||
): SteadyDataView.SteadyChecksquareQueryResult => {
|
||
const pendingResult = currentResult || {
|
||
lineId: indicatorResult.lineId || '',
|
||
lineName: indicatorResult.lineName,
|
||
timeStart: indicatorResult.timeStart || '',
|
||
timeEnd: indicatorResult.timeEnd || '',
|
||
intervalMinutes: indicatorResult.intervalMinutes,
|
||
items: [buildPendingChecksquareItem(indicator)]
|
||
}
|
||
const indicatorCode = indicator.indicatorCode
|
||
const resultItems = indicatorResult.items || []
|
||
const shouldMergeHarmonicItems = isChecksquareHarmonicIndicator(indicator)
|
||
const normalItems = shouldMergeHarmonicItems
|
||
? resultItems.filter(item => !resolveChecksquareHarmonicOrder(item))
|
||
: resultItems
|
||
const harmonicItems = shouldMergeHarmonicItems
|
||
? resultItems.filter(item => resolveChecksquareHarmonicOrder(item))
|
||
: []
|
||
const currentItem = pendingResult.items.find(item => item.indicatorCode === indicatorCode)
|
||
const mergedItem = normalItems[0] || currentItem || buildPendingChecksquareItem(indicator)
|
||
const currentChildren = currentItem?.children || mergedItem.children || []
|
||
const mergedChildren = currentChildren.length
|
||
? currentChildren.map(child => {
|
||
const replacement = harmonicItems.find(item => resolveChecksquareHarmonicOrder(item) === child.harmonicOrder)
|
||
|
||
return replacement
|
||
? normalizeChecksquareResultItemKey(
|
||
{
|
||
...replacement,
|
||
harmonicOrder: child.harmonicOrder
|
||
},
|
||
child.itemKey
|
||
)
|
||
: child
|
||
})
|
||
: harmonicItems
|
||
const replacement = {
|
||
...mergedItem,
|
||
itemKey: currentItem?.itemKey || mergedItem.itemKey,
|
||
indicatorName: indicator.name || mergedItem.indicatorName || mergedItem.indicatorCode,
|
||
children: mergedChildren.length ? mergedChildren : undefined
|
||
} as SteadyDataView.SteadyChecksquareItem
|
||
const finalReplacement = replacement.children?.length ? buildHarmonicParentSummary(replacement, replacement.children) : replacement
|
||
|
||
return {
|
||
...pendingResult,
|
||
lineId: indicatorResult.lineId || pendingResult.lineId,
|
||
lineName: indicatorResult.lineName || pendingResult.lineName,
|
||
timeStart: indicatorResult.timeStart || pendingResult.timeStart,
|
||
timeEnd: indicatorResult.timeEnd || pendingResult.timeEnd,
|
||
intervalMinutes: indicatorResult.intervalMinutes ?? pendingResult.intervalMinutes,
|
||
items: pendingResult.items.map(item => (item.indicatorCode === indicatorCode ? finalReplacement : item))
|
||
}
|
||
}
|