Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
211 changes: 202 additions & 9 deletions packages/traction-widget/components/Charts/Charts.vue
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,11 @@
</template>

<script setup lang="ts">
import { ref } from 'vue';
import { ref, onMounted, onUnmounted, watch, markRaw } from 'vue';
import { FDatePicker, FButton } from '@fesjs/fes-design';
import { ReloadOutlined } from '@fesjs/fes-design/icon';
import { useChart, type ChartConfig } from './useChart';
import type { EChartsOption, TooltipComponentFormatterCallback } from 'echarts';
import echarts from './useEcharts';
import {
getYear, getMonth, getDate, subDays, differenceInDays,
format,
Expand All @@ -38,6 +39,24 @@ import { useLocale } from '../hooks/useLocale';

const locale = useLocale();
const chartsLocale = locale.Charts;

interface BarStyle {
color: string;
borderColor: string;
}

interface ChartConfig {
title: string;
series: {
field: string;
name: string;
itemStyle: BarStyle;
}[];
fetchData: (startTime: number, endTime: number) => Promise<any[]>;
xAxisField: string;
tooltipFormatter?: (params: any[]) => string;
}

interface Props {
chartId: string;
config: ChartConfig;
Expand Down Expand Up @@ -83,13 +102,187 @@ const updateDays = (newDays: number) => {
endDate.value = _endDate.getTime();
};

// 图表相关
const { loading, updateChart } = useChart(
props.chartId,
props.config,
startDate,
endDate
);
// 图表相关逻辑
let chartInstance: echarts.ECharts | null = null;
const loading = ref(false);

const initChart = () => {
const chartDom = document.getElementById(props.chartId);
const instance = echarts.getInstanceByDom(chartDom as HTMLElement);
if (instance) {
instance.dispose();
}
if (chartDom) {
chartInstance = markRaw(echarts.init(chartDom));
}
};

const transformData = (data: any[]) => {
const xAxisData = data.map(item => item[props.config.xAxisField]);
// 计算每个系列的总和
const seriesTotal = props.config.series.reduce((acc, series) => {
const total = data.reduce((sum, item) => sum + (Number(item[series.field]) || 0), 0);
acc[series.name] = total;
return acc;
}, {} as Record<string, number>);

// 计算最大值来确定左侧留白
let maxSum = 0;
let leftGridSize = 16; // 基础留白大小

data.forEach(item => {
// 计算所有系列的当前数据点总和
const currentSum = props.config.series.reduce((sum, series) => {
return sum + (item[series.field] || 0);
}, 0);
maxSum = Math.max(maxSum, currentSum);
});

// 根据最大值的位数调整留白
const maxSumLen = String(maxSum).length;
if (maxSumLen > 0) {
leftGridSize += (maxSumLen - 1) * 8;
}

const series = props.config.series.map(item => ({
name: item.name,
type: 'bar' as const,
data: data.map(d => d[item.field]),
itemStyle: item.itemStyle,
emphasis: {
focus: 'series' as const
}
}));

return {
xAxisData,
series,
leftGridSize,
seriesTotal
};
};

const genTooltipStr = (tempData: any[]) => {
const showList = tempData.map((item: { value: any; seriesName: any; }) => ({
value: item.value,
name: item.seriesName
}));
const dateStr = showList.length ? `${chartsLocale.date}${tempData[0].name} <br />` : '';
const total = showList.reduce((pre: any, cur: { value: any; }) => pre + cur.value, 0);
const totalStr = showList.length ? `${chartsLocale.total}${total} <br />` : '';
const showListStr = showList.reduce((pre: any, cur: { name: any; value: any; }) =>
`${pre}${cur.name}: ${cur.value}<br />`, '');
return dateStr + totalStr + showListStr;
};

const updateChart = async () => {
if (!chartInstance) return;

try {
loading.value = true;
const data = await props.config.fetchData(startDate.value, endDate.value);
const { xAxisData, series, leftGridSize, seriesTotal } = transformData(data);

const option: EChartsOption = {
backgroundColor: '#fff',
grid: {
top: 17,
right: 0,
left: leftGridSize,
bottom: 95,
containLabel: true
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: "shadow",
},
formatter: ((params) => {
const formattedParams = params as any[];
return props.config.tooltipFormatter
? props.config.tooltipFormatter(formattedParams)
: genTooltipStr(formattedParams);
}) as TooltipComponentFormatterCallback<any>
},
dataZoom: {
type: 'slider',
startValue: 0,
endValue: 6,
xAxisIndex: [0],
handleSize: 0,
height: 5,
bottom: 70,
borderColor: 'transparent',
fillerColor: 'transparent',
backgroundColor: 'transparent',
showDataShadow: false,
showDetail: false,
realtime: true,
filterMode: 'filter'
},
legend: {
bottom: 0,
icon: 'rect',
height: 20,
itemWidth: 10,
itemHeight: 10,
itemGap: 32,
itemStyle: {
borderWidth: 1
},
formatter: (name) => {
return `${name}(${seriesTotal[name]})`;
},
data: props.config.series.map(item => ({
name: item.name,
itemStyle: item.itemStyle
}))
},
xAxis: [{
type: 'category',
axisTick: { show: false },
axisLine: { show: false },
data: xAxisData
}],
yAxis: [{
type: 'value',
splitLine: {
lineStyle: {
width: 1,
color: '#F1F1F2'
}
}
}],
series
};

chartInstance.setOption(option);
} finally {
loading.value = false;
}
};

const handleResize = () => {
chartInstance?.resize();
};

// 监听日期变化
watch([startDate, endDate], () => {
updateChart();
}, {
immediate: true
});

onMounted(() => {
initChart();
updateChart();
window.addEventListener('resize', handleResize);
});

onUnmounted(() => {
window.removeEventListener('resize', handleResize);
chartInstance?.dispose();
});

const lastUpdateTime = ref(format(new Date(), 'yyyy-MM-dd HH:mm:ss'));

Expand Down
9 changes: 3 additions & 6 deletions packages/traction-widget/components/Charts/useEcharts.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
// 引入 echarts 核心模块,核心模块提供了 echarts 使用必须要的接口。
import * as echarts from 'echarts/core';
// 引入柱状图图表,图表后缀都为 Chart
import * as charts from 'echarts/charts';
import { BarChart } from 'echarts/charts';
// 引入 Canvas 渲染器,注意引入 CanvasRenderer 或者 SVGRenderer 是必须的一步
import * as renderers from 'echarts/renderers';
import { CanvasRenderer } from 'echarts/renderers';
// 引入提示框,标题,直角坐标系,数据集,内置数据转换器组件,组件后缀都为 Component
import * as components from 'echarts/components';
import { TooltipComponent, GridComponent, DataZoomComponent, LegendComponent } from 'echarts/components';

const { BarChart } = charts;
const { CanvasRenderer } = renderers;
const { TooltipComponent, GridComponent, DataZoomComponent, LegendComponent } = components;
// 注册必须的组件
echarts.use([
TooltipComponent,
Expand Down
Loading