feat(steady): 实现稳态校验任务功能重构
- 添加influxdb配置支持和资源文件打包 - 实现校验任务表格组件和相关工具函数 - 重构校验工作台为任务创建对话框模式 - 实现校验详情面板支持多种异常类型展示 - 更新校验概览表格显示任务基本信息 - 优化校验查询参数和API接口定义 - 实现搜索表单组件化和过滤功能增强
This commit is contained in:
@@ -0,0 +1,312 @@
|
||||
<template>
|
||||
<ProTable
|
||||
ref="proTable"
|
||||
row-key="taskId"
|
||||
:columns="columns"
|
||||
:request-api="getTableList"
|
||||
:search-col="{ xs: 1, sm: 2, md: 2, lg: 4, xl: 4 }"
|
||||
>
|
||||
<template #tableHeader>
|
||||
<el-button type="primary" :icon="Plus" @click="emit('createTask')">新增校验任务</el-button>
|
||||
</template>
|
||||
|
||||
<template #operation="{ row }">
|
||||
<el-button type="primary" link :icon="View" @click="emit('detail', row)">详情</el-button>
|
||||
</template>
|
||||
</ProTable>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, h, reactive, ref } from 'vue'
|
||||
import { ElDatePicker, ElTag, ElTreeSelect } from 'element-plus'
|
||||
import { Plus, View } 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'
|
||||
import {
|
||||
buildChecksquareTaskQueryParams,
|
||||
formatChecksquarePercent,
|
||||
formatChecksquareTaskStatus,
|
||||
resolveChecksquareTaskStatusType,
|
||||
resolveChecksquareText,
|
||||
type ChecksquareTaskSearchParams
|
||||
} from '../utils/checksquareTaskTable'
|
||||
|
||||
defineOptions({
|
||||
name: 'ChecksquareTaskTable'
|
||||
})
|
||||
|
||||
const props = defineProps<{
|
||||
ledgerTree: SteadyDataView.SteadyLedgerNode[]
|
||||
indicatorTree: SteadyDataView.SteadyIndicatorNode[]
|
||||
requestApi: (params: SteadyDataView.SteadyChecksquareTaskQueryParams) => Promise<any>
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
createTask: []
|
||||
detail: [row: SteadyDataView.SteadyChecksquareTask]
|
||||
}>()
|
||||
|
||||
const proTable = ref<ProTableInstance>()
|
||||
|
||||
interface ChecksquareFilterTreeNode {
|
||||
label: string
|
||||
value: string
|
||||
disabled?: boolean
|
||||
children?: ChecksquareFilterTreeNode[]
|
||||
}
|
||||
|
||||
const normalizeLineFilterTree = (nodes: SteadyDataView.SteadyLedgerNode[]): ChecksquareFilterTreeNode[] => {
|
||||
return nodes.map(node => ({
|
||||
label: node.name,
|
||||
value: node.id,
|
||||
disabled: node.level !== 3 || node.selectable === false,
|
||||
children: node.children?.length ? normalizeLineFilterTree(node.children) : undefined
|
||||
}))
|
||||
}
|
||||
|
||||
const normalizeIndicatorFilterTree = (
|
||||
nodes: SteadyDataView.SteadyIndicatorNode[],
|
||||
parentKey = ''
|
||||
): ChecksquareFilterTreeNode[] => {
|
||||
return nodes.map((node, index) => {
|
||||
const isLeaf = !node.children?.length
|
||||
const value =
|
||||
isLeaf && node.indicatorCode
|
||||
? node.indicatorCode
|
||||
: node.id || `${parentKey}${node.groupCode || node.name || 'node'}-${index}`
|
||||
|
||||
return {
|
||||
label: node.unit ? `${node.name}(${node.unit})` : node.name,
|
||||
value,
|
||||
disabled: !isLeaf || !node.indicatorCode,
|
||||
children: node.children?.length ? normalizeIndicatorFilterTree(node.children, `${value}-`) : undefined
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const lineFilterTree = computed(() => normalizeLineFilterTree(props.ledgerTree))
|
||||
const indicatorFilterTree = computed(() => normalizeIndicatorFilterTree(props.indicatorTree))
|
||||
|
||||
const splitTreeSelectValues = (value?: string) => {
|
||||
return (value || '')
|
||||
.split(',')
|
||||
.map(item => item.trim())
|
||||
.filter(Boolean)
|
||||
}
|
||||
|
||||
const normalizeTreeSelectValues = (value: unknown) => {
|
||||
const rawValues = Array.isArray(value) ? value : value === undefined || value === null || value === '' ? [] : [value]
|
||||
|
||||
return Array.from(
|
||||
new Set(
|
||||
rawValues
|
||||
.filter((item): item is string | number => typeof item === 'string' || typeof item === 'number')
|
||||
.map(item => String(item).trim())
|
||||
.filter(Boolean)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
const renderTimeRangeSearch = ({ searchParam }: { searchParam: ChecksquareTaskSearchParams }) =>
|
||||
h(ElDatePicker, {
|
||||
modelValue: searchParam.taskTimeRange,
|
||||
type: 'datetimerange',
|
||||
valueFormat: 'YYYY-MM-DD HH:mm:ss',
|
||||
startPlaceholder: '开始时间',
|
||||
endPlaceholder: '结束时间',
|
||||
clearable: true,
|
||||
'onUpdate:modelValue': (value: string[] | null) => {
|
||||
searchParam.taskTimeRange = value || undefined
|
||||
}
|
||||
})
|
||||
|
||||
const renderLineSearch = ({ searchParam }: { searchParam: ChecksquareTaskSearchParams }) =>
|
||||
h(ElTreeSelect, {
|
||||
class: 'checksquare-search-tree-select',
|
||||
style: { width: '100%' },
|
||||
modelValue: splitTreeSelectValues(searchParam.lineId),
|
||||
data: lineFilterTree.value,
|
||||
nodeKey: 'value',
|
||||
multiple: true,
|
||||
showCheckbox: true,
|
||||
collapseTags: true,
|
||||
collapseTagsTooltip: true,
|
||||
maxCollapseTags: 1,
|
||||
filterable: true,
|
||||
clearable: true,
|
||||
defaultExpandAll: true,
|
||||
checkStrictly: true,
|
||||
props: { label: 'label', children: 'children', disabled: 'disabled' },
|
||||
placeholder: '请选择监测点',
|
||||
'onUpdate:modelValue': (value: unknown) => {
|
||||
const selectedValues = normalizeTreeSelectValues(value)
|
||||
searchParam.lineId = selectedValues.length ? selectedValues.join(',') : undefined
|
||||
}
|
||||
})
|
||||
|
||||
const renderIndicatorSearch = ({ searchParam }: { searchParam: ChecksquareTaskSearchParams }) =>
|
||||
h(ElTreeSelect, {
|
||||
class: 'checksquare-search-tree-select',
|
||||
style: { width: '100%' },
|
||||
modelValue: splitTreeSelectValues(searchParam.indicatorCode),
|
||||
data: indicatorFilterTree.value,
|
||||
nodeKey: 'value',
|
||||
multiple: true,
|
||||
showCheckbox: true,
|
||||
collapseTags: true,
|
||||
collapseTagsTooltip: true,
|
||||
maxCollapseTags: 1,
|
||||
filterable: true,
|
||||
clearable: true,
|
||||
defaultExpandAll: true,
|
||||
checkStrictly: true,
|
||||
props: { label: 'label', children: 'children', disabled: 'disabled' },
|
||||
placeholder: '请选择稳态指标',
|
||||
'onUpdate:modelValue': (value: unknown) => {
|
||||
const selectedValues = normalizeTreeSelectValues(value)
|
||||
searchParam.indicatorCode = selectedValues.length ? selectedValues.join(',') : undefined
|
||||
}
|
||||
})
|
||||
|
||||
const columns = reactive<ColumnProps<SteadyDataView.SteadyChecksquareTask>[]>([
|
||||
{ type: 'index', fixed: 'left', width: 70, label: '序号' },
|
||||
{
|
||||
prop: 'taskNo',
|
||||
label: '任务编号',
|
||||
minWidth: 180,
|
||||
render: ({ row }) => resolveChecksquareText(row.taskNo)
|
||||
},
|
||||
{
|
||||
prop: 'lineId',
|
||||
label: '监测点ID',
|
||||
isShow: false,
|
||||
isSetting: false,
|
||||
search: {
|
||||
label: '监测点',
|
||||
order: 2,
|
||||
render: renderLineSearch
|
||||
}
|
||||
},
|
||||
{
|
||||
prop: 'lineName',
|
||||
label: '监测点名称',
|
||||
minWidth: 160,
|
||||
render: ({ row }) => resolveChecksquareText(row.lineName || row.lineId)
|
||||
},
|
||||
{
|
||||
prop: 'indicatorCode',
|
||||
label: '稳态指标',
|
||||
isShow: false,
|
||||
isSetting: false,
|
||||
search: {
|
||||
label: '稳态指标',
|
||||
order: 3,
|
||||
render: renderIndicatorSearch
|
||||
}
|
||||
},
|
||||
{
|
||||
prop: 'timeStart',
|
||||
label: '开始时间',
|
||||
minWidth: 170,
|
||||
render: ({ row }) => resolveChecksquareText(row.timeStart),
|
||||
search: {
|
||||
label: '检测时间',
|
||||
key: 'taskTimeRange',
|
||||
order: 1,
|
||||
render: renderTimeRangeSearch
|
||||
}
|
||||
},
|
||||
{
|
||||
prop: 'timeEnd',
|
||||
label: '结束时间',
|
||||
minWidth: 170,
|
||||
render: ({ row }) => resolveChecksquareText(row.timeEnd)
|
||||
},
|
||||
{
|
||||
prop: 'taskStatus',
|
||||
label: '任务状态',
|
||||
minWidth: 110,
|
||||
render: ({ row }) =>
|
||||
h(
|
||||
ElTag,
|
||||
{ type: resolveChecksquareTaskStatusType(row.taskStatus), effect: 'plain' },
|
||||
() => formatChecksquareTaskStatus(row.taskStatus)
|
||||
)
|
||||
},
|
||||
{
|
||||
prop: 'itemCount',
|
||||
label: '检测项数',
|
||||
minWidth: 100,
|
||||
align: 'center',
|
||||
render: ({ row }) => resolveChecksquareText(row.itemCount)
|
||||
},
|
||||
{
|
||||
prop: 'abnormalItemCount',
|
||||
label: '异常项数',
|
||||
minWidth: 100,
|
||||
align: 'center',
|
||||
render: ({ row }) => resolveChecksquareText(row.abnormalItemCount),
|
||||
search: {
|
||||
label: '异常状态',
|
||||
key: 'hasAbnormal',
|
||||
order: 4,
|
||||
el: 'select'
|
||||
},
|
||||
enum: [
|
||||
{ label: '存在异常', value: true },
|
||||
{ label: '全部', value: false }
|
||||
],
|
||||
isFilterEnum: false
|
||||
},
|
||||
{
|
||||
prop: 'maxMissingRate',
|
||||
label: '最大缺失率',
|
||||
minWidth: 120,
|
||||
align: 'center',
|
||||
render: ({ row }) => formatChecksquarePercent(row.maxMissingRate)
|
||||
},
|
||||
{
|
||||
prop: 'createTime',
|
||||
label: '创建时间',
|
||||
minWidth: 170,
|
||||
render: ({ row }) => resolveChecksquareText(row.createTime)
|
||||
},
|
||||
{ prop: 'operation', label: '操作', fixed: 'right', width: 96 }
|
||||
])
|
||||
|
||||
const getTableList = (params: ChecksquareTaskSearchParams) => {
|
||||
return props.requestApi(buildChecksquareTaskQueryParams(params))
|
||||
}
|
||||
|
||||
const refresh = () => {
|
||||
proTable.value?.getTableList()
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
refresh
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.checksquare-search-tree-select {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
:deep(.checksquare-search-tree-select .el-select__wrapper) {
|
||||
min-height: 32px;
|
||||
}
|
||||
|
||||
:deep(.checksquare-search-tree-select .el-select__selection) {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
:deep(.checksquare-search-tree-select .el-select__tags-text) {
|
||||
display: inline-block;
|
||||
max-width: 120px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
vertical-align: bottom;
|
||||
white-space: nowrap;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user