fix rest day style in chart

This commit is contained in:
Timi
2026-09-23 16:12:09 +08:00
parent 9f4a7d7b2d
commit f8574c387d
7 changed files with 80 additions and 20 deletions
+21 -2
View File
@@ -24,7 +24,7 @@ import type { EChartsOption } from "echarts";
import ChartRangeSlider from "@/components/ChartRangeSlider.vue"; import ChartRangeSlider from "@/components/ChartRangeSlider.vue";
import { useStoreChart, useStoreChartWindow } from "@/pages/store/component/useStoreChart"; import { useStoreChart, useStoreChartWindow } from "@/pages/store/component/useStoreChart";
type ChartOptionBuilder = (visibleDateTextList: string[]) => EChartsOption; type ChartOptionBuilder = (visibleDateTextList: string[], chartWidth: number) => EChartsOption;
const props = withDefaults(defineProps<{ const props = withDefaults(defineProps<{
caption?: string; caption?: string;
@@ -42,6 +42,7 @@ const props = withDefaults(defineProps<{
}); });
const dateTextList = computed(() => props.dateTextList); const dateTextList = computed(() => props.dateTextList);
const chartWidth = ref(window.innerWidth);
const { const {
rangeText, rangeText,
showSlider: chartShowSlider, showSlider: chartShowSlider,
@@ -51,9 +52,27 @@ const {
handleSliderChange handleSliderChange
} = useStoreChartWindow(dateTextList); } = useStoreChartWindow(dateTextList);
const option = computed<EChartsOption>(() => typeof props.option === "function" const option = computed<EChartsOption>(() => typeof props.option === "function"
? props.option(visibleDateTextList.value) ? props.option(visibleDateTextList.value, chartWidth.value)
: props.option); : props.option);
const { chartElement } = useStoreChart(option); const { chartElement } = useStoreChart(option);
let resizeObserver: ResizeObserver | undefined;
onMounted(() => {
resizeObserver = new window.ResizeObserver(updateChartWidth);
if (chartElement.value) {
resizeObserver.observe(chartElement.value);
updateChartWidth();
}
});
onBeforeUnmount(() => {
resizeObserver?.disconnect();
resizeObserver = undefined;
});
function updateChartWidth(): void {
chartWidth.value = chartElement.value?.clientWidth || window.innerWidth;
}
</script> </script>
<style scoped lang="less"> <style scoped lang="less">
@@ -29,14 +29,14 @@ const props = defineProps<{
const dateTextList = computed(() => props.dateTextList); const dateTextList = computed(() => props.dateTextList);
const { restDaySet } = useRestDayCalendar(dateTextList); const { restDaySet } = useRestDayCalendar(dateTextList);
function buildOption(visibleDateTextList: string[]): EChartsOption { function buildOption(visibleDateTextList: string[], chartWidth: number): EChartsOption {
const labelList = visibleDateTextList.map(item => item.slice(5)); const labelList = visibleDateTextList.map(item => item.slice(5));
return { return {
...buildAxisChartOption(labelList), ...buildAxisChartOption(labelList),
xAxis: { xAxis: {
type: "category", type: "category",
data: labelList, data: labelList,
axisTick: { show: false }, axisTick: { show: true, length: 0, interval: 0 },
axisLabel: { color: "#52607a", interval: "auto", hideOverlap: true } axisLabel: { color: "#52607a", interval: "auto", hideOverlap: true }
}, },
yAxis: [ yAxis: [
@@ -62,7 +62,7 @@ function buildOption(visibleDateTextList: string[]): EChartsOption {
name: "订单数", name: "订单数",
type: "bar", type: "bar",
yAxisIndex: 0, yAxisIndex: 0,
markArea: buildRestDayMarkArea(visibleDateTextList, restDaySet.value, window.innerWidth - 32), markArea: buildRestDayMarkArea(visibleDateTextList, restDaySet.value, chartWidth),
tooltip: { valueFormatter: value => formatQuantity(Number(value)) }, tooltip: { valueFormatter: value => formatQuantity(Number(value)) },
data: visibleDateTextList.map(dateText => getNumber(getDailyItem(props.list, dateText)?.orderCount)) data: visibleDateTextList.map(dateText => getNumber(getDailyItem(props.list, dateText)?.orderCount))
}, },
@@ -29,14 +29,14 @@ const props = defineProps<{
const dateTextList = computed(() => props.dateTextList); const dateTextList = computed(() => props.dateTextList);
const { restDaySet } = useRestDayCalendar(dateTextList); const { restDaySet } = useRestDayCalendar(dateTextList);
function buildOption(visibleDateTextList: string[]): EChartsOption { function buildOption(visibleDateTextList: string[], chartWidth: number): EChartsOption {
const labelList = visibleDateTextList.map(item => item.slice(5)); const labelList = visibleDateTextList.map(item => item.slice(5));
return { return {
...buildAxisChartOption(labelList), ...buildAxisChartOption(labelList),
xAxis: { xAxis: {
type: "category", type: "category",
data: labelList, data: labelList,
axisTick: { show: false }, axisTick: { show: true, length: 0, interval: 0 },
axisLabel: { color: "#52607a", interval: "auto", hideOverlap: true } axisLabel: { color: "#52607a", interval: "auto", hideOverlap: true }
}, },
series: [ series: [
@@ -44,7 +44,7 @@ function buildOption(visibleDateTextList: string[]): EChartsOption {
name: "直接履约", name: "直接履约",
type: "bar", type: "bar",
stack: "fulfillment", stack: "fulfillment",
markArea: buildRestDayMarkArea(visibleDateTextList, restDaySet.value, window.innerWidth - 32), markArea: buildRestDayMarkArea(visibleDateTextList, restDaySet.value, chartWidth),
data: visibleDateTextList.map(dateText => getNumber(getDailyItem(props.list, dateText)?.directBaseQuantity)) data: visibleDateTextList.map(dateText => getNumber(getDailyItem(props.list, dateText)?.directBaseQuantity))
}, },
{ {
@@ -29,14 +29,14 @@ const props = defineProps<{
const dateTextList = computed(() => props.dateTextList); const dateTextList = computed(() => props.dateTextList);
const { restDaySet } = useRestDayCalendar(dateTextList); const { restDaySet } = useRestDayCalendar(dateTextList);
function buildOption(visibleDateTextList: string[]): EChartsOption { function buildOption(visibleDateTextList: string[], chartWidth: number): EChartsOption {
const labelList = visibleDateTextList.map(item => item.slice(5)); const labelList = visibleDateTextList.map(item => item.slice(5));
return { return {
...buildAxisChartOption(labelList), ...buildAxisChartOption(labelList),
xAxis: { xAxis: {
type: "category", type: "category",
data: labelList, data: labelList,
axisTick: { show: false }, axisTick: { show: true, length: 0, interval: 0 },
axisLabel: { color: "#52607a", interval: "auto", hideOverlap: true } axisLabel: { color: "#52607a", interval: "auto", hideOverlap: true }
}, },
yAxis: [ yAxis: [
@@ -62,7 +62,7 @@ function buildOption(visibleDateTextList: string[]): EChartsOption {
type: "bar", type: "bar",
stack: "quantity", stack: "quantity",
yAxisIndex: 0, yAxisIndex: 0,
markArea: buildRestDayMarkArea(visibleDateTextList, restDaySet.value, window.innerWidth - 32), markArea: buildRestDayMarkArea(visibleDateTextList, restDaySet.value, chartWidth),
data: visibleDateTextList.map(dateText => getNumber(getDailyItem(props.list, dateText)?.directBaseQuantity)) data: visibleDateTextList.map(dateText => getNumber(getDailyItem(props.list, dateText)?.directBaseQuantity))
}, },
{ {
@@ -29,7 +29,7 @@ const props = defineProps<{
const dateTextList = computed(() => props.dateTextList); const dateTextList = computed(() => props.dateTextList);
const { restDaySet } = useRestDayCalendar(dateTextList); const { restDaySet } = useRestDayCalendar(dateTextList);
function buildOption(visibleDateTextList: string[]): EChartsOption { function buildOption(visibleDateTextList: string[], chartWidth: number): EChartsOption {
const labelList = visibleDateTextList.map(item => item.slice(5)); const labelList = visibleDateTextList.map(item => item.slice(5));
return { return {
...buildAxisChartOption(labelList), ...buildAxisChartOption(labelList),
@@ -37,7 +37,7 @@ function buildOption(visibleDateTextList: string[]): EChartsOption {
type: "category", type: "category",
data: labelList, data: labelList,
axisLine: { onZero: true }, axisLine: { onZero: true },
axisTick: { show: false }, axisTick: { show: true, length: 0, interval: 0 },
axisLabel: { color: "#52607a", interval: "auto", hideOverlap: true } axisLabel: { color: "#52607a", interval: "auto", hideOverlap: true }
}, },
series: [ series: [
@@ -45,7 +45,7 @@ function buildOption(visibleDateTextList: string[]): EChartsOption {
name: "入库", name: "入库",
type: "bar", type: "bar",
stack: "stock-movement", stack: "stock-movement",
markArea: buildRestDayMarkArea(visibleDateTextList, restDaySet.value, window.innerWidth - 32), markArea: buildRestDayMarkArea(visibleDateTextList, restDaySet.value, chartWidth),
data: visibleDateTextList.map(dateText => Math.abs(getNumber(getDailyItem(props.list, dateText)?.inQuantity))) data: visibleDateTextList.map(dateText => Math.abs(getNumber(getDailyItem(props.list, dateText)?.inQuantity)))
}, },
{ {
@@ -32,6 +32,7 @@ const MAX_TOOLTIP_CUSTOMER_COUNT = 24;
const statList = ref<CustomerDailyStatView[]>([]); const statList = ref<CustomerDailyStatView[]>([]);
let statRequestId = 0; let statRequestId = 0;
const chartWidth = ref(window.innerWidth);
// ---------- 图表窗口 ---------- // ---------- 图表窗口 ----------
@@ -47,6 +48,18 @@ const {
const { restDaySet } = useRestDayCalendar(dateTextList); const { restDaySet } = useRestDayCalendar(dateTextList);
const chartOption = computed<EChartsOption>(() => ({ const chartOption = computed<EChartsOption>(() => ({
...buildStoreBaseChartOption(visibleDateTextList.value), ...buildStoreBaseChartOption(visibleDateTextList.value),
xAxis: {
type: "category",
data: visibleDateTextList.value.map(item => item.slice(5)),
axisTick: {
show: true,
length: 0,
interval: 0
},
axisLabel: {
color: "#52607a"
}
},
grid: { grid: {
top: 48, top: 48,
left: 0, left: 0,
@@ -84,7 +97,6 @@ const chartOption = computed<EChartsOption>(() => ({
name: "客户总量", name: "客户总量",
type: "line", type: "line",
yAxisIndex: 0, yAxisIndex: 0,
markArea: buildRestDayMarkArea(visibleDateTextList.value, restDaySet.value, window.innerWidth - 32),
smooth: true, smooth: true,
showSymbol: false, showSymbol: false,
data: visibleDateTextList.value.map(dateText => getDailyStat(dateText)?.totalCustomerCount || 0) data: visibleDateTextList.value.map(dateText => getDailyStat(dateText)?.totalCustomerCount || 0)
@@ -93,6 +105,7 @@ const chartOption = computed<EChartsOption>(() => ({
name: "新客户量", name: "新客户量",
type: "bar", type: "bar",
yAxisIndex: 1, yAxisIndex: 1,
markArea: buildRestDayMarkArea(visibleDateTextList.value, restDaySet.value, chartWidth.value),
data: visibleDateTextList.value.map(dateText => getDailyStat(dateText)?.newCustomerCount || 0) data: visibleDateTextList.value.map(dateText => getDailyStat(dateText)?.newCustomerCount || 0)
} }
], ],
@@ -104,6 +117,20 @@ const chartOption = computed<EChartsOption>(() => ({
})); }));
const { chartElement } = useStoreChart(chartOption); const { chartElement } = useStoreChart(chartOption);
let resizeObserver: ResizeObserver | undefined;
onMounted(() => {
resizeObserver = new window.ResizeObserver(updateChartWidth);
if (chartElement.value) {
resizeObserver.observe(chartElement.value);
updateChartWidth();
}
});
onBeforeUnmount(() => {
resizeObserver?.disconnect();
resizeObserver = undefined;
});
watch(() => props.dateTextList, async () => { watch(() => props.dateTextList, async () => {
await loadCustomerDailyStat(); await loadCustomerDailyStat();
@@ -128,6 +155,10 @@ async function loadCustomerDailyStat(): Promise<void> {
} }
} }
function updateChartWidth(): void {
chartWidth.value = chartElement.value?.clientWidth || window.innerWidth;
}
function getDailyStat(dateText: string): CustomerDailyStatView | undefined { function getDailyStat(dateText: string): CustomerDailyStatView | undefined {
const dateValue = Number(dateText.replaceAll("-", "")); const dateValue = Number(dateText.replaceAll("-", ""));
return statList.value.find(item => item.dateValue === dateValue); return statList.value.find(item => item.dateValue === dateValue);
@@ -1,3 +1,4 @@
import { onMounted, watch } from "vue";
import type { ComputedRef } from "vue"; import type { ComputedRef } from "vue";
import type { MarkAreaComponentOption } from "echarts"; import type { MarkAreaComponentOption } from "echarts";
import StoreChartAPI from "@/api/StoreChartAPI"; import StoreChartAPI from "@/api/StoreChartAPI";
@@ -10,9 +11,9 @@ export function useRestDayCalendar(dateTextList: ComputedRef<string[]>) {
watch(dateTextList, async () => { watch(dateTextList, async () => {
await loadRestDays(); await loadRestDays();
}, { }, { deep: true });
deep: true, onMounted(async () => {
immediate: true await loadRestDays();
}); });
async function loadRestDays(): Promise<void> { async function loadRestDays(): Promise<void> {
@@ -51,17 +52,26 @@ export function buildRestDayMarkArea(
chartWidth: number chartWidth: number
): MarkAreaComponentOption | undefined { ): MarkAreaComponentOption | undefined {
const data: NonNullable<MarkAreaComponentOption["data"]> = []; const data: NonNullable<MarkAreaComponentOption["data"]> = [];
let restDayBeginIndex = -1;
for (let dateIndex = 0; dateIndex < dateTextList.length; dateIndex++) { for (let dateIndex = 0; dateIndex < dateTextList.length; dateIndex++) {
const dateText = dateTextList[dateIndex]; const dateText = dateTextList[dateIndex];
if (dateText && restDaySet.has(getDateValue(dateText))) { const isRestDay = Boolean(dateText && restDaySet.has(getDateValue(dateText)));
data.push([{ name: "休", xAxis: dateIndex }, { xAxis: dateIndex }]); if (isRestDay && restDayBeginIndex < 0) {
restDayBeginIndex = dateIndex;
} else if (!isRestDay && 0 <= restDayBeginIndex) {
data.push([{ name: "休", xAxis: restDayBeginIndex }, { xAxis: dateIndex - 1 }]);
restDayBeginIndex = -1;
} }
} }
if (0 <= restDayBeginIndex) {
data.push([{ name: "休", xAxis: restDayBeginIndex }, { xAxis: dateTextList.length - 1 }]);
}
if (!data.length) { if (!data.length) {
return undefined; return undefined;
} }
return { return {
silent: true, silent: true,
animation: false,
itemStyle: { itemStyle: {
color: "rgba(148, 163, 184, .12)" color: "rgba(148, 163, 184, .12)"
}, },