耐受图调整、屏蔽非必要的菜单、登录后跳转到变频器页面

This commit is contained in:
caozehui
2026-04-27 08:39:30 +08:00
parent 8744dfb0d8
commit 8ab1a35f3b
8 changed files with 512 additions and 340 deletions

View File

@@ -130,6 +130,7 @@ const initChart = () => {
chart.resize() chart.resize()
}, 0) }, 0)
} }
const getChartInstance = () => chart
const handlerBar = (options: any) => { const handlerBar = (options: any) => {
if (Array.isArray(options.series)) { if (Array.isArray(options.series)) {
options.series.forEach((item: any) => { options.series.forEach((item: any) => {
@@ -253,7 +254,7 @@ onMounted(() => {
initChart() initChart()
resizeObserver.observe(chartRef.value!) resizeObserver.observe(chartRef.value!)
}) })
defineExpose({ initChart }) defineExpose({ initChart, getChartInstance })
onBeforeUnmount(() => { onBeforeUnmount(() => {
resizeObserver.unobserve(chartRef.value!) resizeObserver.unobserve(chartRef.value!)
chart?.dispose() chart?.dispose()

View File

@@ -1,7 +1,7 @@
// ? 全局默认配置项 // ? 全局默认配置项
// 首页地址(默认) // 首页地址(默认)
export const HOME_URL: string = "/home/index"; export const HOME_URL: string = "/machine/freqConverter";
// export const HOME_URL: string = "/machine/controlSource"; // export const HOME_URL: string = "/machine/controlSource";

View File

@@ -26,6 +26,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { computed } from 'vue' import { computed } from 'vue'
import { HOME_URL } from '@/config'
import { useAuthStore } from '@/stores/modules/auth' import { useAuthStore } from '@/stores/modules/auth'
import { useModeStore } from '@/stores/modules/mode' // 引入模式 store import { useModeStore } from '@/stores/modules/mode' // 引入模式 store
import { useTabsStore } from '@/stores/modules/tabs' import { useTabsStore } from '@/stores/modules/tabs'
@@ -74,8 +75,8 @@ const handelOpen = async (item: string, key: string) => {
await initDynamicRouter() await initDynamicRouter()
// 只有当目标路径与当前路径不同时才跳转 // 只有当目标路径与当前路径不同时才跳转
if (router.currentRoute.value.path !== '/home/index') { if (router.currentRoute.value.path !== HOME_URL) {
await router.push({ path: '/home/index' }) await router.push({ path: HOME_URL })
} else { } else {
// 如果已在目标页面,手动触发组件更新 // 如果已在目标页面,手动触发组件更新
window.location.reload() // 或者采用其他方式刷新数据 window.location.reload() // 或者采用其他方式刷新数据

View File

@@ -70,7 +70,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed, ref } from 'vue' import { computed, ref } from 'vue'
import { LOGIN_URL } from '@/config' import { HOME_URL, LOGIN_URL } from '@/config'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import { useUserStore } from '@/stores/modules/user' import { useUserStore } from '@/stores/modules/user'
import { ElMessage, ElMessageBox } from 'element-plus' import { ElMessage, ElMessageBox } from 'element-plus'
@@ -132,7 +132,7 @@ const changeScene = async (value: string) => {
//模式切换 //模式切换
const changeMode = async () => { const changeMode = async () => {
authStore.changeModel() authStore.changeModel()
await router.push('/home/index') await router.push(HOME_URL)
} }
</script> </script>

View File

@@ -29,6 +29,7 @@ import { useRoute, useRouter } from 'vue-router'
import { useGlobalStore } from '@/stores/modules/global' import { useGlobalStore } from '@/stores/modules/global'
import { useTabsStore } from '@/stores/modules/tabs' import { useTabsStore } from '@/stores/modules/tabs'
import { useAuthStore } from '@/stores/modules/auth' import { useAuthStore } from '@/stores/modules/auth'
import { HOME_URL } from '@/config'
import { TabPaneName, TabsPaneContext } from 'element-plus' import { TabPaneName, TabsPaneContext } from 'element-plus'
import MoreButton from './components/MoreButton.vue' import MoreButton from './components/MoreButton.vue'
@@ -73,13 +74,13 @@ watch(
// 初始化需要固定的 tabs // 初始化需要固定的 tabs
const initTabs = () => { const initTabs = () => {
authStore.flatMenuListGet.forEach(item => { authStore.flatMenuListGet.forEach(item => {
if (item.meta.isAffix && !item.meta.isHide && !item.meta.isFull) { if (item.path === HOME_URL && !item.meta.isHide && !item.meta.isFull) {
const tabsParams = { const tabsParams = {
icon: item.meta.icon, icon: item.meta.icon,
title: item.meta.title, title: item.meta.title,
path: item.path, path: item.path,
name: item.name, name: item.name,
close: !item.meta.isAffix, close: false,
isKeepAlive: item.meta.isKeepAlive, isKeepAlive: item.meta.isKeepAlive,
unshift: true unshift: true
} }

View File

@@ -3,6 +3,7 @@ import { type AuthState } from '@/stores/interface'
import { getAuthButtonListApi, getAuthMenuListApi } from '@/api/user/login' import { getAuthButtonListApi, getAuthMenuListApi } from '@/api/user/login'
import { getAllBreadcrumbList, getFlatMenuList, getShowMenuList } from '@/utils' import { getAllBreadcrumbList, getFlatMenuList, getShowMenuList } from '@/utils'
import { AUTH_STORE_KEY } from '@/stores/constant' import { AUTH_STORE_KEY } from '@/stores/constant'
import { HOME_URL } from '@/config'
import { useModeStore } from '@/stores/modules/mode' import { useModeStore } from '@/stores/modules/mode'
import { getLicense } from '@/api/activate' import { getLicense } from '@/api/activate'
import type { Activate } from '@/api/activate/interface' import type { Activate } from '@/api/activate/interface'
@@ -52,7 +53,7 @@ export const useAuthStore = defineStore(AUTH_STORE_KEY, {
? filterMenuByExcludedNames(menuData, ['testSource', 'testScript', 'controlSource']) ? filterMenuByExcludedNames(menuData, ['testSource', 'testScript', 'controlSource'])
: filterMenuByExcludedNames(menuData, ['standardDevice']) : filterMenuByExcludedNames(menuData, ['standardDevice'])
this.authMenuList = filteredMenu this.authMenuList = normalizeHomeAffix(filteredMenu)
}, },
// Set RouteName // Set RouteName
async setRouteName(name: string) { async setRouteName(name: string) {
@@ -112,3 +113,22 @@ function filterMenuByExcludedNames(menuList: any[], excludedNames: string[]): an
return !excludedNames.includes(menu.name) return !excludedNames.includes(menu.name)
}) })
} }
function normalizeHomeAffix(menuList: any[]): any[] {
return menuList.map(menu => {
const nextMenu = { ...menu }
if (nextMenu.meta) {
nextMenu.meta = {
...nextMenu.meta,
isAffix: nextMenu.path === HOME_URL
}
}
if (Array.isArray(nextMenu.children) && nextMenu.children.length > 0) {
nextMenu.children = normalizeHomeAffix(nextMenu.children)
}
return nextMenu
})
}

View File

@@ -120,7 +120,7 @@ const login = (formEl: FormInstance | undefined) => {
await tabsStore.setTabs([]) await tabsStore.setTabs([])
await keepAliveStore.setKeepAliveName([]) await keepAliveStore.setKeepAliveName([])
// 登录默认不显示菜单和导航栏 // 登录默认不显示菜单和导航栏
await authStore.resetAuthStore() await authStore.setShowMenu()
// 跳转到首页 // 跳转到首页
await router.push(HOME_URL) await router.push(HOME_URL)
} finally { } finally {

View File

@@ -1,4 +1,4 @@
<template> <template>
<el-card class="dip-chart-card" shadow="never"> <el-card class="dip-chart-card" shadow="never">
<template #header> <template #header>
<div class="card-header"> <div class="card-header">
@@ -9,6 +9,13 @@
</div> </div>
</div> </div>
<div class="header-actions">
<el-button type="primary" plain :icon="Download" :disabled="!hasChartData" @click="downloadChartImage">
下载图片
</el-button>
<el-button type="primary" plain :icon="Document" :disabled="!hasChartData" @click="exportChartData">
导出数据
</el-button>
<el-button <el-button
type="primary" type="primary"
plain plain
@@ -19,40 +26,42 @@
绘制特性曲线 绘制特性曲线
</el-button> </el-button>
</div> </div>
</div>
</template> </template>
<div class="chart-wrapper"> <div class="chart-wrapper">
<MyEchart :options="chartOptions"/> <MyEchart ref="chartRef" :options="chartOptions" />
</div> </div>
</el-card> </el-card>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import {computed, ref, watch} from 'vue' import { computed, ref, watch } from 'vue'
import {ElMessage} from 'element-plus' import { ElMessage } from 'element-plus'
import { Document, Download } from '@element-plus/icons-vue'
import * as XLSX from 'xlsx'
import MyEchart from '@/components/echarts/line/index.vue' import MyEchart from '@/components/echarts/line/index.vue'
import {getFreqConverterSCurve} from '@/api/device/freqConverter' import { getFreqConverterSCurve } from '@/api/device/freqConverter'
type ChartPointStatus = 'pass' | 'fail' type ChartPointStatus = 'pass' | 'fail'
interface ChartPoint { interface ChartPoint {
duration: number; duration: number
residualVoltage: number; residualVoltage: number
status: ChartPointStatus; status: ChartPointStatus
} }
interface NormalizedTolerantPoint { interface NormalizedTolerantPoint {
duration: number; duration: number
residualVoltage: number; residualVoltage: number
tolerant: number | null; tolerant: number | null
status: ChartPointStatus; status: ChartPointStatus
} }
const props = defineProps<{ const props = defineProps<{
selectedMapping?: Record<string, any> | null; selectedMapping?: Record<string, any> | null
webMsgSend?: any; webMsgSend?: any
resultData?: any; resultData?: any
}>() }>()
const STATUS_COLOR_MAP: Record<ChartPointStatus, string> = { const STATUS_COLOR_MAP: Record<ChartPointStatus, string> = {
@@ -63,6 +72,7 @@ const STATUS_COLOR_MAP: Record<ChartPointStatus, string> = {
const chartPoints = ref<ChartPoint[]>([]) const chartPoints = ref<ChartPoint[]>([])
const characteristicCurveData = ref<Array<[number, number]>>([]) const characteristicCurveData = ref<Array<[number, number]>>([])
const curveLoading = ref(false) const curveLoading = ref(false)
const chartRef = ref<any>(null)
const selectedMappingText = computed(() => { const selectedMappingText = computed(() => {
if (!props.selectedMapping) { if (!props.selectedMapping) {
@@ -72,6 +82,91 @@ const selectedMappingText = computed(() => {
return `变频器:${props.selectedMapping.freqConverterName || '-'}` return `变频器:${props.selectedMapping.freqConverterName || '-'}`
}) })
const positiveDurations = computed(() => {
return [
...chartPoints.value.map(item => item.duration),
...characteristicCurveData.value.map(item => item[0])
].filter(item => Number.isFinite(item) && item > 0)
})
const xAxisMin = computed(() => {
if (!positiveDurations.value.length) {
return 0.001
}
const minValue = Math.min(...positiveDurations.value)
return Math.min(0.001, Number(minValue.toFixed(3)))
})
const xAxisMax = computed(() => {
if (!positiveDurations.value.length) {
return 60
}
const maxValue = Math.max(...positiveDurations.value)
return Math.max(Number((maxValue * 1.05).toFixed(3)), 60)
})
const sortedChartPoints = computed(() => {
return [...chartPoints.value].sort((a, b) => {
if (a.duration !== b.duration) {
return a.duration - b.duration
}
return a.residualVoltage - b.residualVoltage
})
})
const sortedCharacteristicCurveData = computed(() => {
return [...characteristicCurveData.value].sort((a, b) => {
if (a[0] !== b[0]) {
return a[0] - b[0]
}
return a[1] - b[1]
})
})
const hasChartData = computed(() => {
return sortedChartPoints.value.length > 0 || sortedCharacteristicCurveData.value.length > 0
})
const formatLogDurationLabel = (value: number) => {
if (!Number.isFinite(value)) {
return ''
}
if (value >= 1) {
return `${Number(value.toFixed(2))}`
}
return `${Number(value.toFixed(3))}`
}
const sanitizeFileName = (value: string) => {
return value.replace(/[\\/:*?"<>|]/g, '_').trim() || '未命名变频器'
}
const buildFileName = (prefix: string, suffix: string) => {
const freqConverterName = sanitizeFileName(props.selectedMapping?.freqConverterName || '未命名变频器')
return `${prefix}_${freqConverterName}.${suffix}`
}
const triggerDownload = (url: string, fileName: string) => {
const link = document.createElement('a')
link.style.display = 'none'
link.href = url
link.download = fileName
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
}
const toNumber = (value: unknown) => {
const result = Number(value)
return Number.isFinite(result) ? result : null
}
const normalizeTolerantValue = (value: unknown) => { const normalizeTolerantValue = (value: unknown) => {
if (value === undefined || value === null || value === '') { if (value === undefined || value === null || value === '') {
return null return null
@@ -89,246 +184,12 @@ const normalizeDuration = (source: Record<string, any>) => {
return toNumber( return toNumber(
source.durationMs !== undefined && source.durationMs !== null source.durationMs !== undefined && source.durationMs !== null
? Number(source.durationMs) / 1000 ? Number(source.durationMs) / 1000
: source.duration ?? : source.duration ?? source.x ?? source.dipDuration ?? source.retainTime ?? source.durationValue
source.x ??
source.dipDuration ??
source.retainTime ??
source.durationValue
) )
} }
const normalizeResidualVoltageValue = (source: Record<string, any>) => { const normalizeResidualVoltageValue = (source: Record<string, any>) => {
return toNumber( return toNumber(source.residualVoltage ?? source.y ?? source.residual ?? source.voltage ?? source.residual_value)
source.residualVoltage ??
source.y ??
source.residual ??
source.voltage ??
source.residual_value
)
}
const normalizeCurveData = (source: unknown) => {
if (!Array.isArray(source)) {
return [] as Array<[number, number]>
}
return source
.map(item => {
if (Array.isArray(item) && item.length >= 2) {
const duration = Number(item[0])
const residualVoltage = Number(item[1])
if (Number.isFinite(duration) && Number.isFinite(residualVoltage)) {
return [duration, residualVoltage] as [number, number]
}
}
if (item && typeof item === 'object') {
const record = item as Record<string, any>
const tolerant = normalizeTolerantValue(record.tolerant)
if (tolerant !== null && tolerant !== 2) {
return null
}
const duration = normalizeDuration(record)
const residualVoltage = normalizeResidualVoltageValue(record)
if (duration !== null && residualVoltage !== null) {
return [duration, residualVoltage] as [number, number]
}
}
return null
})
.filter((item): item is [number, number] => !!item)
}
const extractCurveData = (payload: any) => {
if (!payload) {
return [] as Array<[number, number]>
}
const candidates = [
payload,
payload?.data,
payload?.data?.records,
payload?.data?.points,
payload?.points,
payload?.records,
payload?.list
]
for (const candidate of candidates) {
const normalized = normalizeCurveData(candidate)
if (normalized.length) {
return normalized
}
}
return [] as Array<[number, number]>
}
const drawCharacteristicCurve = async () => {
const freqConverterId = props.selectedMapping?.freqConverterId
if (!freqConverterId) {
ElMessage.warning('未获取到变频器ID')
return
}
curveLoading.value = true
try {
const result = await getFreqConverterSCurve({
converterId: freqConverterId
})
const normalizedCurveData = extractCurveData(result)
if (!normalizedCurveData.length) {
characteristicCurveData.value = []
ElMessage.warning('未获取到特性曲线数据')
return
}
characteristicCurveData.value = normalizedCurveData
} catch (error) {
console.error('绘制特性曲线失败:', error)
characteristicCurveData.value = []
} finally {
curveLoading.value = false
}
}
const chartOptions = computed(() => {
const maxDuration = 2
// const maxDuration = Math.max(
// 2,
// ...chartPoints.value.map(item => item.duration).filter(item => Number.isFinite(item)),
// ...characteristicCurveData.value.map(item => item[0]).filter(item => Number.isFinite(item))
// )
return {
title: {
text: ''
},
grid: {
top: 30,
left: 48,
right: 22,
bottom: 52
},
tooltip: {
trigger: 'item',
formatter(params: any) {
if (params.seriesType === 'line') {
return ''
}
const [duration, residualVoltage, statusText] = params.value
return [
`持续时间: ${duration} s`,
`残余电压: ${residualVoltage} %`,
`状态: ${statusText}`
].join('<br/>')
}
},
legend: {
top: 0,
right: 0,
data: ['特性测试曲线']
},
xAxis: {
type: 'log',
name: '持续时间(s)',
nameLocation: 'middle',
nameGap: 34,
min: 0.01,
max: 60,
logBase: 10,
minorTick: {
show: true,
splitNumber: 10
},
minorSplitLine: {
show: true,
lineStyle: {
color: '#e8edf6'
}
},
splitLine: {
lineStyle: {
color: '#cfd8e6'
}
},
axisLabel: {
formatter(value: number) {
return value.toFixed(2)
}
}
},
yAxis: {
type: 'value',
name: '暂降幅值',
min: 0,
max: 100,
interval: 10,
minorTick: {
show: true,
splitNumber: 2
},
minorSplitLine: {
show: true,
lineStyle: {
color: '#e8edf6'
}
},
splitLine: {
lineStyle: {
color: '#cfd8e6'
}
},
axisLabel: {
formatter(value: number) {
return `${value}%`
}
}
},
dataZoom: [],
series: [
{
name: '特性测试曲线',
type: 'line',
smooth: true,
showSymbol: true,
symbolSize: 7,
lineStyle: {
color: '#ff2a2a',
width: 3
},
itemStyle: {
color: '#ff2a2a'
},
data: characteristicCurveData.value
},
{
name: '暂降点',
type: 'scatter',
symbolSize: 10,
data: chartPoints.value.map(item => ({
value: [item.duration, item.residualVoltage, getStatusText(item.status)],
itemStyle: {
color: STATUS_COLOR_MAP[item.status]
}
}))
}
],
options: {
animation: false
}
}
})
const toNumber = (value: unknown) => {
const result = Number(value)
return Number.isFinite(result) ? result : null
} }
const normalizeStatus = (value: unknown): ChartPointStatus => { const normalizeStatus = (value: unknown): ChartPointStatus => {
@@ -396,11 +257,7 @@ const normalizeTolerantPoint = (source: Record<string, any>): NormalizedTolerant
} }
const getStatusText = (status: ChartPointStatus) => { const getStatusText = (status: ChartPointStatus) => {
if (status === 'fail') { return status === 'fail' ? '不耐受' : '耐受'
return '不耐受'
}
return '耐受'
} }
const normalizePoint = (source: Record<string, any>): ChartPoint | null => { const normalizePoint = (source: Record<string, any>): ChartPoint | null => {
@@ -416,6 +273,65 @@ const normalizePoint = (source: Record<string, any>): ChartPoint | null => {
} }
} }
const normalizeCurveData = (source: unknown) => {
if (!Array.isArray(source)) {
return [] as Array<[number, number]>
}
return source
.map(item => {
if (Array.isArray(item) && item.length >= 2) {
const duration = Number(item[0])
const residualVoltage = Number(item[1])
if (Number.isFinite(duration) && Number.isFinite(residualVoltage) && duration > 0) {
return [duration, residualVoltage] as [number, number]
}
}
if (item && typeof item === 'object') {
const record = item as Record<string, any>
const tolerant = normalizeTolerantValue(record.tolerant)
if (tolerant !== null && tolerant !== 2) {
return null
}
const duration = normalizeDuration(record)
const residualVoltage = normalizeResidualVoltageValue(record)
if (duration !== null && residualVoltage !== null && duration > 0) {
return [duration, residualVoltage] as [number, number]
}
}
return null
})
.filter((item): item is [number, number] => !!item)
}
const extractCurveData = (payload: any) => {
if (!payload) {
return [] as Array<[number, number]>
}
const candidates = [
payload,
payload?.data,
payload?.data?.records,
payload?.data?.points,
payload?.points,
payload?.records,
payload?.list
]
for (const candidate of candidates) {
const normalized = normalizeCurveData(candidate)
if (normalized.length) {
return normalized
}
}
return [] as Array<[number, number]>
}
const extractCharacteristicCurvePoints = (payload: any) => { const extractCharacteristicCurvePoints = (payload: any) => {
const result: Array<[number, number]> = [] const result: Array<[number, number]> = []
const seen = new Set<string>() const seen = new Set<string>()
@@ -494,6 +410,231 @@ const extractPoints = (payload: any) => {
return result return result
} }
const drawCharacteristicCurve = async () => {
const freqConverterId = props.selectedMapping?.freqConverterId
if (!freqConverterId) {
ElMessage.warning('未获取到变频器ID')
return
}
curveLoading.value = true
try {
const result = await getFreqConverterSCurve({
converterId: freqConverterId
})
const normalizedCurveData = extractCurveData(result)
if (!normalizedCurveData.length) {
characteristicCurveData.value = []
ElMessage.warning('未获取到特性曲线数据')
return
}
characteristicCurveData.value = normalizedCurveData
} catch (error) {
console.error('draw characteristic curve failed:', error)
characteristicCurveData.value = []
} finally {
curveLoading.value = false
}
}
const downloadChartImage = async () => {
if (!hasChartData.value) {
ElMessage.warning('暂无可下载的图表数据')
return
}
await nextTick()
const chartInstance = chartRef.value?.getChartInstance?.()
if (!chartInstance) {
ElMessage.warning('图表尚未渲染完成')
return
}
const imageUrl = chartInstance.getDataURL({
type: 'png',
pixelRatio: 2,
backgroundColor: '#ffffff'
})
triggerDownload(imageUrl, buildFileName('变频器耐受图', 'png'))
ElMessage.success('图表图片导出成功')
}
const exportChartData = () => {
if (!hasChartData.value) {
ElMessage.warning('暂无可导出的点位数据')
return
}
const workbook = XLSX.utils.book_new()
if (sortedChartPoints.value.length) {
const pointSheet = XLSX.utils.json_to_sheet(
sortedChartPoints.value.map((item, index) => ({
序号: index + 1,
持续时间_s: item.duration,
暂降幅值_pct: item.residualVoltage,
状态: getStatusText(item.status)
}))
)
XLSX.utils.book_append_sheet(workbook, pointSheet, '暂降点')
}
if (sortedCharacteristicCurveData.value.length) {
const curveSheet = XLSX.utils.json_to_sheet(
sortedCharacteristicCurveData.value.map((item, index) => ({
序号: index + 1,
持续时间_s: item[0],
暂降幅值_pct: item[1]
}))
)
XLSX.utils.book_append_sheet(workbook, curveSheet, '特性曲线')
}
const workbookBuffer = XLSX.write(workbook, { bookType: 'xlsx', type: 'array' })
const blob = new Blob([workbookBuffer], {
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
})
const blobUrl = window.URL.createObjectURL(blob)
try {
triggerDownload(blobUrl, buildFileName('变频器耐受图数据', 'xlsx'))
ElMessage.success('点位数据导出成功')
} finally {
window.URL.revokeObjectURL(blobUrl)
}
}
const chartOptions = computed(() => {
return {
title: {
text: ''
},
grid: {
top: 30,
left: 48,
right: 22,
bottom: 52
},
tooltip: {
trigger: 'item',
formatter(params: any) {
const rawValue = Array.isArray(params.value) ? params.value : params.value?.value
if (!Array.isArray(rawValue)) {
return ''
}
const [duration, residualVoltage, statusText] = rawValue
return [
`类型: ${params.seriesName}`,
`持续时间: ${duration} s`,
`残余电压: ${residualVoltage} %`,
...(statusText ? [`状态: ${statusText}`] : [])
].join('<br/>')
}
},
legend: {
top: 0,
right: 0,
data: ['特性测试曲线']
},
xAxis: {
type: 'log',
name: '持续时间(s)',
nameLocation: 'middle',
nameGap: 34,
min: xAxisMin.value,
max: xAxisMax.value,
logBase: 10,
minorTick: {
show: true,
splitNumber: 10
},
minorSplitLine: {
show: false,
lineStyle: {
color: '#e8edf6'
}
},
splitLine: {
show: true,
lineStyle: {
color: '#cfd8e6'
}
},
axisLabel: {
formatter(value: number) {
return formatLogDurationLabel(value)
}
}
},
yAxis: {
type: 'value',
name: '暂降幅值',
min: 0,
max: 100,
interval: 10,
minorTick: {
show: true,
splitNumber: 2
},
minorSplitLine: {
show: true,
lineStyle: {
color: '#e8edf6'
}
},
splitLine: {
lineStyle: {
color: '#cfd8e6'
}
},
axisLabel: {
formatter(value: number) {
return `${value}%`
}
}
},
dataZoom: [],
series: [
{
name: '特性测试曲线',
type: 'line',
smooth: true,
showSymbol: true,
symbolSize: 7,
lineStyle: {
color: '#ff2a2a',
width: 3
},
itemStyle: {
color: '#ff2a2a'
},
data: characteristicCurveData.value.map(item => [item[0], item[1], '特性曲线'])
},
{
name: '暂降点',
type: 'scatter',
symbolSize: 10,
data: chartPoints.value.map(item => ({
value: [item.duration, item.residualVoltage, getStatusText(item.status)],
itemStyle: {
color: STATUS_COLOR_MAP[item.status]
}
}))
}
],
options: {
animation: false
}
}
})
watch( watch(
() => props.webMsgSend, () => props.webMsgSend,
newValue => { newValue => {
@@ -526,7 +667,7 @@ watch(
characteristicCurveData.value = nextCurvePoints characteristicCurveData.value = nextCurvePoints
} }
}, },
{deep: true} { deep: true }
) )
watch( watch(
@@ -542,7 +683,7 @@ watch(
characteristicCurveData.value = nextCurvePoints characteristicCurveData.value = nextCurvePoints
} }
}, },
{deep: true, immediate: true} { deep: true, immediate: true }
) )
watch( watch(
@@ -574,6 +715,14 @@ watch(
gap: 12px; gap: 12px;
} }
.header-actions {
display: flex;
align-items: center;
justify-content: flex-end;
gap: 8px;
flex-wrap: wrap;
}
.card-header-main { .card-header-main {
min-width: 0; min-width: 0;
} }