feat(waveform): 添加全通道波形数据显示功能
- 实现全通道模式下的波形数据展示 - 添加通道选择器支持全部/单个通道切换 - 新增全通道趋势分组数据结构 - 重构波形数据获取逻辑支持多通道模式 - 更新图表配置支持动态图例显示控制 - 完善波形数据导出功能支持全通道数据 - 优化工具栏界面适配新的通道选择功能
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
<template>
|
||||
<template>
|
||||
<div class="page-header">
|
||||
|
||||
<div class="action-row">
|
||||
<div class="file-select-row">
|
||||
<el-input
|
||||
@@ -31,7 +30,12 @@
|
||||
placeholder="选择通道"
|
||||
@update:model-value="handleChannelChange"
|
||||
>
|
||||
<el-option v-for="item in channelOptions" :key="item.value" :label="item.label" :value="item.value" />
|
||||
<el-option
|
||||
v-for="item in channelOptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</div>
|
||||
|
||||
@@ -54,7 +58,11 @@
|
||||
|
||||
<div class="toolbar-item">
|
||||
<div class="toolbar-label">数值类型</div>
|
||||
<el-radio-group :model-value="activeValueMode" class="value-mode-switch" @update:model-value="handleValueModeChange">
|
||||
<el-radio-group
|
||||
:model-value="activeValueMode"
|
||||
class="value-mode-switch"
|
||||
@update:model-value="handleValueModeChange"
|
||||
>
|
||||
<el-radio-button v-for="item in valueModeOptions" :key="item.value" :label="item.value">
|
||||
{{ item.label }}
|
||||
</el-radio-button>
|
||||
@@ -72,14 +80,14 @@
|
||||
<script setup lang="ts">
|
||||
import { Download, FolderOpened } from '@element-plus/icons-vue'
|
||||
import { ref } from 'vue'
|
||||
import type { DisplayMode, LabelValueOption, ValueMode, WaveformDetailOption } from './types'
|
||||
import type { ChannelSelectValue, DisplayMode, LabelValueOption, ValueMode, WaveformDetailOption } from './types'
|
||||
|
||||
defineProps<{
|
||||
selectedWaveformFileName: string
|
||||
isParsing: boolean
|
||||
waveformFileAccept: string
|
||||
channelOptions: WaveformDetailOption[]
|
||||
activeChannelIndex: number
|
||||
activeChannelIndex: ChannelSelectValue
|
||||
displayModeOptions: LabelValueOption<DisplayMode>[]
|
||||
activeDisplayMode: DisplayMode
|
||||
valueModeOptions: LabelValueOption<ValueMode>[]
|
||||
@@ -88,7 +96,7 @@ defineProps<{
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
'update:activeChannelIndex': [value: number]
|
||||
'update:activeChannelIndex': [value: ChannelSelectValue]
|
||||
'update:activeDisplayMode': [value: DisplayMode]
|
||||
'update:activeValueMode': [value: ValueMode]
|
||||
'waveform-file-change': [event: Event]
|
||||
@@ -108,7 +116,7 @@ const handleWaveformFileChange = (event: Event) => {
|
||||
emit('waveform-file-change', event)
|
||||
}
|
||||
|
||||
const handleChannelChange = (value: number) => {
|
||||
const handleChannelChange = (value: ChannelSelectValue) => {
|
||||
emit('update:activeChannelIndex', value)
|
||||
}
|
||||
|
||||
@@ -202,6 +210,4 @@ const handleValueModeChange = (value: string | number | boolean | undefined) =>
|
||||
max-width: none;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<template>
|
||||
<template>
|
||||
<section class="waveform-panel">
|
||||
<div class="panel-header">
|
||||
<el-tabs :model-value="activeTrendTab" class="trend-tabs" @update:model-value="handleTrendTabChange">
|
||||
@@ -7,7 +7,26 @@
|
||||
</div>
|
||||
|
||||
<div class="panel-body">
|
||||
<div v-if="hasWaveformData && activeDisplayMode === 'multi-channel'" class="chart-container">
|
||||
<div v-if="hasWaveformData && isAllChannelsActive" class="all-channel-list">
|
||||
<template v-for="group in allChannelTrendGroups" :key="group.key">
|
||||
<div v-if="activeDisplayMode === 'multi-channel'" class="all-channel-chart">
|
||||
<LineChart :options="group.multiChannelOptions" />
|
||||
</div>
|
||||
<template v-else>
|
||||
<div
|
||||
v-for="item in group.singleChannelOptionsList"
|
||||
:key="item.key"
|
||||
class="single-channel-card"
|
||||
:class="{ 'single-channel-card--with-axis': item.isLastChart }"
|
||||
>
|
||||
<div class="single-channel-chart">
|
||||
<LineChart :options="item.options" :group="item.group" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
</div>
|
||||
<div v-else-if="hasWaveformData && activeDisplayMode === 'multi-channel'" class="chart-container">
|
||||
<LineChart :options="activeTrendOptions" />
|
||||
</div>
|
||||
<div v-else-if="hasWaveformData" class="single-channel-list">
|
||||
@@ -25,7 +44,9 @@
|
||||
<div v-else class="empty-block">
|
||||
<div class="empty-title">暂无波形数据</div>
|
||||
<div class="empty-text">请选择同一组 `.cfg` 和 `.dat` 文件后自动解析并展示。</div>
|
||||
<div v-if="lastParseErrorMessage" class="empty-text error-text">最近一次解析失败:{{ lastParseErrorMessage }}</div>
|
||||
<div v-if="lastParseErrorMessage" class="empty-text error-text">
|
||||
最近一次解析失败:{{ lastParseErrorMessage }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
@@ -33,15 +54,23 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import LineChart from '@/components/echarts/line/index.vue'
|
||||
import type { DisplayMode, LabelValueOption, SingleChannelTrendOption, TrendTabValue } from './types'
|
||||
import type {
|
||||
AllChannelTrendGroup,
|
||||
DisplayMode,
|
||||
LabelValueOption,
|
||||
SingleChannelTrendOption,
|
||||
TrendTabValue
|
||||
} from './types'
|
||||
|
||||
defineProps<{
|
||||
hasWaveformData: boolean
|
||||
isAllChannelsActive: boolean
|
||||
activeDisplayMode: DisplayMode
|
||||
activeTrendTab: TrendTabValue
|
||||
trendTabs: LabelValueOption<TrendTabValue>[]
|
||||
activeTrendOptions: Record<string, unknown>
|
||||
singleChannelTrendOptionsList: SingleChannelTrendOption[]
|
||||
allChannelTrendGroups: AllChannelTrendGroup[]
|
||||
lastParseErrorMessage: string
|
||||
}>()
|
||||
|
||||
@@ -125,6 +154,26 @@ const handleTrendTabChange = (value: string | number) => {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.all-channel-list {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
min-height: 0;
|
||||
padding: 8px;
|
||||
overflow: hidden;
|
||||
border: 1px solid var(--el-border-color-lighter);
|
||||
border-radius: 4px;
|
||||
background: var(--cn-color-canvas-bg);
|
||||
}
|
||||
|
||||
.all-channel-chart {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.single-channel-card {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
export type TrendTabValue = 'instant' | 'rms'
|
||||
export type TrendTabValue = 'instant' | 'rms'
|
||||
export type ValueMode = 'primary' | 'secondary'
|
||||
export type DisplayMode = 'single-channel' | 'multi-channel'
|
||||
export type ChannelSelectValue = number | 'all'
|
||||
|
||||
export interface LabelValueOption<T extends string | number = string | number> {
|
||||
label: string
|
||||
@@ -9,7 +10,7 @@ export interface LabelValueOption<T extends string | number = string | number> {
|
||||
|
||||
export interface WaveformDetailOption {
|
||||
label: string
|
||||
value: number
|
||||
value: ChannelSelectValue
|
||||
}
|
||||
|
||||
export interface SingleChannelTrendOption {
|
||||
@@ -19,6 +20,13 @@ export interface SingleChannelTrendOption {
|
||||
options: Record<string, unknown>
|
||||
}
|
||||
|
||||
export interface AllChannelTrendGroup {
|
||||
key: string
|
||||
title: string
|
||||
singleChannelOptionsList: SingleChannelTrendOption[]
|
||||
multiChannelOptions: Record<string, unknown>
|
||||
}
|
||||
|
||||
export interface SummaryItem {
|
||||
label: string
|
||||
value: string | number
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<template>
|
||||
<template>
|
||||
<div class="table-box waveform-page">
|
||||
<WaveformToolbar
|
||||
:selected-waveform-file-name="selectedWaveformFileName"
|
||||
@@ -26,6 +26,8 @@
|
||||
:trend-tabs="trendTabs"
|
||||
:active-trend-options="activeTrendOptions"
|
||||
:single-channel-trend-options-list="singleChannelTrendOptionsList"
|
||||
:all-channel-trend-groups="allChannelTrendGroups"
|
||||
:is-all-channels-active="isAllChannelsActive"
|
||||
:last-parse-error-message="lastParseErrorMessage"
|
||||
@update:active-trend-tab="activeTrendTab = $event"
|
||||
/>
|
||||
@@ -52,6 +54,8 @@ import WaveformInfoPanel from './components/WaveformInfoPanel.vue'
|
||||
import WaveformToolbar from './components/WaveformToolbar.vue'
|
||||
import WaveformTrendPanel from './components/WaveformTrendPanel.vue'
|
||||
import type {
|
||||
AllChannelTrendGroup,
|
||||
ChannelSelectValue,
|
||||
DisplayMode,
|
||||
LabelValueOption,
|
||||
SingleChannelTrendOption,
|
||||
@@ -80,13 +84,15 @@ interface WaveformTrendPayload {
|
||||
|
||||
interface TrendChartLayoutOptions {
|
||||
showTimeAxis?: boolean
|
||||
showLegend?: boolean
|
||||
}
|
||||
|
||||
const activeTrendTab = ref<TrendTabValue>('instant')
|
||||
const activeValueMode = ref<ValueMode>('primary')
|
||||
const activeDisplayMode = ref<DisplayMode>('single-channel')
|
||||
const activeChannelIndex = ref(0)
|
||||
const activeChannelIndex = ref<ChannelSelectValue>('all')
|
||||
const singleChannelTrendChartGroup = 'waveform-single-channel-sync'
|
||||
const allChannelTrendChartGroup = 'waveform-all-channel-sync'
|
||||
const isParsing = ref(false)
|
||||
const selectedCfgFile = ref<File | null>(null)
|
||||
const selectedDatFile = ref<File | null>(null)
|
||||
@@ -205,13 +211,19 @@ const normalizedWaveDetails = computed<Waveform.WaveDataDetail[]>(() => {
|
||||
})
|
||||
|
||||
const channelOptions = computed<WaveformDetailOption[]>(() => {
|
||||
return normalizedWaveDetails.value.map((item, index) => ({
|
||||
const detailOptions = normalizedWaveDetails.value.map((item, index) => ({
|
||||
label: buildChannelLabel(item, index),
|
||||
value: index
|
||||
}))
|
||||
|
||||
return detailOptions.length ? [{ label: '全部', value: 'all' }, ...detailOptions] : []
|
||||
})
|
||||
|
||||
const isAllChannelsActive = computed(() => activeChannelIndex.value === 'all')
|
||||
|
||||
const activeWaveDetail = computed(() => {
|
||||
if (typeof activeChannelIndex.value !== 'number') return null
|
||||
|
||||
return normalizedWaveDetails.value[activeChannelIndex.value] || normalizedWaveDetails.value[0] || null
|
||||
})
|
||||
|
||||
@@ -226,13 +238,15 @@ const isCurrentChannel = (channelName?: string) => {
|
||||
return (channelName || '').toUpperCase().startsWith('I')
|
||||
}
|
||||
|
||||
const activeValueScale = computed(() => {
|
||||
const getValueScale = (detail: Waveform.WaveDataDetail | null) => {
|
||||
if (activeValueMode.value === 'secondary') return 1
|
||||
|
||||
const waveData = activeWaveData.value
|
||||
const ratio = isCurrentChannel(activeWaveDetail.value?.channelName) ? waveData?.ct : waveData?.pt
|
||||
const ratio = isCurrentChannel(detail?.channelName) ? waveData?.ct : waveData?.pt
|
||||
return normalizeRatio(ratio)
|
||||
})
|
||||
}
|
||||
|
||||
const activeValueScale = computed(() => getValueScale(activeWaveDetail.value))
|
||||
|
||||
const safeNumber = (value: unknown) => {
|
||||
const numberValue = Number(value)
|
||||
@@ -262,7 +276,11 @@ const buildChannelLabel = (detail: Waveform.WaveDataDetail, index: number) => {
|
||||
return `通道 ${index + 1}`
|
||||
}
|
||||
|
||||
const buildTrendPayload = (detail: Waveform.WaveDataDetail | null, trendTab: TrendTabValue, scale: number): WaveformTrendPayload => {
|
||||
const buildTrendPayload = (
|
||||
detail: Waveform.WaveDataDetail | null,
|
||||
trendTab: TrendTabValue,
|
||||
scale: number
|
||||
): WaveformTrendPayload => {
|
||||
const trendData = trendTab === 'instant' ? detail?.instantData : detail?.rmsData
|
||||
const aName = detail?.a || 'A相'
|
||||
const bName = detail?.b || 'B相'
|
||||
@@ -301,12 +319,18 @@ const activeTrendPayload = computed(() => {
|
||||
return buildTrendPayload(activeWaveDetail.value, activeTrendTab.value, activeValueScale.value)
|
||||
})
|
||||
|
||||
const currentTrendColors = computed(() => {
|
||||
const customColors = activeWaveDetail.value?.colors?.filter(Boolean) || []
|
||||
const getTrendColors = (detail: Waveform.WaveDataDetail | null) => {
|
||||
const customColors = detail?.colors?.filter(Boolean) || []
|
||||
return customColors.length >= 3 ? customColors.slice(0, 3) : defaultPhaseColors
|
||||
})
|
||||
}
|
||||
|
||||
const hasWaveformData = computed(() => activeTrendPayload.value.series.length > 0)
|
||||
const currentTrendColors = computed(() => getTrendColors(activeWaveDetail.value))
|
||||
|
||||
const hasWaveformData = computed(() => {
|
||||
if (isAllChannelsActive.value) return allChannelTrendGroups.value.length > 0
|
||||
|
||||
return activeTrendPayload.value.series.length > 0
|
||||
})
|
||||
|
||||
const SYMMETRIC_AXIS_SPLIT_COUNT = 6
|
||||
const REGULAR_AXIS_SPLIT_COUNT = 5
|
||||
@@ -408,13 +432,40 @@ const buildTimeAxisLabelFormatter = (timeLabels: string[]) => {
|
||||
}
|
||||
}
|
||||
|
||||
interface TrendTooltipParam {
|
||||
axisValue?: string | number
|
||||
marker?: string
|
||||
seriesName?: string
|
||||
value?: number | string
|
||||
}
|
||||
|
||||
const buildTrendTooltipFormatter = (unit: string) => {
|
||||
return (params: TrendTooltipParam | TrendTooltipParam[]) => {
|
||||
const paramList = Array.isArray(params) ? params : [params]
|
||||
const firstParam = paramList[0]
|
||||
const timeValue = firstParam?.axisValue
|
||||
const valueRows = paramList
|
||||
.map(item => {
|
||||
const marker = item.marker || ''
|
||||
const seriesName = item.seriesName || ''
|
||||
const valueText = unit ? `${formatNumber(item.value)} ${unit}` : formatNumber(item.value)
|
||||
|
||||
return `<div>${marker}${seriesName}<span style="float:right;margin-left:12px;font-weight:600;">${valueText}</span></div>`
|
||||
})
|
||||
.join('')
|
||||
const timeText = timeValue === undefined ? '--' : `${formatNumber(timeValue, 2)} ms`
|
||||
|
||||
return `${valueRows}<div style="margin-top:4px;">时间<span style="float:right;margin-left:12px;">${timeText}</span></div>`
|
||||
}
|
||||
}
|
||||
|
||||
const buildTrendChartOptions = (
|
||||
trendPayload: WaveformTrendPayload,
|
||||
seriesList: WaveformSeriesItem[],
|
||||
chartColors = currentTrendColors.value,
|
||||
layoutOptions: TrendChartLayoutOptions = {}
|
||||
) => {
|
||||
const { showTimeAxis = true } = layoutOptions
|
||||
const { showTimeAxis = true, showLegend = true } = layoutOptions
|
||||
|
||||
return {
|
||||
tooltip: {
|
||||
@@ -427,16 +478,15 @@ const buildTrendChartOptions = (
|
||||
width: 1
|
||||
}
|
||||
},
|
||||
valueFormatter: (value: number) => {
|
||||
return trendPayload.unit ? `${formatNumber(value)} ${trendPayload.unit}` : formatNumber(value)
|
||||
}
|
||||
formatter: buildTrendTooltipFormatter(trendPayload.unit)
|
||||
},
|
||||
legend: {
|
||||
show: showLegend,
|
||||
top: 0,
|
||||
right: 12
|
||||
},
|
||||
grid: {
|
||||
top: '18px',
|
||||
top: showLegend ? '18px' : '6px',
|
||||
left: '24px',
|
||||
right: showTimeAxis ? '32px' : '24px',
|
||||
bottom: showTimeAxis ? '16px' : '6px'
|
||||
@@ -493,24 +543,68 @@ const activeTrendOptions = computed<Record<string, unknown>>(() => {
|
||||
})
|
||||
|
||||
// 单通道模式按相别拆成多个图,便于分别观察各通道波形。
|
||||
const singleChannelTrendOptionsList = computed<SingleChannelTrendOption[]>(() => {
|
||||
const trendPayload = activeTrendPayload.value
|
||||
const buildSingleChannelTrendOptionsList = (
|
||||
detail: Waveform.WaveDataDetail | null,
|
||||
detailIndex: number,
|
||||
chartGroup: string,
|
||||
resolveShowTimeAxis = (seriesIndex: number, seriesCount: number) => seriesIndex === seriesCount - 1
|
||||
): SingleChannelTrendOption[] => {
|
||||
const trendPayload = buildTrendPayload(detail, activeTrendTab.value, getValueScale(detail))
|
||||
const trendColors = getTrendColors(detail)
|
||||
|
||||
// 单通道下每张图只保留一个 series,需要单独指定对应相色。
|
||||
return trendPayload.series.map((item, index) => ({
|
||||
key: item.name,
|
||||
group: singleChannelTrendChartGroup,
|
||||
isLastChart: index === trendPayload.series.length - 1,
|
||||
options: buildTrendChartOptions(
|
||||
trendPayload,
|
||||
[item],
|
||||
[currentTrendColors.value[index] || defaultPhaseColors[index]],
|
||||
{
|
||||
// 仅最后一张图显示时间轴,并通过卡片高度微调补偿其额外占用空间。
|
||||
showTimeAxis: index === trendPayload.series.length - 1
|
||||
return trendPayload.series.map((item, index) => {
|
||||
const showTimeAxis = resolveShowTimeAxis(index, trendPayload.series.length)
|
||||
|
||||
return {
|
||||
key: `${detailIndex}-${item.name}`,
|
||||
group: chartGroup,
|
||||
isLastChart: showTimeAxis,
|
||||
options: buildTrendChartOptions(trendPayload, [item], [trendColors[index] || defaultPhaseColors[index]], {
|
||||
// 单通道下每张图只有一条曲线,图例信息与图表标题重复。
|
||||
showTimeAxis,
|
||||
showLegend: false
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const singleChannelTrendOptionsList = computed<SingleChannelTrendOption[]>(() => {
|
||||
return buildSingleChannelTrendOptionsList(activeWaveDetail.value, -1, singleChannelTrendChartGroup)
|
||||
})
|
||||
|
||||
const allChannelTrendGroups = computed<AllChannelTrendGroup[]>(() => {
|
||||
const availableDetails = normalizedWaveDetails.value
|
||||
.map((detail, index) => {
|
||||
const trendPayload = buildTrendPayload(detail, activeTrendTab.value, getValueScale(detail))
|
||||
|
||||
return {
|
||||
detail,
|
||||
index,
|
||||
trendPayload
|
||||
}
|
||||
)
|
||||
}))
|
||||
})
|
||||
.filter(item => item.trendPayload.series.length > 0)
|
||||
|
||||
return availableDetails.map((item, groupIndex) => {
|
||||
const isLastGroup = groupIndex === availableDetails.length - 1
|
||||
|
||||
return {
|
||||
key: `${item.index}-${buildChannelLabel(item.detail, item.index)}`,
|
||||
title: buildChannelLabel(item.detail, item.index),
|
||||
singleChannelOptionsList: buildSingleChannelTrendOptionsList(
|
||||
item.detail,
|
||||
item.index,
|
||||
allChannelTrendChartGroup,
|
||||
(seriesIndex, seriesCount) => isLastGroup && seriesIndex === seriesCount - 1
|
||||
),
|
||||
multiChannelOptions: buildTrendChartOptions(
|
||||
item.trendPayload,
|
||||
item.trendPayload.series,
|
||||
getTrendColors(item.detail)
|
||||
)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
const summaryItems = computed<SummaryItem[]>(() => {
|
||||
@@ -523,7 +617,14 @@ const summaryItems = computed<SummaryItem[]>(() => {
|
||||
{ label: '触发时间', value: formatWaveformTime(cfgData?.timeTrige) },
|
||||
{ label: '采样率', value: cfgData?.finalSampleRate ? `${cfgData.finalSampleRate} Hz` : '--' },
|
||||
{ label: '总通道数', value: cfgData?.nChannelNum ?? '--' },
|
||||
{ label: '当前通道', value: detail ? buildChannelLabel(detail, activeChannelIndex.value) : '--' },
|
||||
{
|
||||
label: '当前通道',
|
||||
value: isAllChannelsActive.value
|
||||
? '全部'
|
||||
: detail
|
||||
? buildChannelLabel(detail, activeChannelIndex.value as number)
|
||||
: '--'
|
||||
},
|
||||
{ label: '单位', value: detail?.unit || '--' },
|
||||
{ label: '相别数量', value: waveData?.iPhasic ?? '--' },
|
||||
{ label: 'PT / CT', value: `${formatNumber(waveData?.pt, 2)} / ${formatNumber(waveData?.ct, 2)}` },
|
||||
@@ -583,7 +684,7 @@ const handleWaveformFileChange = async (event: Event) => {
|
||||
const loadWaveformData = async (cfgFile: File, datFile: File) => {
|
||||
try {
|
||||
isParsing.value = true
|
||||
activeChannelIndex.value = 0
|
||||
activeChannelIndex.value = 'all'
|
||||
lastParseErrorMessage.value = ''
|
||||
lastVectorParseErrorMessage.value = ''
|
||||
|
||||
@@ -641,16 +742,46 @@ const downloadTrendData = () => {
|
||||
}
|
||||
|
||||
const trendPayload = activeTrendPayload.value
|
||||
const allChannelPayloads = normalizedWaveDetails.value
|
||||
.map((detail, index) => ({
|
||||
label: buildChannelLabel(detail, index),
|
||||
payload: buildTrendPayload(detail, activeTrendTab.value, getValueScale(detail))
|
||||
}))
|
||||
.filter(item => item.payload.series.length > 0)
|
||||
const exportPayload = isAllChannelsActive.value ? allChannelPayloads[0]?.payload : trendPayload
|
||||
|
||||
if (!exportPayload) {
|
||||
ElMessage.warning('暂无可导出的波形数据')
|
||||
return
|
||||
}
|
||||
|
||||
const header = ['时间', ...trendPayload.series.map(item => item.name)]
|
||||
const rows = trendPayload.timeLabels.map((time, index) => {
|
||||
const allChannelHeader = [
|
||||
'时间',
|
||||
...allChannelPayloads.flatMap(item => item.payload.series.map(series => `${item.label}-${series.name}`))
|
||||
]
|
||||
const rows = exportPayload.timeLabels.map((time, index) => {
|
||||
if (isAllChannelsActive.value) {
|
||||
return [
|
||||
time,
|
||||
...allChannelPayloads.flatMap(item => item.payload.series.map(series => series.data[index] ?? ''))
|
||||
]
|
||||
}
|
||||
|
||||
return [time, ...trendPayload.series.map(item => item.data[index] ?? '')]
|
||||
})
|
||||
|
||||
const csvContent = [header, ...rows].map(row => row.join(',')).join('\n')
|
||||
const csvContent = [isAllChannelsActive.value ? allChannelHeader : header, ...rows]
|
||||
.map(row => row.join(','))
|
||||
.join('\n')
|
||||
const blob = new Blob([`\uFEFF${csvContent}`], { type: 'text/csv;charset=utf-8;' })
|
||||
const blobUrl = URL.createObjectURL(blob)
|
||||
const exportFile = document.createElement('a')
|
||||
const channelLabel = activeWaveDetail.value ? buildChannelLabel(activeWaveDetail.value, activeChannelIndex.value) : '波形'
|
||||
const channelLabel = isAllChannelsActive.value
|
||||
? '全部'
|
||||
: activeWaveDetail.value
|
||||
? buildChannelLabel(activeWaveDetail.value, activeChannelIndex.value as number)
|
||||
: '波形'
|
||||
const fileName = `波形查看_${channelLabel}_${valueModeLabelMap[activeValueMode.value]}_${trendLabelMap[activeTrendTab.value]}.csv`
|
||||
|
||||
exportFile.style.display = 'none'
|
||||
@@ -686,4 +817,3 @@ const downloadTrendData = () => {
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user