From c906272c0905b1309503de92affbdb06ec9d4268 Mon Sep 17 00:00:00 2001
From: xiazhengtongxue <133085197+xiazhengtongxue@users.noreply.github.com>
Date: 星期五, 01 五月 2026 09:48:41 +0800
Subject: [PATCH] 1
---
Code/WMS/WIDESEA_WMSClient/src/views/Home.vue | 914 +++++++++++++++++++++++++++++++++++++++++---------------
1 files changed, 660 insertions(+), 254 deletions(-)
diff --git a/Code/WMS/WIDESEA_WMSClient/src/views/Home.vue b/Code/WMS/WIDESEA_WMSClient/src/views/Home.vue
index bb42d01..32359d5 100644
--- a/Code/WMS/WIDESEA_WMSClient/src/views/Home.vue
+++ b/Code/WMS/WIDESEA_WMSClient/src/views/Home.vue
@@ -1,46 +1,92 @@
<template>
<div class="dashboard-container">
- <!-- 椤堕儴锛氭湰鏈堝嚭鍏ュ簱瓒嬪娍 (鍏ㄥ) -->
- <div class="chart-row full-width">
- <div class="chart-card">
- <div class="card-title">鏈湀鍑哄叆搴撹秼鍔�</div>
- <div id="chart-monthly-trend" class="chart-content"></div>
+ <!-- 椤堕儴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">{{ totalWarehouses }}</div>
+ </div>
</div>
- </div>
-
- <!-- 绗簩琛岋細浠婃棩/鏈懆鍑哄叆搴撳姣� -->
- <div class="chart-row">
- <div class="chart-card">
- <div class="card-title">浠婃棩鍑哄叆搴撳姣�</div>
- <div id="chart-today" class="chart-content"></div>
+ <div class="kpi-card">
+ <div class="kpi-icon">馃摝</div>
+ <div class="kpi-info">
+ <div class="kpi-label">鎬诲簱瀛橀噺</div>
+ <div class="kpi-value">{{ totalStock.toLocaleString() }}</div>
+ </div>
</div>
- <div class="chart-card">
- <div class="card-title">鏈懆鍑哄叆搴撳姣�</div>
- <div id="chart-week" class="chart-content"></div>
+ <div class="kpi-card">
+ <div class="kpi-icon">馃搳</div>
+ <div class="kpi-info">
+ <div class="kpi-label">鏈湀鎬诲叆搴�</div>
+ <div class="kpi-value">{{ monthlyInboundTotal.toLocaleString() }}</div>
+ </div>
</div>
- </div>
-
- <!-- 绗笁琛岋細鏈湀瀵规瘮/搴撳瓨鎬婚噺 -->
- <div class="chart-row">
- <div class="chart-card">
- <div class="card-title">鏈湀鍑哄叆搴撳姣�</div>
- <div id="chart-month" class="chart-content"></div>
- </div>
- <div class="chart-card">
- <div class="card-title">褰撳墠搴撳瓨鎬婚噺</div>
- <div class="stock-total">
- <div class="total-number">{{ overviewData.TotalStock || 0 }}</div>
- <div class="total-label">鎵樼洏</div>
+ <div class="kpi-card">
+ <div class="kpi-icon">馃摛</div>
+ <div class="kpi-info">
+ <div class="kpi-label">鏈湀鎬诲嚭搴�</div>
+ <div class="kpi-value">{{ monthlyOutboundTotal.toLocaleString() }}</div>
</div>
</div>
</div>
- <!-- 绗洓琛岋細搴撻緞鍒嗗竷/浠撳簱鍒嗗竷 -->
- <div class="chart-row">
- <div class="chart-card">
- <div class="card-title">搴撳瓨搴撻緞鍒嗗竷</div>
- <div id="chart-stock-age" class="chart-content"></div>
+ <!-- 椤堕儴锛氭湰鏈堝嚭鍏ュ簱瓒嬪娍 - 涓�3涓�2甯冨眬锛屾瘡涓崱鐗囩洿鎺ユ樉绀轰粨搴撴暟瀛� -->
+ <div class="chart-row top-three">
+ <div v-for="warehouse in topWarehouses" :key="warehouse.code" class="chart-card">
+ <div class="card-title">{{ warehouse.name }}</div>
+ <!-- 浠撳簱鏁板瓧鏄剧ず鍖哄煙 -->
+ <div class="warehouse-numbers">
+ <div class="number-item inbound">
+ <span class="number-label">鍏ュ簱</span>
+ <span class="number-value">{{ getMonthlyInbound(warehouse.code) }}</span>
+ </div>
+ <div class="number-item outbound">
+ <span class="number-label">鍑哄簱</span>
+ <span class="number-value">{{ getMonthlyOutbound(warehouse.code) }}</span>
+ </div>
+ <div class="number-item stock">
+ <span class="number-label">搴撳瓨</span>
+ <span class="number-value">{{ getWarehouseStock(warehouse.code) }}</span>
+ </div>
+ </div>
+ <div :id="`chart-${warehouse.code}`" class="chart-content"></div>
</div>
+ </div>
+
+ <div class="chart-row bottom-two">
+ <div v-for="warehouse in bottomWarehouses" :key="warehouse.code" class="chart-card">
+ <div class="card-title">{{ warehouse.name }}</div>
+ <!-- 浠撳簱鏁板瓧鏄剧ず鍖哄煙 -->
+ <div class="warehouse-numbers">
+ <div class="number-item inbound">
+ <span class="number-label">鍏ュ簱</span>
+ <span class="number-value">{{ getMonthlyInbound(warehouse.code) }}</span>
+ </div>
+ <div class="number-item outbound">
+ <span class="number-label">鍑哄簱</span>
+ <span class="number-value">{{ getMonthlyOutbound(warehouse.code) }}</span>
+ </div>
+ <div class="number-item stock">
+ <span class="number-label">搴撳瓨</span>
+ <span class="number-value">{{ getWarehouseStock(warehouse.code) }}</span>
+ </div>
+ </div>
+ <div :id="`chart-${warehouse.code}`" class="chart-content"></div>
+ </div>
+ </div>
+
+ <!-- 姣忔棩鍑哄叆搴撹秼鍔� (鍏ㄥ) -->
+ <div class="chart-row full-width">
+ <div class="chart-card">
+ <div class="card-title">姣忔棩鍑哄叆搴撹秼鍔�</div>
+ <div id="chart-daily" class="chart-content"></div>
+ </div>
+ </div>
+
+ <!-- 浠撳簱鍒嗗竷 -->
+ <div class="chart-row">
<div class="chart-card">
<div class="card-title">鍚勪粨搴撳簱瀛樺垎甯�</div>
<div id="chart-warehouse" class="chart-content"></div>
@@ -57,17 +103,40 @@
data() {
return {
charts: {},
- overviewData: {
- TodayInbound: 0,
- TodayOutbound: 0,
- MonthInbound: 0,
- MonthOutbound: 0,
- TotalStock: 0
+ // 浜斾釜浠撳簱瀹氫箟 - 涓�3涓�
+ topWarehouses: [
+ { code: "GWSC1", name: "楂樻俯1鍙蜂粨搴�" },
+ { code: "CWSC1", name: "甯告俯1鍙蜂粨搴�" },
+ { code: "HCSC1", name: "鍒嗗1鍙蜂粨搴�" }
+ ],
+ // 涓�2涓�
+ bottomWarehouses: [
+ { code: "FJSC1", name: "璐熸瀬鍗�1鍙蜂粨搴�" },
+ { code: "ZJSC1", name: "姝f瀬鍗�1鍙蜂粨搴�" }
+ ],
+ dailyData: [],
+ // 瀛樺偍姣忎釜浠撳簱鐨勬湀搴︽暟鎹�
+ monthlyData: {
+ GWSC1: [],
+ CWSC1: [],
+ HCSC1: [],
+ FJSC1: [],
+ ZJSC1: []
},
- weeklyData: [],
- monthlyData: [],
- stockAgeData: [],
- warehouseData: []
+ // 瀛樺偍姣忎釜浠撳簱鐨勫綋鍓嶅簱瀛�
+ warehouseStocks: {
+ GWSC1: 0,
+ CWSC1: 0,
+ HCSC1: 0,
+ FJSC1: 0,
+ ZJSC1: 0
+ },
+ warehouseData: [],
+ // KPI 姹囨�绘暟鎹�
+ totalWarehouses: 5,
+ totalStock: 0,
+ monthlyInboundTotal: 0,
+ monthlyOutboundTotal: 0
};
},
mounted() {
@@ -77,85 +146,80 @@
},
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.monthlyTrend = echarts.init(document.getElementById("chart-monthly-trend"));
- this.charts.today = echarts.init(document.getElementById("chart-today"));
- this.charts.week = echarts.init(document.getElementById("chart-week"));
- this.charts.month = echarts.init(document.getElementById("chart-month"));
- this.charts.stockAge = echarts.init(document.getElementById("chart-stock-age"));
+ // 鍒濆鍖栨墍鏈変粨搴撳浘琛�
+ const allWarehouses = [...this.topWarehouses, ...this.bottomWarehouses];
+ allWarehouses.forEach(warehouse => {
+ const chartId = `chart-${warehouse.code}`;
+ const el = document.getElementById(chartId);
+ if (el) {
+ this.charts[warehouse.code] = echarts.init(el);
+ }
+ });
+ // 鍒濆鍖栨瘡鏃ュ浘琛ㄥ拰浠撳簱鍒嗗竷鍥捐〃
+ this.charts.daily = echarts.init(document.getElementById("chart-daily"));
this.charts.warehouse = echarts.init(document.getElementById("chart-warehouse"));
},
async loadData() {
- await this.loadOverview();
- await this.loadWeeklyStats();
- await this.loadMonthlyStats();
- await this.loadStockAgeDistribution();
+ // 骞惰鍔犺浇鎵�鏈変粨搴撶殑鏈堝害鏁版嵁锛堝垎鍒紶鍏ヤ笉鍚岀殑Roadway鍙傛暟锛�
+ const allWarehouses = [...this.topWarehouses, ...this.bottomWarehouses];
+ const monthlyPromises = allWarehouses.map(warehouse =>
+ this.loadMonthlyStatsForWarehouse(warehouse.code)
+ );
+ await Promise.all(monthlyPromises);
+ // 鏇存柊鎵�鏈変粨搴撶殑鏈堝害鍥捐〃
+ this.updateAllMonthlyTrendCharts();
+
+ await this.loadDailyStats();
await this.loadStockByWarehouse();
+ await this.loadWarehouseStocks();
+ this.calculateKPIs();
},
- async loadOverview() {
+ async loadMonthlyStatsForWarehouse(roadway) {
+ console.log(`姝e湪鍔犺浇${roadway}鐨勬瘡鏈堢粺璁℃暟鎹�...`);
try {
- const res = await this.http.get("/api/Dashboard/Overview");
- if (res.Status && res.Data) {
- this.overviewData = res.Data;
- this.updateTodayChart();
- this.updateWeekChart();
- this.updateMonthChart();
+ // 鍏抽敭淇锛氬垎鍒紶鍏ヤ笉鍚岀殑Roadway鍙傛暟
+ const res = await this.http.get("/api/Dashboard/MonthlyStats?monthly=12&roadway=" + roadway);
+ if (res.status && res.data) {
+ console.log(`${roadway} 姣忔湀缁熻鏁版嵁:`, res.data);
+ this.monthlyData[roadway] = res.data;
+ } else {
+ this.monthlyData[roadway] = [];
}
} catch (e) {
- console.error("鍔犺浇鎬昏鏁版嵁澶辫触", e);
+ console.error(`鍔犺浇${roadway}姣忔湀缁熻澶辫触`, e);
+ this.monthlyData[roadway] = [];
}
},
- async loadWeeklyStats() {
+ async loadDailyStats() {
try {
- const res = await this.http.get("/api/Dashboard/WeeklyStats", { weeks: 12 });
- if (res.Status && res.Data) {
- this.weeklyData = res.Data;
- this.updateWeekChart();
+ const res = await this.http.get("/api/Dashboard/DailyStats", { days: 30 });
+ if (res.status && res.data) {
+ console.log("姣忔棩缁熻鏁版嵁:", res.data);
+ this.dailyData = res.data;
+ this.updateDailyChart();
}
} catch (e) {
- console.error("鍔犺浇姣忓懆缁熻澶辫触", e);
- }
- },
-
- async loadMonthlyStats() {
- try {
- const res = await this.http.get("/api/Dashboard/MonthlyStats", { months: 12 });
- if (res.Status && res.Data) {
- this.monthlyData = res.Data;
- this.updateMonthlyTrendChart();
- }
- } catch (e) {
- console.error("鍔犺浇姣忔湀缁熻澶辫触", e);
- }
- },
-
- async loadStockAgeDistribution() {
- try {
- const res = await this.http.get("/api/Dashboard/StockAgeDistribution");
- if (res.Status && res.Data) {
- this.stockAgeData = res.Data;
- this.updateStockAgeChart();
- }
- } catch (e) {
- console.error("鍔犺浇搴撻緞鍒嗗竷澶辫触", e);
+ console.error("鍔犺浇姣忔棩缁熻澶辫触", e);
}
},
async loadStockByWarehouse() {
try {
const res = await this.http.get("/api/Dashboard/StockByWarehouse");
- if (res.Status && res.Data) {
- this.warehouseData = res.Data;
+ if (res.status && res.data) {
+ console.log("浠撳簱鍒嗗竷鏁版嵁:", res.data);
+ this.warehouseData = res.data.data || res.data;
this.updateWarehouseChart();
}
} catch (e) {
@@ -163,157 +227,330 @@
}
},
- updateTodayChart() {
- const option = {
- tooltip: { trigger: "axis" },
- legend: { data: ["鍏ュ簱", "鍑哄簱"], textStyle: { color: "#fff" } },
- xAxis: {
- type: "category",
- data: ["浠婃棩"],
- axisLabel: { color: "#fff" }
- },
- yAxis: {
- type: "value",
- axisLabel: { color: "#fff" }
- },
- series: [
- { name: "鍏ュ簱", type: "bar", data: [this.overviewData.TodayInbound], itemStyle: { color: "#5470c6" } },
- { name: "鍑哄簱", type: "bar", data: [this.overviewData.TodayOutbound], itemStyle: { color: "#91cc75" } }
- ]
- };
- this.charts.today.setOption(option, true);
- },
-
- updateWeekChart() {
- const thisWeek = this.getThisWeekData(this.weeklyData);
- const option = {
- tooltip: { trigger: "axis" },
- legend: { data: ["鍏ュ簱", "鍑哄簱"], textStyle: { color: "#fff" } },
- xAxis: {
- type: "category",
- data: ["鏈懆"],
- axisLabel: { color: "#fff" }
- },
- yAxis: {
- type: "value",
- axisLabel: { color: "#fff" }
- },
- series: [
- { name: "鍏ュ簱", type: "bar", data: [thisWeek.Inbound], itemStyle: { color: "#5470c6" } },
- { name: "鍑哄簱", type: "bar", data: [thisWeek.Outbound], itemStyle: { color: "#91cc75" } }
- ]
- };
- this.charts.week.setOption(option, true);
- },
-
- getThisWeekData(weeklyData) {
- if (!weeklyData || weeklyData.length === 0) return { Inbound: 0, Outbound: 0 };
- const thisWeekKey = this.getCurrentWeekKey();
- const thisWeek = weeklyData.find(w => w.Week === thisWeekKey);
- return thisWeek || { Inbound: 0, Outbound: 0 };
- },
-
- getCurrentWeekKey() {
- const now = new Date();
- const diff = (7 + (now.getDay() - 1)) % 7;
- const monday = new Date(now);
- monday.setDate(now.getDate() - diff);
- const year = monday.getFullYear();
- const jan1 = new Date(year, 0, 1);
- const weekNum = Math.ceil(((monday - jan1) / 86400000 + jan1.getDay() + 1) / 7);
- return `${year}-W${String(weekNum).padStart(2, "0")}`;
- },
-
- updateMonthChart() {
- const option = {
- tooltip: { trigger: "axis" },
- legend: { data: ["鍏ュ簱", "鍑哄簱"], textStyle: { color: "#fff" } },
- xAxis: {
- type: "category",
- data: ["鏈湀"],
- axisLabel: { color: "#fff" }
- },
- yAxis: {
- type: "value",
- axisLabel: { color: "#fff" }
- },
- series: [
- { name: "鍏ュ簱", type: "bar", data: [this.overviewData.MonthInbound], itemStyle: { color: "#5470c6" } },
- { name: "鍑哄簱", type: "bar", data: [this.overviewData.MonthOutbound], itemStyle: { color: "#91cc75" } }
- ]
- };
- this.charts.month.setOption(option, true);
- },
-
- updateMonthlyTrendChart() {
- const option = {
- tooltip: { trigger: "axis" },
- legend: { data: ["鍏ュ簱", "鍑哄簱"], textStyle: { color: "#fff" } },
- xAxis: {
- type: "category",
- data: this.monthlyData.map(m => m.Month),
- axisLabel: { color: "#fff", rotate: 45 }
- },
- yAxis: [
- {
- type: "value",
- name: "鏁伴噺",
- axisLabel: { color: "#fff" }
+ async loadWarehouseStocks() {
+ // 妯℃嫙鍔犺浇姣忎釜浠撳簱鐨勫綋鍓嶅簱瀛橀噺
+ // 濡傛灉鍚庣鏈夋帴鍙o紝鍙互鏇挎崲涓虹湡瀹濧PI璋冪敤
+ try {
+ // 灏濊瘯鍔犺浇搴撳瓨鏁版嵁锛屽鏋滄帴鍙d笉瀛樺湪鍒欎娇鐢ㄦā鎷熸暟鎹�
+ const allWarehouses = [...this.topWarehouses, ...this.bottomWarehouses];
+ for (const warehouse of allWarehouses) {
+ try {
+ const res = await this.http.get(`/api/Dashboard/WarehouseStock?warehouse=${warehouse.code}`);
+ if (res.status && res.data) {
+ this.warehouseStocks[warehouse.code] = res.data.stock || 0;
+ } else {
+ // 浠庢湀搴︽暟鎹腑璁$畻妯℃嫙搴撳瓨锛堟渶杩戞湀浠界疮璁″叆搴�-鍑哄簱锛�
+ const monthlyData = this.monthlyData[warehouse.code] || [];
+ let totalInbound = 0;
+ let totalOutbound = 0;
+ monthlyData.forEach(m => {
+ totalInbound += (m.inbound ?? m.Inbound) || 0;
+ totalOutbound += (m.outbound ?? m.Outbound) || 0;
+ });
+ this.warehouseStocks[warehouse.code] = Math.max(0, totalInbound - totalOutbound);
+ }
+ } catch (e) {
+ // 浣跨敤妯℃嫙鏁版嵁
+ const mockStocks = {
+ GWSC1: 12580,
+ CWSC1: 8920,
+ HCSC1: 15600,
+ FJSC1: 4300,
+ ZJSC1: 7200
+ };
+ this.warehouseStocks[warehouse.code] = mockStocks[warehouse.code] || 0;
}
- ],
- series: [
- { name: "鍏ュ簱", type: "line", data: this.monthlyData.map(m => m.Inbound), itemStyle: { color: "#5470c6" } },
- { name: "鍑哄簱", type: "line", data: this.monthlyData.map(m => m.Outbound), itemStyle: { color: "#91cc75" } }
- ]
- };
- this.charts.monthlyTrend.setOption(option, true);
+ }
+ } catch (e) {
+ console.error("鍔犺浇浠撳簱搴撳瓨澶辫触", e);
+ }
},
- updateStockAgeChart() {
+ getMonthlyInbound(warehouseCode) {
+ const data = this.monthlyData[warehouseCode] || [];
+ if (data.length === 0) return 0;
+ // 鑾峰彇鏈�杩戜竴涓湀锛堟渶鍚庝竴鏉★級鐨勫叆搴撴暟
+ const latest = data[data.length - 1];
+ return (latest.inbound ?? latest.Inbound) || 0;
+ },
+
+ getMonthlyOutbound(warehouseCode) {
+ const data = this.monthlyData[warehouseCode] || [];
+ if (data.length === 0) return 0;
+ // 鑾峰彇鏈�杩戜竴涓湀锛堟渶鍚庝竴鏉★級鐨勫嚭搴撴暟
+ const latest = data[data.length - 1];
+ return (latest.outbound ?? latest.Outbound) || 0;
+ },
+
+ getWarehouseStock(warehouseCode) {
+ return this.warehouseStocks[warehouseCode] || 0;
+ },
+
+ calculateKPIs() {
+ // 璁$畻鎬诲簱瀛�
+ let totalStock = 0;
+ for (const code in this.warehouseStocks) {
+ totalStock += this.warehouseStocks[code];
+ }
+ this.totalStock = totalStock;
+
+ // 璁$畻鏈湀鎬诲叆搴撳拰鎬诲嚭搴擄紙鎵�鏈変粨搴撴渶杩戜竴涓湀鐨勫悎璁★級
+ let totalInbound = 0;
+ let totalOutbound = 0;
+ const allWarehouses = [...this.topWarehouses, ...this.bottomWarehouses];
+ allWarehouses.forEach(warehouse => {
+ totalInbound += this.getMonthlyInbound(warehouse.code);
+ totalOutbound += this.getMonthlyOutbound(warehouse.code);
+ });
+ this.monthlyInboundTotal = totalInbound;
+ this.monthlyOutboundTotal = totalOutbound;
+ },
+
+ // 鏇存柊鎵�鏈変粨搴撶殑鏈堝害瓒嬪娍鍥捐〃
+ updateAllMonthlyTrendCharts() {
+ const allWarehouses = [...this.topWarehouses, ...this.bottomWarehouses];
+ allWarehouses.forEach(warehouse => {
+ this.updateMonthlyTrendChartForWarehouse(warehouse.code);
+ });
+ },
+
+ updateMonthlyTrendChartForWarehouse(roadway) {
+ const chart = this.charts[roadway];
+ if (!chart) return;
+
+ const data = this.monthlyData[roadway] || [];
+ // 鍏煎澶у皬鍐欏瓧娈靛悕
+ const monthLabels = data.map(m => m.month || m.Month || "");
+ const inboundData = data.map(m => {
+ const val = m.inbound ?? m.Inbound;
+ return val !== undefined && val !== null ? Number(val) : 0;
+ });
+ const outboundData = data.map(m => {
+ const val = m.outbound ?? m.Outbound;
+ return val !== undefined && val !== null ? Number(val) : 0;
+ });
+
const option = {
- tooltip: { trigger: "item" },
- legend: { data: this.stockAgeData.map(s => s.Range), textStyle: { color: "#fff" } },
+ tooltip: {
+ trigger: "axis",
+ formatter: function(params) {
+ 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: monthLabels,
+ 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: [
{
- type: "pie",
- radius: "60%",
- data: this.stockAgeData.map((s, i) => ({
- name: s.Range,
- value: s.Count
- })),
- emphasis: {
- itemStyle: {
- shadowBlur: 10,
- shadowOffsetX: 0,
- shadowColor: "rgba(0, 0, 0, 0.5)"
- }
+ name: "鍏ュ簱",
+ type: "bar",
+ data: inboundData,
+ itemStyle: {
+ color: "#5470c6",
+ borderRadius: [4, 4, 0, 0]
+ },
+ barWidth: "35%",
+ label: {
+ show: inboundData.length <= 8,
+ position: "top",
+ color: "#5470c6",
+ fontSize: 10
+ }
+ },
+ {
+ name: "鍑哄簱",
+ type: "line",
+ data: outboundData,
+ symbol: "circle",
+ symbolSize: 6,
+ itemStyle: { color: "#91cc75" },
+ lineStyle: { width: 2, type: "solid" },
+ smooth: false,
+ label: {
+ show: outboundData.length <= 8,
+ position: "top",
+ color: "#91cc75",
+ fontSize: 10
}
}
]
};
- this.charts.stockAge.setOption(option, true);
+ chart.setOption(option, true);
},
- updateWarehouseChart() {
+ updateDailyChart() {
+ if (!this.charts.daily) return;
const option = {
tooltip: { trigger: "axis" },
+ legend: { data: ["鍏ュ簱", "鍑哄簱"], textStyle: { color: "#fff" } },
xAxis: {
type: "category",
- data: this.warehouseData.map(w => w.Warehouse),
- axisLabel: { color: "#fff", rotate: 30 }
+ data: this.dailyData.map(d => d.date),
+ axisLabel: {
+ color: "#fff",
+ interval: 0,
+ rotate: 45,
+ fontSize: 12,
+ margin: 10
+ },
+ axisTick: {
+ alignWithLabel: true
+ }
},
yAxis: {
type: "value",
axisLabel: { color: "#fff" }
},
+ grid: {
+ left: "3%",
+ right: "4%",
+ bottom: "15%",
+ top: "10%",
+ containLabel: true
+ },
series: [
{
+ name: "鍏ュ簱",
type: "bar",
- data: this.warehouseData.map(w => w.Count),
+ data: this.dailyData.map(d => d.inbound),
itemStyle: { color: "#5470c6" }
+ },
+ {
+ name: "鍑哄簱",
+ type: "bar",
+ data: this.dailyData.map(d => d.outbound),
+ itemStyle: { color: "#91cc75" }
}
]
};
+ this.charts.daily.setOption(option, true);
+ },
+
+ updateWarehouseChart() {
+ if (!this.charts.warehouse) 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: function(params) {
+ let tip = params[0].name + "<br/>";
+ params.forEach(param => {
+ const dataIndex = param.dataIndex;
+ const warehouse = window.homeComponent?.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: ["宸茬敤瀹归噺", "鍓╀綑瀹归噺"],
+ textStyle: { color: "#fff" }
+ },
+ xAxis: {
+ type: "category",
+ data: warehouseNames,
+ axisLabel: { color: "#fff", rotate: 30, interval: 0 }
+ },
+ yAxis: {
+ type: "value",
+ name: "瀹归噺",
+ axisLabel: { color: "#fff" }
+ },
+ series: [
+ {
+ name: "宸茬敤瀹归噺",
+ type: "bar",
+ data: hasStocks.map((value, index) => ({
+ value: value,
+ label: {
+ show: true,
+ position: "top",
+ formatter: (params) => {
+ const pct = hasStockPercentages[params.dataIndex];
+ return `${params.value} (${pct})`;
+ },
+ color: "#91cc75",
+ fontSize: 11
+ }
+ })),
+ itemStyle: { color: "#91cc75" }
+ },
+ {
+ name: "鍓╀綑瀹归噺",
+ type: "bar",
+ data: noStocks.map((value, index) => ({
+ value: value,
+ label: {
+ show: true,
+ position: "top",
+ formatter: (params) => {
+ const pct = noStockPercentages[params.dataIndex];
+ return `${params.value} (${pct})`;
+ },
+ color: "#fac858",
+ fontSize: 11
+ }
+ })),
+ itemStyle: { color: "#fac858" }
+ }
+ ]
+ };
+
+ window.homeComponent = this;
this.charts.warehouse.setOption(option, true);
}
}
@@ -323,27 +560,100 @@
<style scoped>
.dashboard-container {
padding: 20px;
- background-color: #0e1a2b;
+ color: #e0e0e0;
min-height: calc(100vh - 60px);
+ background: linear-gradient(135deg, #0f172a 0%, #1e293b 100%);
+ background-attachment: fixed;
}
-.chart-row {
+/* KPI 鍗$墖鏍峰紡 */
+.kpi-cards {
+ display: grid;
+ grid-template-columns: repeat(4, 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;
+}
+
+/* 涓�3涓浘琛ㄥ竷灞� */
+.chart-row.top-three {
+ display: grid;
+ grid-template-columns: repeat(3, 1fr);
+ gap: 20px;
+ margin-bottom: 20px;
+}
+
+/* 涓�2涓浘琛ㄥ竷灞� */
+.chart-row.bottom-two {
+ display: grid;
+ grid-template-columns: repeat(2, 1fr);
gap: 20px;
margin-bottom: 20px;
}
.chart-row.full-width {
width: 100%;
+ margin-bottom: 20px;
}
.chart-card {
- flex: 1;
- background: rgba(255, 255, 255, 0.05);
- border: 1px solid rgba(25, 186, 139, 0.17);
- border-radius: 4px;
+ 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);
+ 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);
+ border: 1px solid rgba(64, 224, 208, 0.6);
}
.chart-card::before {
@@ -353,8 +663,9 @@
left: 0;
width: 10px;
height: 10px;
- border-top: 2px solid #02a6b5;
- border-left: 2px solid #02a6b5;
+ border-top: 2px solid #00ffff;
+ border-left: 2px solid #00ffff;
+ box-shadow: -2px -2px 10px #00ffff, 0 0 10px rgba(0, 255, 255, 0.7);
}
.chart-card::after {
@@ -364,50 +675,145 @@
right: 0;
width: 10px;
height: 10px;
- border-top: 2px solid #02a6b5;
- border-right: 2px solid #02a6b5;
+ border-top: 2px solid #00ffff;
+ border-right: 2px solid #00ffff;
+ box-shadow: 2px -2px 10px #00ffff, 0 0 10px rgba(0, 255, 255, 0.7);
+}
+
+.chart-card::before,
+.chart-card::after {
+ animation: neon-flicker 2s infinite alternate;
+}
+
+@keyframes neon-flicker {
+ 0%,
+ 100% {
+ opacity: 1;
+ box-shadow: -2px -2px 10px #00ffff, 0 0 10px rgba(0, 255, 255, 0.7);
+ }
+ 50% {
+ opacity: 0.8;
+ box-shadow: -2px -2px 5px #00ffff, 0 0 5px rgba(0, 255, 255, 0.5);
+ }
}
.card-title {
- color: #fff;
- font-size: 16px;
+ color: #00ffff;
+ 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;
+}
+
+.number-item {
+ text-align: center;
+ flex: 1;
+}
+
+.number-label {
+ display: block;
+ font-size: 11px;
+ color: #8ba0b5;
+ margin-bottom: 4px;
+}
+
+.number-value {
+ display: block;
+ font-size: 20px;
+ font-weight: 700;
+ letter-spacing: 1px;
+}
+
+.number-item.inbound .number-value {
+ color: #5470c6;
+}
+
+.number-item.outbound .number-value {
+ color: #91cc75;
+}
+
+.number-item.stock .number-value {
+ color: #fac858;
}
.chart-content {
- height: 280px;
+ height: 240px;
width: 100%;
-}
-
-.stock-total {
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: center;
- height: 280px;
-}
-
-.total-number {
- font-size: 64px;
- font-weight: bold;
- color: #67caca;
- font-family: -apple-system, BlinkMacSystemFont, Segoe UI, PingFang SC, Microsoft YaHei, Helvetica Neue, Helvetica, Arial, sans-serif;
-}
-
-.total-label {
- font-size: 18px;
- color: #fcf0d8;
- margin-top: 10px;
}
/* 鍏ㄥ鍥捐〃 */
.full-width .chart-card {
- flex: none;
width: 100%;
}
.full-width .chart-content {
height: 350px;
}
-</style>
+
+/* 鍝嶅簲寮忚皟鏁� */
+@media (max-width: 1024px) {
+ .kpi-cards {
+ grid-template-columns: repeat(2, 1fr);
+ }
+ .chart-row.top-three {
+ grid-template-columns: repeat(2, 1fr);
+ }
+ .chart-row.bottom-two {
+ grid-template-columns: repeat(2, 1fr);
+ }
+}
+
+@media (max-width: 768px) {
+ .kpi-cards {
+ grid-template-columns: 1fr;
+ }
+ .chart-row.top-three {
+ grid-template-columns: 1fr;
+ }
+ .chart-row.bottom-two {
+ grid-template-columns: 1fr;
+ }
+ .chart-content {
+ height: 220px;
+ }
+ .full-width .chart-content {
+ height: 280px;
+ }
+ .card-title {
+ font-size: 13px;
+ white-space: normal;
+ }
+ .number-value {
+ font-size: 16px;
+ }
+}
+
+/* 娣诲姞缃戞牸绾挎晥鏋� */
+.dashboard-container::before {
+ content: "";
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ 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;
+ z-index: -1;
+}
+</style>
\ No newline at end of file
--
Gitblit v1.9.3