feat(steady): 实现台账指标树默认选中及图标展示功能
- 添加findFirstSelectableLedgerNode和findFirstLeafIndicator工具函数 - 实现台账树首次加载后默认选中第一个可查询监测点 - 实现指标树首次加载后默认选中第一个叶子指标 - 添加台账层级图标展示及样式配置 - 集成defaultCheckedKeys属性到台账和指标树组件 - 更新趋势查询参数移除bucket字段 - 修复数据质量标识默认值设置问题
This commit is contained in:
@@ -42,7 +42,6 @@ export namespace SteadyDataView {
|
|||||||
statTypes: SteadyTrendStatType[]
|
statTypes: SteadyTrendStatType[]
|
||||||
timeStart: string
|
timeStart: string
|
||||||
timeEnd: string
|
timeEnd: string
|
||||||
bucket?: string
|
|
||||||
qualityFlag?: number
|
qualityFlag?: number
|
||||||
harmonicOrders?: number[]
|
harmonicOrders?: number[]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
:key="selectorResetKey"
|
:key="selectorResetKey"
|
||||||
:tree-data="treeData"
|
:tree-data="treeData"
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
|
:default-checked-keys="defaultCheckedKeys"
|
||||||
@refresh="emit('refresh')"
|
@refresh="emit('refresh')"
|
||||||
@change="emit('change', $event)"
|
@change="emit('change', $event)"
|
||||||
/>
|
/>
|
||||||
@@ -39,6 +40,7 @@ defineProps<{
|
|||||||
collapsed: boolean
|
collapsed: boolean
|
||||||
treeData: SteadyDataView.SteadyIndicatorNode[]
|
treeData: SteadyDataView.SteadyIndicatorNode[]
|
||||||
loading: boolean
|
loading: boolean
|
||||||
|
defaultCheckedKeys: string[]
|
||||||
selectorResetKey: number
|
selectorResetKey: number
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
node-key="treeKey"
|
node-key="treeKey"
|
||||||
show-checkbox
|
show-checkbox
|
||||||
default-expand-all
|
default-expand-all
|
||||||
|
:default-checked-keys="defaultCheckedKeys"
|
||||||
:expand-on-click-node="false"
|
:expand-on-click-node="false"
|
||||||
:props="{ label: 'name', children: 'children' }"
|
:props="{ label: 'name', children: 'children' }"
|
||||||
@check="handleCheck"
|
@check="handleCheck"
|
||||||
@@ -29,7 +30,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, ref } from 'vue'
|
import { computed, nextTick, ref, watch } from 'vue'
|
||||||
import { Refresh } from '@element-plus/icons-vue'
|
import { Refresh } from '@element-plus/icons-vue'
|
||||||
import type { TreeInstance } from 'element-plus'
|
import type { TreeInstance } from 'element-plus'
|
||||||
import type { SteadyDataView } from '@/api/steady/steadyDataView/interface'
|
import type { SteadyDataView } from '@/api/steady/steadyDataView/interface'
|
||||||
@@ -42,6 +43,7 @@ defineOptions({
|
|||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
treeData: SteadyDataView.SteadyIndicatorNode[]
|
treeData: SteadyDataView.SteadyIndicatorNode[]
|
||||||
loading: boolean
|
loading: boolean
|
||||||
|
defaultCheckedKeys: string[]
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
@@ -70,6 +72,19 @@ const handleCheck = () => {
|
|||||||
const checkedNodes = (treeRef.value?.getCheckedNodes(false, false) || []) as SteadyDataView.SteadyIndicatorNode[]
|
const checkedNodes = (treeRef.value?.getCheckedNodes(false, false) || []) as SteadyDataView.SteadyIndicatorNode[]
|
||||||
emit('change', collectLeafIndicators(checkedNodes))
|
emit('change', collectLeafIndicators(checkedNodes))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const applyDefaultCheckedKeys = async () => {
|
||||||
|
await nextTick()
|
||||||
|
treeRef.value?.setCheckedKeys(props.defaultCheckedKeys, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => [normalizedTreeData.value, props.defaultCheckedKeys],
|
||||||
|
() => {
|
||||||
|
applyDefaultCheckedKeys()
|
||||||
|
},
|
||||||
|
{ immediate: true }
|
||||||
|
)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|||||||
@@ -20,13 +20,19 @@
|
|||||||
node-key="id"
|
node-key="id"
|
||||||
show-checkbox
|
show-checkbox
|
||||||
default-expand-all
|
default-expand-all
|
||||||
|
:default-checked-keys="defaultCheckedKeys"
|
||||||
:expand-on-click-node="false"
|
:expand-on-click-node="false"
|
||||||
:props="{ label: 'name', children: 'children' }"
|
:props="{ label: 'name', children: 'children' }"
|
||||||
@check="handleCheck"
|
@check="handleCheck"
|
||||||
>
|
>
|
||||||
<template #default="{ data }">
|
<template #default="{ data }">
|
||||||
<div class="tree-node">
|
<div class="tree-node">
|
||||||
<span class="node-name">{{ data.name }}</span>
|
<span class="node-main">
|
||||||
|
<el-icon :class="['node-icon', `is-level-${normalizeLedgerLevel(data.level)}`]">
|
||||||
|
<component :is="resolveLedgerIcon(data.level)" />
|
||||||
|
</el-icon>
|
||||||
|
<span class="node-name">{{ data.name }}</span>
|
||||||
|
</span>
|
||||||
<span class="node-count">
|
<span class="node-count">
|
||||||
<template v-if="Number(data.deviceCount) || Number(data.lineCount)">
|
<template v-if="Number(data.deviceCount) || Number(data.lineCount)">
|
||||||
{{ Number(data.deviceCount || 0) }} / {{ Number(data.lineCount || 0) }}
|
{{ Number(data.deviceCount || 0) }} / {{ Number(data.lineCount || 0) }}
|
||||||
@@ -40,8 +46,9 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref } from 'vue'
|
import { nextTick, ref, watch } from 'vue'
|
||||||
import { Refresh } from '@element-plus/icons-vue'
|
import type { Component } from 'vue'
|
||||||
|
import { Folder, Location, Monitor, OfficeBuilding, Refresh } from '@element-plus/icons-vue'
|
||||||
import type { TreeInstance } from 'element-plus'
|
import type { TreeInstance } from 'element-plus'
|
||||||
import type { SteadyDataView } from '@/api/steady/steadyDataView/interface'
|
import type { SteadyDataView } from '@/api/steady/steadyDataView/interface'
|
||||||
|
|
||||||
@@ -49,10 +56,11 @@ defineOptions({
|
|||||||
name: 'SteadyLedgerTree'
|
name: 'SteadyLedgerTree'
|
||||||
})
|
})
|
||||||
|
|
||||||
defineProps<{
|
const props = defineProps<{
|
||||||
treeData: SteadyDataView.SteadyLedgerNode[]
|
treeData: SteadyDataView.SteadyLedgerNode[]
|
||||||
loading: boolean
|
loading: boolean
|
||||||
keyword: string
|
keyword: string
|
||||||
|
defaultCheckedKeys: string[]
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
@@ -62,6 +70,26 @@ const emit = defineEmits<{
|
|||||||
}>()
|
}>()
|
||||||
|
|
||||||
const treeRef = ref<TreeInstance>()
|
const treeRef = ref<TreeInstance>()
|
||||||
|
type LedgerLevel = SteadyDataView.SteadyLedgerNode['level']
|
||||||
|
const ledgerIcons: Record<LedgerLevel, Component> = {
|
||||||
|
0: OfficeBuilding,
|
||||||
|
1: Folder,
|
||||||
|
2: Monitor,
|
||||||
|
3: Location
|
||||||
|
}
|
||||||
|
|
||||||
|
const normalizeLedgerLevel = (value: unknown): LedgerLevel => {
|
||||||
|
const level = Number(value)
|
||||||
|
if (level === 0 || level === 1 || level === 2 || level === 3) {
|
||||||
|
return level
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
const resolveLedgerIcon = (value: unknown) => {
|
||||||
|
return ledgerIcons[normalizeLedgerLevel(value)]
|
||||||
|
}
|
||||||
|
|
||||||
const handleKeywordChange = (value: string) => {
|
const handleKeywordChange = (value: string) => {
|
||||||
emit('search', value)
|
emit('search', value)
|
||||||
@@ -70,6 +98,19 @@ const handleKeywordChange = (value: string) => {
|
|||||||
const handleCheck = () => {
|
const handleCheck = () => {
|
||||||
emit('change', (treeRef.value?.getCheckedNodes(false, false) || []) as SteadyDataView.SteadyLedgerNode[])
|
emit('change', (treeRef.value?.getCheckedNodes(false, false) || []) as SteadyDataView.SteadyLedgerNode[])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const applyDefaultCheckedKeys = async () => {
|
||||||
|
await nextTick()
|
||||||
|
treeRef.value?.setCheckedKeys(props.defaultCheckedKeys, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => [props.treeData, props.defaultCheckedKeys],
|
||||||
|
() => {
|
||||||
|
applyDefaultCheckedKeys()
|
||||||
|
},
|
||||||
|
{ immediate: true }
|
||||||
|
)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@@ -105,6 +146,35 @@ const handleCheck = () => {
|
|||||||
min-width: 0;
|
min-width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.node-main {
|
||||||
|
display: inline-flex;
|
||||||
|
min-width: 0;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.node-icon {
|
||||||
|
flex: none;
|
||||||
|
font-size: 15px;
|
||||||
|
color: var(--el-text-color-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.node-icon.is-level-0 {
|
||||||
|
color: var(--el-color-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.node-icon.is-level-1 {
|
||||||
|
color: var(--el-color-success);
|
||||||
|
}
|
||||||
|
|
||||||
|
.node-icon.is-level-2 {
|
||||||
|
color: var(--el-color-warning);
|
||||||
|
}
|
||||||
|
|
||||||
|
.node-icon.is-level-3 {
|
||||||
|
color: var(--el-color-info);
|
||||||
|
}
|
||||||
|
|
||||||
.node-name {
|
.node-name {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
|
|||||||
@@ -25,17 +25,6 @@
|
|||||||
</el-select>
|
</el-select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<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>
|
|
||||||
|
|
||||||
<div class="toolbar-field">
|
<div class="toolbar-field">
|
||||||
<span class="toolbar-field__label">数据:</span>
|
<span class="toolbar-field__label">数据:</span>
|
||||||
<el-select
|
<el-select
|
||||||
@@ -44,8 +33,8 @@
|
|||||||
placeholder="选择数据质量"
|
placeholder="选择数据质量"
|
||||||
@update:model-value="updateField('qualityFlag', $event)"
|
@update:model-value="updateField('qualityFlag', $event)"
|
||||||
>
|
>
|
||||||
<el-option label="仅有效数据" :value="1" />
|
<el-option label="仅有效数据" :value="0" />
|
||||||
<el-option label="仅无效数据" :value="0" />
|
<el-option label="仅无效数据" :value="1" />
|
||||||
</el-select>
|
</el-select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -93,13 +82,6 @@ const emit = defineEmits<{
|
|||||||
reset: []
|
reset: []
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const bucketOptions = [
|
|
||||||
{ label: '1分钟', value: '1m' },
|
|
||||||
{ label: '5分钟', value: '5m' },
|
|
||||||
{ label: '10分钟', value: '10m' },
|
|
||||||
{ label: '30分钟', value: '30m' },
|
|
||||||
{ label: '1小时', value: '1h' }
|
|
||||||
]
|
|
||||||
const harmonicOrderOptions = Array.from({ length: 49 }, (_item, index) => index + 2)
|
const harmonicOrderOptions = Array.from({ length: 49 }, (_item, index) => index + 2)
|
||||||
const statLabelMap: Record<SteadyDataView.SteadyTrendStatType, string> = {
|
const statLabelMap: Record<SteadyDataView.SteadyTrendStatType, string> = {
|
||||||
AVG: '平均值',
|
AVG: '平均值',
|
||||||
@@ -136,7 +118,7 @@ const handleTimeBaseDateChange = (value: Date) => {
|
|||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.trend-toolbar {
|
.trend-toolbar {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: minmax(312px, 1.4fr) repeat(3, minmax(178px, 0.8fr)) auto;
|
grid-template-columns: minmax(312px, 1.4fr) repeat(2, minmax(178px, 0.8fr)) auto;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 12px;
|
padding: 12px;
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
:tree-data="ledgerTree"
|
:tree-data="ledgerTree"
|
||||||
:loading="loading.ledger"
|
:loading="loading.ledger"
|
||||||
:keyword="ledgerKeyword"
|
:keyword="ledgerKeyword"
|
||||||
|
:default-checked-keys="defaultLedgerCheckedKeys"
|
||||||
@refresh="emit('refreshLedger')"
|
@refresh="emit('refreshLedger')"
|
||||||
@search="emit('ledgerSearch', $event)"
|
@search="emit('ledgerSearch', $event)"
|
||||||
@change="emit('ledgerChange', $event)"
|
@change="emit('ledgerChange', $event)"
|
||||||
@@ -30,6 +31,7 @@
|
|||||||
:selector-reset-key="selectorResetKey"
|
:selector-reset-key="selectorResetKey"
|
||||||
:tree-data="indicatorTree"
|
:tree-data="indicatorTree"
|
||||||
:loading="loading.indicator"
|
:loading="loading.indicator"
|
||||||
|
:default-checked-keys="defaultIndicatorCheckedKeys"
|
||||||
@refresh="emit('refreshIndicator')"
|
@refresh="emit('refreshIndicator')"
|
||||||
@change="emit('indicatorChange', $event)"
|
@change="emit('indicatorChange', $event)"
|
||||||
/>
|
/>
|
||||||
@@ -63,6 +65,8 @@ const props = defineProps<{
|
|||||||
trend: boolean
|
trend: boolean
|
||||||
}
|
}
|
||||||
ledgerKeyword: string
|
ledgerKeyword: string
|
||||||
|
defaultLedgerCheckedKeys: string[]
|
||||||
|
defaultIndicatorCheckedKeys: string[]
|
||||||
indicatorPanelCollapsed: boolean
|
indicatorPanelCollapsed: boolean
|
||||||
selectorResetKey: number
|
selectorResetKey: number
|
||||||
}>()
|
}>()
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ const currentDir = path.dirname(fileURLToPath(import.meta.url))
|
|||||||
const componentDir = path.join(currentDir, '..', 'components')
|
const componentDir = path.join(currentDir, '..', 'components')
|
||||||
|
|
||||||
const read = file => fs.readFileSync(path.join(componentDir, file), 'utf8')
|
const read = file => fs.readFileSync(path.join(componentDir, file), 'utf8')
|
||||||
|
const pageSource = fs.readFileSync(path.join(currentDir, '..', 'index.vue'), 'utf8')
|
||||||
|
const selectionRulesSource = fs.readFileSync(path.join(currentDir, '..', 'utils', 'selectionRules.ts'), 'utf8')
|
||||||
|
|
||||||
const expectations = [
|
const expectations = [
|
||||||
[
|
[
|
||||||
@@ -18,6 +20,46 @@ const expectations = [
|
|||||||
'indicator tree excludes half-checked parents when collecting checked nodes',
|
'indicator tree excludes half-checked parents when collecting checked nodes',
|
||||||
/getCheckedNodes\(\s*false\s*,\s*false\s*\)/,
|
/getCheckedNodes\(\s*false\s*,\s*false\s*\)/,
|
||||||
read('SteadyIndicatorTree.vue')
|
read('SteadyIndicatorTree.vue')
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'selection rules expose the first selectable ledger resolver',
|
||||||
|
/export const findFirstSelectableLedgerNode/,
|
||||||
|
selectionRulesSource
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'selection rules expose the first leaf indicator resolver',
|
||||||
|
/export const findFirstLeafIndicator/,
|
||||||
|
selectionRulesSource
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'steady page applies default selected ledger keys after ledger tree load',
|
||||||
|
/defaultLedgerCheckedKeys\.value\s*=/,
|
||||||
|
pageSource
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'steady page applies default selected indicator keys after indicator tree load',
|
||||||
|
/defaultIndicatorCheckedKeys\.value\s*=/,
|
||||||
|
pageSource
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'ledger tree receives default checked keys',
|
||||||
|
/defaultCheckedKeys/,
|
||||||
|
read('SteadyLedgerTree.vue')
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'ledger tree renders level icons',
|
||||||
|
/<component\s+:is="resolveLedgerIcon\(data\.level\)"/,
|
||||||
|
read('SteadyLedgerTree.vue')
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'ledger tree resolves icons by ledger level',
|
||||||
|
/const\s+ledgerIcons[\s\S]*0:[\s\S]*1:[\s\S]*2:[\s\S]*3:/,
|
||||||
|
read('SteadyLedgerTree.vue')
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'indicator tree receives default checked keys',
|
||||||
|
/defaultCheckedKeys/,
|
||||||
|
read('SteadyIndicatorTree.vue')
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -53,17 +53,21 @@ const expectations = [
|
|||||||
['components render indicator checkbox tree', /indicator-tree[\s\S]*show-checkbox[\s\S]*@check/],
|
['components render indicator checkbox tree', /indicator-tree[\s\S]*show-checkbox[\s\S]*@check/],
|
||||||
['components reuse LineChart', /<LineChart/],
|
['components reuse LineChart', /<LineChart/],
|
||||||
['toolbar uses shared time period search', /TimePeriodSearch/],
|
['toolbar uses shared time period search', /TimePeriodSearch/],
|
||||||
['toolbar labels stat bucket quality filters', /toolbar-field__label[\s\S]*统计:[\s\S]*toolbar-field__label[\s\S]*粒度:[\s\S]*toolbar-field__label[\s\S]*数据:/],
|
['toolbar labels stat quality filters', /toolbar-field__label[\s\S]*统计:[\s\S]*toolbar-field__label[\s\S]*数据:/],
|
||||||
|
['toolbar does not render bucket selector', /modelValue\.bucket|bucketOptions|粒度:|选择时间粒度/],
|
||||||
['toolbar does not render phase selector', /modelValue\.phases|phaseOptions|resolvePhaseLabel/],
|
['toolbar does not render phase selector', /modelValue\.phases|phaseOptions|resolvePhaseLabel/],
|
||||||
['toolbar labels bucket options descriptively', /bucketOptions[\s\S]*1分钟[\s\S]*1小时/],
|
|
||||||
['toolbar labels quality options descriptively', /仅有效数据[\s\S]*仅无效数据/],
|
['toolbar labels quality options descriptively', /仅有效数据[\s\S]*仅无效数据/],
|
||||||
|
['toolbar binds valid quality flag to zero', /<el-option\s+label="仅有效数据"\s+:value="0"\s*\/>/],
|
||||||
|
['utilities default to valid quality flag zero', /qualityFlag:\s*0/],
|
||||||
['utilities collect selected line ids', /export const collectSelectedLineIds/],
|
['utilities collect selected line ids', /export const collectSelectedLineIds/],
|
||||||
['utilities validate selection limits', /export const validateTrendSelection[\s\S]*24/],
|
['utilities validate selection limits', /export const validateTrendSelection[\s\S]*24/],
|
||||||
['utilities do not require phase selection', /if\s*\(!phases\.length\)/],
|
['utilities do not require phase selection', /if\s*\(!phases\.length\)/],
|
||||||
['utilities validate harmonic orders', /export const validateHarmonicOrders[\s\S]*6/],
|
['utilities validate harmonic orders', /export const validateHarmonicOrders[\s\S]*6/],
|
||||||
['utilities build trend query payload', /export const buildSteadyTrendQueryPayload/],
|
['utilities build trend query payload', /export const buildSteadyTrendQueryPayload/],
|
||||||
['utilities strip milliseconds from trend query time', /formatSteadyTrendQueryTime[\s\S]*replace\(\s*\/\\\.\[\^.\]\+\$\//],
|
['utilities strip milliseconds from trend query time', /formatSteadyTrendQueryTime[\s\S]*replace\(\s*\/\\\.\[\^.\]\+\$\//],
|
||||||
|
['utilities do not send bucket in trend query payload', /bucket:\s*formState\.bucket/],
|
||||||
['utilities do not send phases in trend query payload', /phases:\s*formState\.phases/],
|
['utilities do not send phases in trend query payload', /phases:\s*formState\.phases/],
|
||||||
|
['trend query params do not include bucket', /interface\s+SteadyTrendQueryParams\s*{[^}]*bucket\??:\s*string/],
|
||||||
['trend query params do not include phases', /phases:\s*string\[\]/],
|
['trend query params do not include phases', /phases:\s*string\[\]/],
|
||||||
['utilities build chart options', /export const buildSteadyTrendChartOptions/]
|
['utilities build chart options', /export const buildSteadyTrendChartOptions/]
|
||||||
]
|
]
|
||||||
@@ -78,7 +82,6 @@ const sourceByExpectation = [
|
|||||||
componentSource,
|
componentSource,
|
||||||
componentSource,
|
componentSource,
|
||||||
pageSource,
|
pageSource,
|
||||||
pageSource,
|
|
||||||
componentSource,
|
componentSource,
|
||||||
pageSource,
|
pageSource,
|
||||||
apiSource,
|
apiSource,
|
||||||
@@ -96,6 +99,8 @@ const sourceByExpectation = [
|
|||||||
componentSource,
|
componentSource,
|
||||||
componentSource,
|
componentSource,
|
||||||
componentSource,
|
componentSource,
|
||||||
|
componentSource,
|
||||||
|
componentSource,
|
||||||
utilitySource,
|
utilitySource,
|
||||||
utilitySource,
|
utilitySource,
|
||||||
utilitySource,
|
utilitySource,
|
||||||
@@ -103,6 +108,9 @@ const sourceByExpectation = [
|
|||||||
utilitySource,
|
utilitySource,
|
||||||
utilitySource,
|
utilitySource,
|
||||||
utilitySource,
|
utilitySource,
|
||||||
|
utilitySource,
|
||||||
|
utilitySource,
|
||||||
|
interfaceSource,
|
||||||
interfaceSource,
|
interfaceSource,
|
||||||
utilitySource
|
utilitySource
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -10,6 +10,8 @@
|
|||||||
:show-harmonic-orders="showHarmonicOrders"
|
:show-harmonic-orders="showHarmonicOrders"
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
:ledger-keyword="ledgerKeyword"
|
:ledger-keyword="ledgerKeyword"
|
||||||
|
:default-ledger-checked-keys="defaultLedgerCheckedKeys"
|
||||||
|
:default-indicator-checked-keys="defaultIndicatorCheckedKeys"
|
||||||
:selector-reset-key="selectorResetKey"
|
:selector-reset-key="selectorResetKey"
|
||||||
@refresh-ledger="loadLedgerTree"
|
@refresh-ledger="loadLedgerTree"
|
||||||
@ledger-search="handleLedgerSearch"
|
@ledger-search="handleLedgerSearch"
|
||||||
@@ -30,6 +32,8 @@ import type { SteadyDataView } from '@/api/steady/steadyDataView/interface'
|
|||||||
import SteadyTrendWorkbench from './components/SteadyTrendWorkbench.vue'
|
import SteadyTrendWorkbench from './components/SteadyTrendWorkbench.vue'
|
||||||
import {
|
import {
|
||||||
collectSelectedLineIds,
|
collectSelectedLineIds,
|
||||||
|
findFirstLeafIndicator,
|
||||||
|
findFirstSelectableLedgerNode,
|
||||||
hasHarmonicIndicator,
|
hasHarmonicIndicator,
|
||||||
resolveAvailableStats,
|
resolveAvailableStats,
|
||||||
validateTrendSelection
|
validateTrendSelection
|
||||||
@@ -49,6 +53,8 @@ const trendForm = ref(defaultTrendFormState())
|
|||||||
const ledgerKeyword = ref('')
|
const ledgerKeyword = ref('')
|
||||||
const indicatorPanelCollapsed = ref(false)
|
const indicatorPanelCollapsed = ref(false)
|
||||||
const selectorResetKey = ref(0)
|
const selectorResetKey = ref(0)
|
||||||
|
const defaultLedgerCheckedKeys = ref<string[]>([])
|
||||||
|
const defaultIndicatorCheckedKeys = ref<string[]>([])
|
||||||
const loading = reactive({
|
const loading = reactive({
|
||||||
ledger: false,
|
ledger: false,
|
||||||
indicator: false,
|
indicator: false,
|
||||||
@@ -75,6 +81,10 @@ const loadLedgerTree = async (keyword = ledgerKeyword.value) => {
|
|||||||
try {
|
try {
|
||||||
const response = await getSteadyTrendLedgerTree(keyword ? { keyword } : undefined)
|
const response = await getSteadyTrendLedgerTree(keyword ? { keyword } : undefined)
|
||||||
ledgerTree.value = unwrapData(response) || []
|
ledgerTree.value = unwrapData(response) || []
|
||||||
|
const firstLedgerNode = findFirstSelectableLedgerNode(ledgerTree.value)
|
||||||
|
// 台账树首次加载后默认选中第一个可查询监测点,避免趋势查询初始状态为空。
|
||||||
|
selectedLedgerNodes.value = firstLedgerNode ? [firstLedgerNode] : []
|
||||||
|
defaultLedgerCheckedKeys.value = firstLedgerNode ? [firstLedgerNode.id] : []
|
||||||
} finally {
|
} finally {
|
||||||
loading.ledger = false
|
loading.ledger = false
|
||||||
}
|
}
|
||||||
@@ -85,6 +95,11 @@ const loadIndicatorTree = async () => {
|
|||||||
try {
|
try {
|
||||||
const response = await getSteadyTrendIndicatorTree()
|
const response = await getSteadyTrendIndicatorTree()
|
||||||
indicatorTree.value = unwrapData(response) || []
|
indicatorTree.value = unwrapData(response) || []
|
||||||
|
const firstIndicator = findFirstLeafIndicator(indicatorTree.value)
|
||||||
|
const firstIndicatorKey = firstIndicator?.id || firstIndicator?.indicatorCode
|
||||||
|
// 指标树首次加载后默认选中第一个叶子指标,并同步驱动统计类型默认值。
|
||||||
|
selectedIndicators.value = firstIndicator ? [firstIndicator] : []
|
||||||
|
defaultIndicatorCheckedKeys.value = firstIndicatorKey ? [firstIndicatorKey] : []
|
||||||
} finally {
|
} finally {
|
||||||
loading.indicator = false
|
loading.indicator = false
|
||||||
}
|
}
|
||||||
@@ -109,6 +124,8 @@ const resetTrendState = () => {
|
|||||||
trendForm.value = defaultTrendFormState()
|
trendForm.value = defaultTrendFormState()
|
||||||
selectedLedgerNodes.value = []
|
selectedLedgerNodes.value = []
|
||||||
selectedIndicators.value = []
|
selectedIndicators.value = []
|
||||||
|
defaultLedgerCheckedKeys.value = []
|
||||||
|
defaultIndicatorCheckedKeys.value = []
|
||||||
trendResult.value = null
|
trendResult.value = null
|
||||||
selectorResetKey.value += 1
|
selectorResetKey.value += 1
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,6 +38,39 @@ export const collectLeafIndicators = (nodes: SteadyDataView.SteadyIndicatorNode[
|
|||||||
return indicators
|
return indicators
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const findFirstSelectableLedgerNode = (
|
||||||
|
nodes: SteadyDataView.SteadyLedgerNode[]
|
||||||
|
): SteadyDataView.SteadyLedgerNode | null => {
|
||||||
|
for (const node of nodes) {
|
||||||
|
if (node.level === 3 || node.selectable) {
|
||||||
|
return node
|
||||||
|
}
|
||||||
|
|
||||||
|
const childNode = findFirstSelectableLedgerNode(node.children || [])
|
||||||
|
if (childNode) return childNode
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
export const findFirstLeafIndicator = (
|
||||||
|
nodes: SteadyDataView.SteadyIndicatorNode[]
|
||||||
|
): SteadyDataView.SteadyIndicatorNode | null => {
|
||||||
|
for (const node of nodes) {
|
||||||
|
if (node.children?.length) {
|
||||||
|
const childNode = findFirstLeafIndicator(node.children)
|
||||||
|
if (childNode) return childNode
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node.indicatorCode) {
|
||||||
|
return node
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
export const hasHarmonicIndicator = (indicators: SteadyDataView.SteadyIndicatorNode[]) => {
|
export const hasHarmonicIndicator = (indicators: SteadyDataView.SteadyIndicatorNode[]) => {
|
||||||
return indicators.some(item => item.harmonic || Boolean(item.harmonicOrderStart || item.harmonicOrderEnd))
|
return indicators.some(item => item.harmonic || Boolean(item.harmonicOrderStart || item.harmonicOrderEnd))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ export interface SteadyTrendFormState {
|
|||||||
timeUnit: TimePeriodUnit
|
timeUnit: TimePeriodUnit
|
||||||
timeBaseDate: Date
|
timeBaseDate: Date
|
||||||
statTypes: SteadyDataView.SteadyTrendStatType[]
|
statTypes: SteadyDataView.SteadyTrendStatType[]
|
||||||
bucket: string
|
|
||||||
qualityFlag?: number
|
qualityFlag?: number
|
||||||
harmonicOrders: number[]
|
harmonicOrders: number[]
|
||||||
}
|
}
|
||||||
@@ -19,8 +18,7 @@ export const defaultTrendFormState = (): SteadyTrendFormState => {
|
|||||||
timeUnit: 'month',
|
timeUnit: 'month',
|
||||||
timeBaseDate: baseDate,
|
timeBaseDate: baseDate,
|
||||||
statTypes: ['AVG'],
|
statTypes: ['AVG'],
|
||||||
bucket: '10m',
|
qualityFlag: 0,
|
||||||
qualityFlag: 1,
|
|
||||||
harmonicOrders: []
|
harmonicOrders: []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -41,7 +39,6 @@ export const buildSteadyTrendQueryPayload = (
|
|||||||
statTypes: formState.statTypes,
|
statTypes: formState.statTypes,
|
||||||
timeStart: formatSteadyTrendQueryTime(formState.timeRange[0] || ''),
|
timeStart: formatSteadyTrendQueryTime(formState.timeRange[0] || ''),
|
||||||
timeEnd: formatSteadyTrendQueryTime(formState.timeRange[1] || ''),
|
timeEnd: formatSteadyTrendQueryTime(formState.timeRange[1] || ''),
|
||||||
bucket: formState.bucket,
|
|
||||||
qualityFlag: formState.qualityFlag,
|
qualityFlag: formState.qualityFlag,
|
||||||
harmonicOrders: formState.harmonicOrders.length ? formState.harmonicOrders : undefined
|
harmonicOrders: formState.harmonicOrders.length ? formState.harmonicOrders : undefined
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user