refactor(event): 重构事件列表和稳态数据视图组件结构

- 将事件列表页面逻辑拆分为 EventListTable 组件
- 新增 MeasurementPointDialog 和 VoltageToleranceDialog 弹窗组件
- 重构稳态数据视图为主工作台组件 SteadyTrendWorkbench
- 移除不再使用的相别参数和相关逻辑
- 更新事件详情工具函数和接口参数映射
- 优化波形查看功能的数据传递方式
- 修正事件描述字段命名和严重程度解析逻辑
This commit is contained in:
2026-05-18 08:46:42 +08:00
parent 609fdd5379
commit f9ed6c6245
39 changed files with 1943 additions and 755 deletions

View File

@@ -0,0 +1,115 @@
<template>
<aside class="indicator-floating-panel" :class="{ 'is-collapsed': collapsed }">
<el-button
class="indicator-toggle"
:icon="collapsed ? ArrowLeft : ArrowRight"
circle
@click="emit('update:collapsed', !collapsed)"
/>
<button
v-if="collapsed"
class="indicator-collapsed-trigger"
type="button"
@click="emit('update:collapsed', false)"
>
{{ collapsedLabel }}
</button>
<div v-show="!collapsed" class="indicator-panel-body">
<SteadyIndicatorTree
:key="selectorResetKey"
:tree-data="treeData"
:loading="loading"
@refresh="emit('refresh')"
@change="emit('change', $event)"
/>
</div>
</aside>
</template>
<script setup lang="ts">
import { ArrowLeft, ArrowRight } from '@element-plus/icons-vue'
import type { SteadyDataView } from '@/api/steady/steadyDataView/interface'
import SteadyIndicatorTree from './SteadyIndicatorTree.vue'
defineOptions({
name: 'SteadyIndicatorFloatingPanel'
})
defineProps<{
collapsed: boolean
treeData: SteadyDataView.SteadyIndicatorNode[]
loading: boolean
selectorResetKey: number
}>()
const emit = defineEmits<{
'update:collapsed': [value: boolean]
refresh: []
change: [nodes: SteadyDataView.SteadyIndicatorNode[]]
}>()
const collapsedLabel = '\u7a33\u6001\u6307\u6807'
</script>
<style scoped lang="scss">
.indicator-floating-panel {
position: absolute;
top: 12px;
right: 12px;
bottom: 12px;
z-index: 2;
width: 360px;
transition: width 0.2s ease;
}
.indicator-floating-panel.is-collapsed {
width: 44px;
}
.indicator-toggle {
position: absolute;
top: 12px;
left: -18px;
z-index: 3;
}
.indicator-panel-body {
width: 100%;
height: 100%;
}
.indicator-panel-body :deep(.steady-tree-card) {
height: 100%;
box-shadow: var(--el-box-shadow-light);
}
.indicator-collapsed-trigger {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
padding: 8px 0;
border: 1px solid var(--el-border-color-light);
border-radius: 4px;
background: var(--el-bg-color);
box-shadow: var(--el-box-shadow-light);
color: var(--el-text-color-primary);
cursor: pointer;
font-size: 13px;
font-weight: 600;
line-height: 1.2;
writing-mode: vertical-rl;
}
.indicator-collapsed-trigger:hover {
border-color: var(--el-color-primary-light-5);
color: var(--el-color-primary);
}
@media (max-width: 1360px) {
.indicator-floating-panel {
width: 320px;
}
}
</style>

View File

@@ -67,7 +67,7 @@ const normalizedTreeData = computed(() => {
})
const handleCheck = () => {
const checkedNodes = (treeRef.value?.getCheckedNodes(false, true) || []) as SteadyDataView.SteadyIndicatorNode[]
const checkedNodes = (treeRef.value?.getCheckedNodes(false, false) || []) as SteadyDataView.SteadyIndicatorNode[]
emit('change', collectLeafIndicators(checkedNodes))
}
</script>

View File

@@ -68,7 +68,7 @@ const handleKeywordChange = (value: string) => {
}
const handleCheck = () => {
emit('change', (treeRef.value?.getCheckedNodes(false, true) || []) as SteadyDataView.SteadyLedgerNode[])
emit('change', (treeRef.value?.getCheckedNodes(false, false) || []) as SteadyDataView.SteadyLedgerNode[])
}
</script>

View File

