From 01051a30308b5bc5138179ddfe2b16edcc58be8d Mon Sep 17 00:00:00 2001
From: zhangwencui <1064582902@qq.com>
Date: 星期三, 01 四月 2026 14:48:35 +0800
Subject: [PATCH] 销售统计看板修改
---
src/views/reportAnalysis/salesStatistics/index.vue | 790 +++++++++++++++++++++++++++++++++++++++++++++-----------
src/api/reportAnalysis/salesStatistics.js | 16 +
2 files changed, 649 insertions(+), 157 deletions(-)
diff --git a/src/api/reportAnalysis/salesStatistics.js b/src/api/reportAnalysis/salesStatistics.js
index 05b259e..bce6f28 100644
--- a/src/api/reportAnalysis/salesStatistics.js
+++ b/src/api/reportAnalysis/salesStatistics.js
@@ -14,4 +14,20 @@
url: '/home/total',
method: 'get'
})
+}
+// 閿�閲忓垎鏋愯秼鍔垮浘
+export function getSalesAnalysisTrend(params) {
+ return request({
+ url: '/home/salesAnalysis',
+ method: 'get',
+ params: params
+ })
+}
+// 閿�鍞噾棰濆垎鏋�
+export function getSalesAmountAnalysis(params) {
+ return request({
+ url: '/home/salesAmount',
+ method: 'get',
+ params: params
+ })
}
\ No newline at end of file
diff --git a/src/views/reportAnalysis/salesStatistics/index.vue b/src/views/reportAnalysis/salesStatistics/index.vue
index 9f35388..f500213 100644
--- a/src/views/reportAnalysis/salesStatistics/index.vue
+++ b/src/views/reportAnalysis/salesStatistics/index.vue
@@ -6,7 +6,7 @@
<div class="bi-topbar">
<img class="bi-topbar-title-bg"
src="@/assets/BI/biaoti.png"
- alt="閿�鍞湅鏉跨粺璁�" />
+ alt="閿�鍞粺璁$湅鏉�" />
<div class="bi-topbar-content">
<div class="bi-topbar-left">
<button class="fullscreen-btn"
@@ -35,7 +35,7 @@
<span>26鈩�</span>
<span class="bi-topbar-sep">婀垮害锛�1</span> -->
</div>
- <div class="bi-topbar-title">閿�鍞湅鏉跨粺璁�</div>
+ <div class="bi-topbar-title">閿�鍞粺璁$湅鏉�</div>
<div class="bi-topbar-meta">
<span class="bi-topbar-time">{{ currentTime }}</span>
<span class="bi-topbar-sep">|</span>
@@ -50,19 +50,19 @@
title="閿�閲忓垎鏋愯秼鍔垮浘" />
<div class="panel-tabs">
<span class="tab-item"
- :class="{ active: blockTimeDimension === 'year' }"
- @click="handleBlockTimeDimensionChange('year')">骞�</span>
+ :class="{ active: chartTimeDimension === '骞�' }"
+ @click="handleChartTimeDimensionChange('骞�')">骞�</span>
<span class="tab-item"
- :class="{ active: blockTimeDimension === 'month' }"
- @click="handleBlockTimeDimensionChange('month')">鏈�</span>
+ :class="{ active: chartTimeDimension === '鏈�' }"
+ @click="handleChartTimeDimensionChange('鏈�')">鏈�</span>
</div>
<div class="panel-tabs2">
<span class="tab-item"
- :class="{ active: blockProductType === '鐮屽潡' }"
- @click="handleBlockProductTypeChange('鐮屽潡')">鐮屽潡</span>
+ :class="{ active: chartProductType === '鐮屽潡' }"
+ @click="handleChartProductTypeChange('鐮屽潡')">鐮屽潡</span>
<span class="tab-item"
- :class="{ active: blockProductType === '鏉挎潗' }"
- @click="handleBlockProductTypeChange('鏉挎潗')">鏉挎潗</span>
+ :class="{ active: chartProductType === '鏉挎潗' }"
+ @click="handleChartProductTypeChange('鏉挎潗')">鏉挎潗</span>
</div>
<div class="bi-panel-body">
<div class="chart-unit-row">
@@ -78,19 +78,19 @@
title="閿�鍞噾棰濆垎鏋�" />
<div class="panel-tabs">
<span class="tab-item"
- :class="{ active: boardTimeDimension === 'year' }"
- @click="handleBoardTimeDimensionChange('year')">骞�</span>
+ :class="{ active: chartTimeDimension2 === '骞�' }"
+ @click="handleChartTimeDimensionChange2('骞�')">骞�</span>
<span class="tab-item"
- :class="{ active: boardTimeDimension === 'month' }"
- @click="handleBoardTimeDimensionChange('month')">鏈�</span>
+ :class="{ active: chartTimeDimension2 === '鏈�' }"
+ @click="handleChartTimeDimensionChange2('鏈�')">鏈�</span>
</div>
<div class="panel-tabs2">
<span class="tab-item"
- :class="{ active: boardProductType === '鐮屽潡' }"
- @click="handleBoardProductTypeChange('鐮屽潡')">鐮屽潡</span>
+ :class="{ active: chartProductType2 === '鐮屽潡' }"
+ @click="handleChartProductTypeChange2('鐮屽潡')">鐮屽潡</span>
<span class="tab-item"
- :class="{ active: boardProductType === '鏉挎潗' }"
- @click="handleBoardProductTypeChange('鏉挎潗')">鏉挎潗</span>
+ :class="{ active: chartProductType2 === '鏉挎潗' }"
+ @click="handleChartProductTypeChange2('鏉挎潗')">鏉挎潗</span>
</div>
<div class="bi-panel-body">
<div class="chart-unit-row">
@@ -131,27 +131,27 @@
title="閿�閲忔暟鎹粺璁�" />
<div class="panel-tabs">
<span class="tab-item"
- :class="{ active: blockTimeDimension === 'year' }"
- @click="handleBlockTimeDimensionChange('year')">骞�</span>
+ :class="{ active: tableTimeDimension === '骞�' }"
+ @click="handleTableTimeDimensionChange('骞�')">骞�</span>
<span class="tab-item"
- :class="{ active: blockTimeDimension === 'month' }"
- @click="handleBlockTimeDimensionChange('month')">鏈�</span>
+ :class="{ active: tableTimeDimension === '鏈�' }"
+ @click="handleTableTimeDimensionChange('鏈�')">鏈�</span>
</div>
<div class="panel-tabs2">
<span class="tab-item"
- :class="{ active: blockProductType === '鐮屽潡' }"
- @click="handleBlockProductTypeChange('鐮屽潡')">鐮屽潡</span>
+ :class="{ active: tableProductType === '鐮屽潡' }"
+ @click="handleTableProductTypeChange('鐮屽潡')">鐮屽潡</span>
<span class="tab-item"
- :class="{ active: blockProductType === '鏉挎潗' }"
- @click="handleBlockProductTypeChange('鏉挎潗')">鏉挎潗</span>
+ :class="{ active: tableProductType === '鏉挎潗' }"
+ @click="handleTableProductTypeChange('鏉挎潗')">鏉挎潗</span>
</div>
<div class="bi-panel-body">
<div class="chart-filter-tabs">
- <span v-for="area in salesAreas"
+ <span v-for="area in tableSalesAreas"
:key="area"
class="cf-tab"
- :class="{ active: blockSelectedArea === area }"
- @click="handleBlockAreaChange(area)">{{ area }}</span>
+ :class="{ active: tableSelectedArea === area }"
+ @click="handleTableAreaChange(area)">{{ area }}</span>
</div>
<div class="scroll-table-container">
<table class="scroll-table">
@@ -166,10 +166,10 @@
</thead>
<div class="scroll-table-content">
<tbody ref="blockTableBody">
- <tr :class="item.sort % 2 === 0 ? 'evenTableTr' : 'oddTableTr'"
- v-for="(item, index) in blockSalesData"
+ <tr :class="(index + 1) % 2 === 0 ? 'evenTableTr' : 'oddTableTr'"
+ v-for="(item, index) in filteredTableSalesData"
:key="item.period + item.area + index">
- <td>{{ item.sort }}</td>
+ <td>{{ index + 1 }}</td>
<td>{{ item.productType }}</td>
<td>{{ item.period }}</td>
<td>{{ item.area }}</td>
@@ -181,7 +181,7 @@
</div>
<div class="panel-summary-row">
<div class="summary-label">鍚堣</div>
- <div class="summary-value">127384 m鲁</div>
+ <div class="summary-value">{{ filteredTableSalesTotal }} m鲁</div>
</div>
</div>
</div>
@@ -211,44 +211,46 @@
title="閿�鍞鏁版嵁缁熻" />
<div class="panel-tabs">
<span class="tab-item"
- :class="{ active: boardTimeDimension === 'year' }"
- @click="handleBoardTimeDimensionChange('year')">骞�</span>
+ :class="{ active: tableTimeDimension2 === '骞�' }"
+ @click="handleTableTimeDimensionChange2('骞�')">骞�</span>
<span class="tab-item"
- :class="{ active: boardTimeDimension === 'month' }"
- @click="handleBoardTimeDimensionChange('month')">鏈�</span>
+ :class="{ active: tableTimeDimension2 === '鏈�' }"
+ @click="handleTableTimeDimensionChange2('鏈�')">鏈�</span>
</div>
<div class="panel-tabs2">
<span class="tab-item"
- :class="{ active: boardProductType === '鐮屽潡' }"
- @click="handleBoardProductTypeChange('鐮屽潡')">鐮屽潡</span>
+ :class="{ active: tableProductType2 === '鐮屽潡' }"
+ @click="handleTableProductTypeChange2('鐮屽潡')">鐮屽潡</span>
<span class="tab-item"
- :class="{ active: boardProductType === '鏉挎潗' }"
- @click="handleBoardProductTypeChange('鏉挎潗')">鏉挎潗</span>
+ :class="{ active: tableProductType2 === '鏉挎潗' }"
+ @click="handleTableProductTypeChange2('鏉挎潗')">鏉挎潗</span>
</div>
<div class="bi-panel-body">
<div class="chart-filter-tabs">
- <span v-for="area in salesAreas"
+ <span v-for="area in tableSalesAreas"
:key="area"
class="cf-tab"
- :class="{ active: boardSelectedArea === area }"
- @click="handleBoardAreaChange(area)">{{ area }}</span>
+ :class="{ active: tableSelectedArea === area }"
+ @click="handleTableAreaChange2(area)">{{ area }}</span>
</div>
<div class="scroll-table-container">
<table class="scroll-table">
<thead>
<tr>
<th>搴忓彿</th>
+ <th>浜у搧绫诲瀷</th>
<th>骞存湀</th>
<th>閿�鍞尯</th>
- <th>閿�鍞锛堜竾鍏冿級</th>
+ <th>閿�鍞锛堝厓锛�</th>
</tr>
</thead>
<div class="scroll-table-content">
<tbody ref="boardTableBody">
- <tr :class="item.sort % 2 === 0 ? 'evenTableTr' : 'oddTableTr'"
- v-for="(item, index) in boardSalesData"
+ <tr :class="(index + 1) % 2 === 0 ? 'evenTableTr' : 'oddTableTr'"
+ v-for="(item, index) in filteredAmountSalesData"
:key="item.period + item.area + index">
- <td>{{ item.sort }}</td>
+ <td>{{ index + 1 }}</td>
+ <td>{{ item.productType }}</td>
<td>{{ item.period }}</td>
<td>{{ item.area }}</td>
<td>{{ item.sales }}</td>
@@ -259,7 +261,7 @@
</div>
<div class="panel-summary-row">
<div class="summary-label">鍚堣</div>
- <div class="summary-value2">127384 涓囧厓</div>
+ <div class="summary-value2">{{ filteredAmountSalesTotal }} 鍏�</div>
</div>
</div>
</div>
@@ -283,6 +285,8 @@
import {
getDashboardStatistics,
getCustomerTrends,
+ getSalesAnalysisTrend,
+ getSalesAmountAnalysis,
} from "@/api/reportAnalysis/salesStatistics";
const router = useRouter();
@@ -343,10 +347,21 @@
const boardTableBody = ref(null);
// 閫夋嫨鍣ㄦ暟鎹�
- const blockTimeDimension = ref("year");
- const blockSelectedArea = ref("鍏ㄩ儴");
- const blockProductType = ref("鐮屽潡");
- const boardTimeDimension = ref("year");
+ // 閿�閲忓垎鏋愯秼鍔垮浘
+ const chartTimeDimension = ref("骞�");
+ const chartTimeDimension2 = ref("骞�");
+
+ const chartSelectedArea = ref("鍏ㄩ儴");
+ const chartProductType = ref("鐮屽潡");
+ const chartProductType2 = ref("鐮屽潡");
+
+ // 閿�閲忔暟鎹粺璁�
+ const tableTimeDimension = ref("骞�");
+ const tableSelectedArea = ref("鍏ㄩ儴");
+ const tableSelectedArea2 = ref("鍏ㄩ儴");
+
+ const tableProductType = ref("鐮屽潡");
+ const boardTimeDimension = ref("骞�");
const boardSelectedArea = ref("鍏ㄩ儴");
const boardProductType = ref("鏉挎潗");
const customerTimeDimension = ref("骞�");
@@ -675,6 +690,14 @@
// 瀹㈡埛瓒嬪娍鏁版嵁
const customerTrendsData = ref([]);
+ // 閿�閲忓垎鏋愯秼鍔挎暟鎹�
+ const salesAnalysisTrendData = ref([]);
+ // 閿�閲忔暟鎹粺璁¤〃鏍兼暟鎹�
+ const tableSalesData = ref([]);
+ // 閿�閲忔暟鎹粺璁¤〃鏍兼�昏
+ const tableSalesTotal = ref(0);
+ // 鍔ㄦ�侀攢鍞尯鍩熷垪琛�
+ const tableSalesAreas = ref([]);
// 鍙樺寲鐜囪绠楋紙妯℃嫙锛�
const salesVolumeChange = ref("+5.2");
@@ -717,6 +740,126 @@
}
};
+ // 鑾峰彇閿�閲忓垎鏋愯秼鍔挎暟鎹�
+ const fetchSalesAnalysisTrendData = async () => {
+ try {
+ const response = await getSalesAnalysisTrend({
+ type: chartProductType.value, // 鐮屽潡鎴栨澘鏉�
+ days: chartTimeDimension.value, // 骞存垨鏈�
+ });
+ if (response && response.data) {
+ // API杩斿洖鐨勬暟鎹粨鏋勫涓嬶細
+ // {
+ // "dates": ["2026-01-01", "2025-01-01", ...],
+ // "customerTrends": [{"鍐呰挋鍙�": 470, "閾跺窛": 3600, ...}, ...]
+ // }
+ salesAnalysisTrendData.value = response.data;
+ updateCharts();
+ }
+ } catch (error) {
+ console.error("鑾峰彇閿�閲忓垎鏋愯秼鍔挎暟鎹け璐�:", error);
+ }
+ };
+
+ // 鑾峰彇閿�閲忔暟鎹粺璁¤〃鏍兼暟鎹�
+ const fetchTableSalesData = async () => {
+ try {
+ const response = await getSalesAnalysisTrend({
+ type: tableProductType.value, // 鐮屽潡鎴栨澘鏉�
+ days: tableTimeDimension.value, // 骞存垨鏈�
+ });
+ if (response && response.data) {
+ // API杩斿洖鐨勬暟鎹粨鏋勫涓嬶細
+ // {
+ // "dates": ["2026-01-01", "2025-01-01", ...],
+ // "customerTrends": [{"鍐呰挋鍙�": 470, "閾跺窛": 3600, ...}, ...]
+ // }
+ updateTableSalesData(response.data);
+ }
+ } catch (error) {
+ console.error("鑾峰彇閿�閲忔暟鎹粺璁¤〃鏍兼暟鎹け璐�:", error);
+ }
+ };
+
+ // 鏇存柊閿�閲忔暟鎹粺璁¤〃鏍�
+ const updateTableSalesData = data => {
+ if (!data || !data.dates || !data.customerTrends) {
+ return;
+ }
+ console.log(data, "datas");
+ const dates = data.dates;
+ const customerTrends = data.customerTrends;
+ const tableData = [];
+ let total = 0;
+ const areaSet = new Set();
+
+ dates.forEach((date, index) => {
+ const trend = customerTrends[index];
+ if (trend) {
+ // 鎻愬彇鎵�鏈夐攢鍞尯鍩�
+ Object.keys(trend).forEach(area => {
+ if (area !== "鍏ㄩ儴") {
+ areaSet.add(area);
+ const sales = trend[area] || 0;
+ tableData.push({
+ period: date,
+ area: area,
+ productType: tableProductType.value,
+ sales: sales,
+ sort: tableData.length + 1,
+ });
+ total += sales;
+ }
+ });
+ }
+ });
+
+ // 鏇存柊閿�鍞尯鍩熷垪琛紝娣诲姞"鍏ㄩ儴"閫夐」
+ tableSalesAreas.value = ["鍏ㄩ儴", ...Array.from(areaSet)];
+ // 纭繚 tableSelectedArea 鍦ㄩ攢鍞尯鍩熷垪琛ㄤ腑
+ if (
+ tableSalesAreas.value.length > 0 &&
+ !tableSalesAreas.value.includes(tableSelectedArea.value)
+ ) {
+ tableSelectedArea.value = "鍏ㄩ儴";
+ }
+
+ console.log(tableData);
+ tableSalesData.value = tableData;
+ tableSalesTotal.value = total;
+ };
+
+ // 澶勭悊鍥捐〃鏃堕棿缁村害鍙樺寲
+ const handleChartTimeDimensionChange = dimension => {
+ chartTimeDimension.value = dimension;
+ fetchSalesAnalysisTrendData();
+ };
+
+ // 澶勭悊鍥捐〃浜у搧绫诲瀷鍙樺寲
+ const handleChartProductTypeChange = type => {
+ chartProductType.value = type;
+ fetchSalesAnalysisTrendData();
+ };
+
+ // 澶勭悊琛ㄦ牸鏃堕棿缁村害鍙樺寲
+ const handleTableTimeDimensionChange = dimension => {
+ tableTimeDimension.value = dimension;
+ fetchTableSalesData();
+ // 閲嶆柊鍚姩婊氬姩锛屾牴鎹椂闂寸淮搴﹀喅瀹氭槸鍚︽粴鍔�
+ startBlockTableScroll();
+ };
+
+ // 澶勭悊琛ㄦ牸浜у搧绫诲瀷鍙樺寲
+ const handleTableProductTypeChange = type => {
+ tableProductType.value = type;
+ fetchTableSalesData();
+ };
+
+ // 澶勭悊琛ㄦ牸閿�鍞尯鍩熷彉鍖�
+ const handleTableAreaChange = area => {
+ tableSelectedArea.value = area;
+ };
+
// 琛ㄦ牸鏁版嵁
const tableData = computed(() => {
return filteredData.value.map(item => {
@@ -734,17 +877,45 @@
});
});
+ // 绛涢�夊悗鐨勮〃鏍兼暟鎹�
+ const filteredTableSalesData = computed(() => {
+ if (tableSelectedArea.value === "鍏ㄩ儴") {
+ // 鎸夊勾鏈堝垎缁勬眹鎬绘暟鎹�
+ const groupedData = {};
+ tableSalesData.value.forEach(item => {
+ const key = item.period;
+ if (!groupedData[key]) {
+ groupedData[key] = {
+ period: item.period,
+ area: "鍏ㄩ儴",
+ productType: item.productType,
+ sales: 0,
+ sort: 0,
+ };
+ }
+ groupedData[key].sales += item.sales;
+ });
+ // 杞崲涓烘暟缁勫苟鎸夊勾鏈堟帓搴�
+ return Object.values(groupedData).sort((a, b) => {
+ return new Date(b.period) - new Date(a.period);
+ });
+ } else {
+ return tableSalesData.value.filter(
+ item => item.area === tableSelectedArea.value
+ );
+ }
+ });
+
+ // 绛涢�夊悗鐨勮〃鏍兼暟鎹�昏
+ const filteredTableSalesTotal = computed(() => {
+ return filteredTableSalesData.value.reduce(
+ (total, item) => total + item.sales,
+ 0
+ );
+ });
+
// 閿�閲忚秼鍔垮浘琛ㄩ厤缃�
const salesVolumeChartOption = computed(() => {
- // 涓烘瘡涓攢鍞尯鐢熸垚鏁版嵁
- const salesAreas = [
- "鍏ㄩ儴",
- "A閿�鍞尯",
- "B閿�鍞尯",
- "C閿�鍞尯",
- "D閿�鍞尯",
- "E閿�鍞尯",
- ];
const colors = [
"#00A4ED",
"#34D8F7",
@@ -752,54 +923,111 @@
"#8A6BFF",
"#C8C447",
"#FF6B6B",
+ "#FF9500",
+ "#4CD964",
+ "#5AC8FA",
];
- const year = 2024;
const periodType = blockTimeDimension.value;
// 鐢熸垚鏃堕棿娈�
let periods = [];
- if (periodType === "year") {
- // 骞村害鏁版嵁锛�12涓湀
- for (let month = 1; month <= 12; month++) {
- periods.push(`${year}-${month.toString().padStart(2, "0")}`);
+ let salesAreas = [];
+ let series = [];
+
+ if (
+ salesAnalysisTrendData.value &&
+ salesAnalysisTrendData.value.dates &&
+ salesAnalysisTrendData.value.customerTrends
+ ) {
+ // 浣跨敤API杩斿洖鐨勬棩鏈�
+ periods = salesAnalysisTrendData.value.dates;
+ // 鎻愬彇閿�鍞尯鍩�
+ const customerTrends = salesAnalysisTrendData.value.customerTrends;
+ if (customerTrends.length > 0) {
+ // 鎻愬彇閿�鍞尯鍩熷苟纭繚"鍏ㄩ儴"鍦ㄧ涓�涓綅缃�
+ const allAreas = Object.keys(customerTrends[0]);
+ salesAreas = allAreas.sort((a, b) => {
+ if (a === "鍏ㄩ儴") return -1;
+ if (b === "鍏ㄩ儴") return 1;
+ return 0;
+ });
+ // 涓烘瘡涓攢鍞尯鐢熸垚鏁版嵁
+ series = salesAreas.map((area, index) => {
+ const data = customerTrends.map(trend => trend[area] || 0);
+ return {
+ name: area,
+ data: data,
+ type: "line",
+ smooth: false,
+ // symbolSize: getResponsiveValue(8),
+ lineStyle: {
+ width: getResponsiveValue(1),
+ color: colors[index % colors.length],
+ },
+ itemStyle: { color: colors[index % colors.length] },
+ areaStyle: {
+ opacity: 0.4,
+ color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+ { offset: 0, color: colors[index % colors.length] + "80" },
+ { offset: 1, color: colors[index % colors.length] + "00" },
+ ]),
+ },
+ };
+ });
}
} else {
- // 鏈堝害鏁版嵁锛�30澶�
- const month = 1;
- for (let day = 1; day <= 30; day++) {
- periods.push(
- `${year}-${month.toString().padStart(2, "0")}-${day
- .toString()
- .padStart(2, "0")}`
- );
+ // 妯℃嫙鏁版嵁
+ salesAreas = [
+ "鍏ㄩ儴",
+ "A閿�鍞尯",
+ "B閿�鍞尯",
+ "C閿�鍞尯",
+ "D閿�鍞尯",
+ "E閿�鍞尯",
+ ];
+ const year = 2024;
+ if (periodType === "year") {
+ // 骞村害鏁版嵁锛�12涓湀
+ for (let month = 1; month <= 12; month++) {
+ periods.push(`${year}-${month.toString().padStart(2, "0")}`);
+ }
+ } else {
+ // 鏈堝害鏁版嵁锛�30澶�
+ const month = 1;
+ for (let day = 1; day <= 30; day++) {
+ periods.push(
+ `${year}-${month.toString().padStart(2, "0")}-${day
+ .toString()
+ .padStart(2, "0")}`
+ );
+ }
}
- }
+ // 涓烘瘡涓攢鍞尯鐢熸垚鏁版嵁
+ series = salesAreas.map((area, index) => {
+ const data = periods.map(() => {
+ return periodType === "year"
+ ? Math.floor(Math.random() * 500) + 800
+ : Math.floor(Math.random() * 50) + 20;
+ });
- // 涓烘瘡涓攢鍞尯鐢熸垚鏁版嵁
- const series = salesAreas.map((area, index) => {
- const data = periods.map(() => {
- return periodType === "year"
- ? Math.floor(Math.random() * 500) + 800
- : Math.floor(Math.random() * 50) + 20;
+ return {
+ name: area,
+ data: data,
+ type: "line",
+ smooth: false,
+ // symbolSize: getResponsiveValue(8),
+ lineStyle: { width: getResponsiveValue(1), color: colors[index] },
+ itemStyle: { color: colors[index] },
+ areaStyle: {
+ opacity: 0.4,
+ color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+ { offset: 0, color: colors[index] + "80" },
+ { offset: 1, color: colors[index] + "00" },
+ ]),
+ },
+ };
});
-
- return {
- name: area,
- data: data,
- type: "line",
- smooth: false,
- // symbolSize: getResponsiveValue(8),
- lineStyle: { width: getResponsiveValue(1), color: colors[index] },
- itemStyle: { color: colors[index] },
- areaStyle: {
- opacity: 0.4,
- color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
- { offset: 0, color: colors[index] + "80" },
- { offset: 1, color: colors[index] + "00" },
- ]),
- },
- };
- });
+ }
return {
backgroundColor: "transparent",
@@ -864,15 +1092,20 @@
// 閿�鍞噾棰濊秼鍔垮浘琛ㄩ厤缃�
const salesAmountChartOption = computed(() => {
- // 涓烘瘡涓攢鍞尯鐢熸垚鏁版嵁
- const salesAreas = [
- "鍏ㄩ儴",
- "A閿�鍞尯",
- "B閿�鍞尯",
- "C閿�鍞尯",
- "D閿�鍞尯",
- "E閿�鍞尯",
- ];
+ const { dates = [], customerTrends = [] } = salesAmountChartData.value;
+
+ // 鎻愬彇鎵�鏈夐攢鍞尯鍩�
+ const areaSet = new Set();
+ customerTrends.forEach(item => {
+ Object.keys(item).forEach(key => areaSet.add(key));
+ });
+ // 纭繚"鍏ㄩ儴"鍦ㄧ涓�涓綅缃�
+ const salesAreas = Array.from(areaSet).sort((a, b) => {
+ if (a === "鍏ㄩ儴") return -1;
+ if (b === "鍏ㄩ儴") return 1;
+ return 0;
+ });
+
const colors = [
"#00A4ED",
"#34D8F7",
@@ -881,48 +1114,26 @@
"#C8C447",
"#FF6B6B",
];
- const year = 2024;
- const periodType = boardTimeDimension.value;
-
- // 鐢熸垚鏃堕棿娈�
- let periods = [];
- if (periodType === "year") {
- // 骞村害鏁版嵁锛�12涓湀
- for (let month = 1; month <= 12; month++) {
- periods.push(`${year}-${month.toString().padStart(2, "0")}`);
- }
- } else {
- // 鏈堝害鏁版嵁锛�30澶�
- const month = 1;
- for (let day = 1; day <= 30; day++) {
- periods.push(
- `${year}-${month.toString().padStart(2, "0")}-${day
- .toString()
- .padStart(2, "0")}`
- );
- }
- }
// 涓烘瘡涓攢鍞尯鐢熸垚鏁版嵁
const series = salesAreas.map((area, index) => {
- const data = periods.map(() => {
- return periodType === "year"
- ? Math.floor(Math.random() * 50000) + 80000
- : Math.floor(Math.random() * 5000) + 2000;
- });
+ const data = customerTrends.map(item => item[area] || 0);
return {
name: area,
data: data,
type: "bar",
smooth: true,
- lineStyle: { width: getResponsiveValue(3), color: colors[index] },
- itemStyle: { color: colors[index] },
+ lineStyle: {
+ width: getResponsiveValue(3),
+ color: colors[index % colors.length],
+ },
+ itemStyle: { color: colors[index % colors.length] },
areaStyle: {
opacity: 0.2,
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
- { offset: 0, color: colors[index] + "80" },
- { offset: 1, color: colors[index] + "00" },
+ { offset: 0, color: colors[index % colors.length] + "80" },
+ { offset: 1, color: colors[index % colors.length] + "00" },
]),
},
};
@@ -937,7 +1148,7 @@
borderWidth: getResponsiveValue(1),
textStyle: { color: "#B8C8E0", fontSize: getResponsiveValue(11) },
formatter: function (params) {
- let result = params[0].name + "<br/>";
+ let result = params[0]?.name + "<br/>" || "";
params.forEach(param => {
result += `${param.marker}${param.seriesName}: ${param.value} 鍏�<br/>`;
});
@@ -964,7 +1175,7 @@
},
xAxis: {
type: "category",
- data: periods,
+ data: dates,
axisLine: { lineStyle: { color: "rgba(184,200,224,0.25)" } },
axisTick: { show: false },
axisLabel: {
@@ -1164,7 +1375,12 @@
areaSet.add(key);
});
});
- salesAreas = Array.from(areaSet);
+ // 纭繚"鍏ㄩ儴"鍦ㄧ涓�涓綅缃�
+ salesAreas = Array.from(areaSet).sort((a, b) => {
+ if (a === "鍏ㄩ儴") return -1;
+ if (b === "鍏ㄩ儴") return 1;
+ return 0;
+ });
// 涓烘瘡涓攢鍞尯鍩熺敓鎴愭暟鎹�
series = salesAreas.map((area, index) => {
@@ -1382,15 +1598,18 @@
let boardScrollTimer = null;
let blockCurrentIndex = 0;
let boardCurrentIndex = 0;
-
const startBlockTableScroll = () => {
if (blockScrollTimer) {
clearInterval(blockScrollTimer);
}
- const scrollTable = () => {
- if (!blockTableBody.value || blockSalesData.value.length === 0) return;
+ // 鍙湁褰撴椂闂寸淮搴︿笉鏄�"骞�"鏃舵墠鍚姩婊氬姩
+ if (tableTimeDimension.value === "骞�") {
+ return;
+ }
+ const scrollTable = () => {
+ if (!blockTableBody.value) return;
const rows = blockTableBody.value.querySelectorAll("tr");
if (rows.length === 0) return;
@@ -1403,9 +1622,10 @@
blockTableBody.value.style.transition = "none";
blockTableBody.value.style.transform = "translateY(0)";
- const firstItem = blockSalesData.value[0];
- blockSalesData.value.shift();
- blockSalesData.value.push(firstItem);
+ // 鐩存帴鎿嶄綔DOM锛屽皢绗竴琛岀Щ鍒版渶鍚�
+ const firstRow = rows[0];
+ blockTableBody.value.removeChild(firstRow);
+ blockTableBody.value.appendChild(firstRow);
}, 500);
};
@@ -1450,6 +1670,10 @@
clearInterval(boardScrollTimer);
boardScrollTimer = null;
}
+ if (amountScrollTimer.value) {
+ clearInterval(amountScrollTimer.value);
+ amountScrollTimer.value = null;
+ }
};
// 澶勭悊鏃堕棿缁村害閫夋嫨
@@ -1462,7 +1686,7 @@
boardTimeDimension.value = dimension;
generateBoardSalesData();
};
-
+ const blockProductType = ref("鐮屽潡");
// 澶勭悊浜у搧绫诲瀷閫夋嫨
const handleBlockProductTypeChange = type => {
blockProductType.value = type;
@@ -1473,7 +1697,7 @@
boardProductType.value = type;
generateBoardSalesData();
};
-
+ const blockSelectedArea = ref("鍏ㄩ儴");
// 澶勭悊閿�鍞尯閫夋嫨
const handleBlockAreaChange = area => {
blockSelectedArea.value = area;
@@ -1490,6 +1714,7 @@
customerTimeDimension.value = dimension;
fetchCustomerTrendsData();
};
+ const blockTimeDimension = ref("骞�");
// 鐢熸垚鐮屽潡閿�鍞暟鎹�
const generateBlockSalesData = () => {
@@ -1622,18 +1847,269 @@
// 鑾峰彇鏁版嵁
await fetchDashboardData();
await fetchCustomerTrendsData();
+ await fetchSalesAnalysisTrendData();
+ await fetchTableSalesData();
+ await fetchSalesAmountChartData();
+ await fetchSalesAmountTableData();
// 绛夊緟DOM鏇存柊鍚庡垵濮嬪寲鍥捐〃
nextTick(() => {
initCharts();
// 鍚姩琛ㄦ牸婊氬姩鍔ㄧ敾
startBlockTableScroll();
- startBoardTableScroll();
+ startAmountTableScroll();
});
// 娣诲姞绐楀彛澶у皬鍙樺寲鐩戝惉
window.addEventListener("resize", handleResize);
document.addEventListener("fullscreenchange", handleFullscreenChange);
+ });
+
+ // 鐩戝惉鍥捐〃鏃堕棿缁村害鍜屼骇鍝佺被鍨嬪彉鍖�
+ watch([chartTimeDimension, chartProductType], async () => {
+ await fetchSalesAnalysisTrendData();
+ });
+
+ // 鐩戝惉琛ㄦ牸鏃堕棿缁村害鍜屼骇鍝佺被鍨嬪彉鍖�
+ watch([tableTimeDimension, tableProductType], async () => {
+ await fetchTableSalesData();
+ });
+
+ // 閿�鍞噾棰濆垎鏋愬浘琛ㄦ暟鎹紙鍙充笂锛�
+ const salesAmountChartData = ref({
+ dates: [],
+ customerTrends: [],
+ });
+
+ // 閿�鍞鏁版嵁缁熻琛ㄦ牸鏁版嵁锛堝彸涓嬶級
+ const salesAmountTableData = ref({
+ dates: [],
+ customerTrends: [],
+ });
+
+ // 閿�鍞鏁版嵁缁熻琛ㄦ牸绛涢�夌姸鎬侊紙鍙充笅锛�
+ const tableTimeDimension2 = ref("骞�");
+ const tableProductType2 = ref("鐮屽潡");
+
+ // 閿�鍞鏁版嵁缁熻琛ㄦ牸鏁版嵁
+ const amountSalesData = ref([]);
+ const amountScrollTimer = ref(null);
+
+ // 鑾峰彇閿�鍞噾棰濆垎鏋愬浘琛ㄦ暟鎹紙鍙充笂锛�
+ const fetchSalesAmountChartData = async () => {
+ try {
+ const response = await getSalesAmountAnalysis({
+ type: chartProductType2.value,
+ days: chartTimeDimension2.value,
+ });
+ if (response?.data) {
+ salesAmountChartData.value = response.data;
+ updateCharts();
+ }
+ } catch (error) {
+ console.error("鑾峰彇閿�鍞噾棰濆垎鏋愬浘琛ㄦ暟鎹け璐�:", error);
+ // 浣跨敤妯℃嫙鏁版嵁
+ salesAmountChartData.value = {
+ dates: [
+ "2026-01-01",
+ "2025-01-01",
+ "2024-01-01",
+ "2023-01-01",
+ "2022-01-01",
+ ],
+ customerTrends: [
+ { 鍐呰挋鍙�: 100, 閾跺窛: 200, 鑷彁: 300, 鍏朵粬: 150, 鍏ㄩ儴: 750 },
+ { 鍐呰挋鍙�: 80, 閾跺窛: 180, 鑷彁: 280, 鍏朵粬: 130, 鍏ㄩ儴: 670 },
+ { 鍐呰挋鍙�: 90, 閾跺窛: 190, 鑷彁: 290, 鍏朵粬: 140, 鍏ㄩ儴: 710 },
+ { 鍐呰挋鍙�: 70, 閾跺窛: 170, 鑷彁: 270, 鍏朵粬: 120, 鍏ㄩ儴: 630 },
+ { 鍐呰挋鍙�: 110, 閾跺窛: 210, 鑷彁: 310, 鍏朵粬: 160, 鍏ㄩ儴: 790 },
+ ],
+ };
+ }
+ };
+
+ // 鑾峰彇閿�鍞鏁版嵁缁熻琛ㄦ牸鏁版嵁锛堝彸涓嬶級
+ const fetchSalesAmountTableData = async () => {
+ try {
+ const response = await getSalesAmountAnalysis({
+ type: tableProductType2.value,
+ days: tableTimeDimension2.value,
+ });
+ if (response?.data) {
+ salesAmountTableData.value = response.data;
+ updateAmountSalesData();
+ }
+ } catch (error) {
+ console.error("鑾峰彇閿�鍞鏁版嵁缁熻琛ㄦ牸鏁版嵁澶辫触:", error);
+ // 浣跨敤妯℃嫙鏁版嵁
+ salesAmountTableData.value = {
+ dates: [
+ "2026-01-01",
+ "2025-01-01",
+ "2024-01-01",
+ "2023-01-01",
+ "2022-01-01",
+ ],
+ customerTrends: [
+ { 鍐呰挋鍙�: 100, 閾跺窛: 200, 鑷彁: 300, 鍏朵粬: 150, 鍏ㄩ儴: 750 },
+ { 鍐呰挋鍙�: 80, 閾跺窛: 180, 鑷彁: 280, 鍏朵粬: 130, 鍏ㄩ儴: 670 },
+ { 鍐呰挋鍙�: 90, 閾跺窛: 190, 鑷彁: 290, 鍏朵粬: 140, 鍏ㄩ儴: 710 },
+ { 鍐呰挋鍙�: 70, 閾跺窛: 170, 鑷彁: 270, 鍏朵粬: 120, 鍏ㄩ儴: 630 },
+ { 鍐呰挋鍙�: 110, 閾跺窛: 210, 鑷彁: 310, 鍏朵粬: 160, 鍏ㄩ儴: 790 },
+ ],
+ };
+ updateAmountSalesData();
+ }
+ };
+
+ // 鏇存柊閿�鍞噾棰濆垎鏋愯〃鏍兼暟鎹�
+ const updateAmountSalesData = () => {
+ const data = [];
+ const { dates, customerTrends } = salesAmountTableData.value;
+
+ // 鎻愬彇鎵�鏈夐攢鍞尯鍩�
+ const areaSet = new Set();
+ customerTrends.forEach(item => {
+ Object.keys(item).forEach(key => areaSet.add(key));
+ });
+
+ // 鏇存柊閿�鍞尯鍩熷垪琛紝纭繚"鍏ㄩ儴"鍦ㄧ涓�浣�
+ tableSalesAreas.value = [
+ "鍏ㄩ儴",
+ ...Array.from(areaSet).filter(area => area !== "鍏ㄩ儴"),
+ ];
+
+ // 纭繚閫変腑鐨勫尯鍩熷湪鍒楄〃涓�
+ if (!tableSalesAreas.value.includes(tableSelectedArea.value)) {
+ tableSelectedArea.value = "鍏ㄩ儴";
+ }
+
+ // 鐢熸垚琛ㄦ牸鏁版嵁
+ dates.forEach((date, index) => {
+ const trends = customerTrends[index] || {};
+ Object.keys(trends).forEach(area => {
+ data.push({
+ period: date,
+ area: area,
+ productType: tableProductType2.value,
+ sales: trends[area],
+ sort: data.length + 1,
+ });
+ });
+ });
+
+ amountSalesData.value = data;
+ };
+
+ // 绛涢�夊悗鐨勯攢鍞噾棰濆垎鏋愯〃鏍兼暟鎹�
+ const filteredAmountSalesData = computed(() => {
+ if (tableSelectedArea.value === "鍏ㄩ儴") {
+ // 鎸夊勾鏈堝垎缁勬眹鎬绘暟鎹�
+ const groupedData = {};
+ amountSalesData.value.forEach(item => {
+ const key = item.period;
+ if (!groupedData[key]) {
+ groupedData[key] = {
+ period: item.period,
+ area: "鍏ㄩ儴",
+ productType: tableProductType2.value,
+ sales: 0,
+ };
+ }
+ groupedData[key].sales += item.sales;
+ });
+ // 杞崲涓烘暟缁勫苟鎸夊勾鏈堟帓搴�
+ return Object.values(groupedData).sort((a, b) => {
+ return new Date(b.period) - new Date(a.period);
+ });
+ } else {
+ return amountSalesData.value.filter(
+ item => item.area === tableSelectedArea.value
+ );
+ }
+ });
+
+ // 閿�鍞噾棰濆垎鏋愯〃鏍兼�昏
+ const filteredAmountSalesTotal = computed(() => {
+ return filteredAmountSalesData.value.reduce(
+ (total, item) => total + item.sales,
+ 0
+ );
+ });
+
+ // 澶勭悊閿�鍞噾棰濆垎鏋愬浘琛ㄦ椂闂寸淮搴﹀彉鍖栵紙鍙充笂锛�
+ const handleChartTimeDimensionChange2 = dimension => {
+ chartTimeDimension2.value = dimension;
+ fetchSalesAmountChartData();
+ };
+
+ // 澶勭悊閿�鍞噾棰濆垎鏋愬浘琛ㄤ骇鍝佺被鍨嬪彉鍖栵紙鍙充笂锛�
+ const handleChartProductTypeChange2 = type => {
+ chartProductType2.value = type;
+ fetchSalesAmountChartData();
+ };
+
+ // 澶勭悊閿�鍞鏁版嵁缁熻琛ㄦ牸鏃堕棿缁村害鍙樺寲锛堝彸涓嬶級
+ const handleTableTimeDimensionChange2 = dimension => {
+ tableTimeDimension2.value = dimension;
+ fetchSalesAmountTableData();
+ // 閲嶆柊鍚姩婊氬姩锛屾牴鎹椂闂寸淮搴﹀喅瀹氭槸鍚︽粴鍔�
+ startAmountTableScroll();
+ };
+
+ // 澶勭悊閿�鍞鏁版嵁缁熻琛ㄦ牸浜у搧绫诲瀷鍙樺寲锛堝彸涓嬶級
+ const handleTableProductTypeChange2 = type => {
+ tableProductType2.value = type;
+ fetchSalesAmountTableData();
+ };
+
+ // 澶勭悊閿�鍞鏁版嵁缁熻琛ㄦ牸閿�鍞尯鍙樺寲锛堝彸涓嬶級
+ const handleTableAreaChange2 = area => {
+ tableSelectedArea.value = area;
+ };
+
+ // 鍚姩閿�鍞噾棰濆垎鏋愯〃鏍兼粴鍔�
+ const startAmountTableScroll = () => {
+ if (amountScrollTimer.value) {
+ clearInterval(amountScrollTimer.value);
+ }
+ // 鍙湁褰撴椂闂寸淮搴︿笉鏄�"骞�"鏃舵墠鍚姩婊氬姩
+ if (tableTimeDimension2.value === "骞�") {
+ return;
+ }
+
+ const scrollTable = () => {
+ if (!boardTableBody.value) return;
+ const rows = boardTableBody.value.querySelectorAll("tr");
+ if (rows.length === 0) return;
+
+ const rowHeight = rows[0].offsetHeight;
+
+ boardTableBody.value.style.transition = "transform 0.5s ease-in-out";
+ boardTableBody.value.style.transform = `translateY(-${rowHeight}px)`;
+
+ setTimeout(() => {
+ boardTableBody.value.style.transition = "none";
+ boardTableBody.value.style.transform = "translateY(0)";
+
+ // 鐩存帴鎿嶄綔DOM锛屽皢绗竴琛岀Щ鍒版渶鍚�
+ const firstRow = rows[0];
+ boardTableBody.value.removeChild(firstRow);
+ boardTableBody.value.appendChild(firstRow);
+ }, 500);
+ };
+
+ amountScrollTimer.value = setInterval(scrollTable, 2000);
+ };
+
+ // 鐩戝惉閿�鍞噾棰濆垎鏋愬浘琛ㄦ椂闂寸淮搴﹀拰浜у搧绫诲瀷鍙樺寲锛堝彸涓婏級
+ watch([chartTimeDimension2, chartProductType2], async () => {
+ // await fetchSalesAmountChartData();
+ });
+
+ // 鐩戝惉閿�鍞鏁版嵁缁熻琛ㄦ牸鏃堕棿缁村害鍜屼骇鍝佺被鍨嬪彉鍖栵紙鍙充笅锛�
+ watch([tableTimeDimension2, tableProductType2], async () => {
+ // await fetchSalesAmountTableData();
});
// 鑾峰彇浜у搧绫诲瀷鏍囩绫诲瀷
@@ -1958,12 +2434,12 @@
}
/* .scroll-table tbody tr:nth-child(odd) {
- background-color: rgba(64, 158, 255, 0.05);
- }
+ background-color: rgba(64, 158, 255, 0.05);
+ }
- .scroll-table tbody tr:nth-child(even) {
- background-color: rgba(64, 158, 255, 0.1);
- } */
+ .scroll-table tbody tr:nth-child(even) {
+ background-color: rgba(64, 158, 255, 0.1);
+ } */
.oddTableTr {
background-color: rgba(64, 158, 255, 0.05);
}
@@ -2062,7 +2538,7 @@
font-size: 1.4vh;
font-weight: 800;
color: #00a4ed;
- margin-right: 5.8vh;
+ margin-right: 1.8vh;
text-shadow: 0 0 1vh rgba(0, 164, 237, 0.5);
}
.diamond {
--
Gitblit v1.9.3