| | |
| | | <div class="table-card"> |
| | | <div class="card-title"> |
| | | <span>宿¶ä½ä¸ä»»å¡</span> |
| | | <el-button type="primary" size="small" @click="refreshData"> |
| | | <el-icon><Refresh /></el-icon> |
| | | å·æ° |
| | | </el-button> |
| | | </div> |
| | | <el-table :data="taskList" style="width: 100%" height="200"> |
| | | <el-table-column prop="taskNo" label="ä»»å¡ç¼å·" width="150" /> |
| | | <el-table-column prop="type" label="ä»»å¡ç±»å" width="100"> |
| | | <el-table :data="showTaskList" style="width: 100%" :height="tableHeight"> |
| | | <el-table-column prop="taskNum" label="ä»»å¡å·" /> |
| | | <el-table-column prop="taskStatus" label="ä»»å¡ç¶æ" width="120"> |
| | | <template #default="{ row }"> |
| | | <el-tag :type="row.type === 'å
¥åº' ? 'success' : 'warning'">{{ row.type }}</el-tag> |
| | | <div class="status-container" :class="getStatusClass(row.taskStatus)"> |
| | | <div class="status-dot"></div> |
| | | <span class="status-text">{{ getTaskStatusText(row.taskStatus) }}</span> |
| | | </div> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="material" label="ç©æåç§°" /> |
| | | <el-table-column prop="quantity" label="æ°é" width="100" /> |
| | | <el-table-column prop="location" label="åºä½" width="120" /> |
| | | <el-table-column prop="status" label="ç¶æ" width="100"> |
| | | <el-table-column prop="taskType" label="ä»»å¡ç±»å" width="100"> |
| | | <template #default="{ row }"> |
| | | <el-tag :type="getStatusType(row.status)">{{ row.status }}</el-tag> |
| | | <div class="type-container" :class="getTypeClass(row.taskType)"> |
| | | <el-icon class="type-icon"> |
| | | <Box v-if="getTypeClass(row.taskType) === 'type-inbound'" /> |
| | | <Upload v-else-if="getTypeClass(row.taskType) === 'type-outbound'" /> |
| | | <Refresh v-else-if="getTypeClass(row.taskType) === 'type-transfer'" /> |
| | | <Operation v-else /> |
| | | </el-icon> |
| | | <span class="type-text">{{ getTaskTypeText(row.taskType) }}</span> |
| | | </div> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="operator" label="æä½å" width="100" /> |
| | | <el-table-column prop="time" label="æ¶é´" width="160" /> |
| | | <el-table-column prop="palletCode" label="æçç¼å·" /> |
| | | <el-table-column prop="sourceAddress" label="èµ·ç¹ä½ç½®"/> |
| | | <el-table-column prop="targetAddress" label="ç»ç¹ä½ç½®"/> |
| | | <el-table-column prop="createDate" label="å建æ¶é´"/> |
| | | </el-table> |
| | | </div> |
| | | </div> |
| | |
| | | </template> |
| | | |
| | | <script> |
| | | import { ref, onMounted, onUnmounted } from 'vue' |
| | | import { ref, onMounted, onUnmounted, nextTick } from 'vue' |
| | | import * as echarts from 'echarts' |
| | | import { http } from '@/utils/http' |
| | | import { formatDateTime } from '@/utils' |
| | | import { ElMessage } from 'element-plus' |
| | | // 导å
¥Element Plus徿 |
| | | import { |
| | | DataBoard, Box, Operation, Warning, Download, Upload, List, |
| | | Top, Bottom, Refresh, ArrowRight, Clock |
| | | } from '@element-plus/icons-vue' |
| | | |
| | | export default { |
| | | name: 'Dashboard', |
| | | components: { |
| | | DataBoard, Box, Operation, Warning, Download, Upload, List, |
| | | Top, Bottom, Refresh, ArrowRight, Clock |
| | | }, |
| | | setup() { |
| | | const currentTime = ref('') |
| | | const trendChartRef = ref(null) |
| | | const categoryChartRef = ref(null) |
| | | const efficiencyChartRef = ref(null) |
| | | const tableHeight = ref(200) |
| | | |
| | | // å¾è¡¨å®ä¾å¼ç¨ |
| | | const trendChart = ref(null) |
| | | const categoryChart = ref(null) |
| | | const efficiencyChart = ref(null) |
| | | |
| | | // å端è¿åæ°æ®ï¼ååºå¼ï¼ |
| | | const bigscreendata = ref({ |
| | | totalStockQuantity: 0, |
| | | unOutBoundOrderCount: 0, |
| | | dailyCompletionRate: 0, |
| | | unhandledExceptionCount: 0, |
| | | locationUtilizationRate: 0, |
| | | inStockPallet: 0, |
| | | freeStockPallet: 0, |
| | | dailyInOutBoundList: [], |
| | | taskList: [], |
| | | inboundCount: 0, |
| | | outboundCount: 0, |
| | | inventoryLocationDist: [], |
| | | completeTask: [] |
| | | }) |
| | | |
| | | // ä»»å¡ç¶ææ å° |
| | | const taskStatusMap = { |
| | | 100: "æ°å»º", |
| | | 105: "å·²åé", |
| | | 200: "å åæºå¾
æ§è¡", |
| | | 210: "å åæºæ§è¡ä¸", |
| | | 220: "å åæºå®æ", |
| | | 400: "è¾é线å¾
æ§è¡", |
| | | 410: "è¾é线æ§è¡ä¸", |
| | | 420: "è¾éçº¿å®æ", |
| | | 300: "AGVå¾
æ§è¡", |
| | | 310: "AGVæ§è¡ä¸", |
| | | 315: "AGVåè´§ä¸", |
| | | 320: "AGVå¾
ç»§ç»æ§è¡", |
| | | 325: "AGVæ¾è´§ä¸", |
| | | 330: "AGV宿", |
| | | 900: "ä»»å¡å®æ", |
| | | 970: "ä»»å¡æèµ·", |
| | | 980: "ä»»å¡åæ¶", |
| | | 990: "ä»»å¡å¼å¸¸", |
| | | 110: "æåæºæ§è¡ä¸" |
| | | } |
| | | |
| | | // å
³é®ææ ï¼ååºå¼ï¼ |
| | | const metrics = ref([ |
| | | { label: 'æ»åºåé', value: '12,580', icon: 'Box', color: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)', trend: 8.5 }, |
| | | { label: '仿¥å
¥åº', value: '1,280', icon: 'Download', color: 'linear-gradient(135deg, #f093fb 0%, #f5576c 100%)', trend: 12.3 }, |
| | | { label: '仿¥åºåº', value: '965', icon: 'Upload', color: 'linear-gradient(135deg, #4facfe 0%, #00f2fe 100%)', trend: -3.2 }, |
| | | { label: 'å¾
å¤çä»»å¡', value: '48', icon: 'List', color: 'linear-gradient(135deg, #43e97b 0%, #38f9d7 100%)', trend: 5.7 } |
| | | { |
| | | label: 'æ»åºåé', |
| | | value: 0, |
| | | icon: 'Box', |
| | | color: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)', |
| | | trend: 8.5 |
| | | }, |
| | | { |
| | | label: '仿¥å
¥åº', |
| | | value: 0, |
| | | icon: 'Download', |
| | | color: 'linear-gradient(135deg, #f093fb 0%, #f5576c 100%)', |
| | | trend: 12.3 |
| | | }, |
| | | { |
| | | label: '仿¥åºåº', |
| | | value: 0, |
| | | icon: 'Upload', |
| | | color: 'linear-gradient(135deg, #4facfe 0%, #00f2fe 100%)', |
| | | trend: -3.2 |
| | | }, |
| | | { |
| | | label: 'å¾
å¤çä»»å¡', |
| | | value: 0, |
| | | icon: 'List', |
| | | color: 'linear-gradient(135deg, #43e97b 0%, #38f9d7 100%)', |
| | | trend: 5.7 |
| | | } |
| | | ]) |
| | | |
| | | // ä»»å¡å表ç¸å
³ |
| | | const taskList = ref([]) |
| | | const showTaskList = ref([]) |
| | | const currentTaskIndex = ref(7) // åå§ä»ç¬¬8æ¡å¼å§ï¼å7æ¡é»è®¤æ¾ç¤ºï¼ |
| | | let taskCarouselTimer = null |
| | | |
| | | // èªå¨å·æ°ç¸å
³é
ç½® |
| | | const lastInboundToday = ref(0) // ä¸ä¸æ¬¡å½å¤©å
¥åºé |
| | | const lastOutboundToday = ref(0) // ä¸ä¸æ¬¡å½å¤©åºåºé |
| | | const refreshInterval = ref(5 * 60 * 1000) // 宿¶å·æ°é´éï¼5åéï¼ |
| | | const minRefreshGap = ref(30 * 1000) // æå°å·æ°é´éï¼é²æï¼30ç§ï¼ |
| | | let lastRefreshTime = ref(0) // ä¸ä¸æ¬¡å·æ°æ¶é´ |
| | | let autoRefreshTimer = null // èªå¨å·æ°å®æ¶å¨ |
| | | |
| | | // è·åä»»å¡ç¶æææ¬ |
| | | const getTaskStatusText = (statusNum) => { |
| | | if (statusNum === undefined || statusNum === null || isNaN(statusNum)) { |
| | | return "æªç¥ç¶æ"; |
| | | } |
| | | return taskStatusMap[statusNum] || "æªç¥ç¶æ"; |
| | | } |
| | | |
| | | // å¯å¨ä»»å¡è½®æ |
| | | const startTaskCarousel = () => { |
| | | if (taskCarouselTimer) clearInterval(taskCarouselTimer); |
| | | |
| | | const totalTask = bigscreendata.value.taskList.length; |
| | | if (totalTask <= 7) { // 任塿°<=7æ¶ä¸è½®æ |
| | | showTaskList.value = [...bigscreendata.value.taskList]; |
| | | return; |
| | | } |
| | | |
| | | taskCarouselTimer = setInterval(() => { |
| | | const tableElement = document.querySelector('.el-table'); |
| | | if (tableElement) { |
| | | tableElement.classList.add('flash-effect'); |
| | | setTimeout(() => { |
| | | tableElement.classList.remove('flash-effect'); |
| | | }, 600); |
| | | } |
| | | |
| | | // æ°å¢ä¸1æ¡ï¼å 餿å1æ¡ï¼ä¿æ7æ¡æ¾ç¤ºï¼ |
| | | showTaskList.value.push(bigscreendata.value.taskList[currentTaskIndex.value]); |
| | | showTaskList.value.shift(); |
| | | |
| | | // 循ç¯ç´¢å¼ |
| | | currentTaskIndex.value++; |
| | | if (currentTaskIndex.value >= totalTask) { |
| | | currentTaskIndex.value = 0; |
| | | } |
| | | }, 5000); // 5ç§è½®æä¸æ¬¡ |
| | | } |
| | | |
| | | // è·åä»»å¡ç±»åææ¬ |
| | | const getTaskTypeText = (taskTypeNum) => { |
| | | if (!taskTypeNum || isNaN(taskTypeNum)) return "æªç¥ç±»å"; |
| | | |
| | | if (taskTypeNum >= 500 && taskTypeNum < 900) return "å
¥åº"; |
| | | if (taskTypeNum >= 100 && taskTypeNum < 500) return "åºåº"; |
| | | if (taskTypeNum >= 900 && taskTypeNum < 1000) return "ç§»åº"; |
| | | return "å
¶ä»ä½ä¸"; |
| | | } |
| | | |
| | | // è·åä»»å¡ç¶ææ ·å¼ç±» |
| | | const getStatusClass = (statusNum) => { |
| | | if (statusNum === undefined || statusNum === null || isNaN(statusNum)) { |
| | | return "status-unknown"; |
| | | } |
| | | |
| | | if (statusNum >= 900) return "status-completed"; // 宿 |
| | | if (statusNum >= 400) return "status-processing"; // è¾é线æ§è¡ä¸ |
| | | if (statusNum >= 300) return "status-processing"; // AGVæ§è¡ä¸ |
| | | if (statusNum >= 200) return "status-processing"; // å åæºæ§è¡ä¸ |
| | | if (statusNum >= 100) return "status-pending"; // æ°å»ºãå·²åé |
| | | if (statusNum === 970) return "status-suspended"; // æèµ· |
| | | if (statusNum === 980) return "status-canceled"; // åæ¶ |
| | | if (statusNum === 990) return "status-error"; // å¼å¸¸ |
| | | |
| | | return "status-unknown"; |
| | | } |
| | | |
| | | // è·åä»»å¡ç±»åæ ·å¼ç±» |
| | | const getTypeClass = (taskTypeNum) => { |
| | | if (!taskTypeNum || isNaN(taskTypeNum)) return "type-unknown"; |
| | | |
| | | if (taskTypeNum >= 500 && taskTypeNum < 900) return "type-inbound"; // å
¥åº |
| | | if (taskTypeNum >= 100 && taskTypeNum < 500) return "type-outbound"; // åºåº |
| | | if (taskTypeNum >= 900 && taskTypeNum < 1000) return "type-transfer"; // ç§»åº |
| | | |
| | | return "type-other"; // å
¶ä»ä½ä¸ |
| | | } |
| | | |
| | | // ä»å端è·åæ°æ® |
| | | const fetchBigGreenData = async () => { |
| | | try { |
| | | const res = await http.get('/api/BigScreen/GetBigGreenData'); |
| | | console.log('大屿°æ®', res); |
| | | bigscreendata.value = res.data || res; |
| | | |
| | | updateMetrics(); |
| | | |
| | | taskList.value = bigscreendata.value.taskList || []; |
| | | showTaskList.value = taskList.value.slice(0, 7); |
| | | startTaskCarousel(); |
| | | |
| | | // æ°æ®å è½½å®æååå§åå¾è¡¨ |
| | | nextTick(() => { |
| | | initEfficiencyChart(); |
| | | initTrendChart(); |
| | | initCategoryChart(); |
| | | }); |
| | | } catch (error) { |
| | | console.error('è·å大屿°æ®å¤±è´¥:', error); |
| | | ElMessage.error('è·åæ°æ®å¤±è´¥ï¼è¯·ç¨åéè¯'); |
| | | } |
| | | }; |
| | | |
| | | // æ´æ°å
³é®ææ |
| | | const updateMetrics = () => { |
| | | metrics.value[0].value = bigscreendata.value.totalStockQuantity || 0 |
| | | metrics.value[1].value = bigscreendata.value.inboundCount || 0 |
| | | metrics.value[2].value = bigscreendata.value.outboundCount || 0 |
| | | metrics.value[3].value = bigscreendata.value.unOutBoundOrderCount || 0 |
| | | } |
| | | |
| | | // æ´æ°æ¶é´ |
| | | let timer |
| | |
| | | currentTime.value = formatDateTime(new Date()) |
| | | } |
| | | |
| | | // è·åç¶æç±»å |
| | | const getStatusType = (status) => { |
| | | const map = { |
| | | 'è¿è¡ä¸': 'primary', |
| | | '已宿': 'success', |
| | | 'å¾
å¤ç': 'info', |
| | | 'å¼å¸¸': 'danger' |
| | | } |
| | | return map[status] || 'info' |
| | | // åå§åè¶å¿å¾ï¼ç´æ¥ä½¿ç¨åç«¯æ¥æï¼ |
| | | const initTrendChart = () => { |
| | | if (!trendChartRef.value) return |
| | | |
| | | trendChart.value = echarts.init(trendChartRef.value) |
| | | |
| | | // ç´æ¥ä»å端è·åæ¥æåæ°æ® |
| | | const dates = [] |
| | | const inboundData = [] |
| | | const outboundData = [] |
| | | |
| | | if (bigscreendata.value.dailyInOutBoundList && bigscreendata.value.dailyInOutBoundList.length > 0) { |
| | | bigscreendata.value.dailyInOutBoundList.forEach(item => { |
| | | dates.push(item.date) // ç´æ¥ä½¿ç¨å端è¿åçæ¥æ |
| | | inboundData.push(item.dailyInboundQuantity || 0) |
| | | outboundData.push(item.dailyOutboundQuantity || 0) |
| | | }) |
| | | } |
| | | |
| | | // åå§åè¶å¿å¾ |
| | | const initTrendChart = () => { |
| | | const chart = echarts.init(trendChartRef.value) |
| | | const hasData = dates.length > 0 |
| | | |
| | | const option = { |
| | | tooltip: { trigger: 'axis' }, |
| | | |
| | | legend: { |
| | | data: ['å
¥åºé', 'åºåºé'], |
| | | textStyle: { color: '#fff' } |
| | | }, |
| | | grid: { |
| | | left: '3%', right: '4%', bottom: '3%', top: '15%', |
| | | left: '3%', |
| | | right: '4%', |
| | | bottom: '3%', |
| | | top: '15%', |
| | | containLabel: true |
| | | }, |
| | | xAxis: { |
| | | type: 'category', |
| | | boundaryGap: false, |
| | | data: ['00:00', '04:00', '08:00', '12:00', '16:00', '20:00', '24:00'], |
| | | axisLine: { lineStyle: { color: '#fff' } } |
| | | data: dates, |
| | | axisLine: { lineStyle: { color: '#fff' } }, |
| | | axisLabel: { color: '#fff' } |
| | | }, |
| | | yAxis: { |
| | | type: 'value', |
| | | axisLine: { lineStyle: { color: '#fff' } }, |
| | | splitLine: { lineStyle: { color: 'rgba(255,255,255,0.1)' } } |
| | | splitLine: { lineStyle: { color: 'rgba(255,255,255,0.1)' } }, |
| | | axisLabel: { color: '#fff' }, |
| | | min: 0 |
| | | }, |
| | | series: [ |
| | | { |
| | | name: 'å
¥åºé', |
| | | type: 'line', |
| | | smooth: true, |
| | | data: [120, 200, 450, 680, 520, 780, 650], |
| | | itemStyle: { color: '#43e97b' }, |
| | | data: inboundData, |
| | | itemStyle: { color: '#5470c6' }, |
| | | lineStyle: { color: '#5470c6' }, |
| | | areaStyle: { |
| | | color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ |
| | | { offset: 0, color: 'rgba(67, 233, 123, 0.5)' }, |
| | | { offset: 1, color: 'rgba(67, 233, 123, 0)' } |
| | | { offset: 0, color: 'rgba(84, 112, 198, 0.5)' }, |
| | | { offset: 1, color: 'rgba(84, 112, 198, 0)' } |
| | | ]) |
| | | } |
| | | }, |
| | | showSymbol: hasData, |
| | | symbol: 'circle', |
| | | symbolSize: 6 |
| | | }, |
| | | { |
| | | name: 'åºåºé', |
| | | type: 'line', |
| | | smooth: true, |
| | | data: [80, 150, 380, 520, 420, 650, 580], |
| | | itemStyle: { color: '#4facfe' }, |
| | | data: outboundData, |
| | | itemStyle: { color: '#91cc75' }, |
| | | lineStyle: { color: '#91cc75' }, |
| | | areaStyle: { |
| | | color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ |
| | | { offset: 0, color: 'rgba(79, 172, 254, 0.5)' }, |
| | | { offset: 1, color: 'rgba(79, 172, 254, 0)' } |
| | | { offset: 0, color: 'rgba(145, 204, 117, 0.5)' }, |
| | | { offset: 1, color: 'rgba(145, 204, 117, 0)' } |
| | | ]) |
| | | }, |
| | | showSymbol: hasData, |
| | | symbol: 'circle', |
| | | symbolSize: 6 |
| | | } |
| | | ], |
| | | |
| | | } |
| | | ] |
| | | } |
| | | chart.setOption(option) |
| | | return chart |
| | | |
| | | trendChart.value.setOption(option) |
| | | return trendChart.value |
| | | } |
| | | |
| | | // åå§ååç±»å æ¯å¾ |
| | | const initCategoryChart = () => { |
| | | const chart = echarts.init(categoryChartRef.value) |
| | | if (!categoryChartRef.value) return |
| | | |
| | | categoryChart.value = echarts.init(categoryChartRef.value) |
| | | |
| | | const option = { |
| | | tooltip: { trigger: 'item' }, |
| | | legend: { |
| | |
| | | emphasis: { |
| | | label: { show: true, fontSize: 16, fontWeight: 'bold' } |
| | | }, |
| | | data: [ |
| | | data: bigscreendata.value.inventoryLocationDist.length > 0 |
| | | ? bigscreendata.value.inventoryLocationDist |
| | | : [ |
| | | { value: 3580, name: 'åææ', itemStyle: { color: '#5470c6' } }, |
| | | { value: 2840, name: 'åæå', itemStyle: { color: '#91cc75' } }, |
| | | { value: 4120, name: 'æå', itemStyle: { color: '#fac858' } }, |
| | |
| | | } |
| | | ] |
| | | } |
| | | //chart.setOption(option) |
| | | return chart |
| | | |
| | | categoryChart.value.setOption(option) |
| | | return categoryChart.value |
| | | } |
| | | |
| | | // åå§åæçç»è®¡å¾ |
| | | // åå§åæçç»è®¡å¾ |
| | | // åå§åæçç»è®¡å¾ï¼ä¿®å¤çï¼ |
| | | const initEfficiencyChart = () => { |
| | | const chart = echarts.init(efficiencyChartRef.value) |
| | | if (!efficiencyChartRef.value) { |
| | | console.warn('æçå¾è¡¨å®¹å¨ä¸åå¨ï¼'); |
| | | return; |
| | | } |
| | | |
| | | // ç¡®ä¿å®¹å¨æé«åº¦ |
| | | efficiencyChartRef.value.style.height = '250px'; |
| | | efficiencyChartRef.value.style.width = '100%'; |
| | | |
| | | // 鿝æ§å®ä¾ |
| | | if (efficiencyChart.value) { |
| | | efficiencyChart.value.dispose(); |
| | | } |
| | | |
| | | // åå§å ECharts |
| | | efficiencyChart.value = echarts.init(efficiencyChartRef.value); |
| | | |
| | | // æ°æ®å¤ç |
| | | const taskData = { |
| | | 'å
¥åº': 0, |
| | | 'åºåº': 0 |
| | | }; |
| | | bigscreendata.value.completeTask.forEach(item => { |
| | | if (item.taskType && typeof item.count === 'number') { |
| | | taskData[item.taskType] = item.count; |
| | | } |
| | | }); |
| | | |
| | | const option = { |
| | | tooltip: { trigger: 'axis', axisPointer: { type: 'shadow' } }, |
| | | tooltip: { |
| | | trigger: 'axis', |
| | | axisPointer: { type: 'shadow' }, |
| | | formatter: params => { |
| | | return params.map(p => `${p.seriesName}: ${p.value || 0} å`).join('<br/>'); |
| | | } |
| | | }, |
| | | grid: { |
| | | left: '3%', right: '4%', bottom: '3%', top: '10%', |
| | | left: '5%', |
| | | right: '5%', |
| | | bottom: '10%', |
| | | top: '15%', |
| | | containLabel: true |
| | | }, |
| | | xAxis: { |
| | | type: 'category', |
| | | data: ['å
¥åºä½ä¸', 'åºåºä½ä¸', 'çç¹ä½ä¸', 'è°æ¨ä½ä¸', '补货ä½ä¸'], |
| | | data: ['å
¥åºä½ä¸', 'åºåºä½ä¸'], |
| | | axisLine: { lineStyle: { color: '#fff' } }, |
| | | axisLabel: { color: '#fff' } |
| | | }, |
| | | yAxis: { |
| | | type: 'value', |
| | | name: 'æç(åä½/å°æ¶)', |
| | | name: '宿æ°é(å)', |
| | | nameTextStyle: { color: '#fff' }, |
| | | axisLine: { lineStyle: { color: '#fff' } }, |
| | | splitLine: { lineStyle: { color: 'rgba(255,255,255,0.1)' } }, |
| | | axisLabel: { color: '#fff' } |
| | | axisLabel: { color: '#fff' }, |
| | | min: 0 |
| | | }, |
| | | series: [ |
| | | { |
| | | data: [180, 156, 95, 78, 120], |
| | | name: 'ä½ä¸æ°é', |
| | | data: [taskData['å
¥åº'], taskData['åºåº']], |
| | | type: 'bar', |
| | | barWidth: '40%', |
| | | itemStyle: { |
| | |
| | | { offset: 1, color: '#188df0' } |
| | | ]), |
| | | borderRadius: [5, 5, 0, 0] |
| | | }, |
| | | label: { |
| | | show: true, |
| | | position: 'top', |
| | | color: '#fff', |
| | | fontSize: 12 |
| | | } |
| | | } |
| | | ] |
| | | }; |
| | | |
| | | efficiencyChart.value.setOption(option, true); |
| | | console.log('æçå¾è¡¨æ¸²æå®æï¼æ°æ®ï¼', taskData); |
| | | }; |
| | | |
| | | // å·æ°å¾è¡¨æ°æ®ï¼isDataChangeï¼æ¯å¦æ¯æ°æ®åå导è´çå·æ°ï¼ |
| | | const refreshCharts = (isDataChange = false) => { |
| | | // æ°æ®ååæ¶æ·»å éªçææ |
| | | if (isDataChange) { |
| | | const chartElements = [trendChartRef.value, categoryChartRef.value, efficiencyChartRef.value] |
| | | chartElements.forEach(el => { |
| | | if (el) { |
| | | el.classList.add('flash-effect'); |
| | | setTimeout(() => el.classList.remove('flash-effect'), 600); |
| | | } |
| | | chart.setOption(option) |
| | | return chart |
| | | }) |
| | | } |
| | | |
| | | // è·åä»»å¡åè¡¨æ°æ® |
| | | const fetchTaskList = async () => { |
| | | try { |
| | | // æ¨¡ææ°æ®ï¼å®é
ä½¿ç¨æ¶è°ç¨æ¥å£ |
| | | // const res = await http.get('/wms/task/list') |
| | | // 鿝æ§å¾è¡¨å®ä¾ |
| | | if (trendChart.value) trendChart.value.dispose() |
| | | if (categoryChart.value) categoryChart.value.dispose() |
| | | if (efficiencyChart.value) efficiencyChart.value.dispose() |
| | | |
| | | // æ¨¡ææ°æ® |
| | | taskList.value = [ |
| | | { taskNo: 'RK20241224001', type: 'å
¥åº', material: '颿¿ Aå', quantity: 500, location: 'A-01-01', status: 'è¿è¡ä¸', operator: 'å¼ ä¸', time: '2024-12-24 14:25:30' }, |
| | | { taskNo: 'CK20241224002', type: 'åºåº', material: 'èºä¸ M8', quantity: 2000, location: 'B-03-05', status: '已宿', operator: 'æå', time: '2024-12-24 14:20:15' }, |
| | | { taskNo: 'RK20241224003', type: 'å
¥åº', material: 'éæ¿ Bå', quantity: 300, location: 'A-02-03', status: 'å¾
å¤ç', operator: 'çäº', time: '2024-12-24 14:15:00' }, |
| | | { taskNo: 'CK20241224004', type: 'åºåº', material: 'èºæ¯ M8', quantity: 1500, location: 'B-02-01', status: 'è¿è¡ä¸', operator: 'èµµå
', time: '2024-12-24 14:10:45' }, |
| | | { taskNo: 'PD20241224001', type: 'çç¹', material: 'å«ç', quantity: 5000, location: 'C-01-01', status: 'è¿è¡ä¸', operator: 'åä¸', time: '2024-12-24 14:05:20' } |
| | | ] |
| | | } catch (error) { |
| | | console.error('è·åä»»å¡å表失败:', error) |
| | | } |
| | | // éæ°åå§åå¾è¡¨ |
| | | initTrendChart() |
| | | initCategoryChart() |
| | | initEfficiencyChart() |
| | | } |
| | | |
| | | // å·æ°æ°æ® |
| | | // æå¨å·æ°æ°æ® |
| | | const refreshData = () => { |
| | | fetchTaskList() |
| | | fetchBigGreenData() |
| | | } |
| | | |
| | | // çªå£å¤§å°æ¹åæ¶éç»å¾è¡¨ |
| | | // å¯å¨èªå¨å·æ° |
| | | const startAutoRefresh = () => { |
| | | if (autoRefreshTimer) clearInterval(autoRefreshTimer) |
| | | autoRefreshTimer = setInterval(() => { |
| | | console.log('宿¶å·æ°æ°æ®:', new Date().toLocaleString()) |
| | | fetchBigGreenData() |
| | | }, refreshInterval.value) |
| | | } |
| | | |
| | | // çªå£å¤§å°æ¹åæ¶éç»å¾è¡¨åè°æ´è¡¨æ ¼é«åº¦ |
| | | const handleResize = () => { |
| | | try { |
| | | const refs = [trendChartRef.value, categoryChartRef.value, efficiencyChartRef.value] |
| | | refs.forEach(dom => { |
| | | if (dom) { |
| | | const chart = echarts.getInstanceByDom(dom) |
| | | if (chart) { |
| | | chart.resize() |
| | | } |
| | | } |
| | | const windowHeight = window.innerHeight |
| | | const headerHeight = 60 |
| | | const navHeight = 50 |
| | | const metricsHeight = 120 |
| | | const chartsHeight = 300 |
| | | const padding = 80 |
| | | |
| | | const availableHeight = windowHeight - headerHeight - navHeight - metricsHeight - chartsHeight - padding |
| | | tableHeight.value = Math.max(200, Math.min(availableHeight, 400)) |
| | | |
| | | // éç»å¾è¡¨ |
| | | const charts = [trendChart.value, categoryChart.value, efficiencyChart.value] |
| | | charts.forEach(chart => { |
| | | if (chart) chart.resize() |
| | | }) |
| | | } catch (error) { |
| | | console.warn('å¾è¡¨éç»æ¶åºé:', error) |
| | |
| | | } |
| | | |
| | | onMounted(() => { |
| | | // åå§åæ¶é´ |
| | | updateTime() |
| | | timer = setInterval(updateTime, 1000) |
| | | |
| | | // åå§åæ°æ®åå¾è¡¨ |
| | | fetchBigGreenData() |
| | | |
| | | nextTick(() => { |
| | | initTrendChart() |
| | | initCategoryChart() |
| | | initEfficiencyChart() |
| | | fetchTaskList() |
| | | handleResize() |
| | | startAutoRefresh() // å¯å¨èªå¨å·æ° |
| | | }) |
| | | |
| | | // çå¬çªå£å¤§å°åå |
| | | window.addEventListener('resize', handleResize) |
| | | }) |
| | | |
| | | onUnmounted(() => { |
| | | // æ¸
餿æå®æ¶å¨ |
| | | clearInterval(timer) |
| | | if (taskCarouselTimer) clearInterval(taskCarouselTimer) |
| | | if (autoRefreshTimer) clearInterval(autoRefreshTimer) |
| | | |
| | | // ç§»é¤çªå£çå¬ |
| | | window.removeEventListener('resize', handleResize) |
| | | |
| | | // 鿝å¾è¡¨å®ä¾ |
| | | try { |
| | | const refs = [trendChartRef.value, categoryChartRef.value, efficiencyChartRef.value] |
| | | refs.forEach(dom => { |
| | | if (dom) { |
| | | const chart = echarts.getInstanceByDom(dom) |
| | | if (chart) { |
| | | chart.dispose() |
| | | } |
| | | } |
| | | }) |
| | | if (trendChart.value) trendChart.value.dispose() |
| | | if (categoryChart.value) categoryChart.value.dispose() |
| | | if (efficiencyChart.value) efficiencyChart.value.dispose() |
| | | } catch (error) { |
| | | console.warn('å¾è¡¨éæ¯æ¶åºé:', error) |
| | | } |
| | |
| | | currentTime, |
| | | metrics, |
| | | taskList, |
| | | showTaskList, |
| | | trendChartRef, |
| | | categoryChartRef, |
| | | efficiencyChartRef, |
| | | getStatusType, |
| | | tableHeight, |
| | | getTaskTypeText, |
| | | getTaskStatusText, |
| | | getStatusClass, |
| | | getTypeClass, |
| | | refreshData |
| | | } |
| | | } |
| | |
| | | <style scoped> |
| | | .dashboard-container { |
| | | width: 100%; |
| | | height: 100%; |
| | | height: 100vh; |
| | | padding: 20px; |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 15px; |
| | | overflow-y: auto; |
| | | background-color: #0a0e27; |
| | | } |
| | | |
| | | .header { |
| | |
| | | .metric-value { |
| | | font-size: 28px; |
| | | font-weight: bold; |
| | | color: #fff; |
| | | margin-bottom: 5px; |
| | | } |
| | | |
| | |
| | | border-radius: 10px; |
| | | } |
| | | |
| | | /* Element Plus Table æ ·å¼è¦ç */ |
| | | :deep(.el-table) { |
| | | background: transparent !important; |
| | | } |
| | |
| | | |
| | | :deep(.el-table td) { |
| | | border-color: rgba(255, 255, 255, 0.1) !important; |
| | | color: rgba(255, 255, 255, 0.9) !important; |
| | | } |
| | | |
| | | :deep(.el-table tr) { |
| | |
| | | background: rgba(255, 255, 255, 0.05) !important; |
| | | } |
| | | |
| | | /* ä»»å¡ç¶ææ ·å¼ */ |
| | | .status-container { |
| | | display: flex; |
| | | align-items: center; |
| | | padding: 4px 10px; |
| | | border-radius: 20px; |
| | | position: relative; |
| | | overflow: hidden; |
| | | font-size: 12px; |
| | | font-weight: 500; |
| | | } |
| | | |
| | | .status-dot { |
| | | width: 8px; |
| | | height: 8px; |
| | | border-radius: 50%; |
| | | margin-right: 6px; |
| | | position: relative; |
| | | } |
| | | |
| | | .status-dot::after { |
| | | content: ''; |
| | | position: absolute; |
| | | width: 100%; |
| | | height: 100%; |
| | | border-radius: 50%; |
| | | animation: pulse 2s infinite; |
| | | } |
| | | |
| | | .status-text { |
| | | white-space: nowrap; |
| | | } |
| | | |
| | | /* ä¸åç¶æçé¢è² */ |
| | | .status-completed { |
| | | background: rgba(103, 194, 58, 0.15); |
| | | color: #67c23a; |
| | | } |
| | | |
| | | .status-completed .status-dot { |
| | | background: #67c23a; |
| | | } |
| | | |
| | | .status-completed .status-dot::after { |
| | | background: rgba(103, 194, 58, 0.5); |
| | | } |
| | | |
| | | .status-processing { |
| | | background: rgba(64, 158, 255, 0.15); |
| | | color: #409eff; |
| | | } |
| | | |
| | | .status-processing .status-dot { |
| | | background: #409eff; |
| | | } |
| | | |
| | | .status-processing .status-dot::after { |
| | | background: rgba(64, 158, 255, 0.5); |
| | | } |
| | | |
| | | .status-pending { |
| | | background: rgba(144, 147, 153, 0.15); |
| | | color: #909399; |
| | | } |
| | | |
| | | .status-pending .status-dot { |
| | | background: #909399; |
| | | } |
| | | |
| | | .status-pending .status-dot::after { |
| | | background: rgba(144, 147, 153, 0.5); |
| | | } |
| | | |
| | | .status-suspended { |
| | | background: rgba(230, 162, 60, 0.15); |
| | | color: #e6a23c; |
| | | } |
| | | |
| | | .status-suspended .status-dot { |
| | | background: #e6a23c; |
| | | } |
| | | |
| | | .status-suspended .status-dot::after { |
| | | background: rgba(230, 162, 60, 0.5); |
| | | } |
| | | |
| | | .status-canceled { |
| | | background: rgba(144, 147, 153, 0.15); |
| | | color: #909399; |
| | | } |
| | | |
| | | .status-canceled .status-dot { |
| | | background: #909399; |
| | | } |
| | | |
| | | .status-canceled .status-dot::after { |
| | | background: rgba(144, 147, 153, 0.5); |
| | | } |
| | | |
| | | .status-error { |
| | | background: rgba(245, 108, 108, 0.15); |
| | | color: #f56c6c; |
| | | } |
| | | |
| | | .status-error .status-dot { |
| | | background: #f56c6c; |
| | | } |
| | | |
| | | .status-error .status-dot::after { |
| | | background: rgba(245, 108, 108, 0.5); |
| | | } |
| | | |
| | | /* ä»»å¡ç±»åæ ·å¼ */ |
| | | .type-container { |
| | | display: flex; |
| | | align-items: center; |
| | | padding: 4px 10px; |
| | | border-radius: 20px; |
| | | position: relative; |
| | | font-size: 12px; |
| | | font-weight: 500; |
| | | } |
| | | |
| | | .type-icon { |
| | | margin-right: 6px; |
| | | font-size: 14px; |
| | | } |
| | | |
| | | .type-text { |
| | | white-space: nowrap; |
| | | } |
| | | |
| | | /* ä¸åç±»åçé¢è² */ |
| | | .type-inbound { |
| | | background: rgba(103, 194, 58, 0.15); |
| | | color: #67c23a; |
| | | } |
| | | |
| | | .type-outbound { |
| | | background: rgba(230, 162, 60, 0.15); |
| | | color: #e6a23c; |
| | | } |
| | | |
| | | .type-transfer { |
| | | background: rgba(64, 158, 255, 0.15); |
| | | color: #409eff; |
| | | } |
| | | |
| | | .type-other { |
| | | background: rgba(144, 147, 153, 0.15); |
| | | color: #909399; |
| | | } |
| | | |
| | | /* å¨ç»ææ */ |
| | | @keyframes pulse { |
| | | 0% { |
| | | transform: scale(1); |
| | | opacity: 1; |
| | | } |
| | | 50% { |
| | | transform: scale(1.5); |
| | | opacity: 0.3; |
| | | } |
| | | 100% { |
| | | transform: scale(1); |
| | | opacity: 1; |
| | | } |
| | | } |
| | | |
| | | /* éªçææï¼è¡¨æ ¼åå¾è¡¨å·æ°æ¶ï¼ */ |
| | | .flash-effect { |
| | | animation: flash 0.6s ease-in-out; |
| | | } |
| | | |
| | | @keyframes flash { |
| | | 0% { |
| | | box-shadow: 0 0 0 rgba(79, 172, 254, 0); |
| | | } |
| | | 20% { |
| | | box-shadow: 0 0 15px rgba(79, 172, 254, 0.7); |
| | | } |
| | | 40% { |
| | | box-shadow: 0 0 0 rgba(79, 172, 254, 0); |
| | | } |
| | | 60% { |
| | | box-shadow: 0 0 15px rgba(79, 172, 254, 0.7); |
| | | } |
| | | 80% { |
| | | box-shadow: 0 0 0 rgba(79, 172, 254, 0); |
| | | } |
| | | 100% { |
| | | box-shadow: 0 0 0 rgba(79, 172, 254, 0); |
| | | } |
| | | } |
| | | |
| | | /* ååºå¼éé
*/ |
| | | @media screen and (max-width: 1920px) { |
| | | .metric-value { |