From 886a1c8667779917c905bdf6f43162920d63a3b6 Mon Sep 17 00:00:00 2001 From: zhongzm Date: Fri, 22 Nov 2024 15:42:38 +0800 Subject: [PATCH 1/3] =?UTF-8?q?fix=EF=BC=9A=E4=BF=AE=E6=94=B9=E5=8F=96?= =?UTF-8?q?=E8=89=B2=E8=8C=83=E5=9B=B4=E9=80=82=E5=BA=94=E6=9A=97=E8=89=B2?= =?UTF-8?q?=E6=A8=A1=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/utils/generate-utils.ts | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/src/utils/generate-utils.ts b/src/utils/generate-utils.ts index 57e009e5..863eeb28 100644 --- a/src/utils/generate-utils.ts +++ b/src/utils/generate-utils.ts @@ -13,12 +13,28 @@ export function generateColorHEX(): string { * @returns rgb(24 144 255) / rgba(0,0,0,.85) */ export function generateColorRGBA(hasAlpha: boolean = false) { - const red = Math.floor(Math.random() * 256); - const green = Math.floor(Math.random() * 256); - const blue = Math.floor(Math.random() * 256); + let red:number; + let green: number; + let blue:number; + red = Math.floor(Math.random() * 256)+100;//+100增加顏色亮度 + green = Math.floor(Math.random() * 256)+100; + blue = Math.floor(Math.random() * 256)+100; + + const brightChannel = Math.floor(Math.random() * 3); + switch (brightChannel) { + case 0: + red = Math.min(255, red + 50); + break; + case 1: + green = Math.min(255, green + 50); + break; + case 2: + blue = Math.min(255, blue + 50); + break; + } if (hasAlpha) { - const alpha = Math.floor(Math.random() * 100); + const alpha = Math.floor(Math.random() * 100)+50; return `rgb(${red}, ${green}, ${blue}, 0.${alpha})`; } From 45d8314e290b502056b71c1a4ec062ca2daf7a94 Mon Sep 17 00:00:00 2001 From: zhongzm Date: Fri, 22 Nov 2024 15:43:00 +0800 Subject: [PATCH 2/3] =?UTF-8?q?fix=EF=BC=9A=E6=9A=97=E8=89=B2=E6=A8=A1?= =?UTF-8?q?=E5=BC=8F=E6=A0=B7=E5=BC=8F=E9=80=82=E5=BA=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/perfManage/kpiOverView/index.vue | 178 ++++++++++++++++++++- 1 file changed, 174 insertions(+), 4 deletions(-) diff --git a/src/views/perfManage/kpiOverView/index.vue b/src/views/perfManage/kpiOverView/index.vue index da865a72..3ab8f8b6 100644 --- a/src/views/perfManage/kpiOverView/index.vue +++ b/src/views/perfManage/kpiOverView/index.vue @@ -14,6 +14,7 @@ import { OptionsType, WS } from '@/plugins/ws-websocket'; import { generateColorRGBA } from '@/utils/generate-utils'; import { LineOutlined } from '@ant-design/icons-vue'; import { TableColumnType } from 'ant-design-vue'; +import { viewTransitionTheme } from 'antdv-pro-layout'; const { t, currentLocale } = useI18n(); //定义KPI接口 interface KPIBase{ @@ -69,8 +70,8 @@ const ranges = ref([ ]) //日期范围响应式变量 const dateRange = ref<[string, string]>([ - dayjs().startOf('hour').valueOf().toString(), - dayjs().endOf('hour').valueOf().toString(), + dayjs().subtract(1, 'hour').startOf('hour').valueOf().toString(), // 上一小时开始 + dayjs().startOf('hour').add(1, 'hour').valueOf().toString(), // 当前小时结束 ]); //实时数据状态 const isRealtime = ref(false); @@ -265,6 +266,134 @@ const getSeriesConfig = () => ({ smooth:0.6, showSymbol: true, }); +// 添加一个函数来获取当前主题下的网格线颜色 +const getSplitLineColor = () => { + return document.documentElement.getAttribute('data-theme') === 'dark' + ? '#333333' + : '#E8E8E8'; // 亮色模式返回 undefined,使用默认颜色 +}; + +// 添加主题变化的观察器 +const themeObserver = new MutationObserver(() => { + if (chart) { + // 获取当前的主题色 + const splitLineColor = getSplitLineColor(); + + // 重新设置完整的图表配置并触发重新渲染 + requestAnimationFrame(() => { + const option = { + title: { + text: t('views.perfManage.kpiOverView.kpiChartTitle'), + left: 'center', + textStyle: { + color: document.documentElement.getAttribute('data-theme') === 'dark' + ? '#CACADA' + : '#333' + } + }, + xAxis: { + // 保持现有的 xAxis 配置 + type: 'category', + boundaryGap: false, + data: chartData.value.map(item => + dayjs(Number(item.date)).format('YYYY-MM-DD HH:mm:ss') + ), + axisLabel: { + color: document.documentElement.getAttribute('data-theme') === 'dark' + ? '#CACADA' + : '#333' + }, + splitLine: { + show: true, + lineStyle: { + color: splitLineColor + } + } + }, + yAxis: { + type: 'value', + axisLabel: { + formatter: '{value}', + color: document.documentElement.getAttribute('data-theme') === 'dark' + ? '#CACADA' + : '#333' + }, + splitNumber: 5, + scale: true, + splitLine: { + show: true, + lineStyle: { + color: splitLineColor + } + } + }, + legend: { + show: false, + selected: Object.fromEntries( + selectedKPIs.value.map(kpiId => [ + kpiColumns.value.find(col => col.kpiId === kpiId)?.title || kpiId, + selectedRow.value ? kpiId === selectedRow.value : true + ]) + ) + }, + tooltip: { + trigger: 'axis', + position: function (point: number[], params: any, dom: HTMLElement, rect: any, size: { viewSize: number[], contentSize: number[] }) { + const [x, y] = point; + const [viewWidth] = size.viewSize; + const [tooltipWidth, tooltipHeight] = size.contentSize; + + // 距离右侧的距离 + const rightSpace = viewWidth - x; + + // 计算垂直方向的居中位置 + // 将 tooltip 的中心点对齐到鼠标位置 + const verticalOffset = -tooltipHeight / 2; + + // 如果右侧空间不足以显示tooltip(假设需要20px的安全距离) + if (rightSpace < tooltipWidth + 20) { + // 向左显示,垂直居中 + return [x - tooltipWidth - 10, y + verticalOffset]; + } + + // 默认向右显示,垂直居中 + return [x + 10, y + verticalOffset]; + }, + backgroundColor: document.documentElement.getAttribute('data-theme') === 'dark' + ? 'rgba(48, 48, 48, 0.8)' + : 'rgba(255, 255, 255, 0.9)', + borderColor: document.documentElement.getAttribute('data-theme') === 'dark' + ? '#555' + : '#ddd', + textStyle: { + color: document.documentElement.getAttribute('data-theme') === 'dark' + ? '#CACADA' + : '#333' + }, + extraCssText: 'box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);' + }, + // 重新设置系列数据 + series: selectedKPIs.value.map(kpiId => { + const kpi = kpiColumns.value.find(col => col.kpiId === kpiId); + if (!kpi) return null; + const color = kpiColors.get(kpiId) || generateColorRGBA(); + return { + name: kpi.title, + type: 'line', + data: chartData.value.map(item => item[kpiId] || 0), + itemStyle: { color }, + ...getSeriesConfig(), + }; + }).filter(Boolean) + }; + + // 使用新的配置更新图表 + chart!.setOption(option, true); + // 强制重新渲染 + chart!.resize(); + }); + } +}); const updateChart = () => { if (!chart || !kpiColumns.value.length) return; @@ -299,6 +428,12 @@ const updateChart = () => { title: { text: t('views.perfManage.kpiOverView.kpiChartTitle'), left: 'center', + // 添加文字颜色配置,根据主题切换 + textStyle: { + color: document.documentElement.getAttribute('data-theme') === 'dark' + ? '#CACADA' + : '#333' + } }, tooltip: { trigger: 'axis', @@ -323,6 +458,18 @@ const updateChart = () => { // 默认向右显示,垂直居中 return [x + 10, y + verticalOffset]; }, + backgroundColor: document.documentElement.getAttribute('data-theme') === 'dark' + ? 'rgba(48, 48, 48, 0.8)' + : 'rgba(255, 255, 255, 0.9)', + borderColor: document.documentElement.getAttribute('data-theme') === 'dark' + ? '#555' + : '#ddd', + textStyle: { + color: document.documentElement.getAttribute('data-theme') === 'dark' + ? '#CACADA' + : '#333' + }, + extraCssText: 'box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);' }, legend: { data: selectedKPIs.value.map(kpiId => @@ -356,11 +503,16 @@ const updateChart = () => { xAxis: { // 指定x轴类型为类目轴,适用于离散的类目数据 type: 'category', + axisLabel: { + formatter: '{value}', + color: document.documentElement.getAttribute('data-theme') === 'dark' + ? '#CACADA' + : '#333' + }, splitLine: { show: true, lineStyle: { - // 使用深浅的间隔色 - color: '#aaa', + color: getSplitLineColor() } }, //控制坐标轴两边留白 @@ -402,11 +554,20 @@ const updateChart = () => { type: 'value', axisLabel: { formatter: '{value}', + color: document.documentElement.getAttribute('data-theme') === 'dark' + ? '#CACADA' + : '#333' }, // 添加自���计算的分割段数 splitNumber: 5, // 添加自动计算的最小/最大值范围 scale: true, + splitLine: { + show: true, + lineStyle: { + color: getSplitLineColor() + } + } }, series: series, //配置数据 }; @@ -435,6 +596,8 @@ const updateChart = () => { } }; + + //钩子函数 onMounted(async () => { try { @@ -457,6 +620,11 @@ onMounted(async () => { } else { console.error('Chart container not found'); } + // 添加主题观察器 + themeObserver.observe(document.documentElement, { + attributes: true, + attributeFilter: ['data-theme'] + }); } catch (error) { console.error('Failed to initialize:', error); message.error(t('common.initFail')); @@ -525,6 +693,8 @@ onUnmounted(() => { chart.dispose(); chart = null; } + // 断开主题观察器 + themeObserver.disconnect(); }); // 实时数据更新图表数据方法 From f318f61b4ae3555eb942ce8d8d7edf98cd93ed49 Mon Sep 17 00:00:00 2001 From: zhongzm Date: Fri, 22 Nov 2024 16:15:25 +0800 Subject: [PATCH 3/3] =?UTF-8?q?fix=EF=BC=9A=E6=98=8E=E6=9A=97=E4=B8=BB?= =?UTF-8?q?=E9=A2=98=E9=9A=8F=E6=9C=BA=E9=A2=9C=E8=89=B2=E6=96=B9=E6=B3=95?= =?UTF-8?q?=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/utils/generate-utils.ts | 50 +++++++++------- src/views/perfManage/kpiOverView/index.vue | 66 ++++++++++++++-------- 2 files changed, 72 insertions(+), 44 deletions(-) diff --git a/src/utils/generate-utils.ts b/src/utils/generate-utils.ts index 863eeb28..ec39385b 100644 --- a/src/utils/generate-utils.ts +++ b/src/utils/generate-utils.ts @@ -13,29 +13,41 @@ export function generateColorHEX(): string { * @returns rgb(24 144 255) / rgba(0,0,0,.85) */ export function generateColorRGBA(hasAlpha: boolean = false) { - let red:number; - let green: number; - let blue:number; - red = Math.floor(Math.random() * 256)+100;//+100增加顏色亮度 - green = Math.floor(Math.random() * 256)+100; - blue = Math.floor(Math.random() * 256)+100; + const isDark = document.documentElement.getAttribute('data-theme') === 'dark'; - const brightChannel = Math.floor(Math.random() * 3); - switch (brightChannel) { - case 0: - red = Math.min(255, red + 50); - break; - case 1: - green = Math.min(255, green + 50); - break; - case 2: - blue = Math.min(255, blue + 50); - break; + let red: number; + let green: number; + let blue: number; + + if (isDark) { + // 暗色模式下生成较亮的颜色 + red = Math.floor(Math.random() * 156) + 100; // 100-255 + green = Math.floor(Math.random() * 156) + 100; // 100-255 + blue = Math.floor(Math.random() * 156) + 100; // 100-255 + + // 确保至少有一个通道的值较高,使颜色更明亮 + const brightChannel = Math.floor(Math.random() * 3); + switch (brightChannel) { + case 0: + red = Math.min(255, red + 50); + break; + case 1: + green = Math.min(255, green + 50); + break; + case 2: + blue = Math.min(255, blue + 50); + break; + } + } else { + // 亮色模式下生成正常的颜色 + red = Math.floor(Math.random() * 256); // 0-255 + green = Math.floor(Math.random() * 256); // 0-255 + blue = Math.floor(Math.random() * 256); // 0-255 } if (hasAlpha) { - const alpha = Math.floor(Math.random() * 100)+50; - return `rgb(${red}, ${green}, ${blue}, 0.${alpha})`; + const alpha = Math.floor(Math.random() * 100); + return `rgba(${red}, ${green}, ${blue}, 0.${alpha})`; } return `rgb(${red}, ${green}, ${blue})`; diff --git a/src/views/perfManage/kpiOverView/index.vue b/src/views/perfManage/kpiOverView/index.vue index 3ab8f8b6..b7a900e3 100644 --- a/src/views/perfManage/kpiOverView/index.vue +++ b/src/views/perfManage/kpiOverView/index.vue @@ -247,6 +247,7 @@ const fetchChartData = async () => { chartData.value = processChartData(allData); updateChart(); + updateKpiStats(); } catch (error) { console.error('Failed to fetch chart data:', error); message.error(t('common.getInfoFail')); @@ -276,11 +277,31 @@ const getSplitLineColor = () => { // 添加主题变化的观察器 const themeObserver = new MutationObserver(() => { if (chart) { + //清空颜色缓存 + kpiColors.clear(); // 获取当前的主题色 const splitLineColor = getSplitLineColor(); // 重新设置完整的图表配置并触发重新渲染 requestAnimationFrame(() => { + // 先生成所有新的颜色并存储 + selectedKPIs.value.forEach(kpiId => { + const color = generateColorRGBA(); + kpiColors.set(kpiId, color); + }); + // 使用存储的颜色更新图表系列 + const series = selectedKPIs.value.map(kpiId => { + const kpi = kpiColumns.value.find(col => col.kpiId === kpiId); + if (!kpi) return null; + + return { + name: kpi.title, + type: 'line', + data: chartData.value.map(item => item[kpiId] || 0), + itemStyle: { color: kpiColors.get(kpiId) }, + ...getSeriesConfig(), + }; + }).filter(Boolean); const option = { title: { text: t('views.perfManage.kpiOverView.kpiChartTitle'), @@ -373,24 +394,17 @@ const themeObserver = new MutationObserver(() => { extraCssText: 'box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);' }, // 重新设置系列数据 - series: selectedKPIs.value.map(kpiId => { - const kpi = kpiColumns.value.find(col => col.kpiId === kpiId); - if (!kpi) return null; - const color = kpiColors.get(kpiId) || generateColorRGBA(); - return { - name: kpi.title, - type: 'line', - data: chartData.value.map(item => item[kpiId] || 0), - itemStyle: { color }, - ...getSeriesConfig(), - }; - }).filter(Boolean) + series }; // 使用新的配置更新图表 chart!.setOption(option, true); // 强制重新渲染 chart!.resize(); + // 更新统计数据 + nextTick(() => { + updateKpiStats(); + }); }); } }); @@ -407,14 +421,6 @@ const updateChart = () => { const color = kpiColors.get(kpiId)||generateColorRGBA(); kpiColors.set(kpiId, color); - // 获取数据数组 - const data = chartData.value.map((item, index) => { - const value = item[kpiId] || 0; - // 如果是最后一个数据点,添加特殊样式 - - return value; - }); - return { name: kpi.title, type: 'line', @@ -743,10 +749,16 @@ interface KPIStats { } // 添加计算属性,用于计算每个指标的最大值和最小值 -const kpiStats = computed((): KPIStats[] => { - if (!chartData.value.length || !kpiColumns.value.length) return []; +// 将 kpiStats 从计算属性改为响应式引用 +const kpiStats = ref([]); - return selectedKPIs.value.map(kpiId => { +// 添加一个计算函数来更新统计数据 +const updateKpiStats = () => { + if (!chartData.value.length || !kpiColumns.value.length) { + kpiStats.value = []; + return; + } + kpiStats.value = selectedKPIs.value.map(kpiId => { // 找到对应的KPI标题 const kpi = kpiColumns.value.find(col => col.kpiId === kpiId); if (!kpi) return null; @@ -767,7 +779,8 @@ const kpiStats = computed((): KPIStats[] => { avg:avg }; }).filter((item): item is KPIStats => item !== null); -}); +}; + // 添加表格列定义 const statsColumns: TableColumnType[] = [ @@ -776,9 +789,12 @@ const statsColumns: TableColumnType[] = [ key: 'icon', width: 50, customRender: ({ record }: { record: KPIStats }) => { + // 获取当前主题下的颜色 + // 直接使用 kpiColors 中存储的颜色 + const color = kpiColors.get(record.kpiId); return h(LineOutlined, { style: { - color: kpiColors.get(record.kpiId) || '#000', // 使用与折线图相同的颜色 + color: color || '#000', // 使用与折线图相同的颜色 fontSize: '30px', // 增大图标尺寸到30px fontWeight: 'bold', // 加粗 }