张诺
13 小时以前 3de8b71ee1c6be3e41b77d6633c3f1a1b66c40f2
src/views/reportAnalysis/dataDashboard/index.vue
@@ -1,5 +1,6 @@
<template>
    <div class="data-dashboard">
      <div class="scale-container">
         <div class="data-dashboard" :style="{ transform: `scale(${scaleRatio})` }">
      <!-- 全屏按钮 - 移动到左上角 -->
      <button class="fullscreen-btn" @click="toggleFullscreen" :title="isFullscreen ? '退出全屏' : '全屏显示'">
        <svg v-if="!isFullscreen" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
@@ -12,6 +13,7 @@
      <!-- 顶部标题栏 -->
      <div class="dashboard-header">
        <div class="factory-name">{{ userStore.currentFactoryName }}</div>
      </div>
      <!-- 主要内容区域 -->
@@ -53,7 +55,7 @@
        <!-- 质量统计 -->
            <div class="panel-header">
               <span class="panel-title">质量统计</span>
               <span class="panel-title">近4月质量统计</span>
            </div>
            <div class="main-panel">
               <div class="panel-item-customers">
@@ -173,13 +175,13 @@
        </div>
            
            <div class="financial-header">
               <span class="financial-title">财务分析</span>
               <span class="financial-title">各生产订单的完成进度统计</span>
            </div>
            <div class="main-panel">
               <div class="panel-item-customers">
                  <div class="event-header">
                     <img src="@/assets/BI/shijianmingxiicon@2x.png" alt="图标" class="event-icon" />
                     <span class="event-title">经营成果分析</span>
                     <span class="event-title">经营分析</span>
                  </div>
                  <Echarts ref="chart"
                               :chartStyle="chartStyle"
@@ -190,7 +192,7 @@
                               :xAxis="xAxis3"
                               :yAxis="yAxis3"
                               :options="{backgroundColor: 'transparent', textStyle: {color: '#B8C8E0'}}"
                               style="height: 300px"></Echarts>
                               style="height: 170px"></Echarts>
               </div>
            </div>
      </div>
@@ -204,11 +206,11 @@
            <div class="panel-item-customers">
               <div style="display: flex;justify-content: space-between;margin-bottom: 20px;">
                  <div class="section-title">应收应付统计</div>
                  <el-radio-group v-model="radio1" size="large" @change="statisticsReceivable" class="custom-radio-group">
                     <el-radio-button label="按周" :value="1" />
                     <el-radio-button label="按月" :value="2" />
                     <el-radio-button label="按季度" :value="3" />
                  </el-radio-group>
<!--                  <el-radio-group v-model="radio1" size="large" @change="statisticsReceivable" class="custom-radio-group">-->
<!--                     <el-radio-button label="按周" :value="1" />-->
<!--                     <el-radio-button label="按月" :value="2" />-->
<!--                     <el-radio-button label="按季度" :value="3" />-->
<!--                  </el-radio-group>-->
               </div>
               <Echarts ref="chart"
                            :color="barColors2"
@@ -225,7 +227,7 @@
        <!-- 回款与开票分析 -->
         <div class="panel-header">
               <span class="panel-title">回款与开票分析</span>
               <span class="panel-title">近一月回款与开票分析</span>
            </div>
        <div class="panel-item-customers" style="padding-top: 60px;">
               <Echarts ref="chart" :chartStyle="chartStyle" :grid="grid" :legend="lineLegend" :series="lineSeries"
@@ -233,19 +235,23 @@
            </div>
      </div>
      </div>
      </div>
    </div>
