From ef96a52b953e3ce094bba9f644a00a3698e48726 Mon Sep 17 00:00:00 2001
From: 647556386 <647556386@qq.com>
Date: 星期三, 21 一月 2026 19:29:12 +0800
Subject: [PATCH] 1
---
项目代码/WIDESEA_WMSClient/src/views/Home.vue | 1191 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 1,181 insertions(+), 10 deletions(-)
diff --git "a/\351\241\271\347\233\256\344\273\243\347\240\201/WIDESEA_WMSClient/src/views/Home.vue" "b/\351\241\271\347\233\256\344\273\243\347\240\201/WIDESEA_WMSClient/src/views/Home.vue"
index 820437a..f3b4fb6 100644
--- "a/\351\241\271\347\233\256\344\273\243\347\240\201/WIDESEA_WMSClient/src/views/Home.vue"
+++ "b/\351\241\271\347\233\256\344\273\243\347\240\201/WIDESEA_WMSClient/src/views/Home.vue"
@@ -1,24 +1,1195 @@
<template>
- <div class="title"></div>
+ <div class="wms-dashboard">
+ <!-- 缁熻鍗$墖鍖哄煙 -->
+ <el-row :gutter="20" class="stats-card-row">
+ <el-col :span="4">
+ <div class="stats-card">
+ <div class="metric-icon">
+ <el-icon :size="32">
+ <Box />
+ </el-icon>
+ </div>
+ <div class="card-title">寰呭叆搴撹鍗�</div>
+ <div class="card-value">{{ bigscreendata.unInBoundOrderCount }}</div>
+ </div>
+ </el-col>
+ <el-col :span="4">
+ <div class="stats-card">
+ <div class="metric-icon">
+ <el-icon :size="32">
+ <Document />
+ </el-icon>
+ </div>
+ <div class="card-title">寰呭嚭搴撹鍗�</div>
+ <div class="card-value">{{ bigscreendata.unOutBoundOrderCount }}</div>
+ </div>
+ </el-col>
+ <el-col :span="4">
+ <div class="stats-card">
+ <div class="metric-icon">
+ <el-icon :size="32">
+ <Download />
+ </el-icon>
+ </div>
+ <div class="card-title">浠婃棩鍏ュ簱瀹屾垚鏁�</div>
+ <div class="card-value">{{ bigscreendata.inboundCount }}</div>
+ </div>
+ </el-col>
+ <el-col :span="4">
+ <div class="stats-card">
+ <div class="metric-icon">
+ <el-icon :size="32">
+ <Upload />
+ </el-icon>
+ </div>
+ <div class="card-title">浠婃棩鍑哄簱瀹屾垚鏁�</div>
+ <div class="card-value">{{ bigscreendata.outboundCount }}</div>
+ </div>
+ </el-col>
+ <el-col :span="4">
+ <div class="stats-card">
+ <div class="metric-icon">
+ <el-icon :size="32">
+ <Box />
+ </el-icon>
+ </div>
+ <div class="card-title">鏈夎揣鏂欑</div>
+ <div class="card-value">{{ formatNumber(bigscreendata.inStockPallet) }}</div>
+ </div>
+ </el-col>
+ <el-col :span="4">
+ <div class="stats-card">
+ <div class="metric-icon">
+ <el-icon :size="32">
+ <Box />
+ </el-icon>
+ </div>
+ <div class="card-title">绌虹鏁伴噺</div>
+ <div class="card-value">{{ formatNumber(bigscreendata.freeStockPallet) }}</div>
+ </div>
+ </el-col>
+ </el-row>
+
+ <!-- 鍥捐〃鍖哄煙锛堢涓�琛岋級 -->
+ <el-row :gutter="20" class="chart-row">
+ <el-col :span="8">
+ <div class="chart-card">
+ <div class="chart-title">搴撲綅鍒╃敤鐜�</div>
+ <div ref="locationRateRef" class="chart-container"></div>
+ </div>
+ </el-col>
+ <el-col :span="8">
+ <div class="chart-card">
+ <div class="chart-title">鐗╂枡涓存湡淇℃伅</div>
+ <div class="expiration-table-container">
+ <el-table
+ :data="expirationTableData"
+ border
+ stripe
+ style="width: 100%;"
+ :empty-text="emptyText"
+ >
+ <el-table-column prop="materielCode" label="鐗╂枡缂栫爜" align="center" />
+ <el-table-column prop="materielName" label="鐗╂枡鍚嶇О" align="center" show-overflow-tooltip />
+ <el-table-column prop="batchNo" label="鎵规鍙�" align="center" />
+ <el-table-column prop="validDate" label="鏈夋晥鏈�" align="center" />
+ <el-table-column label="涓存湡绛夌骇" align="center">
+ <template #default="{ row }">
+ <span :class="getExpireLevelClass(row.expireLevel)">{{ row.expireLevel }}</span>
+ </template>
+ </el-table-column>
+ <el-table-column prop="daysToExpiration" label="涓存湡澶╂暟" align="center">
+ <template #default="{ row }">
+ <span :class="row.daysToExpiration < 0 ? 'text-red' : ''">
+ {{ row.daysToExpiration < 0 ? '宸茶繃鏈�' : `${row.daysToExpiration}澶ー }}
+ </span>
+ </template>
+ </el-table-column>
+ <el-table-column prop="stockQuantity" label="搴撳瓨鏁伴噺" align="center" />
+ <el-table-column prop="locationCode" label="搴撲綅" align="center" />
+ <el-table-column prop="palletCode" label="鎵樼洏缂栧彿" align="center" />
+ </el-table>
+ </div>
+ </div>
+ </el-col>
+ <el-col :span="8">
+ <div class="chart-card">
+ <div class="chart-title">杩�7鏃ュ嚭鍏ュ簱瓒嬪娍锛堝浘鍍忓寲璧板娍锛�</div>
+ <div ref="stockTrendRef" class="chart-container"></div>
+ </div>
+ </el-col>
+ </el-row>
+
+ <!-- 鍥捐〃鍖哄煙锛堢浜岃锛� -->
+ <el-row :gutter="20" class="chart-row">
+ </el-row>
+
+ <!-- 琛ㄦ牸鍖哄煙 - 瀹炴椂浣滀笟鐩戞帶 -->
+ <el-row :gutter="20" class="table-row" width="100%">
+ <el-col :span="24">
+ <div class="table-card">
+ <div class="table-title">瀹炴椂浣滀笟鐩戞帶</div>
+ <el-table
+ :data="showTaskList"
+ border
+ style="width: 100%;"
+ :empty-text="showTaskList.length === 0 ? '鏆傛棤浣滀笟鏁版嵁' : ''"
+ >
+ <el-table-column prop="upperOrderNo" label="鍗曟嵁缂栧彿" />
+ <el-table-column label="鍗曟嵁鐘舵��" >
+ <template #default="{ row }">
+ <span class="task-status" :class="getStatusClass(row.taskStatus)">
+ {{ getTaskStatusText(row.taskStatus) }}
+ </span>
+ </template>
+ </el-table-column>
+ <el-table-column label="鍗曟嵁绫诲瀷" >
+ <template #default="{ row }">
+ <span class="task-type" :class="getTypeClass(row.taskType)">
+ {{ getTaskTypeText(row) }}
+ </span>
+ </template>
+ </el-table-column>
+ <el-table-column label="鍥炰紶MES鐘舵��" >
+ <template #default="{ row }">
+ <span class="task-status" :class="getMESStatusClass(row.returnToMESStatus)">
+ {{ getMESStatusText(row.returnToMESStatus) }}
+ </span>
+ </template>
+ </el-table-column>
+ <el-table-column prop="factoryArea" label="鍘傚尯" />
+ <el-table-column prop="modifier" label="淇敼浜�" />
+ <el-table-column prop="createDate" label="鍒涘缓鏃堕棿"/>
+ <el-table-column prop="modifyDate" label="淇敼鏃堕棿"/>
+ </el-table>
+ <div class="table-pagination">
+ <el-pagination layout="prev, pager, next, jumper" :current-page="1" :total="mergedTaskList.length" />
+ </div>
+ </div>
+ </el-col>
+ </el-row>
+ </div>
</template>
-<script>
-import { ref, reactive } from 'vue'
+<script setup>
+import { ref, onMounted, onUnmounted, nextTick, computed } from 'vue';
+import * as echarts from 'echarts';
+import http from "@/api/http.js";
+import { Box, Document, Download, Upload } from '@element-plus/icons-vue';
+import { ElMessage } from 'element-plus';
-export default {
- setup() {
+// 鍝嶅簲寮忔暟鎹� - 鏂板 completeTask 瀛楁
+const bigscreendata = ref({
+ totalStockQuantity: 0,
+ unOutBoundOrderCount: 0,
+ unInBoundOrderCount:0,
+ dailyCompletionRate: 0,
+ unhandledExceptionCount: 0,
+ locationUtilizationRate: 0,
+ inStockPallet: 0,
+ freeStockPallet: 0,
+ dailyInOutBoundList: [],
+ completeTask: [], // 鏂板锛氬悗绔繑鍥炵殑宓屽浠诲姟鏁版嵁
+ inboundCount: 0,
+ outboundCount: 0,
+ inventoryLocationDist: [],
+ exceptionTypeTrend: [],
+ nearExpirationList: []
+});
+
+// 涓存湡琛ㄦ牸鏁版嵁锛堜繚鎸佷笉鍙橈級
+const expirationTableData = computed(() => {
+ const expirationList = bigscreendata.value.nearExpirationList || [];
+ const uniqueMap = new Map();
+ expirationList.forEach(item => {
+ const uniqueKey = [
+ item.materielCode || '鏈煡缂栫爜',
+ item.batchNo || '鏈煡鎵规',
+ item.palletCode || '鏈煡鎵樼洏',
+ ].join('|');
+ if (!uniqueMap.has(uniqueKey)) {
+ uniqueMap.set(uniqueKey, item);
+ }
+ });
+ const uniqueExpirationList = Array.from(uniqueMap.values());
+ return uniqueExpirationList.map(item => {
+ const daysToExpiration = item.daysToExpiration || 0;
+ let expireLevel = '';
+ if (daysToExpiration < 0) {
+ expireLevel = '宸茶繃鏈�';
+ } else if (daysToExpiration <= 7) {
+ expireLevel = '7澶╁唴涓存湡';
+ } else if (daysToExpiration <= 15) {
+ expireLevel = '15澶╁唴涓存湡';
+ } else if (daysToExpiration <= 30) {
+ expireLevel = '30澶╁唴涓存湡';
+ } else {
+ expireLevel = '30澶╀互涓�';
+ }
return {
+ materielCode: item.materielCode || '鏈煡缂栫爜',
+ materielName: item.materielName || '鏈煡鍚嶇О',
+ batchNo: item.batchNo || '鏈煡鎵规',
+ validDate: item.validDate || '鏈煡鏈夋晥鏈�',
+ daysToExpiration: daysToExpiration,
+ expireLevel: expireLevel,
+ stockQuantity: item.stockQuantity || 0,
+ locationCode: item.locationCode || '鏈煡搴撲綅',
+ palletCode: item.palletCode || '鏈煡鎵樼洏',
+ unit: item.unit || 'PCS'
+ };
+ });
+});
+// 绌烘暟鎹彁绀烘枃鏈紙淇濇寔涓嶅彉锛�
+const emptyText = computed(() => {
+ const expirationList = bigscreendata.value.nearExpirationList || [];
+ return expirationList.length === 0 ? '鏆傛棤涓存湡鐗╂枡鏁版嵁' : '';
+});
+
+// 鑾峰彇涓存湡绛夌骇鏍峰紡绫伙紙淇濇寔涓嶅彉锛�
+const getExpireLevelClass = (level) => {
+ switch(level) {
+ case '宸茶繃鏈�': return 'expire-level expired';
+ case '7澶╁唴涓存湡': return 'expire-level urgent';
+ case '15澶╁唴涓存湡': return 'expire-level warning';
+ case '30澶╁唴涓存湡': return 'expire-level normal';
+ case '30澶╀互涓�': return 'expire-level low';
+ default: return 'expire-level default';
+ }
+};
+
+// 鏍稿績锛氬叆搴�/鍑哄簱涓氬姟绫诲瀷鏋氫妇鏄犲皠
+const inboundBusinessTypeMap = {
+ 11: "閲囪喘鍏ュ簱",
+ 12: "鏉傛敹鍗�",
+ 13: "鐢熶骇閫�鏂欏崟",
+ 14: "澶栧崗閫�鏂欏崟",
+ 15: "閿�鍞��搴撳崟",
+ 3: "璋冩嫧鍏ュ簱鍗�"
+};
+
+const outboundBusinessTypeMap = {
+ 21: "宸ュ崟棰嗘枡鍑哄簱鍗�",
+ 22: "鏉傚彂鍗�",
+ 23: "閫�璐у崟",
+ 24: "閿�鍞嚭搴撳崟",
+ 25: "澶栧崗棰嗘枡鐢宠鍗�",
+ 2: "璋冩嫧鍑哄簱鍗�",
+};
+
+// 鏍稿績淇敼锛氫粠 completeTask 涓彁鍙栬鍗曟暟鎹�
+const mergedTaskList = computed(() => {
+ // 1. 鑾峰彇 completeTask 鏁扮粍锛屽鐞嗙┖鍊�
+ const completeTaskList = bigscreendata.value.completeTask || [];
+ // 2. 鍙栫涓�涓厓绱狅紙鍚庣杩斿洖鏁扮粍闀垮害涓�1锛�
+ const taskData = completeTaskList[0] || {};
+ // 3. 鎻愬彇鍏ュ簱/鍑哄簱璁㈠崟锛屽鐞嗙┖鍊�
+ const inboundOrders = taskData.inboundOrders || [];
+ const outboundOrders = taskData.outboundOrders || [];
+
+ console.log('浠巆ompleteTask鎻愬彇鐨勫叆搴撹鍗曪細', inboundOrders);
+ console.log('浠巆ompleteTask鎻愬彇鐨勫嚭搴撹鍗曪細', outboundOrders);
+
+ // 鏍煎紡鍖栧叆搴撹鍗�
+ const formattedInbound = inboundOrders.map(item => ({
+ upperOrderNo: item.upperOrderNo || item.inboundOrderNo || '鏈煡鍗曞彿',
+ taskStatus: item.orderStatus || 0,
+ taskType: 'inbound',
+ businessType: item.businessType || '',
+ returnToMESStatus: item.returnToMESStatus || 0,
+ factoryArea: item.factoryArea || '鏈煡鍘傚尯',
+ modifier: item.modifier || '鏈煡淇敼浜�',
+ createDate: item.createDate || '鏈煡鏃堕棿',
+ modifyDate: item.modifyDate || '鏈煡鏃堕棿'
+ }));
+
+ // 鏍煎紡鍖栧嚭搴撹鍗�
+ const formattedOutbound = outboundOrders.map(item => ({
+ upperOrderNo: item.upperOrderNo || item.orderNo || '鏈煡鍗曞彿',
+ taskStatus: item.orderStatus || 0,
+ taskType: 'outbound',
+ businessType: item.businessType || '',
+ returnToMESStatus: item.returnToMESStatus || 0,
+ factoryArea: item.factoryArea || '鏈煡鍘傚尯',
+ modifier: item.modifier || '鏈煡淇敼浜�',
+ createDate: item.createDate || '鏈煡鏃堕棿',
+ modifyDate: item.modifyDate || '鏈煡鏃堕棿'
+ }));
+
+ const merged = [...formattedInbound, ...formattedOutbound];
+ console.log('鏈�缁堝悎骞剁殑浣滀笟鍒楄〃锛�', merged);
+ return merged;
+});
+
+// 璁㈠崟鐘舵�佹槧灏�
+const orderStatusMap = {
+ 0: "鏈紑濮�",
+};
+
+// 鑾峰彇璁㈠崟鐘舵�佹枃鏈�
+const getTaskStatusText = (statusNum) => {
+ if (statusNum === 1) {
+ if (statusNum.taskType === 'inbound') {
+ return "鍏ュ簱涓�";
+ } else if (statusNum.taskType === 'outbound') {
+ return "鍑哄簱涓�";
+ } else {
+ return "澶勭悊涓�";
}
}
-}
+ return orderStatusMap[statusNum];
+};
+
+// MES鍥炰紶鐘舵�佹槧灏�
+const mesStatusMap = {
+ 0: "鏈洖浼�",
+ 1: "鍥炰紶鎴愬姛",
+ 2: "鍥炰紶澶辫触",
+ 3: "閮ㄥ垎鍥炰紶鎴愬姛",
+ 4: "閮ㄥ垎鍥炰紶澶辫触",
+};
+
+// 鑾峰彇MES鍥炰紶鐘舵�佹枃鏈�
+const getMESStatusText = (statusNum) => {
+ if (statusNum === undefined || statusNum === null || isNaN(statusNum)) {
+ return "鏈煡鐘舵��";
+ }
+ return mesStatusMap[statusNum] || `鏈煡鐘舵��(${statusNum})`;
+};
+
+// 鑾峰彇MES鍥炰紶鐘舵�佹牱寮忕被
+const getMESStatusClass = (statusNum) => {
+ if (statusNum === undefined || statusNum === null || isNaN(statusNum)) {
+ return "status-unknown";
+ }
+ if (statusNum === 0) return "status-pending";
+ if (statusNum === 1) return "status-completed";
+ if (statusNum === 2) return "status-error";
+ if (statusNum === 3) return "status-processing";
+ if (statusNum === 4) return "status-error";
+ return "status-unknown";
+};
+
+// 鑾峰彇鍗曟嵁绫诲瀷鏂囨湰
+const getTaskTypeText = (row) => {
+ const businessType = Number(row.businessType) || 0;
+
+ if (row.taskType === 'inbound') {
+ const typeName = inboundBusinessTypeMap[businessType] || `鏈煡绫诲瀷(${businessType})`;
+ return `${typeName}`;
+ } else if (row.taskType === 'outbound') {
+ const typeName = outboundBusinessTypeMap[businessType] || `鏈煡绫诲瀷(${businessType})`;
+ return `${typeName}`;
+ } else {
+ return "鍏朵粬浣滀笟";
+ }
+};
+
+// 鑾峰彇浠诲姟鐘舵�佹牱寮忕被
+const getStatusClass = (statusNum) => {
+ if (statusNum === undefined || statusNum === null || isNaN(statusNum)) {
+ return "status-unknown";
+ }
+ if (statusNum === 3) return "status-completed";
+ if (statusNum === 2) return "status-processing";
+ if (statusNum === 0) return "status-pending";
+ if (statusNum === 4) return "status-canceled";
+ if (statusNum === 5) return "status-error";
+ if (statusNum === 1) return "status-completed";
+ return "status-unknown";
+};
+
+// 鑾峰彇浠诲姟绫诲瀷鏍峰紡绫�
+const getTypeClass = (taskType) => {
+ if (taskType === 'inbound') return "type-inbound";
+ if (taskType === 'outbound') return "type-outbound";
+ return "type-other";
+};
+
+// 琛ㄦ牸鏄剧ず鐨勪换鍔″垪琛紙杞挱鐢級
+const showTaskList = ref([]);
+const currentTaskIndex = ref(0);
+let taskCarouselTimer = null;
+
+// 鏁板瓧鏍煎紡鍖�
+const formatNumber = (num) => {
+ if (!num) return '0';
+ return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
+};
+
+// 杞挱閫昏緫
+const startTaskCarousel = () => {
+ if (taskCarouselTimer) clearInterval(taskCarouselTimer);
+
+ const totalTask = mergedTaskList.value.length;
+ console.log('浣滀笟鍒楄〃鎬绘暟锛�', totalTask);
+
+ if (totalTask <= 5) {
+ showTaskList.value = [...mergedTaskList.value];
+ } else {
+ showTaskList.value = mergedTaskList.value.slice(0, 5);
+ currentTaskIndex.value = 5;
+ }
+
+ if (totalTask > 5) {
+ taskCarouselTimer = setInterval(() => {
+ showTaskList.value.shift();
+ showTaskList.value.push(mergedTaskList.value[currentTaskIndex.value]);
+ currentTaskIndex.value++;
+ if (currentTaskIndex.value >= totalTask) {
+ currentTaskIndex.value = 0;
+ }
+ }, 5000);
+ }
+};
+
+// 鑾峰彇鍚庣鏁版嵁
+const fetchBigGreenData = async () => {
+ try {
+ const res = await http.get('/api/BigScreen/GetBigGreenData');
+ console.log('鍚庣鍘熷鏁版嵁锛�', res.data);
+
+ bigscreendata.value = res.data || res;
+ nextTick(() => {
+ startTaskCarousel();
+ refreshCharts();
+ });
+ } catch (error) {
+ ElMessage.error('鏁版嵁鑾峰彇澶辫触锛岃妫�鏌ュ悗绔帴鍙f槸鍚︽甯�');
+ console.error('鏁版嵁鑾峰彇澶辫触锛�', error);
+ }
+};
+
+// 澶囩敤妯℃嫙鏁版嵁
+const operationList = ref([
+ { opNo: 'JW251224001', opType: '鍏ュ簱', operator: '寮犱笁', startTime: '15:30:22', status: '澶勭悊涓�' },
+ { opNo: 'CK251224002', opType: '鍑哄簱', operator: '鏉庡洓', startTime: '15:25:10', status: '宸插畬鎴�' },
+ { opNo: 'PD251224003', opType: '鐩樼偣', operator: '鐜嬩簲', startTime: '15:20:05', status: '寰呯‘璁�' },
+ { opNo: 'SC251224005', opType: '涓婃灦', operator: '瀛欎竷', startTime: '15:10:18', status: '寮傚父' }
+]);
+
+// 鍥捐〃鐩稿叧鏂规硶锛堜繚鎸佷笉鍙橈級
+const inventoryPieRef = ref(null);
+const stockTrendRef = ref(null);
+const locationRateRef = ref(null);
+const exceptionTrendRef = ref(null);
+
+let inventoryPieChart = null;
+let stockTrendChart = null;
+let locationRateChart = null;
+let exceptionTrendChart = null;
+
+const initInventoryPie= () => {
+ if (!inventoryPieRef.value) return;
+ if (inventoryPieChart) {
+ inventoryPieChart.dispose();
+ }
+ inventoryPieChart = echarts.init(inventoryPieRef.value);
+ const locationData = bigscreendata.value.inventoryLocationDist.length
+ ? bigscreendata.value.inventoryLocationDist
+ : [
+ { value: 48.7, name: '甯告俯鍖篈鍖�', itemStyle: { color: '#5470c6' } },
+ { value: 29.2, name: '鍐疯棌鍖築鍖�', itemStyle: { color: '#91cc75' } },
+ { value: 21.9, name: '淇濈◣鍖篊鍖�', itemStyle: { color: '#fac858' } },
+ { value: 2.2, name: '娈嬫鍝佸尯D鍖�', itemStyle: { color: '#ee6666' } }
+ ];
+ const option = {
+ tooltip: {
+ trigger: 'item',
+ formatter: '{a} <br/>{b}: {c}%'
+ },
+ legend: {
+ bottom: 0,
+ left: 'center',
+ data: locationData.map(item => item.name)
+ },
+ series: [{
+ name: '搴撳瓨搴撲綅鍒嗗竷',
+ type: 'pie',
+ radius: ['40%', '70%'],
+ center: ['50%', '40%'],
+ avoidLabelOverlap: false,
+ itemStyle: {
+ borderRadius: 10,
+ borderColor: '#fff',
+ borderWidth: 2
+ },
+ label: {
+ show: false,
+ position: 'center'
+ },
+ emphasis: {
+ label: {
+ show: true,
+ fontSize: 20,
+ fontWeight: 'bold'
+ }
+ },
+ labelLine: {
+ show: false
+ },
+ data: locationData
+ }]
+ };
+ inventoryPieChart.setOption(option);
+ return inventoryPieChart;
+};
+
+const initStockTrend = () => {
+ if (!stockTrendRef.value) return;
+ if (stockTrendChart) {
+ stockTrendChart.dispose();
+ }
+ stockTrendChart = echarts.init(stockTrendRef.value);
+ const trendData = bigscreendata.value.dailyInOutBoundList;
+ const maxInbound = trendData.length ? Math.max(...trendData.map(item => item.dailyInboundQuantity || 0)) : 0;
+ const maxOutbound = trendData.length ? Math.max(...trendData.map(item => item.dailyOutboundQuantity || 0)) : 0;
+ const maxValue = Math.max(maxInbound, maxOutbound);
+ const option = {
+ tooltip: {
+ trigger: 'axis',
+ axisPointer: {
+ type: 'cross'
+ }
+ },
+ legend: {
+ data: ['鍏ュ簱閲�', '鍑哄簱閲�'],
+ top: 10
+ },
+ grid: {
+ left: '3%',
+ right: '4%',
+ bottom: '3%',
+ top: '15%',
+ containLabel: true
+ },
+ xAxis: {
+ type: 'category',
+ boundaryGap: true,
+ data: trendData.map(item => item.date)
+ },
+ yAxis: {
+ type: 'value',
+ name: '鏁伴噺',
+ min: 0,
+ max: maxValue > 0 ? Math.ceil(maxValue * 1.2) : 10
+ },
+ series: [
+ {
+ name: '鍏ュ簱閲�',
+ type: 'bar',
+ barWidth: '30%',
+ data: trendData.map(item => item.dailyInboundQuantity),
+ itemStyle: {
+ color: '#52c41a',
+ borderRadius: [4, 4, 0, 0]
+ }
+ },
+ {
+ name: '鍑哄簱閲�',
+ type: 'bar',
+ barWidth: '30%',
+ data: trendData.map(item => item.dailyOutboundQuantity),
+ itemStyle: {
+ color: '#1890ff',
+ borderRadius: [4, 4, 0, 0]
+ }
+ }
+ ]
+ };
+ stockTrendChart.setOption(option);
+ return stockTrendChart;
+};
+
+const initLocationRate = () => {
+ if (!locationRateRef.value) return;
+ if (locationRateChart) {
+ locationRateChart.dispose();
+ }
+ locationRateChart = echarts.init(locationRateRef.value);
+ const utilizationRate = bigscreendata.value.locationUtilizationRate || 0;
+ const freeRate = 100 - utilizationRate;
+ const option = {
+ tooltip: {
+ trigger: 'item',
+ formatter: '{b}: {c}%'
+ },
+ legend: {
+ bottom: 0,
+ left: 'center',
+ data: ['宸插崰鐢ㄥ簱浣�', '绌洪棽搴撲綅'],
+ textStyle: { fontSize: 12, color: '#666' }
+ },
+ graphic: [
+ {
+ type: 'text',
+ left: 'right',
+ top: '10%',
+ style: {
+ text: `${utilizationRate}%`,
+ fontSize: 24,
+ fontWeight: 'bold',
+ fill: '#333'
+ }
+ },
+ {
+ type: 'text',
+ left: 'right',
+ top: '25%',
+ style: {
+ text: '搴撲綅鍒╃敤鐜�',
+ fontSize: 14,
+ fill: '#666'
+ }
+ }
+ ],
+ series: [
+ {
+ type: 'pie',
+ radius: ['50%', '70%'],
+ center: ['40%', '50%'],
+ avoidLabelOverlap: false,
+ label: { show: false },
+ labelLine: { show: false },
+ data: [
+ { value: utilizationRate, name: '宸插崰鐢ㄥ簱浣�', itemStyle: { color: '#1890ff' } },
+ { value: freeRate, name: '绌洪棽搴撲綅', itemStyle: { color: '#e5e9f2' } }
+ ]
+ }
+ ]
+ };
+ locationRateChart.setOption(option);
+ return locationRateChart;
+};
+
+const initExceptionTrend = () => {
+ if (!exceptionTrendRef.value) return;
+ if (exceptionTrendChart) {
+ exceptionTrendChart.dispose();
+ }
+ exceptionTrendChart = echarts.init(exceptionTrendRef.value);
+ const exceptionData = bigscreendata.value.exceptionTypeTrend.length
+ ? bigscreendata.value.exceptionTypeTrend
+ : {
+ dates: ['12-18', '12-19', '12-20', '12-21', '12-22', '12-23', '12-24'],
+ stockShort: [10, 11, 9, 12, 10, 13, 12],
+ orderTimeout: [8, 9, 7, 8, 7, 9, 8],
+ locationException: [4, 5, 2, 4, 3, 5, 4],
+ checkDiff: [2, 3, 1, 2, 1, 3, 2]
+ };
+ const option = {
+ tooltip: {
+ trigger: 'axis',
+ axisPointer: {
+ type: 'cross'
+ }
+ },
+ legend: {
+ data: ['搴撳瓨涓嶈冻', '璁㈠崟瓒呮椂', '搴撲綅寮傚父', '鐩樼偣宸紓'],
+ top: 10
+ },
+ grid: {
+ left: '3%',
+ right: '4%',
+ bottom: '3%',
+ top: '15%',
+ containLabel: true
+ },
+ xAxis: {
+ type: 'category',
+ boundaryGap: false,
+ data: exceptionData.dates
+ },
+ yAxis: {
+ type: 'value',
+ name: '寮傚父鏁伴噺'
+ },
+ series: [
+ {
+ name: '搴撳瓨涓嶈冻',
+ type: 'line',
+ smooth: true,
+ symbol: 'circle',
+ symbolSize: 8,
+ data: exceptionData.stockShort,
+ itemStyle: { color: '#ff4d4f' },
+ lineStyle: { width: 3 }
+ },
+ {
+ name: '璁㈠崟瓒呮椂',
+ type: 'line',
+ smooth: true,
+ symbol: 'circle',
+ symbolSize: 8,
+ data: exceptionData.orderTimeout,
+ itemStyle: { color: '#faad14' },
+ lineStyle: { width: 3 }
+ },
+ {
+ name: '搴撲綅寮傚父',
+ type: 'line',
+ smooth: true,
+ symbol: 'circle',
+ symbolSize: 8,
+ data: exceptionData.locationException,
+ itemStyle: { color: '#722ed1' },
+ lineStyle: { width: 3 }
+ },
+ {
+ name: '鐩樼偣宸紓',
+ type: 'line',
+ smooth: true,
+ symbol: 'circle',
+ symbolSize: 8,
+ data: exceptionData.checkDiff,
+ itemStyle: { color: '#13c2c2' },
+ lineStyle: { width: 3 }
+ }
+ ]
+ };
+ exceptionTrendChart.setOption(option);
+ return exceptionTrendChart;
+};
+
+const refreshCharts = () => {
+ const charts = [
+ initStockTrend,
+ initLocationRate
+ ];
+ charts.forEach(initFunc => {
+ const chart = initFunc();
+ if (chart) {
+ chart.resize();
+ }
+ });
+};
+
+const handleResize = () => {
+ const charts = [
+ inventoryPieChart,
+ stockTrendChart,
+ locationRateChart,
+ exceptionTrendChart
+ ];
+ charts.forEach(chart => {
+ if (chart) {
+ chart.resize();
+ }
+ });
+};
+
+// 缁勪欢鐢熷懡鍛ㄦ湡
+onMounted(() => {
+ fetchBigGreenData();
+ nextTick(() => {
+ initInventoryPie();
+ initStockTrend();
+ initLocationRate();
+ initExceptionTrend();
+ window.addEventListener('resize', handleResize);
+ });
+});
+
+onUnmounted(() => {
+ const charts = [
+ inventoryPieChart,
+ stockTrendChart,
+ locationRateChart,
+ exceptionTrendChart
+ ];
+ charts.forEach(chart => {
+ if (chart) {
+ chart.dispose();
+ }
+ });
+ clearInterval(taskCarouselTimer);
+ window.removeEventListener('resize', handleResize);
+});
</script>
<style scoped>
-.title {
- line-height: 70vh;
+.wms-dashboard {
+ padding: 24px;
+ background: linear-gradient(135deg, #f0f2f5 0%, #e6e9f0 100%);
+ min-height: 100vh;
+ box-sizing: border-box;
+ font-family: "Segoe UI", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Helvetica Neue", Helvetica, Arial, sans-serif;
+}
+
+.header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 20px;
+ background: white;
+ padding: 15px 20px;
+ border-radius: 8px;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
+}
+
+.stats-card-row,
+.chart-row,
+.table-row {
+ margin-bottom: 20px;
+}
+
+.stats-card, .chart-card, .table-card {
+ background: #fff;
+ border-radius: 8px;
+ box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.04);
+ border: 1px solid #ebeef5;
+}
+
+.stats-card {
+ height: 140px;
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ padding: 20px 15px;
+ transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
+ position: relative;
+ overflow: hidden;
+ background: linear-gradient(145deg, #ffffff 0%, #f9fafc 100%);
+}
+
+.stats-card:hover {
+ transform: translateY(-6px) scale(1.02);
+ box-shadow: 0 12px 24px rgba(0, 0, 0, 0.1);
+ border-color: #409eff;
+ background: linear-gradient(145deg, #ffffff 0%, #f0f2f5 100%);
+}
+
+.metric-icon {
+ width: 56px;
+ height: 56px;
+ border-radius: 16px;
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ color: white;
+ margin-bottom: 12px;
+ box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3);
+ transition: all 0.3s ease;
+}
+
+.card-title {
+ font-size: 15px;
+ color: #606266;
+ margin-bottom: 10px;
+ font-weight: 500;
+ letter-spacing: 0.5px;
+}
+
+.card-value {
+ font-size: 32px;
+ font-weight: 700;
+ margin: 8px 0 4px;
+ color: #2c3e50;
+ text-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
+ line-height: 1.2;
+ background: linear-gradient(to right, #409eff, #36cfc9);
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+ background-clip: text;
+}
+
+.card-change {
+ margin-top: 3px;
+}
+
+.chart-card {
+ height: 400px;
+ padding: 24px;
+ display: flex;
+ flex-direction: column;
+ background: linear-gradient(180deg, #ffffff 0%, #f8f9fa 100%);
+ border: none;
+ box-shadow: 0 6px 16px rgba(0, 0, 0, 0.06);
+ border-radius: 12px;
+ transition: all 0.3s ease;
+ overflow: hidden;
+ position: relative;
+}
+
+.chart-card:hover {
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.08);
+ transform: translateY(-2px);
+}
+
+.chart-container {
+ width: 100%;
+ height: 100%;
+ min-height: 300px;
+ flex: 1;
+ position: relative;
+ border-radius: 8px;
+ background: rgba(255, 255, 255, 0.7);
+ backdrop-filter: blur(5px);
+ box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.03);
+}
+
+.expiration-table-container {
+ width: 100%;
+ height: 100%;
+ min-height: 300px;
+ flex: 1;
+ overflow-y: auto;
+}
+
+.expire-level {
+ padding: 2px 8px;
+ border-radius: 4px;
+ font-size: 12px;
+ font-weight: 500;
+}
+.expire-level.expired {
+ background-color: #fff2f0;
+ color: #ff4d4f;
+ border: 1px solid #ffccc7;
+}
+.expire-level.urgent {
+ background-color: #fff7e6;
+ color: #fa8c16;
+ border: 1px solid #ffd591;
+}
+.expire-level.warning {
+ background-color: #f6ffed;
+ color: #52c41a;
+ border: 1px solid #b7eb8f;
+}
+.expire-level.normal {
+ background-color: #e6f7ff;
+ color: #1890ff;
+ border: 1px solid #91d5ff;
+}
+.expire-level.low {
+ background-color: #f0f2f5;
+ color: #666666;
+ border: 1px solid #d9d9d9;
+}
+.expire-level.default {
+ background-color: #fafafa;
+ color: #8c8c8c;
+ border: 1px solid #e8e8e8;
+}
+
+.text-red {
+ color: #ff4d4f;
+ font-weight: 500;
+}
+
+.chart-title,
+.table-title {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 24px;
+ font-size: 18px;
+ font-weight: 600;
+ color: #2c3e50;
+ padding-left: 12px;
+ border-left: 4px solid #409eff;
+ position: relative;
+ letter-spacing: 0.5px;
+}
+
+.view-btn {
+ font-size: 12px;
+}
+
+.table-card {
+ padding: 24px;
+ background: #fff;
+ border-radius: 12px;
+ box-shadow: 0 6px 16px rgba(0, 0, 0, 0.06);
+ border: 1px solid rgba(0, 0, 0, 0.04);
+ overflow: hidden;
+ transition: all 0.3s ease;
+}
+
+.table-card:hover {
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.08);
+ transform: translateY(-2px);
+}
+
+.table-pagination {
+ display: flex;
+ justify-content: flex-end;
+ align-items: center;
+ margin-top: 20px;
+ padding-top: 15px;
+ border-top: 1px solid #ebeef5;
+}
+
+:deep(.el-table) {
+ border-radius: 6px;
+ overflow: hidden;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
+}
+
+:deep(.el-table th) {
+ background-color: #f5f7fa;
+ color: #606266;
+ font-weight: 600;
+ padding: 12px 0;
+}
+
+:deep(.el-table td) {
+ padding: 12px 0;
+}
+
+:deep(.el-table--border) {
+ border-radius: 6px;
+}
+
+:deep(.el-table--border::after) {
+ display: none;
+}
+
+:deep(.el-table--group::after) {
+ display: none;
+}
+
+:deep(.el-table::before) {
+ display: none;
+}
+
+:deep(.el-table__fixed-right::before) {
+ display: none;
+}
+
+:deep(.el-table__fixed::before) {
+ display: none;
+}
+
+:deep(.el-pagination) {
+ margin-top: 10px;
+}
+
+:deep(.el-pagination .btn-prev),
+:deep(.el-pagination .btn-next),
+:deep(.el-pagination .el-pager li) {
+ border-radius: 4px;
+ margin: 0 2px;
+ transition: all 0.3s;
+}
+
+:deep(.el-pagination .btn-prev:hover),
+:deep(.el-pagination .btn-next:hover),
+:deep(.el-pagination .el-pager li:hover) {
+ transform: translateY(-2px);
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+}
+
+:deep(.el-pagination .el-pager li.active) {
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+ color: #fff;
+}
+
+.task-status {
+ display: inline-block;
+ padding: 6px 12px;
+ border-radius: 20px;
+ font-size: 13px;
+ font-weight: 500;
text-align: center;
- font-size: 28px;
- color: orange;
+ min-width: 80px;
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
+ transition: all 0.2s ease;
+ letter-spacing: 0.5px;
+}
+
+.status-pending {
+ background-color: rgba(64, 158, 255, 0.1);
+ color: #409eff;
+ border: 1px solid rgba(64, 158, 255, 0.2);
+}
+
+.status-processing {
+ background-color: rgba(103, 194, 58, 0.1);
+ color: #67c23a;
+ border: 1px solid rgba(103, 194, 58, 0.2);
+}
+
+.status-completed {
+ background-color: rgba(103, 194, 58, 0.1);
+ color: #67c23a;
+ border: 1px solid rgba(103, 194, 58, 0.2);
+}
+
+.status-suspended {
+ background-color: rgba(230, 162, 60, 0.1);
+ color: #e6a23c;
+ border: 1px solid rgba(230, 162, 60, 0.2);
+}
+
+.status-canceled {
+ background-color: rgba(144, 147, 153, 0.1);
+ color: #909399;
+ border: 1px solid rgba(144, 147, 153, 0.2);
+}
+
+.status-error {
+ background-color: rgba(245, 108, 108, 0.1);
+ color: #f56c6c;
+ border: 1px solid rgba(245, 108, 108, 0.2);
+}
+
+.status-unknown {
+ background-color: rgba(144, 147, 153, 0.1);
+ color: #909399;
+ border: 1px solid rgba(144, 147, 153, 0.2);
+}
+
+.task-type {
+ display: inline-block;
+ padding: 6px 12px;
+ border-radius: 20px;
+ font-size: 13px;
+ font-weight: 500;
+ text-align: center;
+ min-width: 80px;
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
+ transition: all 0.2s ease;
+ letter-spacing: 0.5px;
+}
+
+
+
+.type-inbound {
+ background-color: rgba(64, 158, 255, 0.1);
+ color: #409eff;
+ border: 1px solid rgba(64, 158, 255, 0.2);
+}
+
+.type-outbound {
+ background-color: rgba(103, 194, 58, 0.1);
+ color: #67c23a;
+ border: 1px solid rgba(103, 194, 58, 0.2);
+}
+
+.type-transfer {
+ background-color: rgba(230, 162, 60, 0.1);
+ color: #e6a23c;
+ border: 1px solid rgba(230, 162, 60, 0.2);
+}
+
+.type-other {
+ background-color: rgba(144, 147, 153, 0.1);
+ color: #909399;
+ border: 1px solid rgba(144, 147, 153, 0.2);
+}
+
+.type-unknown {
+ background-color: rgba(144, 147, 153, 0.1);
+ color: #909399;
+ border: 1px solid rgba(144, 147, 153, 0.2);
+}
+
+.btn-group {
+ margin-left: 10px;
}
</style>
\ No newline at end of file
--
Gitblit v1.9.3