@@ -1,65 +1,67 @@
<template>
<section class="card trend-toolbar">
<TimePeriodSearch
class="trend-toolbar__time"
:unit="modelValue.timeUnit"
:model-value="modelValue.timeBaseDate"
@update:unit="handleTimeUnitChange"
@update:model-value="handleTimeBaseDateChange"
/>
<div class="toolbar-field toolbar-field--time">
<span class="toolbar-field__label">时间</span>
<TimePeriodSearch
class="trend-toolbar__time"
:unit="modelValue.timeUnit"
:model-value="modelValue.timeBaseDate"
@update:unit="handleTimeUnitChange"
@update:model-value="handleTimeBaseDateChange"
/>
</div>
<el-select
:model-value="modelValue.phases"
multiple
collapse-tags
collapse-tags-tooltip
placeholder="选择相别"
@update:model-value="updateField('phases', $event)"
>
<el-option v-for="item in phaseOptions" :key="item" :label="resolvePhaseLabel(item)" :value="item" />
</el-select>
<div class="toolbar-field">
<span class="toolbar-field__label">统计</span>
<el-select
:model-value="modelValue.statTypes"
multiple
collapse-tags
collapse-tags-tooltip
placeholder="选择统计类型"
@update:model-value="updateField('statTypes', $event)"
>
<el-option v-for="item in statOptions" :key="item" :label="statLabelMap[item]" :value="item" />
</el-select>
</div>
<el-select
:model-value="modelValue.statTypes"
multiple
collapse-tags
collapse-tags-tooltip
placeholder="选择统计类型"
@update:model-value="updateField('statTypes', $event)"
>
<el-option v-for="item in statOptions" :key="item" :label="statLabelMap[item]" :value="item" />
</el-select>
<div class="toolbar-field">
<span class="toolbar-field__label">粒度</span>
<el-select
:model-value="modelValue.bucket"
placeholder="选择时间粒度"
@update:model-value="updateField('bucket', $event)"
>
<el-option v-for="item in bucketOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</div>
<el-select
:model-value="modelValue.bucket"
placeholder="选择时间粒度"
@update:model-value="updateField('bucket', $event)"
>
<el-option v-for="item in bucketOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
<div class="toolbar-field">
<span class="toolbar-field__label">数据</span>
<el-select
:model-value="modelValue.qualityFlag"
clearable
placeholder="选择数据质量"
@update:model-value="updateField('qualityFlag', $event)"
>
<el-option label="仅有效数据" :value="1" />
<el-option label="仅无效数据" :value="0" />
</el-select>
</div>
<el-select
:model-value="modelValue.qualityFlag"
clearable
placeholder="选择数据质量"
@update:model-value="updateField('qualityFlag', $event)"
>
<el-option label="仅有效数据" :value="1" />
<el-option label="仅无效数据" :value="0" />
</el-select>
<el-select
v-if="showHarmonicOrders"
:model-value="modelValue.harmonicOrders"
class="harmonic-select"
multiple
collapse-tags
collapse-tags-tooltip
placeholder="选择谐波次数"
@update:model-value="updateField('harmonicOrders', $event)"
>
<el-option v-for="item in harmonicOrderOptions" :key="item" :label="`${item}次`" :value="item" />
</el-select>
<div v-if="showHarmonicOrders" class="toolbar-field harmonic-select">
<span class="toolbar-field__label">谐波次数</span>
<el-select
:model-value="modelValue.harmonicOrders"
multiple
collapse-tags
collapse-tags-tooltip
placeholder="选择谐波次数"
@update:model-value="updateField('harmonicOrders', $event)"
>
<el-option v-for="item in harmonicOrderOptions" :key="item" :label="`${item}次`" :value="item" />
</el-select>
</div>
<div class="toolbar-actions">
<el-button type="primary" :loading="loading" @click="emit('query')">查询</el-button>
@@ -80,7 +82,6 @@ defineOptions({
const props = defineProps<{
modelValue: SteadyTrendFormState
phaseOptions: string[]
statOptions: SteadyDataView.SteadyTrendStatType[]
showHarmonicOrders: boolean
loading: boolean
@@ -106,14 +107,6 @@ const statLabelMap: Record<SteadyDataView.SteadyTrendStatType, string> = {
MIN: '最小值',
CP95: '95%概率大值'
}
const phaseLabelMap: Record<string, string> = {
A: 'A相',
B: 'B相',
C: 'C相',
T: '总相'
}
const resolvePhaseLabel = (phase: string) => phaseLabelMap[phase] || `${phase}`
const updateField = <K extends keyof SteadyTrendFormState>(field: K, value: SteadyTrendFormState[K]) => {
emit('update:modelValue', {
@@ -143,13 +136,37 @@ const handleTimeBaseDateChange = (value: Date) => {
<style scoped lang="scss">
.trend-toolbar {
display: grid;
grid-template-columns: minmax(260px, 1.3fr) repeat(4, minmax(132px, 0.7fr)) auto;
grid-template-columns: minmax(312px, 1.4fr) repeat(3, minmax(178px, 0.8fr)) auto;
gap: 10px;
align-items: center;
padding: 12px;
}
.toolbar-field {
display: flex;
min-width: 0;
align-items: center;
gap: 6px;
}
.toolbar-field--time {
min-width: 312px;
}
.toolbar-field__label {
flex: 0 0 auto;
color: #606266;
font-size: 14px;
white-space: nowrap;
}
.toolbar-field :deep(.el-select) {
flex: 1 1 0;
min-width: 0;
}
.trend-toolbar__time {
flex: 1 1 0;
min-width: 260px;
}

View File

@@ -0,0 +1,131 @@
<template>
<div class="steady-trend-layout">
<aside class="selector-column">
<SteadyLedgerTree
:key="selectorResetKey"
:tree-data="ledgerTree"
:loading="loading.ledger"
:keyword="ledgerKeyword"
@refresh="emit('refreshLedger')"
@search="emit('ledgerSearch', $event)"
@change="emit('ledgerChange', $event)"
/>
</aside>
<main class="trend-main">
<SteadyTrendToolbar
v-model="trendFormProxy"
:stat-options="statOptions"
:show-harmonic-orders="showHarmonicOrders"
:loading="loading.trend"
@query="emit('queryTrend')"
@reset="emit('resetTrend')"
/>
<div class="trend-content">
<SteadyTrendChartPanel :trend-result="trendResult" :loading="loading.trend" />
<SteadyIndicatorFloatingPanel
v-model:collapsed="indicatorPanelCollapsedProxy"
:selector-reset-key="selectorResetKey"
:tree-data="indicatorTree"
:loading="loading.indicator"
@refresh="emit('refreshIndicator')"
@change="emit('indicatorChange', $event)"
/>
</div>
</main>
</div>
</template>
<script setup lang="ts">
import type { SteadyDataView } from '@/api/steady/steadyDataView/interface'
import type { SteadyTrendFormState } from '../utils/trendPayload'
import SteadyIndicatorFloatingPanel from './SteadyIndicatorFloatingPanel.vue'
import SteadyLedgerTree from './SteadyLedgerTree.vue'
import SteadyTrendChartPanel from './SteadyTrendChartPanel.vue'
import SteadyTrendToolbar from './SteadyTrendToolbar.vue'
defineOptions({
name: 'SteadyTrendWorkbench'
})
const props = defineProps<{
ledgerTree: SteadyDataView.SteadyLedgerNode[]
indicatorTree: SteadyDataView.SteadyIndicatorNode[]
trendResult: SteadyDataView.SteadyTrendQueryResult | null
trendForm: SteadyTrendFormState
statOptions: SteadyDataView.SteadyTrendStatType[]
showHarmonicOrders: boolean
loading: {
ledger: boolean
indicator: boolean
trend: boolean
}
ledgerKeyword: string
indicatorPanelCollapsed: boolean
selectorResetKey: number
}>()
const emit = defineEmits<{
'update:trendForm': [value: SteadyTrendFormState]
'update:indicatorPanelCollapsed': [value: boolean]
refreshLedger: []
ledgerSearch: [value: string]
ledgerChange: [nodes: SteadyDataView.SteadyLedgerNode[]]
refreshIndicator: []
indicatorChange: [nodes: SteadyDataView.SteadyIndicatorNode[]]
queryTrend: []
resetTrend: []
}>()
const trendFormProxy = computed({
get: () => props.trendForm,
set: value => emit('update:trendForm', value)
})
const indicatorPanelCollapsedProxy = computed({
get: () => props.indicatorPanelCollapsed,
set: value => emit('update:indicatorPanelCollapsed', value)
})
</script>
<style scoped lang="scss">
.steady-trend-layout {
display: grid;
grid-template-columns: 320px minmax(0, 1fr);
gap: 12px;
width: 100%;
height: 100%;
min-height: 0;
}
.selector-column {
display: grid;
min-height: 0;
}
.trend-main {
display: grid;
grid-template-rows: auto minmax(0, 1fr);
gap: 12px;
min-width: 0;
min-height: 0;
}
.trend-content {
position: relative;
min-width: 0;
min-height: 0;
}
.trend-content :deep(.trend-chart-panel) {
height: 100%;
}
@media (max-width: 1360px) {
.steady-trend-layout {
grid-template-columns: 280px minmax(0, 1fr);
}
}
</style>