</template>
<script setup>
import * as echarts from 'echarts'
import { ref, onMounted, onBeforeUnmount, nextTick } from 'vue'
import { ref, reactive, onMounted, onBeforeUnmount, nextTick } from 'vue'
import autofit from 'autofit.js'
import Echarts from "@/components/Echarts/echarts.vue";
import useUserStore from '@/store/modules/user'
import {
   analysisCustomerContractAmounts, getAmountHalfYear,
   homeTodos,
   qualityStatistics,
   statisticsReceivablePayable
   statisticsReceivablePayable,
   getProgressStatistics,
     getWorkInProcessTurnover
} from "@/api/viewIndex.js";
import {staffOnJobListPage} from "@/api/personnelManagement/employeeRecord.js";
import {listCustomer} from "@/api/basicData/customerFile.js";
@@ -258,6 +264,15 @@
// 全屏相关状态
const isFullscreen = ref(false);
// 缩放比例
const scaleRatio = ref(1)
// 设计尺寸(基准尺寸)- 根据实际设计稿调整
const designWidth = 1920
const designHeight = 1080
// 用户store
const userStore = useUserStore()
// 响应式数据
const currentTime = ref('')
@@ -407,82 +422,59 @@
const barLegend = {
   show: true,
   textStyle: { color: '#B8C8E0' },
   data: ['原材料不合格数', '过程不合格数', '出厂不合格数']
   data: ['原材料合格数', '过程合格数', '出不合格数']
}
const barLegend1 = {
   show: true,
   show: false,
   textStyle: { color: '#B8C8E0' },
   data: ['总收入', '总支出', '净收入']
   data: []
}
const barSeries11 = ref([
   {
      name: '总收入',
      name: '生产订单统计',
      type: 'bar',
      barGap: 0,
      emphasis: {
         focus: 'series'
      },
      itemStyle: {
         color: {
            type: 'linear',
            x: 0,
            y: 0,
            x2: 0,
            y2: 1,
            colorStops: [
               { offset: 1, color: '#00A4ED' },
               { offset: 0, color: '#4EE4FF' }
         // 使用函数根据数据索引返回不同颜色
         color: function(params) {
            const colorStops = [
               [
                  { offset: 1, color: '#00A4ED' },
                  { offset: 0, color: '#4EE4FF' }
               ],
               [
                  { offset: 1, color: '#3378FF' },
                  { offset: 0, color: '#4E8AFF' }
               ],
               [
                  { offset: 1, color: '#FF6B6B' },
                  { offset: 0, color: '#FF8E8E' }
               ],
               [
                  { offset: 1, color: '#537EF5' },
                  { offset: 0, color: '#9061F8' }
               ]
            ]
            const stops = colorStops[params.dataIndex] || colorStops[0]
            return {
               type: 'linear',
               x: 0,
               y: 0,
               x2: 0,
               y2: 1,
               colorStops: stops
            }
         }
      },
      data: []
   },
   {
      name: '总支出',
      type: 'bar',
      emphasis: {
         focus: 'series'
      },
      itemStyle: {
         color: {
            type: 'linear',
            x: 0,
            y: 0,
            x2: 0,
            y2: 1,
            colorStops: [
               { offset: 1, color: '#3378FF' },
               { offset: 0, color: '#4E8AFF' }
            ]
         }
      },
      data: []
   },
   {
      name: '净收入',
      type: 'bar',
      emphasis: {
         focus: 'series'
      },
      itemStyle: {
         color: {
            type: 'linear',
            x: 0,
            y: 0,
            x2: 0,
            y2: 1,
            colorStops: [
               { offset: 1, color: '#537EF5' },
               { offset: 0, color: '#9061F8' }
            ]
         }
      },
      data: []
   },
   }
])
const barSeries1 = ref([
   {
      name: '原材料不合格数',
      name: '原材料合格数',
      type: 'bar',
      barGap: 0,
      emphasis: {
@@ -504,7 +496,7 @@
      data: []
   },
   {
      name: '过程不合格数',
      name: '过程合格数',
      type: 'bar',
      emphasis: {
         focus: 'series'
@@ -525,7 +517,7 @@
      data: []
   },
   {
      name: '出厂不合格数',
      name: '出厂合格数',
      type: 'bar',
      emphasis: {
         focus: 'series'
@@ -590,13 +582,35 @@
// 待办事项
const todoList = ref([])
// 窗口大小变化处理
const handleResize = () => {
// 计算缩放比例
const calculateScale = () => {
  const container = document.querySelector('.scale-container')
  if (!container) return
  // 获取容器的实际尺寸
   const rect = container.getBoundingClientRect?.()
   const containerWidth = container.clientWidth || rect?.width || window.innerWidth
   const containerHeight = container.clientHeight || rect?.height || window.innerHeight
  // 计算宽高缩放比例,取较小值以保证内容完整显示(等比缩放)
  const scaleX = containerWidth / designWidth
  const scaleY = containerHeight / designHeight
  scaleRatio.value = Math.min(scaleX, scaleY)
  // 触发图表resize
  charts.value.forEach(chart => {
    if (chart && chart.resize) {
      chart.resize()
    }
  })
}
// 窗口大小变化处理
const handleResize = () => {
  // 延迟执行,确保DOM更新完成
  setTimeout(() => {
    calculateScale()
  }, 100)
}
// 销毁图表实例
@@ -635,15 +649,45 @@
      qualityStatisticsObject.value.factoryNum = res.data.factoryNum
   })
}
// 财务统计
const accountStatisticsInfo = () => {
   listPageAnalysis().then((res) => {
      xAxis3.value[0].data = res.data.days
      barSeries11.value[0].data = res.data.totalIncome
      barSeries11.value[1].data = res.data.totalExpense
      barSeries11.value[2].data = res.data.netIncome
// 各生产订单的完成进度统计
const progressStatisticsInfo = () => {
   getProgressStatistics().then((res) => {
      console.log("生产订单完成进度统计数据:", res)
      if (!res || !res.data) {
         console.warn('生产订单完成进度统计数据为空')
         return
      }
      // 设置X轴数据 - 使用分类名称
      xAxis3.value[0].data = ['已完成进度数', '总订单数', '未完成订单数', '已完成订单数']
      // 设置单个系列的数据 - 每个X轴分类对应一个值
      if (barSeries11.value && barSeries11.value.length > 0) {
         barSeries11.value[0].data = [
            res.data.completedProgressCount || 0,
            res.data.totalOrderCount || 0,
            res.data.uncompletedOrderCount || 0,
            res.data.completedOrderCount || 0
         ]
      }
      console.log('图表数据设置完成:', {
         xAxis: xAxis3.value[0].data,
         series: barSeries11.value[0]?.data
      })
   }).catch((error) => {
      console.error('获取生产订单完成进度统计失败:', error)
   })
}
// 财务统计
// const accountStatisticsInfo = () => {
//    listPageAnalysis().then((res) => {
//       xAxis3.value[0].data = res.data.days
//       barSeries11.value[0].data = res.data.totalIncome
//    })
// }
const getNum = () => {
   const params = {
      pageNum: -1,
@@ -667,10 +711,10 @@
   getLedgerPage(params).then((res) => {
      equipmentNum.value = res.data.total
   });
   getRepairPage(params).then((res) => {
   getRepairPage({...params, status:0}).then((res) => {
      equipmentRepair.value = res.data.total
   });
   getUpkeepPage(params).then((res) => {
   getUpkeepPage({...params, status:0}).then((res) => {
      equipmentMaintain.value = res.data.total
   });
   measuringInstrumentListPage(params).then((res) => {
@@ -876,229 +920,29 @@
  updateTime()
  timer.value = setInterval(updateTime, 1000)
}
// 客户饼图
const initCustomerPieChart = () => {
  if (!customerPieChartRef.value) return
  const chart = echarts.init(customerPieChartRef.value)
  const option = {
    tooltip: {
      trigger: 'item',
      formatter: '{a} <br/>{b}: {c} ({d}%)'
    },
    series: [{
      name: '客户分布',
      type: 'pie',
      radius: ['40%', '70%'],
      center: ['50%', '50%'],
      data: [
        { value: 25, name: '潜在客户', itemStyle: { color: '#00d4ff' } },
        { value: 25, name: '意向客户', itemStyle: { color: '#0099ff' } },
        { value: 25, name: '签约客户', itemStyle: { color: '#6666ff' } },
        { value: 25, name: '流失客户', itemStyle: { color: '#ffcc00' } }
      ],
      label: {
        show: false
      }
    }]
  }
  chart.setOption(option)
  charts.value.push(chart)
}
// 销售柱状图
const initSalesBarChart = () => {
  if (!salesBarChartRef.value) return
  const chart = echarts.init(salesBarChartRef.value)
  const option = {
    tooltip: {
      trigger: 'axis'
    },
    xAxis: {
      type: 'category',
      data: ['6/9', '6/10', '6/11', '6/12', '6/13'],
      axisLine: { lineStyle: { color: '#333' } },
      axisLabel: { color: '#B8C8E0' },
    },
    yAxis: {
      type: 'value',
      axisLine: { show: false },
      axisTick: { show: false },
      axisLabel: { color: '#B8C8E0' },
      splitLine: { lineStyle: { color: '#333' } }
    },
    series: [{
      data: [150, 200, 180, 220, 190],
      type: 'bar',
      itemStyle: {
        color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
          { offset: 0, color: 'rgba(0,164,237,0)' },
          { offset: 1, color: '#4EE4FF' }
        ])
      }
    }]
  }
  chart.setOption(option)
  charts.value.push(chart)
}
// 数据统计横向柱状图
const initDataBarChart = () => {
  if (!dataBarChartRef.value) return
  const chart = echarts.init(dataBarChartRef.value)
  const option = {
    tooltip: {
      trigger: 'axis'
    },
    grid: {
      left: '10%',
      right: '10%',
      top: '10%',
      bottom: '10%'
    },
    xAxis: {
      type: 'value',
      axisLine: { show: false },
      axisTick: { show: false },
      axisLabel: { color: '#B8C8E0' },
      splitLine: { lineStyle: { color: '#333' } }
    },
    yAxis: {
      type: 'category',
      data: ['设计数据', '财务数据', '生产数据', '合同数据'],
      axisLine: { lineStyle: { color: '#333' } },
      axisLabel: { color: '#B8C8E0' }
    },
    series: [{
      data: [80, 100, 120, 90],
      type: 'bar',
      itemStyle: {
        color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [
          { offset: 0, color: 'rgba(0,164,237,0)' },
          { offset: 1, color: '#4EE4FF' }
        ])
      }
    }]
  }
  chart.setOption(option)
  charts.value.push(chart)
}
// 财务分析面积图
const initFinancialAreaChart = () => {
  if (!financialAreaChartRef.value) return
  const chart = echarts.init(financialAreaChartRef.value)
  const option = {
    tooltip: {
      trigger: 'axis'
    },
    grid: {
      left: '10%',
      right: '10%',
      top: '10%',
      bottom: '20%'
    },
    xAxis: {
      type: 'category',
      data: ['6/9', '6/10', '6/11', '6/12', '6/13'],
      axisLine: { lineStyle: { color: '#333' } },
      axisLabel: { color: '#B8C8E0' }
    },
    yAxis: {
      type: 'value',
      axisLine: { show: false },
      axisTick: { show: false },
      axisLabel: { color: '#B8C8E0' },
      splitLine: { lineStyle: { color: '#333' } }
    },
    series: [{
      data: [150, 180, 200, 170, 190],
      type: 'line',
      areaStyle: {
        color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
          { offset: 0, color: 'rgba(0, 212, 255, 0.3)' },
          { offset: 1, color: 'rgba(0, 212, 255, 0.1)' }
        ])
      },
      lineStyle: { color: '#00d4ff' },
      itemStyle: { color: '#00d4ff' }
    }]
  }
  chart.setOption(option)
  charts.value.push(chart)
}
// 实时数据折线图
const initRealtimeLineChart = () => {
  if (!realtimeLineChartRef.value) return
  const chart = echarts.init(realtimeLineChartRef.value)
  const option = {
    tooltip: {
      trigger: 'axis'
    },
    grid: {
      left: '10%',
      right: '10%',
      top: '10%',
      bottom: '20%'
    },
    xAxis: {
      type: 'category',
      data: ['6/9', '6/10', '6/11', '6/12', '6/13'],
      axisLine: { lineStyle: { color: '#333' } },
      axisLabel: { color: '#B8C8E0' }
    },
    yAxis: {
      type: 'value',
      axisLine: { show: false },
      axisTick: { show: false },
      axisLabel: { color: '#B8C8E0' },
      splitLine: { lineStyle: { color: '#333' } }
    },
    series: [
      {
        name: '数据1',
        data: [120, 140, 160, 130, 150],
        type: 'line',
        lineStyle: { color: '#00d4ff' },
        itemStyle: { color: '#00d4ff' }
      },
      {
        name: '数据2',
        data: [100, 120, 140, 110, 130],
        type: 'line',
        lineStyle: { color: '#0099ff' },
        itemStyle: { color: '#0099ff' }
      }
    ]
  }
  chart.setOption(option)
  charts.value.push(chart)
}
// 全屏功能实现 - 针对data-dashboard元素
// 全屏功能实现 - 针对scale-container元素
const toggleFullscreen = () => {
  const element = document.querySelector('.data-dashboard')
  if (!element) return
  if (!isFullscreen.value) {
    if (element.requestFullscreen) {
      element.requestFullscreen()
    } else if (element.webkitRequestFullscreen) {
      element.webkitRequestFullscreen()
    } else if (element.msRequestFullscreen) {
      element.msRequestFullscreen()
    }
  } else {
    if (document.exitFullscreen) {
      document.exitFullscreen()
    } else if (document.webkitExitFullscreen) {
      document.webkitExitFullscreen()
    } else if (document.msExitFullscreen) {
      document.msExitFullscreen()
    }
  }
   const element = document.querySelector('.scale-container')
   if (!element) return
   if (!isFullscreen.value) {
      if (element.requestFullscreen) {
         element.requestFullscreen()
      } else if (element.webkitRequestFullscreen) {
         element.webkitRequestFullscreen()
      } else if (element.msRequestFullscreen) {
         element.msRequestFullscreen()
      }
   } else {
      if (document.exitFullscreen) {
         document.exitFullscreen()
      } else if (document.webkitExitFullscreen) {
         document.webkitExitFullscreen()
      } else if (document.msExitFullscreen) {
         document.msExitFullscreen()
      }
   }
}
// 监听全屏变化事件
@@ -1106,7 +950,12 @@
  const fullscreenElement = document.fullscreenElement || 
                           document.webkitFullscreenElement || 
                           document.msFullscreenElement
  isFullscreen.value = fullscreenElement && fullscreenElement.classList.contains('data-dashboard')
  isFullscreen.value = fullscreenElement && fullscreenElement.classList.contains('scale-container')
  // 全屏状态变化时,延迟重新计算缩放比例(确保DOM更新完成)
  setTimeout(() => {
    calculateScale()
  }, 200)
}
// 生命周期钩子
@@ -1114,8 +963,11 @@
  initTime()
  // 使用nextTick确保DOM完全渲染后再初始化图表
  nextTick(() => {
    // 初始化autofit自适应
    autofit.init({ dh: 1440, dw: 2560, el: '.data-dashboard', resize: true }, false)
    // 计算初始缩放比例
    calculateScale()
    // 初始化autofit自适应(如果需要保留autofit,可以保留,但主要缩放由scale-container控制)
    // autofit.init({ dh: 800, dw: 1280, el: '.data-dashboard', resize: true }, false)
    
    // 添加自动滚动动画效果 - 客户信息列表
    const contractList = refContractList.value
@@ -1175,9 +1027,13 @@
  })
  
  window.addEventListener('resize', handleResize)
  window.addEventListener('fullscreenchange', handleFullscreenChange)
  window.addEventListener('webkitfullscreenchange', handleFullscreenChange)
  window.addEventListener('MSFullscreenChange', handleFullscreenChange)
  analysisCustomer()
  qualityStatisticsInfo()
   accountStatisticsInfo()
   // accountStatisticsInfo()
   progressStatisticsInfo()
  getNum()
  getLedgerNum()
  todoInfoS()
@@ -1240,67 +1096,80 @@
</script>
<style scoped>
/* 外部缩放容器 - 占据整个视口 */
.scale-container {
  position: relative;
   width: 100%;
   /* 页面在常规布局下(有顶栏)默认减去 84px,避免内容被裁切 */
   height: calc(100vh - 84px);
  display: flex;
  align-items: center;
  justify-content: center;
  background-color: #000;
   overflow: hidden;
}
/* 内部内容区域 - 固定设计尺寸 */
.data-dashboard {
  position: relative;
  width: 100vw;
  overflow: hidden;
  width: 1920px;
  height: 1080px;
   background-image: url("@/assets/BI/backImage@2x.png");
   background-size: cover;
   background-position: center;
   background-repeat: no-repeat;
   transform-origin: center center;
}
/* 全屏状态的样式 */
.data-dashboard:fullscreen {
  width: 100%;
  height: 100%;
/* 全屏状态的样式 - 作用于scale-container */
.scale-container:fullscreen {
   width: 100vw;
   height: 100vh;
  margin: 0;
  padding: 0;
  background-color: inherit;
  background-color: #000;
  z-index: 9999;
}
/* Webkit浏览器前缀 */
.data-dashboard:-webkit-full-screen {
  width: 100%;
  height: 100%;
.scale-container:-webkit-full-screen {
  width: 100vw;
  height: 100vh;
  margin: 0;
  padding: 0;
  background-color: inherit;
  background-color: #000;
  z-index: 9999;
}
/* MS浏览器前缀 */
.data-dashboard:-ms-fullscreen {
  width: 100%;
  height: 100%;
.scale-container:-ms-fullscreen {
  width: 100vw;
  height: 100vh;
  margin: 0;
  padding: 0;
  background-color: inherit;
  background-color: #000;
  z-index: 9999;
}
/* 全屏状态下的内容区域适配 */
.data-dashboard:fullscreen .dashboard-content {
  height: calc(100vh - 120px);
}
.data-dashboard:-webkit-full-screen .dashboard-content {
  height: calc(100vh - 120px);
}
.data-dashboard:-ms-fullscreen .dashboard-content {
  height: calc(100vh - 120px);
}
.dashboard-header {
  position: relative;
  z-index: 1;
  height: 170px;
   height: 86px;
   background-image: url("@/assets/BI/biaoti.png");
   background-size: cover;
   background-position: center;
   background-repeat: no-repeat;
  display: flex;
  align-items: center;
  justify-content: center;
}
.factory-name {
  font-weight: 600;
font-size: 52px;
color: #FFFFFF;
top: 16px;
position: absolute;
}
.fullscreen-btn {
@@ -1332,7 +1201,7 @@
  display: flex;
  gap: 30px;
  padding: 0 30px;
  height: calc(100vh - 120px);
   height: calc(100% - 86px);
  overflow: hidden;
}
@@ -1413,21 +1282,12 @@
   background-position: center;
   background-repeat: no-repeat;
}
.panel-item {
  background: rgba(0, 20, 60, 0.8);
  border: 1px solid rgba(0, 212, 255, 0.3);
  border-radius: 12px;
  padding: 30px;
  backdrop-filter: blur(10px);
  min-height: 200px;
}
.panel-header {
   background-image: url("@/assets/BI/kehuhetongback@2x.png");
   background-size: 100% 100%;
   background-position: center;
   background-repeat: no-repeat;
   height: 36px;
}
.panel-title {
@@ -1667,9 +1527,6 @@
   font-size: 20px;
   color: #FFFFFE;
}
.data-statistics {
  flex: 1;
}
.financial-header {
   background-image: url("@/assets/BI/caiwufenxiback@2x.png");
   background-size: 100% 100%;
@@ -1683,66 +1540,6 @@
   color: #D9ECFF;
   padding-left: 46px;
   line-height: 36px;
}
.data-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 20px;
  margin-bottom: 20px;
}
.legend-item {
  display: flex;
  align-items: center;
  gap: 12px;
}
.legend-color {
  width: 12px;
  height: 12px;
  border-radius: 4px;
}
.legend-text {
  font-size: 12px;
  color: #999;
}
.horizontal-bar-chart {
  height: 150px;
}
.financial-analysis,
.realtime-analysis {
  flex: 1;
}
.financial-tabs,
.realtime-tabs {
  display: flex;
  gap: 16px;
  margin-bottom: 20px;
}
.tab {
  padding: 12px 24px;
  background: rgba(0, 0, 0, 0.3);
  border: 1px solid rgba(0, 212, 255, 0.3);
  border-radius: 6px;
  color: #999;
  cursor: pointer;
  transition: all 0.3s;
}
.tab.active {
  background: rgba(0, 212, 255, 0.2);
  color: #00d4ff;
  border-color: #00d4ff;
}
.area-chart,
.line-chart {
  height: 150px;
}
/* 自定义单选按钮组样式 */