ÏîÄ¿´úÂë/WMS/WIDESEA_WMSClient/src/views/Home.vue
@@ -1,219 +1,530 @@
<template>
  <div class="home-contianer">
  <div class="dashboard-container">
    <div class="overview-section">
      <div class="data-overview">
        <h3>数据总览</h3>
        <div class="metrics">
          <div class="metric-item" v-for="(item, index) in dataMetrics" :key="index">
            <div class="metric-name">{{ item.name }}</div>
            <div class="metric-value">{{ item.value }}</div>
          </div>
        </div>
      </div>
    </div>
    <div class="charts-section">
      <el-row :gutter="20">
        <el-col :lg="12">
          <div class="chart-container">
            <h3>出库量</h3>
            <div v-if="loading" class="loading">加载中...</div>
            <div v-else-if="error" class="error">数据加载失败</div>
            <div v-else-if="!chartData.outbound.values.length" class="no-data">暂无出库数据</div>
            <div v-else ref="outboundChart" class="chart"></div>
          </div>
        </el-col>
        <el-col :lg="12">
          <div class="chart-container">
            <h3>入库量</h3>
            <div v-if="loading" class="loading">加载中...</div>
            <div v-else-if="error" class="error">数据加载失败</div>
            <div v-else-if="!chartData.inbound.values.length" class="no-data">暂无入库数据</div>
            <div v-else ref="inboundChart" class="chart"></div>
          </div>
        </el-col>
      </el-row>
    </div>
    <div style="margin-top: 20px;"></div>
    <div class="charts-section">
      <el-row :gutter="20">
        <el-col :lg="24">
          <div class="chart-container">
            <h3>月出入库量</h3>
            <div v-if="loading" class="loading">加载中...</div>
            <div v-else-if="error" class="error">数据加载失败</div>
            <div v-else-if="!chartData.monthData.inValue.length || !chartData.monthData.outValue.length" class="no-data">
              æš‚无出库数据</div>
            <div v-else ref="monthDataChart" class="chart1"></div>
          </div>
        </el-col>
      </el-row>
    </div>
  </div>
