From 51922d7093b9c8f52417bfdd0fe9aa087d1fb5be Mon Sep 17 00:00:00 2001
From: xiazhengtongxue <133085197+xiazhengtongxue@users.noreply.github.com>
Date: 星期五, 01 五月 2026 18:31:01 +0800
Subject: [PATCH] feat: 优化仓库仪表盘界面并添加电池和空托盘统计功能
---
Code/WMS/WIDESEA_WMSClient/src/views/Home.vue | 850 ++++++++++++++++++++++++++++++++++++++++++++------------
1 files changed, 666 insertions(+), 184 deletions(-)
diff --git a/Code/WMS/WIDESEA_WMSClient/src/views/Home.vue b/Code/WMS/WIDESEA_WMSClient/src/views/Home.vue
index 9023c51..96461c7 100644
--- a/Code/WMS/WIDESEA_WMSClient/src/views/Home.vue
+++ b/Code/WMS/WIDESEA_WMSClient/src/views/Home.vue
@@ -1,10 +1,82 @@
<template>
<div class="dashboard-container">
- <!-- 鍚勪粨搴撴湀搴﹀嚭鍏ュ簱瀵规瘮鍥� -->
- <div class="chart-row full-width">
+ <!-- 椤堕儴KPI鍗$墖锛氭樉绀烘�昏揣浣嶅強鍚勪粨搴撹揣浣� -->
+ <div class="kpi-cards">
+ <div class="kpi-card">
+ <div class="kpi-icon">馃彋锔�</div>
+ <div class="kpi-info">
+ <div class="kpi-label">鎬昏揣浣�</div>
+ <div class="kpi-value">{{ totalLocation }}</div>
+ </div>
+ </div>
+ <div class="kpi-card">
+ <div class="kpi-icon">馃敟</div>
+ <div class="kpi-info">
+ <div class="kpi-label">鍖栨垚搴�</div>
+ <div class="kpi-value">{{ warehouseLocations.hc }}</div>
+ </div>
+ </div>
+ <div class="kpi-card">
+ <div class="kpi-icon">馃尅锔�</div>
+ <div class="kpi-info">
+ <div class="kpi-label">楂樻俯搴�</div>
+ <div class="kpi-value">{{ warehouseLocations.gw }}</div>
+ </div>
+ </div>
+ <div class="kpi-card">
+ <div class="kpi-icon">鉂勶笍</div>
+ <div class="kpi-info">
+ <div class="kpi-label">甯告俯搴�</div>
+ <div class="kpi-value">{{ warehouseLocations.cw }}</div>
+ </div>
+ </div>
+ <div class="kpi-card">
+ <div class="kpi-icon">馃摐</div>
+ <div class="kpi-info">
+ <div class="kpi-label">鏋佸嵎搴�</div>
+ <div class="kpi-value">{{ warehouseLocations.jj }}</div>
+ </div>
+ </div>
+ </div>
+
+ <!-- 绗竴琛岋細4涓瘡鏃ュ嚭鍏ュ簱瓒嬪娍鍥撅紙姣忚2涓級 -->
+ <div class="chart-row daily-grid">
+ <div class="chart-card" v-for="warehouse in dailyWarehouses" :key="warehouse.code">
+ <div class="card-title">{{ warehouse.name }} - 姣忔棩瓒嬪娍</div>
+ <!-- 浠撳簱鏁板瓧鏄剧ず鍖哄煙锛氭樉绀烘瘡鏃ユ�婚噺鍜岀┖鎵樼洏鏁伴噺 -->
+ <div class="warehouse-numbers">
+ <!-- 鏋佸嵎搴撴樉绀烘湁璐ф墭鐩橈紙鐢垫睜鏁伴噺锛夊拰绌烘墭鐩� -->
+ <template v-if="warehouse.code === 'ROLL'">
+ <div class="number-item battery">
+ <span class="number-label">鐢垫睜鏁伴噺</span>
+ <span class="number-value">{{ getBatteryCount(warehouse.code) }}</span>
+ </div>
+ <div class="number-item empty-tray">
+ <span class="number-label">绌烘墭鐩樻暟閲�</span>
+ <span class="number-value">{{ getEmptyTrayCount(warehouse.code) }}</span>
+ </div>
+ </template>
+ <!-- 鍏朵粬浠撳簱鏄剧ず鐢垫睜鏁伴噺鍜岀┖鎵樼洏鏁伴噺 -->
+ <template v-else>
+ <div class="number-item inbound">
+ <span class="number-label">鐢垫睜鏁伴噺</span>
+ <span class="number-value">{{ getBatteryCount(warehouse.code) }}</span>
+ </div>
+ <div class="number-item empty-tray">
+ <span class="number-label">绌烘墭鐩樻暟閲�</span>
+ <span class="number-value">{{ getEmptyTrayCount(warehouse.code) }}</span>
+ </div>
+ </template>
+ </div>
+ <div :id="`daily-chart-${warehouse.code}`" class="chart-content"></div>
+ </div>
+ </div>
+
+ <!-- 浠撳簱鍒嗗竷锛堟煴鐘跺浘锛屾樉绀哄悇浠撳簱宸茬敤/鍓╀綑瀹归噺锛� -->
+ <div class="chart-row">
<div class="chart-card">
- <div class="card-title">鍚勪粨搴撴湀搴﹀嚭鍏ュ簱瀵规瘮</div>
- <div id="chart-warehouse-monthly" class="chart-content"></div>
+ <div class="card-title">鍚勪粨搴撳簱瀛樺垎甯�</div>
+ <div id="chart-warehouse" class="chart-content"></div>
</div>
</div>
</div>
@@ -18,9 +90,54 @@
data() {
return {
charts: {},
- monthlyData: [],
- warehouseNames: ['FJSC1', 'ZJSC1', 'GWSC1', 'CWSC1', 'HCSC1']
+ // 鍥涗釜鏍稿績浠撳簱锛堝悎骞朵簡姝h礋鏋佸嵎搴撲负鏋佸嵎搴擄級
+ dailyWarehouses: [
+ { code: "HCSC1", name: "鍖栨垚搴�", type: "hc" },
+ { code: "GWSC1", name: "楂樻俯搴�", type: "gw" },
+ { code: "CWSC1", name: "甯告俯搴�", type: "cw" },
+ { code: "ROLL", name: "鏋佸嵎搴�", type: "jj" }
+ ],
+ // 鍘熷姣忔棩鏁版嵁瀛樺偍 (鍏朵腑ROLL涓哄悎骞跺悗鐨勬瀬鍗峰簱鏁版嵁)
+ dailyDataMap: {
+ GWSC1: [],
+ CWSC1: [],
+ HCSC1: [],
+ ROLL: []
+ },
+ // 瀛樺偍姣忎釜浠撳簱鐨勭數姹犳暟閲�
+ warehouseStocks: {
+ GWSC1: 0,
+ CWSC1: 0,
+ HCSC1: 0,
+ ROLL: 0
+ },
+ // 鏋佸嵎搴撶壒娈婃暟鎹�
+ rollData: {
+ batteryCount: 0, // 鐢垫睜鏁伴噺
+ emptyTrayCount: 0 // 绌烘墭鐩樻暟閲�
+ },
+ // 鍏朵粬浠撳簱鐨勭┖鎵樼洏鏁伴噺
+ emptyTrayCounts: {
+ GWSC1: 0,
+ CWSC1: 0,
+ HCSC1: 0
+ },
+ warehouseData: [], // 浠撳簱鍒嗗竷鍥炬暟鎹�
+ // 浠撳簱璐т綅鏁版嵁锛堝浐瀹氶厤缃級
+ warehouseLocations: {
+ hc: 35, // 鍖栨垚搴�
+ gw: 324, // 楂樻俯搴�
+ cw: 140, // 甯告俯搴�
+ jj: 104 // 鏋佸嵎搴�
+ }
};
+ },
+ computed: {
+ // 鎬昏揣浣嶈绠�
+ totalLocation() {
+ return this.warehouseLocations.hc + this.warehouseLocations.gw +
+ this.warehouseLocations.cw + this.warehouseLocations.jj;
+ }
},
mounted() {
this.initCharts();
@@ -29,198 +146,461 @@
},
beforeUnmount() {
window.removeEventListener("resize", this.handleResize);
- Object.values(this.charts).forEach(chart => chart.dispose());
+ Object.values(this.charts).forEach(chart => chart && chart.dispose());
},
methods: {
handleResize() {
- Object.values(this.charts).forEach(chart => chart.resize());
+ Object.values(this.charts).forEach(chart => chart && chart.resize());
},
initCharts() {
- this.charts.warehouseMonthly = echarts.init(document.getElementById("chart-warehouse-monthly"));
+ // 鍒濆鍖栨瘡鏃ュ浘琛�
+ this.dailyWarehouses.forEach(warehouse => {
+ const chartId = `daily-chart-${warehouse.code}`;
+ const el = document.getElementById(chartId);
+ if (el) {
+ this.charts[`daily-${warehouse.code}`] = echarts.init(el);
+ }
+ });
+ // 鍒濆鍖栦粨搴撳垎甯冨浘琛�
+ this.charts.warehouse = echarts.init(document.getElementById("chart-warehouse"));
},
async loadData() {
- await this.loadMonthlyStats();
- },
-
- async loadMonthlyStats() {
try {
- const promises = this.warehouseNames.map(warehouse =>
- this.http.get("/api/Dashboard/MonthlyStats", {
- months: 6,
- Roadway: warehouse
- })
- );
+ // 骞惰鍔犺浇鎵�鏈夋暟鎹�
+ await Promise.all([
+ this.loadAllDailyStats(),
+ this.loadStockAndTrayCount(),
+ this.loadStockByWarehouse()
+ ]);
- const results = await Promise.all(promises);
-
- this.monthlyData = results.map((res, index) => ({
- warehouse: this.warehouseNames[index],
- warehouseName: this.getWarehouseName(this.warehouseNames[index]),
- data: res.data || []
- }));
-
- this.updateWarehouseMonthlyChart();
- } catch (e) {
- console.error("鍔犺浇姣忔湀缁熻澶辫触", e);
+ // 鏇存柊鎵�鏈夊浘琛�
+ this.updateAllDailyCharts();
+ } catch (error) {
+ console.error("鍔犺浇鏁版嵁澶辫触:", error);
+ this.$message?.error("鏁版嵁鍔犺浇澶辫触锛岃绋嶅悗閲嶈瘯");
}
},
- getWarehouseName(code) {
- const nameMap = {
- 'FJSC1': '璐熸瀬鍗�1鍙蜂粨搴�',
- 'ZJSC1': '姝f瀬鍗�1鍙蜂粨搴�',
- 'GWSC1': '楂樻俯1鍙蜂粨搴�',
- 'CWSC1': '甯告俯1鍙蜂粨搴�',
- 'HCSC1': '鍒嗗1鍙蜂粨搴�'
- };
- return nameMap[code] || code;
- },
-
- updateWarehouseMonthlyChart() {
- // 鑾峰彇鎵�鏈夋湀浠�
- const months = this.monthlyData[0]?.data.map(d => `${d.month}鏈坄) || [];
-
- // 涓烘瘡涓粨搴撶敓鎴愮郴鍒楁暟鎹�
- const series = [];
-
- this.monthlyData.forEach((warehouseData, index) => {
- const data = warehouseData.data;
+ async loadAllDailyStats() {
+ console.log("姝e湪鍔犺浇鎵�鏈変粨搴撶殑姣忔棩缁熻鏁版嵁...");
+ const res = await this.http.get("/api/Dashboard/DailyStats?days=10");
+ if (res.status && res.data) {
+ console.log("鎵�鏈変粨搴撴瘡鏃ョ粺璁℃暟鎹�:", res.data);
- series.push({
- name: warehouseData.warehouseName,
- type: 'bar',
- data: data.map(d => ({
- value: (d.inbound || 0) + (d.outbound || 0),
- inbound: d.inbound || 0,
- outbound: d.outbound || 0,
- label: {
- show: true,
- position: 'top',
- formatter: function(params) {
- return `鍏�:${params.data.inbound}\n鍑�:${params.data.outbound}`;
- },
- fontSize: 10,
- color: '#fff',
- lineHeight: 15
- }
- })),
- barWidth: '15%',
- barGap: '10%',
- itemStyle: {
- color: this.getBarColor(index),
- borderRadius: [3, 3, 0, 0]
+ const dataArray = res.data;
+
+ // 鎸変粨搴撳垎绫绘暟鎹�
+ dataArray.forEach(item => {
+ const roadway = item.roadway;
+ const dailyStats = item.dailyStats || [];
+
+ switch(roadway) {
+ case "GWSC1":
+ this.dailyDataMap.GWSC1 = dailyStats;
+ break;
+ case "CWSC1":
+ this.dailyDataMap.CWSC1 = dailyStats;
+ break;
+ case "HCSC1":
+ this.dailyDataMap.HCSC1 = dailyStats;
+ break;
+ case "ZJSC1":
+ case "FJSC1":
+ // 鏋佸嵎搴撴暟鎹悎骞跺鐞�
+ this.mergeRollDailyStats(dailyStats);
+ break;
}
});
+
+ console.log("GWSC1鏁版嵁:", this.dailyDataMap.GWSC1);
+ console.log("CWSC1鏁版嵁:", this.dailyDataMap.CWSC1);
+ console.log("HCSC1鏁版嵁:", this.dailyDataMap.HCSC1);
+ console.log("鏋佸嵎搴撳悎骞舵暟鎹�:", this.dailyDataMap.ROLL);
+ } else {
+ console.error("鑾峰彇姣忔棩鏁版嵁澶辫触");
+ }
+ },
+
+ mergeRollDailyStats(newData) {
+ // 鍚堝苟姝f瀬鍗峰簱鍜岃礋鏋佸嵎搴撶殑姣忔棩鏁版嵁
+ if (!this.dailyDataMap.ROLL.length) {
+ this.dailyDataMap.ROLL = [...newData];
+ } else {
+ const dateMap = new Map();
+ [...this.dailyDataMap.ROLL, ...newData].forEach(item => {
+ const date = item.date;
+ if (date) {
+ if (!dateMap.has(date)) {
+ dateMap.set(date, { date, inbound: 0, outbound: 0 });
+ }
+ const existing = dateMap.get(date);
+ existing.inbound += item.inbound || 0;
+ existing.outbound += item.outbound || 0;
+ }
+ });
+
+ const sortedDates = Array.from(dateMap.keys()).sort();
+ const mergedData = sortedDates.map(date => dateMap.get(date));
+ this.dailyDataMap["ROLL"] = mergedData;
+ }
+ },
+
+ async loadStockAndTrayCount() {
+ // 鍔犺浇鐢垫睜鏁伴噺鍜岀┖鎵樼洏鏁伴噺
+ console.log("姝e湪鍔犺浇鐢垫睜鏁伴噺鍜岀┖鎵樼洏鏁版嵁...");
+ const res = await this.http.get("/api/Dashboard/StockAndTrayCount");
+
+ if (res.status && res.data) {
+ console.log("鐢垫睜鍜岀┖鎵樼洏鏁版嵁:", res.data);
+
+ // 閲嶇疆鏁版嵁
+ this.rollData.batteryCount = 0;
+ this.rollData.emptyTrayCount = 0;
+
+ // 鏍规嵁杩斿洖鐨勬暟鎹粨鏋勮В鏋�
+ res.data.forEach(item => {
+ const warehouseName = item.warehouseName;
+ const batteryCount = item.batteryCount || 0;
+ const emptyTrayCount = item.emptyTrayCount || 0;
+
+ // 鏍规嵁浠撳簱鍚嶇О鏄犲皠鍒板搴旂殑浠g爜
+ if (warehouseName === "楂樻俯搴�") {
+ this.emptyTrayCounts.GWSC1 = emptyTrayCount;
+ this.warehouseStocks.GWSC1 = batteryCount;
+ } else if (warehouseName === "甯告俯搴�") {
+ this.emptyTrayCounts.CWSC1 = emptyTrayCount;
+ this.warehouseStocks.CWSC1 = batteryCount;
+ } else if (warehouseName === "鍖栨垚搴�") {
+ this.emptyTrayCounts.HCSC1 = emptyTrayCount;
+ this.warehouseStocks.HCSC1 = batteryCount;
+ } else if (warehouseName === "鏋佸嵎搴�") {
+ // 鏋佸嵎搴撻渶瑕佸悎骞朵袱涓瀬鍗峰簱鐨勬暟鎹�
+ this.rollData.batteryCount += batteryCount;
+ this.rollData.emptyTrayCount += emptyTrayCount;
+ }
+ });
+
+ // 璁剧疆鏋佸嵎搴撴�荤數姹犳暟閲�
+ this.warehouseStocks.ROLL = this.rollData.batteryCount;
+
+ console.log("鐗规畩鏁版嵁鍔犺浇瀹屾垚:", {
+ rollData: this.rollData,
+ emptyTrayCounts: this.emptyTrayCounts,
+ warehouseStocks: this.warehouseStocks
+ });
+ } else {
+ console.error("鑾峰彇鐢垫睜鍜岀┖鎵樼洏鏁版嵁澶辫触");
+ throw new Error("鑾峰彇鐢垫睜鍜岀┖鎵樼洏鏁版嵁澶辫触");
+ }
+ },
+
+ async loadStockByWarehouse() {
+ console.log("姝e湪鍔犺浇浠撳簱鍒嗗竷鏁版嵁...");
+ const res = await this.http.get("/api/Dashboard/StockByWarehouse");
+
+ if (res.status && res.data) {
+ console.log("浠撳簱鍒嗗竷鏁版嵁:", res.data);
+ const rawData = res.data.data || res.data;
+
+ // 澶勭悊鏋佸嵎搴撳悎骞�
+ let rollHasStock = 0;
+ let rollNoStock = 0;
+ let rollTotal = 0;
+ const otherWarehouses = [];
+
+ rawData.forEach(item => {
+ if (item.warehouse === "鏋佸嵎搴�" || item.warehouse.includes("鏋佸嵎搴�")) {
+ // 鍚堝苟鏋佸嵎搴撴暟鎹�
+ rollHasStock += item.hasStock || 0;
+ rollNoStock += item.noStock || 0;
+ rollTotal += item.total || 0;
+ } else {
+ otherWarehouses.push(item);
+ }
+ });
+
+ // 娣诲姞鍚堝苟鍚庣殑鏋佸嵎搴�
+ if (rollTotal > 0) {
+ const hasStockPercentage = ((rollHasStock / rollTotal) * 100).toFixed(1) + "%";
+ const noStockPercentage = ((rollNoStock / rollTotal) * 100).toFixed(1) + "%";
+
+ otherWarehouses.push({
+ warehouse: "鏋佸嵎搴�",
+ hasStock: rollHasStock,
+ noStock: rollNoStock,
+ total: rollTotal,
+ hasStockPercentage: hasStockPercentage,
+ noStockPercentage: noStockPercentage
+ });
+ }
+
+ this.warehouseData = otherWarehouses;
+ this.updateWarehouseChart();
+ } else {
+ console.error("鑾峰彇浠撳簱鍒嗗竷鏁版嵁澶辫触");
+ throw new Error("鑾峰彇浠撳簱鍒嗗竷鏁版嵁澶辫触");
+ }
+ },
+
+ getDailyTotalInbound(warehouseCode) {
+ const data = this.dailyDataMap[warehouseCode] || [];
+ return data.reduce((sum, item) => sum + (item.inbound || 0), 0);
+ },
+
+ getDailyTotalOutbound(warehouseCode) {
+ const data = this.dailyDataMap[warehouseCode] || [];
+ return data.reduce((sum, item) => sum + (item.outbound || 0), 0);
+ },
+
+ getWarehouseStock(warehouseCode) {
+ return this.warehouseStocks[warehouseCode] || 0;
+ },
+
+ getBatteryCount(warehouseCode) {
+ if (warehouseCode === 'ROLL') {
+ return this.rollData.batteryCount;
+ } else if (warehouseCode === 'GWSC1') {
+ return this.warehouseStocks.GWSC1;
+ } else if (warehouseCode === 'CWSC1') {
+ return this.warehouseStocks.CWSC1;
+ } else if (warehouseCode === 'HCSC1') {
+ return this.warehouseStocks.HCSC1;
+ }
+ return 0;
+ },
+
+ getEmptyTrayCount(warehouseCode) {
+ if (warehouseCode === 'ROLL') {
+ return this.rollData.emptyTrayCount;
+ } else if (warehouseCode === 'GWSC1') {
+ return this.emptyTrayCounts.GWSC1;
+ } else if (warehouseCode === 'CWSC1') {
+ return this.emptyTrayCounts.CWSC1;
+ } else if (warehouseCode === 'HCSC1') {
+ return this.emptyTrayCounts.HCSC1;
+ }
+ return 0;
+ },
+
+ updateAllDailyCharts() {
+ this.dailyWarehouses.forEach(warehouse => {
+ this.updateDailyChartForWarehouse(warehouse.code);
});
+ },
+
+ updateDailyChartForWarehouse(roadway) {
+ const chart = this.charts[`daily-${roadway}`];
+ if (!chart) return;
+
+ const data = this.dailyDataMap[roadway] || [];
+
+ // 濡傛灉娌℃湁鏁版嵁锛屾樉绀虹┖鍥捐〃鎻愮ず
+ if (!data.length) {
+ chart.setOption({
+ title: {
+ show: true,
+ text: '鏆傛棤鏁版嵁',
+ left: 'center',
+ top: 'center',
+ textStyle: { color: '#ccc', fontSize: 14 }
+ }
+ }, true);
+ return;
+ }
+
+ const dates = data.map(d => d.date);
+ const inboundData = data.map(d => d.inbound || 0);
+ const outboundData = data.map(d => d.outbound || 0);
const option = {
- title: {
- text: '鍚勪粨搴撴湀搴﹀嚭鍏ュ簱瀵规瘮',
- textStyle: {
- color: '#00ffff',
- fontSize: 16
- },
- left: 'center',
- top: 10
- },
- tooltip: {
- trigger: 'axis',
- axisPointer: {
- type: 'shadow'
- },
+ tooltip: {
+ trigger: "axis",
formatter: function(params) {
- let tip = `<strong>${params[0].axisValue}</strong><br/>`;
+ let result = params[0].axisValue + "<br/>";
+ params.forEach(p => {
+ result += `${p.marker}${p.seriesName}: ${p.value}<br/>`;
+ });
+ return result;
+ }
+ },
+ legend: {
+ data: ["鍏ュ簱", "鍑哄簱"],
+ textStyle: { color: "#fff" },
+ top: 0,
+ right: 10,
+ itemWidth: 20,
+ itemHeight: 12
+ },
+ grid: {
+ left: "8%",
+ right: "8%",
+ top: "18%",
+ bottom: "12%",
+ containLabel: true
+ },
+ xAxis: {
+ type: "category",
+ data: dates,
+ axisLabel: {
+ color: "#ccc",
+ rotate: 45,
+ fontSize: 10,
+ interval: 0,
+ margin: 8
+ },
+ axisLine: { lineStyle: { color: "#4a5b6e" } }
+ },
+ yAxis: {
+ type: "value",
+ name: "鏁伴噺",
+ nameTextStyle: { color: "#ccc", fontSize: 11 },
+ axisLabel: { color: "#ccc" },
+ splitLine: { lineStyle: { color: "#2a3a4a", type: "dashed" } }
+ },
+ series: [
+ {
+ name: "鍏ュ簱",
+ type: "bar",
+ data: inboundData,
+ itemStyle: {
+ color: "#5470c6",
+ borderRadius: [4, 4, 0, 0]
+ },
+ barWidth: "40%",
+ label: {
+ show: true,
+ position: "top",
+ color: "#5470c6",
+ fontSize: 10,
+ formatter: (params) => params.value
+ }
+ },
+ {
+ name: "鍑哄簱",
+ type: "line",
+ data: outboundData,
+ symbol: "circle",
+ symbolSize: 6,
+ itemStyle: { color: "#91cc75" },
+ lineStyle: { width: 2, type: "solid" },
+ smooth: false,
+ label: {
+ show: true,
+ position: "top",
+ color: "#91cc75",
+ fontSize: 10,
+ formatter: (params) => params.value
+ }
+ }
+ ]
+ };
+ chart.setOption(option, true);
+ },
+
+ updateWarehouseChart() {
+ if (!this.charts.warehouse) return;
+
+ if (!this.warehouseData.length) {
+ this.charts.warehouse.setOption({
+ title: {
+ show: true,
+ text: '鏆傛棤浠撳簱鏁版嵁',
+ left: 'center',
+ top: 'center',
+ textStyle: { color: '#ccc' }
+ }
+ });
+ return;
+ }
+
+ const warehouseNames = this.warehouseData.map(w => w.warehouse);
+ const hasStocks = this.warehouseData.map(w => w.hasStock);
+ const noStocks = this.warehouseData.map(w => w.noStock);
+ const hasStockPercentages = this.warehouseData.map(w => w.hasStockPercentage);
+ const noStockPercentages = this.warehouseData.map(w => w.noStockPercentage);
+
+ const option = {
+ tooltip: {
+ trigger: "axis",
+ axisPointer: { type: "shadow" },
+ formatter: (params) => {
+ let tip = params[0].name + "<br/>";
params.forEach(param => {
- tip += `<span style="display:inline-block;width:10px;height:10px;border-radius:50%;background:${param.color};margin-right:5px;"></span>`;
- tip += `${param.seriesName}: `;
- tip += `鍏ュ簱:${param.data.inbound} | 鍑哄簱:${param.data.outbound} | 鎬昏:${param.value}<br/>`;
+ const dataIndex = param.dataIndex;
+ const warehouse = this.warehouseData[dataIndex];
+ if (warehouse) {
+ if (param.seriesName === "宸茬敤瀹归噺") {
+ tip += `${param.marker}${param.seriesName}: ${param.value} (${warehouse.hasStockPercentage})<br/>`;
+ tip += `鏈夊簱瀛�: ${warehouse.hasStock}<br/>`;
+ tip += `鏃犲簱瀛�: ${warehouse.noStock}<br/>`;
+ tip += `鎬诲閲�: ${warehouse.total}`;
+ } else if (param.seriesName === "鍓╀綑瀹归噺") {
+ tip += `${param.marker}${param.seriesName}: ${param.value} (${warehouse.noStockPercentage})<br/>`;
+ }
+ } else {
+ tip += `${param.marker}${param.seriesName}: ${param.value}<br/>`;
+ }
});
return tip;
}
},
legend: {
- data: this.monthlyData.map(d => d.warehouseName),
- textStyle: { color: '#fff', fontSize: 11 },
- top: 45,
- left: 'center',
- type: 'scroll'
- },
- grid: {
- left: '3%',
- right: '4%',
- bottom: '10%',
- top: '20%',
- containLabel: true
+ data: ["宸茬敤瀹归噺", "鍓╀綑瀹归噺"],
+ textStyle: { color: "#fff" }
},
xAxis: {
- type: 'category',
- data: months,
- axisLabel: {
- color: '#fff',
- fontSize: 11
- },
- axisLine: {
- lineStyle: { color: 'rgba(255,255,255,0.3)' }
- },
- splitLine: {
- show: true,
- lineStyle: {
- color: 'rgba(255,255,255,0.1)',
- type: 'dashed'
- }
- }
+ type: "category",
+ data: warehouseNames,
+ axisLabel: { color: "#fff", rotate: 30, interval: 0 }
},
yAxis: {
- type: 'value',
- name: '鏁伴噺',
- nameTextStyle: { color: '#fff' },
- axisLabel: { color: '#fff' },
- splitLine: {
- lineStyle: {
- color: 'rgba(255,255,255,0.1)',
- type: 'dashed'
- }
- }
+ type: "value",
+ name: "瀹归噺",
+ axisLabel: { color: "#fff" }
},
- dataZoom: [
+ series: [
{
- type: 'inside',
- start: 0,
- end: 100
+ name: "宸茬敤瀹归噺",
+ type: "bar",
+ data: hasStocks.map((value, index) => ({
+ value: value,
+ label: {
+ show: true,
+ position: "top",
+ formatter: () => {
+ const pct = hasStockPercentages[index];
+ return `${value} (${pct})`;
+ },
+ color: "#91cc75",
+ fontSize: 11
+ }
+ })),
+ itemStyle: { color: "#91cc75" }
},
{
- start: 0,
- end: 100,
- height: 20,
- bottom: 0,
- borderColor: 'rgba(255,255,255,0.3)',
- fillerColor: 'rgba(0,255,255,0.1)',
- handleStyle: {
- color: '#00ffff',
- borderColor: '#00ffff'
- },
- textStyle: {
- color: '#fff'
- }
+ name: "鍓╀綑瀹归噺",
+ type: "bar",
+ data: noStocks.map((value, index) => ({
+ value: value,
+ label: {
+ show: true,
+ position: "top",
+ formatter: () => {
+ const pct = noStockPercentages[index];
+ return `${value} (${pct})`;
+ },
+ color: "#fac858",
+ fontSize: 11
+ }
+ })),
+ itemStyle: { color: "#fac858" }
}
- ],
- series: series
+ ]
};
- this.charts.warehouseMonthly.setOption(option, true);
- },
-
- getBarColor(index) {
- const colors = [
- '#5470c6', // 钃�
- '#fac858', // 榛�
- '#73c0de', // 澶╄摑
- '#fc8452', // 姗�
- '#ea7ccc' // 绮�
- ];
- return colors[index] || '#5470c6';
+ this.charts.warehouse.setOption(option, true);
}
}
};
@@ -235,36 +615,80 @@
background-attachment: fixed;
}
-.chart-row {
+/* KPI 鍗$墖鏍峰紡 - 5鍒楀竷灞� */
+.kpi-cards {
+ display: grid;
+ grid-template-columns: repeat(5, 1fr);
+ gap: 20px;
+ margin-bottom: 24px;
+}
+
+.kpi-card {
+ background: rgba(10, 16, 35, 0.7);
+ backdrop-filter: blur(10px);
+ border: 1px solid rgba(64, 224, 208, 0.3);
+ border-radius: 16px;
+ padding: 16px 20px;
display: flex;
+ align-items: center;
+ gap: 16px;
+ transition: all 0.3s ease;
+ box-shadow: 0 0 15px rgba(0, 255, 255, 0.1);
+}
+
+.kpi-card:hover {
+ transform: translateY(-3px);
+ border-color: rgba(64, 224, 208, 0.6);
+ box-shadow: 0 0 25px rgba(0, 255, 255, 0.2);
+}
+
+.kpi-icon {
+ font-size: 32px;
+ opacity: 0.9;
+}
+
+.kpi-info {
+ flex: 1;
+}
+
+.kpi-label {
+ font-size: 13px;
+ color: #8ba0b5;
+ margin-bottom: 6px;
+ letter-spacing: 1px;
+}
+
+.kpi-value {
+ font-size: 28px;
+ font-weight: 700;
+ color: #00ffff;
+ text-shadow: 0 0 10px rgba(0, 255, 255, 0.5);
+ line-height: 1.2;
+}
+
+/* 姣忔棩鍥捐〃甯冨眬 - 姣忚2涓� */
+.chart-row.daily-grid {
+ display: grid;
+ grid-template-columns: repeat(2, 1fr);
gap: 20px;
margin-bottom: 20px;
}
-.chart-row.full-width {
- width: 100%;
-}
-
.chart-card {
- flex: 1;
background: rgba(10, 16, 35, 0.6);
backdrop-filter: blur(10px);
border: 1px solid rgba(64, 224, 208, 0.3);
border-radius: 12px;
padding: 15px;
position: relative;
- box-shadow:
- 0 0 15px rgba(0, 255, 255, 0.1),
- inset 0 0 10px rgba(64, 224, 208, 0.1);
+ box-shadow: 0 0 15px rgba(0, 255, 255, 0.1), inset 0 0 10px rgba(64, 224, 208, 0.1);
transition: all 0.3s ease;
overflow: hidden;
}
.chart-card:hover {
transform: translateY(-5px);
- box-shadow:
- 0 0 25px rgba(0, 255, 255, 0.3),
- inset 0 0 15px rgba(64, 224, 208, 0.2);
+ box-shadow: 0 0 25px rgba(0, 255, 255, 0.3), inset 0 0 15px rgba(64, 224, 208, 0.2);
border: 1px solid rgba(64, 224, 208, 0.6);
}
@@ -294,25 +718,84 @@
.card-title {
color: #00ffff;
- font-size: 16px;
+ font-size: 15px;
text-align: center;
- margin-bottom: 10px;
+ margin-bottom: 12px;
text-shadow: 0 0 10px rgba(0, 255, 255, 0.7);
font-weight: 500;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+
+/* 浠撳簱鏁板瓧鏄剧ず鍖哄煙 */
+.warehouse-numbers {
+ display: flex;
+ justify-content: space-around;
+ margin-bottom: 12px;
+ padding: 8px 0;
+ background: rgba(0, 0, 0, 0.3);
+ border-radius: 8px;
+ flex-wrap: wrap;
+}
+
+.number-item {
+ text-align: center;
+ flex: 1;
+ min-width: 80px;
+}
+
+.number-label {
+ display: block;
+ font-size: 11px;
+ color: #8ba0b5;
+ margin-bottom: 4px;
+}
+
+.number-value {
+ display: block;
+ font-size: 18px;
+ font-weight: 700;
+ letter-spacing: 1px;
+}
+
+.number-item.inbound .number-value {
+ color: #5470c6;
+}
+
+.number-item.battery .number-value {
+ color: #ee6666;
+}
+
+.number-item.empty-tray .number-value {
+ color: #fc8452;
}
.chart-content {
- height: 500px;
+ height: 280px;
width: 100%;
}
-.full-width .chart-card {
- flex: none;
- width: 100%;
-}
-
-.full-width .chart-content {
- height: 500px;
+@media (max-width: 768px) {
+ .kpi-cards {
+ grid-template-columns: repeat(2, 1fr);
+ }
+ .chart-row.daily-grid {
+ grid-template-columns: 1fr;
+ }
+ .chart-content {
+ height: 240px;
+ }
+ .card-title {
+ font-size: 13px;
+ white-space: normal;
+ }
+ .number-value {
+ font-size: 14px;
+ }
+ .number-item {
+ min-width: 60px;
+ }
}
.dashboard-container::before {
@@ -322,8 +805,7 @@
left: 0;
right: 0;
bottom: 0;
- background-image:
- linear-gradient(rgba(64, 224, 208, 0.05) 1px, transparent 1px),
+ background-image: linear-gradient(rgba(64, 224, 208, 0.05) 1px, transparent 1px),
linear-gradient(90deg, rgba(64, 224, 208, 0.05) 1px, transparent 1px);
background-size: 30px 30px;
pointer-events: none;
--
Gitblit v1.9.3