renmingwang
2026-03-30 675f3f475a61dd0ad8ffb76e183baa9b78f14f45
ÏîÄ¿´úÂë/WMS/WIDESEA_WMSClient/src/views/Home.vue
@@ -1,24 +1,531 @@
<template>
  <div class="title"></div>
  <div id="big-data-container" class="big-data-container">
    <div class="head">
      <h1>WMS系统监控中心</h1>
      <div class="head-actions">
        <el-button type="primary" @click="toggleFullScreen" size="small" round>
          <el-icon><FullScreen /></el-icon>
          {{ isFullScreen ? '退出全屏' : '全屏显示' }}
        </el-button>
      </div>
    </div>
    <div class="data-container">
      <div class="data-left">
        <div class="data-left-item">
          <div class="title">周出入库统计</div>
          <div id="chart-week-inout" style="height: calc(50% - 10px)"></div>
        </div>
        <div class="data-left-item">
          <div class="title">月出入库趋势</div>
          <div id="chart-month-inout" style="height: calc(50% - 10px)"></div>
        </div>
      </div>
      <div class="data-center">
        <div class="data-center-item">
          <div class="title">库存数据</div>
          <div class="center-top-num">
            <div class="item">
              <div class="text">总货位数量</div>
              <div class="num">{{ totalLocations }}</div>
            </div>
            <div class="item">
              <div class="text">今日入库</div>
              <div class="num">{{ todayInbound }}</div>
            </div>
            <div class="item">
              <div class="text">今日出库</div>
              <div class="num">{{ todayOutbound }}</div>
            </div>
            <div class="item">
              <div class="text">本周入库</div>
              <div class="num">{{ weekInbound }}</div>
            </div>
            <div class="item">
              <div class="text">本周出库</div>
              <div class="num">{{ weekOutbound }}</div>
            </div>
            <div class="item">
              <div class="text">本月入库</div>
              <div class="num">{{ monthInbound }}</div>
            </div>
            <div class="item">
              <div class="text">本月出库</div>
              <div class="num">{{ monthOutbound }}</div>
            </div>
            <!-- <div class="item">
              <div class="text">待处理订单</div>
              <div class="num">{{ pendingOrders }}</div>
            </div> -->
          </div>
        </div>
        <div class="data-center-item">
          <div class="title">日出入库明细</div>
          <div id="chart-daily-inout" style="height: calc(100% - 30px)"></div>
        </div>
      </div>
      <div class="data-right">
        <div class="data-right-item">
          <div class="title">库存变化趋势</div>
          <div id="chart-stock-trend" style="height: calc(50% - 10px)"></div>
        </div>
        <div class="data-right-item">
          <div class="title">货位状态分布</div>
          <div id="chart-warehouse-utilization" style="height: calc(50% - 10px)"></div>
        </div>
      </div>
    </div>
  </div>
