import type { ECharts, EChartsOption } from "echarts"; import { BarChart, LineChart } from "echarts/charts"; import { GridComponent, LegendComponent, TooltipComponent } from "echarts/components"; import { init, use } from "echarts/core"; import { CanvasRenderer } from "echarts/renderers"; import type { ComputedRef, Ref } from "vue"; use([ BarChart, LineChart, GridComponent, LegendComponent, TooltipComponent, CanvasRenderer ]); export function buildCustomerBaseChartOption(dateTextList: string[]): EChartsOption { return { color: ["#2563eb", "#0f766e", "#d97706", "#db2777", "#7c3aed"], grid: { top: 48, left: 12, right: 12, bottom: 12, containLabel: true }, legend: { top: 0, icon: "roundRect", textStyle: { color: "#52607a" } }, tooltip: { trigger: "axis" }, xAxis: { type: "category", data: dateTextList.map(item => item.slice(5)), axisTick: { show: false }, axisLabel: { color: "#52607a" } }, yAxis: { type: "value", axisLabel: { color: "#52607a" }, splitLine: { lineStyle: { color: "rgba(148, 163, 184, .2)" } } } }; } export function useCustomerChart(option: ComputedRef | Ref) { const chartElement = ref(); let chart: ECharts | undefined; watch(option, async () => { await nextTick(); renderChart(); }, { deep: true }); onMounted(async () => { await nextTick(); renderChart(); window.addEventListener("resize", resizeChart); }); onBeforeUnmount(() => { window.removeEventListener("resize", resizeChart); chart?.dispose(); chart = undefined; }); function renderChart(): void { if (!chartElement.value) { return; } chart = chart || init(chartElement.value); chart.setOption(option.value, true); } function resizeChart(): void { chart?.resize(); } return { chartElement }; } export function useCustomerChartWindow(dateTextList: ComputedRef) { const sliderValue = ref([0, 0]); const viewportWidth = ref(window.innerWidth); let isSliderInitialized = false; const visibleDayCount = computed(() => Math.max(6, Math.floor(viewportWidth.value / 64))); const maxSliderValue = computed(() => Math.max(0, dateTextList.value.length - 1)); const visibleDateTextList = computed(() => dateTextList.value.slice(sliderValue.value[0], sliderValue.value[1] + 1)); const showSlider = computed(() => visibleDayCount.value < dateTextList.value.length); const rangeText = computed(() => { const list = visibleDateTextList.value; const beginText = list[0]?.slice(5) || ""; const endText = list[list.length - 1]?.slice(5) || ""; return beginText && endText ? `${beginText} 至 ${endText}` : ""; }); watch(maxSliderValue, value => { if (!isSliderInitialized) { sliderValue.value = getLatestSliderRange(); isSliderInitialized = true; return; } const [start, end] = sliderValue.value; if (value < end) { sliderValue.value = [Math.max(0, value - Math.max(0, end - start)), value]; } }, { immediate: true }); onMounted(() => { window.addEventListener("resize", updateViewportWidth); }); onBeforeUnmount(() => { window.removeEventListener("resize", updateViewportWidth); }); function handleSliderChange(value: number | number[]): void { if (!Array.isArray(value)) { return; } const [start = 0, end = start] = value; sliderValue.value = [start, Math.max(start, end)]; } function updateViewportWidth(): void { viewportWidth.value = window.innerWidth; } function getLatestSliderRange(): number[] { const end = maxSliderValue.value; const start = Math.max(0, end - visibleDayCount.value + 1); return [start, end]; } return { rangeText, showSlider, sliderValue, maxSliderValue, visibleDateTextList, handleSliderChange }; }