</template>
<script>
import { ref, onMounted, onUnmounted } from 'vue';
export default {
  components: {},
  data() {
    return {
      n: 90,
      value1: '1',
    };
  },
  setup() {
    let open = (item) => {
      window.open(item.url, '_blank');
    };
    let interval;
    onMounted(() => {
      // interval = setInterval(() => {
      //   chart2.xAxis[0].data.splice(0, 1);
      //   let lastYear =
      //     chart2.xAxis[0].data[chart2.xAxis[0].data.length - 1] * 1 + 1;
      //   chart2.xAxis[0].data.push(lastYear);
<script setup>
import http from '../api/http.js';
import { ref, reactive, onMounted, onUnmounted, nextTick } from 'vue';
import * as echarts from 'echarts';
      //   chart2.series[0].data.splice(0, 1);
      //   chart2.series[0].data.push(~~(Math.random() * 1000));
// å“åº”式数据
const dataMetrics = ref([]);
const chartData = reactive({
  outbound: { dates: [], values: [] },
  inbound: { dates: [], values: [] },
  monthData: { dates: [], inValue: [], outValue: [] }
});
const loading = ref(true);
const error = ref(false);
      //   chart2.series[1].data.splice(0, 1);
      //   chart2.series[1].data.push(~~(Math.random() * 1000));
      //   $chart2.setOption(chart2);
      // }, 2000);
    });
    onUnmounted(() => {
// å›¾è¡¨å¼•用和实例
const outboundChart = ref(null);
const inboundChart = ref(null);
const monthDataChart = ref(null);
const outboundInstance = ref(null);
const inboundInstance = ref(null);
const monthDataInstance = ref(null);
const charts = ref([]);
    });
    return { open };
  },
  destroyed() {
// åˆå§‹åŒ–图表
const initCharts = () => {
  if (!outboundChart.value || !inboundChart.value || !monthDataChart.value) {
    console.log('图表容器未找到,延迟初始化');
    return;
  }
  outboundInstance.value = echarts.init(outboundChart.value);
  inboundInstance.value = echarts.init(inboundChart.value);
  monthDataInstance.value = echarts.init(monthDataChart.value);
  charts.value = [outboundInstance.value, inboundInstance.value, monthDataInstance.value];
  // å‡ºåº“量图表配置(柱状图)
  const outboundOption = {
    tooltip: {
      trigger: 'axis',
      axisPointer: {
        type: 'shadow' // é˜´å½±æŒ‡ç¤ºå™¨
      },
      formatter: function (params) {
        const data = params[0];
        return `
          <div style="font-weight: bold; margin-bottom: 5px;">${data.name}</div>
          <div style="display: flex; align-items: center;">
            <span style="display: inline-block; width: 10px; height: 10px; background: ${data.color}; border-radius: 50%; margin-right: 5px;"></span>
            <span>${data.seriesName}: </span>
            <span style="font-weight: bold; margin-left: 5px;">${data.value}</span>
          </div>
        `;
      },
      backgroundColor: 'rgba(255, 255, 255, 0.9)',
      borderColor: '#ddd',
      borderWidth: 1,
      textStyle: {
        color: '#333'
      }
    },
    grid: {
      left: '3%',
      right: '4%',
      bottom: '3%',
      containLabel: true
    },
    xAxis: {
      type: 'category',
      data: chartData.outbound.dates,
      axisLabel: {
        rotate: 45
      }
    },
    yAxis: {
      type: 'value',
      name: '数量'
    },
    series: [{
      name: '出库量',
      data: chartData.outbound.values,
      type: 'bar',
      itemStyle: {
        color: '#e06e6e'
      },
      barWidth: '60%',
      animation: true,
      label: {
        show: true,
        position: 'top',
        formatter: '{c}',
        color: '#e06e6e'
      }
    }]
  };
  // å…¥åº“量图表配置(折线图)
  const inboundOption = {
    tooltip: {
      trigger: 'axis',
      axisPointer: {
        type: 'line' // çº¿åž‹æŒ‡ç¤ºå™¨
      },
      formatter: function (params) {
        const data = params[0];
        return `
          <div style="font-weight: bold; margin-bottom: 5px;">${data.name}</div>
          <div style="display: flex; align-items: center;">
            <span style="display: inline-block; width: 10px; height: 10px; background: ${data.color}; border-radius: 50%; margin-right: 5px;"></span>
            <span>${data.seriesName}: </span>
            <span style="font-weight: bold; margin-left: 5px;">${data.value}</span>
          </div>
        `;
      },
      backgroundColor: 'rgba(255, 255, 255, 0.9)',
      borderColor: '#ddd',
      borderWidth: 1,
      textStyle: {
        color: '#333'
      }
    },
    grid: {
      left: '3%',
      right: '4%',
      bottom: '3%',
      containLabel: true
    },
    xAxis: {
      type: 'category',
      data: chartData.inbound.dates,
      axisLabel: {
        rotate: 45
      }
    },
    yAxis: {
      type: 'value',
      name: '数量'
    },
    series: [{
      name: '入库量',
      data: chartData.inbound.values,
      type: 'line',
      itemStyle: {
        color: '#4a7bff'
      },
      lineStyle: {
        width: 3
      },
      smooth: true,
      animation: true,
      label: {
        show: true,
        position: 'top',
        formatter: '{c}',
        color: '#4a7bff'
      }
    }]
  };
  // æœˆå‡ºå…¥åº“量图表配置(双折线图)
  const monthDataOption = {
    tooltip: {
      trigger: 'axis',
      axisPointer: {
        type: 'cross', // åå­—准星指示器
        crossStyle: {
          color: '#999'
        }
      },
      formatter: function (params) {
        let html = `<div style="font-weight: bold; margin-bottom: 5px;">${params[0].name}</div>`;
        params.forEach(param => {
          html += `
            <div style="display: flex; align-items: center; margin: 2px 0;">
              <span style="display: inline-block; width: 10px; height: 10px; background: ${param.color}; border-radius: 50%; margin-right: 5px;"></span>
              <span>${param.seriesName}: </span>
              <span style="font-weight: bold; margin-left: 5px;">${param.value}</span>
            </div>
          `;
        });
        return html;
      },
      backgroundColor: 'rgba(255, 255, 255, 0.9)',
      borderColor: '#ddd',
      borderWidth: 1,
      textStyle: {
        color: '#333'
      }
    },
    legend: {
      data: ['入库量', '出库量'],
      bottom: 0
    },
    grid: {
      left: '3%',
      right: '4%',
      bottom: '10%',
      containLabel: true
    },
    xAxis: {
      type: 'category',
      data: chartData.monthData.dates,
      axisLabel: {
        rotate: 45
      },
      axisPointer: {
        type: 'shadow'
      }
    },
    yAxis: {
      type: 'value',
      name: '数量'
    },
    series: [
      {
        name: '入库量',
        data: chartData.monthData.inValue,
        type: 'line',
        itemStyle: {
          color: '#4a7bff'
        },
        lineStyle: {
          width: 3
        },
        smooth: true,
        animation: true,
        label: {
          show: true,
          position: 'top',
          formatter: '{c}',
          color: '#4a7bff'
        }
      },
      {
        name: '出库量',
        data: chartData.monthData.outValue,
        type: 'line',
        itemStyle: {
          color: '#e06e6e'
        },
        lineStyle: {
          width: 3
        },
        smooth: true,
        animation: true,
        label: {
          show: true,
          position: 'top',
          formatter: '{c}',
          color: '#e06e6e'
        }
      }
    ]
  };
  outboundInstance.value.setOption(outboundOption);
  inboundInstance.value.setOption(inboundOption);
  monthDataInstance.value.setOption(monthDataOption);
};
// æ›´æ–°å›¾è¡¨æ•°æ®
const updateCharts = () => {
  nextTick(() => {
    if (outboundInstance.value && chartData.outbound.values.length > 0) {
      outboundInstance.value.setOption({
        xAxis: { data: chartData.outbound.dates },
        series: [{ data: chartData.outbound.values }]
      });
    }
    if (inboundInstance.value && chartData.inbound.values.length > 0) {
      inboundInstance.value.setOption({
        xAxis: { data: chartData.inbound.dates },
        series: [{ data: chartData.inbound.values }]
      });
    }
    if (monthDataInstance.value && chartData.monthData.dates.length > 0) {
      monthDataInstance.value.setOption({
        xAxis: { data: chartData.monthData.dates },
        series: [
          { data: chartData.monthData.inValue },
          { data: chartData.monthData.outValue }
        ]
      });
    }
  });
};
// å“åº”式窗口调整
const handleResize = () => {
  charts.value.forEach(chart => chart && chart.resize());
};
// æ•°æ®èŽ·å–
const fetchData = async () => {
  try {
    loading.value = true;
    error.value = false;
    const response = await http.post("api/StockInfo/GetStockData", {});
    console.log('API响应数据:', response.data);
    if (response.data && response.data.success !== false) {
      handleDataUpdate(response.data);
    } else {
      throw new Error('API返回数据格式错误');
    }
    loading.value = false;
  } catch (err) {
    console.error('API请求失败:', err);
    loading.value = false;
    error.value = true;
  }
};
// window.addEventListener("resize", function () {
//   $chart2.setOption(chart2);
// });
// æ•°æ®å¤„理
const handleDataUpdate = (data) => {
  console.log('处理数据:', data);
  // æ›´æ–°æ•°æ®æŒ‡æ ‡
  if (data.metrics && Array.isArray(data.metrics)) {
    dataMetrics.value = data.metrics.map(item => ({
      name: item.name || item.Name || '未知指标',
      value: item.value != null ? item.value : item.Value || 0
    }));
  }
  // æ›´æ–°å›¾è¡¨æ•°æ®
  if (data.outbound) {
    chartData.outbound.dates = data.outbound.dates || [];
    chartData.outbound.values = data.outbound.values || [];
  }
  if (data.inbound) {
    chartData.inbound.dates = data.inbound.dates || [];
    chartData.inbound.values = data.inbound.values || [];
  }
  if (data.monthData) {
    chartData.monthData.dates = data.monthData.dates || [];
    chartData.monthData.inValue = data.monthData.inValue || [];
    chartData.monthData.outValue = data.monthData.outValue || [];
  }
  console.log('更新后的数据指标:', dataMetrics.value);
  console.log('更新后的图表数据:', chartData);
  // å»¶è¿Ÿåˆå§‹åŒ–图表,确保数据已更新
  nextTick(() => {
    if (!outboundInstance.value || !inboundInstance.value || !monthDataInstance.value) {
      initCharts();
    } else {
      updateCharts();
    }
  });
};
// è½®è¯¢æŽ§åˆ¶
const intervalId = ref(null);
const startPolling = () => {
  fetchData();
  intervalId.value = setInterval(fetchData, 5 * 60 * 1000);
};
const stopPolling = () => {
  if (intervalId.value) {
    clearInterval(intervalId.value);
    intervalId.value = null;
  }
};
// ç”Ÿå‘½å‘¨æœŸ
onMounted(() => {
  startPolling();
  window.addEventListener('resize', handleResize);
});
onUnmounted(() => {
  stopPolling();
  charts.value.forEach(chart => chart && chart.dispose());
  window.removeEventListener('resize', handleResize);
});
</script>
<style lang="less" scoped>
.home-contianer {
  padding: 6px;
  background: #eee;
  width: 100%;
  height: 100%;
  // max-width: 800px;
  // position: absolute;
  top: 0;
  right: 0;
  left: 0;
  margin: 0 auto;
  .h-top {
    display: flex;
    .h-top-left {
      height: 100%;
      width: 300px;
      background: white;
    }
    height: 300px;
  }
  .h-top > div {
    border: 1px solid #e8e7e7;
    border-radius: 5px;
    // margin: 6px;
  }
  .h-top-center {
    height: 100%;
    background: white;
    margin: 0 6px;
    display: flex;
    flex-direction: column;
    flex: 1;
    .item1 .num {
      padding-top: 28px;
    }
    .item2 .num {
      padding-bottom: 20px;
    }
    .n-item {
      width: 100%;
      height: 100%;
      text-align: center;
      cursor: pointer;
      // display: flex;
      .item {
        border-right: 1px solid #e5e5e5;
        width: 33.3333333%;
        float: left;
        height: 50%;
        border-bottom: 1px solid #e5e5e5;
        padding: 47px 0;
        font-size: 13px;
      }
      .item:hover {
        background: #f9f9f9;
        cursor: pointer;
      }
      .item:last-child {
        border-right: 0;
      }
      .item3,
      .item6 {
        border-right: 0;
      }
      .num {
        word-break: break-all;
        color: #282727;
        font-size: 30px;
        transition: transform 0.8s;
      }
      .num:hover {
        color: #55ce80;
        transform: scale(1.2);
      }
      .text {
        font-size: 13px;
        color: #777;
      }
    }
  }
  .h-top-right {
    // flex: 1;
    width: 400px;
    height: 100%;
    background: white;
  }
  .h3 {
    padding: 7px 15px;
    font-weight: 500;
    background: #fff;
    border-bottom: 1px dotted #d4d4d4;
  }
<style scoped>
.dashboard-container {
  padding: 20px;
  background-color: #f5f6fa;
  min-height: 100vh;
}
.task-table {
  table {
    width: 100%;
    .thead {
      font-weight: bold;
    }
    tr {
      cursor: pointer;
      td {
        border-bottom: 1px solid #f3f3f3;
        padding: 9px 8px;
        font-size: 12px;
      }
    }
    tr:hover {
      background: #eee;
    }
  }
}
.h-chart {
  height: 340px;
  margin: 6px 0px;
.overview-section {
  display: flex;
  .h-left-grid {
    width: 300px;
    height: 100%;
    background: white;
    display: inline-block;
    .name {
      margin-left: 7px;
    }
    .item:hover {
      background: #f9f9f9;
      cursor: pointer;
    }
    .item {
      padding: 22px 14px;
      float: left;
      width: 50%;
      height: 33.33333%;
      border-bottom: 1px solid #eee;
      border-right: 1px solid #eee;
      i {
        font-size: 30px;
      }
      .desc {
        font-size: 12px;
        color: #c3c3c3;
        padding: 5px 0 0 4px;
        line-height: 1.5;
      }
    }
  }
  gap: 20px;
  margin-bottom: 20px;
}
#h-chart2 {
  border-radius: 3px;
  background: white;
  padding-top: 10px;
  height: 100%;
  width: 0;
  flex: 1;
  margin: 0 7px;
}
#h-chart3 {
  border-radius: 3px;
  padding: 10px 10px 0 10px;
  background: white;
  // padding-top: 10px;
  height: 100%;
  width: 400px;
.data-overview {
  flex: 1;
  background: white;
  padding: 16px;
  border-radius: 8px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
</style>
.metrics {
  display: flex;
  justify-content: space-between;
  margin-top: 16px;
}
.metric-item {
  text-align: center;
  flex: 1;
  padding: 10px;
}
.metric-name {
  font-size: 14px;
  color: #666;
  margin-bottom: 8px;
}
.metric-value {
  font-size: 20px;
  font-weight: bold;
  margin: 8px 0;
  color: #333;
}
.charts-section {
  gap: 20px;
}
.chart-container {
  flex: 1;
  background: white;
  padding: 16px;
  border-radius: 8px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.chart {
  height: 550px;
  width: 100%;
}
.chart1 {
  height: 550px;
  width: 100%;
}
.loading,
.error,
.no-data {
  display: flex;
  justify-content: center;
  align-items: center;
  height: 500px;
  font-size: 18px;
}
.loading {
  color: #999;
}
.error {
  color: #2d8cf0;
}
.no-data {
  color: #909399;
}
</style>