</template>
<script>
import { ref, reactive } from 'vue'
import { ref, reactive, onMounted, onUnmounted } from 'vue'
import * as echarts from 'echarts'
import http from '@/api/http.js'
import { FullScreen } from '@element-plus/icons-vue'
let chartDailyInout, chartWeekInout, chartMonthInout, chartWarehouseUtilization, chartStockTrend
export default {
  setup() {
    return {
    const totalLocations = ref('0')
    const todayInbound = ref('0')
    const todayOutbound = ref('0')
    const weekInbound = ref('0')
    const weekOutbound = ref('0')
    const monthInbound = ref('0')
    const monthOutbound = ref('0')
    const pendingOrders = ref('0')
    // å…¨å±çŠ¶æ€
    const isFullScreen = ref(false)
    const realTimeTasks = reactive([
     ])
    // å…¨å±åˆ‡æ¢æ–¹æ³•
    const toggleFullScreen = () => {
      // èŽ·å–é¦–é¡µå†…å®¹å®¹å™¨å…ƒç´ 
      const element = document.getElementById('big-data-container')
      if (!element) return
      if (!isFullScreen.value) {
        // è¿›å…¥å…¨å±
        if (element.requestFullscreen) {
          element.requestFullscreen()
        } else if (element.mozRequestFullScreen) {
          element.mozRequestFullScreen()
        } else if (element.webkitRequestFullscreen) {
          element.webkitRequestFullscreen()
        } else if (element.msRequestFullscreen) {
          element.msRequestFullscreen()
        }
      } else {
        // é€€å‡ºå…¨å±
        if (document.exitFullscreen) {
          document.exitFullscreen()
        } else if (document.mozCancelFullScreen) {
          document.mozCancelFullScreen()
        } else if (document.webkitExitFullscreen) {
          document.webkitExitFullscreen()
        } else if (document.msExitFullscreen) {
          document.msExitFullscreen()
        }
      }
    }
    // ç›‘听全屏状态变化
    const handleFullScreenChange = () => {
      isFullScreen.value = !!(document.fullscreenElement || document.mozFullScreenElement || document.webkitFullscreenElement || document.msFullscreenElement)
      // å…³é”®ï¼šå…¨å±çŠ¶æ€å˜åŒ–æ—¶å¼ºåˆ¶è§¦å‘å›¾è¡¨resize
      handleResize()
    }
    // èŽ·å–å®žæ—¶æ•°æ®
    const fetchData = async () => {
      try {
        // èŽ·å–åº“å­˜ç»Ÿè®¡æ•°æ®
        const stockStatistics = await http.post('/api/Home/GetDt_TaskHty', {}, false)
        console.log('库存统计数据:', stockStatistics)
        if (stockStatistics) {
          if (stockStatistics.status) {
            const data = stockStatistics.data;
            console.log('库存数据:', data)
            totalLocations.value = data.totalLocations ? data.totalLocations.toLocaleString() : '0';
            todayInbound.value = data.todayInbound ? data.todayInbound.toLocaleString() : '0';
            todayOutbound.value = data.todayOutbound ? data.todayOutbound.toLocaleString() : '0';
            weekInbound.value = data.weekInbound ? data.weekInbound.toLocaleString() : '0';
            weekOutbound.value = data.weekOutbound ? data.weekOutbound.toLocaleString() : '0';
            monthInbound.value = data.monthInbound ? data.monthInbound.toLocaleString() : '0';
            monthOutbound.value = data.monthOutbound ? data.monthOutbound.toLocaleString() : '0';
            pendingOrders.value = data.pendingOrders ? data.pendingOrders.toLocaleString() : '0';
            // æ›´æ–°å›¾è¡¨æ•°æ®
            updateCharts(data);
          } else {
            console.error('库存统计API返回失败:', stockStatistics.message)
          }
        } else {
          console.error('库存统计API返回数据为空')
        }
      } catch (error) {
        console.error('获取数据失败:', error)
        // ä½¿ç”¨é»˜è®¤æ•°æ®ï¼Œç¡®ä¿å¤§å±æ­£å¸¸æ˜¾ç¤º
        console.log('使用默认数据显示')
      }
    }
    // æ›´æ–°å›¾è¡¨æ•°æ®
    const updateCharts = (stockData) => {
      if (!stockData) return
      console.log('更新图表数据:', stockData)
      // æ›´æ–°åº“存变化趋势图
      if (chartStockTrend) {
        // ç”Ÿæˆæœ€æ–°çš„æœ€è¿‘7天日期
        const recentDays = generateRecentDays()
        chartStockTrend.setOption({
          xAxis: {
            data: recentDays
          },
          series: [{
            data: stockData.stockTrend || []
          }]
        })
      }
      // æ›´æ–°æ—¥å‡ºå…¥åº“明细
      if (chartDailyInout) {
        chartDailyInout.setOption({
          series: [
            { name: '入库', data: stockData.dailyInout?.inbound || [] },
            { name: '出库', data: stockData.dailyInout?.outbound || [] }
          ]
        })
      }
      // æ›´æ–°å‘¨å‡ºå…¥åº“统计
      if (chartWeekInout) {
        chartWeekInout.setOption({
          series: [
            { name: '入库', data: stockData.weekInout?.inbound || [] },
            { name: '出库', data: stockData.weekInout?.outbound || [] }
          ]
        })
      }
      // æ›´æ–°æœˆå‡ºå…¥åº“趋势
      if (chartMonthInout) {
        chartMonthInout.setOption({
          series: [
            { name: '入库', data: stockData.monthInout?.inbound || [] },
            { name: '出库', data: stockData.monthInout?.outbound || [] }
          ]
        })
      }
      // æ›´æ–°è´§ä½çŠ¶æ€åˆ†å¸ƒ
      if (chartWarehouseUtilization) {
        chartWarehouseUtilization.setOption({
          series: [{
            data: [
              { value: stockData.warehouseUtilization?.inStock || 0, name: '有货' },
              { value: stockData.warehouseUtilization?.free || 0, name: '空闲' },
              { value: stockData.warehouseUtilization?.inStockLock || 0, name: '有货锁定' },
              // { value: stockData.warehouseUtilization?.lockLocations || 0, name: '锁定' },
              // { value: stockData.warehouseUtilization?.freeLock || 0, name: '空闲锁定' },
              // { value: stockData.warehouseUtilization?.palletLock || 0, name: '大托盘锁定' }
            ]
          }]
        })
      }
    }
    // ç”Ÿæˆæœ€è¿‘7天的连续日期,格式为"dd日"
    const generateRecentDays = () => {
      const days = []
      const today = new Date()
      // ç”Ÿæˆæœ€è¿‘7天的日期,从6天前到今天
      for (let i = 6; i >= 0; i--) {
        const date = new Date(today)
        date.setDate(today.getDate() - i)
        days.push(`${date.getDate().toString().padStart(2, '0')}日`)
      }
      return days
    }
    // åˆå§‹åŒ–图表
    const initCharts = () => {
      // æ—¥å‡ºå…¥åº“明细 - æŒ‰å°æ—¶ç»Ÿè®¡
      chartDailyInout = echarts.init(document.getElementById('chart-daily-inout'))
      chartDailyInout.setOption({
        tooltip: { trigger: 'axis', axisPointer: { type: 'shadow' } },
        grid: { left: '0%', top: '10px', right: '0%', bottom: '4%', containLabel: true },
        xAxis: { type: 'category', data: ['08时', '09时', '10时', '11时', '12时', '13时', '14时', '15时', '16时', '17时'], axisLine: { show: true, lineStyle: { color: 'rgba(255,255,255,.5)' } }, axisLabel: { color: 'rgba(255,255,255,.8)' } },
        yAxis: { type: 'value', axisLine: { show: true, lineStyle: { color: 'rgba(255,255,255,.5)' } }, axisLabel: { color: 'rgba(255,255,255,.8)' } },
        series: [
          { name: '入库', type: 'bar', data: [], barWidth: '25%', itemStyle: { normal: { color: '#2f89cf' } } },
          { name: '出库', type: 'bar', data: [], barWidth: '25%', itemStyle: { normal: { color: '#46d000' } } }
        ]
      })
      // å‘¨å‡ºå…¥åº“统计
      chartWeekInout = echarts.init(document.getElementById('chart-week-inout'))
      chartWeekInout.setOption({
        tooltip: { trigger: 'axis', axisPointer: { type: 'shadow' } },
        grid: { left: '0%', top: '10px', right: '0%', bottom: '4%', containLabel: true },
        xAxis: { type: 'category', data: ['周一', '周二', '周三', '周四', '周五', '周六', '周日'], axisLine: { show: true, lineStyle: { color: 'rgba(255,255,255,.5)' } }, axisLabel: { color: 'rgba(255,255,255,.8)' } },
        yAxis: { type: 'value', axisLine: { show: true, lineStyle: { color: 'rgba(255,255,255,.5)' } }, axisLabel: { color: 'rgba(255,255,255,.8)' } },
        series: [
          { name: '入库', type: 'bar', data: [], barWidth: '25%', itemStyle: { normal: { color: '#2f89cf' } } },
          { name: '出库', type: 'bar', data: [], barWidth: '25%', itemStyle: { normal: { color: '#46d000' } } }
        ]
      })
      // æœˆå‡ºå…¥åº“趋势
      chartMonthInout = echarts.init(document.getElementById('chart-month-inout'))
      chartMonthInout.setOption({
        tooltip: { trigger: 'axis' },
        grid: { left: '0%', top: '10px', right: '0%', bottom: '4%', containLabel: true },
        xAxis: { type: 'category', data: ['01日', '05日', '10日', '15日', '20日', '25日', '30日'], axisLine: { show: true, lineStyle: { color: 'rgba(255,255,255,.5)' } }, axisLabel: { color: 'rgba(255,255,255,.8)' } },
        yAxis: { type: 'value', axisLine: { show: true, lineStyle: { color: 'rgba(255,255,255,.5)' } }, axisLabel: { color: 'rgba(255,255,255,.8)' } },
        series: [
          { name: '入库', type: 'line', data: [], smooth: true, lineStyle: { color: '#2f89cf' }, areaStyle: { color: 'rgba(47, 137, 207, 0.3)' } },
          { name: '出库', type: 'line', data: [], smooth: true, lineStyle: { color: '#46d000' }, areaStyle: { color: 'rgba(70, 208, 0, 0.3)' } }
        ]
      })
      // è´§ä½çŠ¶æ€åˆ†å¸ƒ
      chartWarehouseUtilization = echarts.init(document.getElementById('chart-warehouse-utilization'))
      chartWarehouseUtilization.setOption({
        tooltip: { trigger: 'item' },
        legend: { orient: 'vertical', left: 'left', textStyle: { color: 'rgba(255,255,255,.8)' } },
        series: [
          { name: '货位状态分布', type: 'pie', radius: '60%', data: [], emphasis: { itemStyle: { shadowBlur: 10, shadowOffsetX: 0, shadowColor: 'rgba(0, 0, 0, 0.5)' } } }
        ]
      })
      // åº“存变化趋势
      chartStockTrend = echarts.init(document.getElementById('chart-stock-trend'))
      const recentDays = generateRecentDays()
      chartStockTrend.setOption({
        tooltip: { trigger: 'axis' },
        grid: { left: '0%', top: '10px', right: '0%', bottom: '4%', containLabel: true },
        xAxis: { type: 'category', data: recentDays, axisLine: { show: true, lineStyle: { color: 'rgba(255,255,255,.5)' } }, axisLabel: { color: 'rgba(255,255,255,.8)' } },
        yAxis: { type: 'value', axisLine: { show: true, lineStyle: { color: 'rgba(255,255,255,.5)' } }, axisLabel: { color: 'rgba(255,255,255,.8)' } },
        series: [{ type: 'line', data: [], smooth: true, lineStyle: { color: '#4ecdc4' }, areaStyle: { color: 'rgba(78, 205, 196, 0.3)' } }]
      })
    }
    // çª—口大小变化时重新调整图表
    const handleResize = () => {
      chartDailyInout && chartDailyInout.resize()
      chartWeekInout && chartWeekInout.resize()
      chartMonthInout && chartMonthInout.resize()
      chartWarehouseUtilization && chartWarehouseUtilization.resize()
      chartStockTrend && chartStockTrend.resize()
    }
    let dataTimer = null
    onMounted(() => {
      initCharts()
      window.addEventListener('resize', handleResize)
      // æ·»åŠ å…¨å±çŠ¶æ€å˜åŒ–ç›‘å¬
      document.addEventListener('fullscreenchange', handleFullScreenChange)
      document.addEventListener('mozfullscreenchange', handleFullScreenChange)
      document.addEventListener('webkitfullscreenchange', handleFullScreenChange)
      document.addEventListener('msfullscreenchange', handleFullScreenChange)
      // åˆå§‹èŽ·å–æ•°æ®
      fetchData()
      dataTimer = setInterval(() => {
        fetchData()
      }, 600000)//十分钟更新一次
    })
    onUnmounted(() => {
      window.removeEventListener('resize', handleResize)
      // ç§»é™¤å…¨å±çŠ¶æ€å˜åŒ–ç›‘å¬
      document.removeEventListener('fullscreenchange', handleFullScreenChange)
      document.removeEventListener('mozfullscreenchange', handleFullScreenChange)
      document.removeEventListener('webkitfullscreenchange', handleFullScreenChange)
      document.removeEventListener('msfullscreenchange', handleFullScreenChange)
      // æ¸…除定时器
      if (dataTimer) {
        clearInterval(dataTimer)
        dataTimer = null
      }
      // é”€æ¯å›¾è¡¨å®žä¾‹
      chartDailyInout && chartDailyInout.dispose()
      chartWeekInout && chartWeekInout.dispose()
      chartMonthInout && chartMonthInout.dispose()
      chartWarehouseUtilization && chartWarehouseUtilization.dispose()
      chartStockTrend && chartStockTrend.dispose()
    })
    return {
      totalLocations,
      todayInbound,
      todayOutbound,
      weekInbound,
      weekOutbound,
      monthInbound,
      monthOutbound,
      pendingOrders,
      isFullScreen,
      toggleFullScreen
    }
  }
}
</script>
<style scoped>
.title {
  line-height: 70vh;
  text-align: center;
  font-size: 28px;
  color: orange;
<style lang="less" scoped>
.big-data-container {
  width: 100%;
  height: 100vh;
  background: #000000;
  color: #fff;
  padding: 20px;
  box-sizing: border-box;
  overflow: hidden;
  .head {
    height: 60px;
    display: flex;
    justify-content: center;
    align-items: center;
    font-size: 24px;
    font-weight: bold;
    color: #00ccff;
    padding: 0 20px;
    box-sizing: border-box;
    margin-bottom: 10px;
    position: relative;
  }
  .head-actions {
    position: absolute;
    right: 20px;
    display: flex;
    gap: 10px;
  }
  .data-container {
      display: flex;
      height: calc(100% - 80px);
      gap: 20px;
      overflow: hidden;
      .data-left {
        width: 30%;
        display: flex;
        flex-direction: column;
        gap: 20px;
        .data-left-item {
          flex: 1;
          background: rgba(10, 30, 50, 0.6);
          border-radius: 12px;
          padding: 10px;
          box-sizing: border-box;
          min-height: 0;
        }
      }
      .data-center {
        width: 40%;
        display: flex;
        flex-direction: column;
        gap: 20px;
        .center-top-num {
          flex: 1;
          display: grid;
          grid-template-columns: repeat(4, 1fr);
          grid-template-rows: repeat(2, 1fr);
          gap: 15px;
          background: rgba(10, 30, 50, 0.6);
          border-radius: 12px;
          padding: 10px;
          box-sizing: border-box;
          min-height: 0;
          .item {
            background: rgba(10, 40, 70, 0.7);
            border-radius: 12px;
            padding: 15px;
            box-sizing: border-box;
            display: flex;
            flex-direction: column;
            justify-content: center;
            align-items: center;
            font-size: 14px;
            .text {
              margin-bottom: 8px;
              color: rgba(255, 255, 255, 0.7);
            }
            .num {
              font-size: 24px;
              font-weight: bold;
              color: #00ccff;
            }
          }
        }
        .data-center-item {
          flex: 1;
          background: rgba(10, 30, 50, 0.6);
          border-radius: 12px;
          padding: 10px;
          box-sizing: border-box;
          display: flex;
          flex-direction: column;
          min-height: 0;
          > div[id^="chart-"] {
            flex: 1;
            width: 100%;
            height: 100% !important;
          }
        }
      }
      .data-right {
        width: 30%;
        display: flex;
        flex-direction: column;
        gap: 20px;
        .data-right-item {
          flex: 1;
          background: rgba(10, 30, 50, 0.6);
          border-radius: 12px;
          padding: 10px;
          box-sizing: border-box;
          min-height: 0;
        }
      }
    }
  .title {
    height: 30px;
    line-height: 30px;
    text-align: center;
    font-size: 16px;
    font-weight: bold;
    color: #00ccff;
  }
}
</style>