gaoluyang
2 天以前 cb78cb10939dc95b55e26b68cd805f2717ea48f0
1.首页联调
已修改4个文件
1535 ■■■■■ 文件已修改
src/api/viewIndex.js 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/Echarts/echarts.vue 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/store/modules/user.js 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/index.vue 1436 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/viewIndex.js
@@ -1,48 +1,43 @@
// 首页接口
import request from '@/utils/request'
// 本月合同金额
export const getContractAmount = () => {
// 销售-采购-库存数据
export const getBusiness = () => {
    return request({
        url: '/sales/ledger/getContractAmount',
        url: '/home/business',
        method: 'get'
    })
}
// 本月开票金额
export const getInvoiceAmount = () => {
// 客户合同金额分析
export const analysisCustomerContractAmounts = () => {
    return request({
        url: '/invoiceLedger/getInvoiceAmount',
        url: '/home/analysisCustomerContractAmounts',
        method: 'get'
    })
}
// 本月回款金额
export const getReceiptAmount = () => {
// 质检分析
export const qualityStatistics = () => {
    return request({
        url: '/receiptPayment/getReceiptAmount',
        url: '/home/qualityStatistics',
        method: 'get'
    })
}
// 客户合同金额TOP5统计
export const getTopFiveList = () => {
// 应收应付统计
export const statisticsReceivablePayable = (query) => {
    return request({
        url: '/sales/ledger/getTopFiveList',
        url: '/home/statisticsReceivablePayable',
        method: 'get',
        params: query
    })
}
// 待办事项
export const homeTodos = () => {
    return request({
        url: '/home/todos',
        method: 'get'
    })
}
// 回款饼状图
export const getAmountMouth = () => {
    return request({
        url: '/receiptPayment/getAmountMouth',
        method: 'get'
    })
}
// 付款饼状图
export const paymentMonthList = () => {
    return request({
        url: '/purchase/paymentRegistration/paymentMonthList',
        method: 'get'
    })
}
// 线形图
export const getAmountHalfYear = () => {
    return request({
src/components/Echarts/echarts.vue
@@ -70,7 +70,15 @@
      maskColor: 'rgba(255, 255, 255, 0.8)',
      zlevel: 0
    })
  }
  },
  color: {
    type: Array,
    default: () => []
  },
    option: {
        type: Object,
        default: () => ({})
    },
})
import { watch } from 'vue'
@@ -83,28 +91,26 @@
function generateChart(option) {
  const copiedOption = JSON.parse(JSON.stringify(option)) // ✅ 深拷贝
  
  // if (copiedOption.series && copiedOption.series.length > 0) {
  //   copiedOption.series.forEach((s, index) => {
  //     if (s.type === 'line') {
  //       s.itemStyle = {
  //         color: props.lineColors[index] || props.lineColors[0]
  //       }
  //       s.lineStyle = {
  //         color: props.lineColors[index] || props.lineColors[0]
  //       }
  //     } else if (s.type === 'bar') {
  //       s.itemStyle = {
  //         color: props.barColors[index] || props.barColors[0]
  //       }
  //     }
  //   })
  // }
  if (copiedOption.series && copiedOption.series.length > 0) {
    copiedOption.series.forEach((s, index) => {
      if (s.type === 'line' && props.lineColors.length) {
        s.itemStyle = s.itemStyle || {}
        s.lineStyle = s.lineStyle || {}
        s.itemStyle.color = props.lineColors[index] || props.lineColors[0]
        s.lineStyle.color = props.lineColors[index] || props.lineColors[0]
      } else if (s.type === 'bar' && props.barColors.length) {
        s.itemStyle = s.itemStyle || {}
        s.itemStyle.color = props.barColors[index] || props.barColors[0]
      }
    })
  }
  
  chartInstance.setOption(copiedOption)
}
function renderChart() {
  const option = {
    color: props.color.length ? props.color : undefined,
    backgroundColor: props.options.backgroundColor || '#fff',
    xAxis: props.xAxis,
    yAxis: props.yAxis,
src/store/modules/user.js
@@ -31,6 +31,16 @@
          })
        })
      },
      getCurrentTime() {
        const now = new Date();
        const year = now.getFullYear();       // 获取年份
        const month = String(now.getMonth() + 1).padStart(2, '0');  // 月份从0开始,要+1,并补零
        const day = String(now.getDate()).padStart(2, '0');         // 日期补零
        const hours = String(now.getHours()).padStart(2, '0');      // 小时补零
        const minutes = String(now.getMinutes()).padStart(2, '0');  // 分钟补零
        const seconds = String(now.getSeconds()).padStart(2, '0');  // 秒数补零
        return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
      },
      // 获取用户信息
      getInfo() {
        return new Promise((resolve, reject) => {
@@ -49,7 +59,9 @@
            this.avatar = avatar
            this.currentFactoryName = user.currentFactoryName
            this.nickName = user.nickName
            this.roleName = user.roles[0].roleName
            this.currentDeptId = user.tenantId
            this.currentLoginTime = this.getCurrentTime()
            resolve(res)
          }).catch(error => {
            reject(error)
src/views/index.vue
@@ -1,624 +1,812 @@
<template>
  <div class="app-container home">
    <div style="display: flex;">
      <div>
        <div class="card-top-left">
          <div class="title">
            <span style="font-weight: bold">本月销售、采购情况计划</span>
          </div>
          <div class="card-group">
            <div class="info-card">
              <div class="info-message">
                <div class="info-number">{{ contractAmount }}</div>
                <div class="info-title">合同金额(元)</div>
              </div>
              <img src="@/assets/images/icon1.png" alt="" style="width: 63px;height: 63px">
            </div>
            <div class="info-card1">
              <div class="info-message">
                <div class="info-number">{{ invoiceAmount }}</div>
                <div class="info-title">开票金额(元)</div>
              </div>
              <img src="@/assets/images/icon2.png" alt="" style="width: 63px;height: 63px">
            </div>
            <div class="info-card2">
              <div class="info-message">
                <div class="info-number">{{ receiptAmount }}</div>
                <div class="info-title">回款金额(元)</div>
              </div>
              <img src="@/assets/images/icon%203.png" alt="" style="width: 63px;height: 63px">
            </div>
          </div>
        </div>
        <div class="card-top-left">
          <div class="title">
            <span style="font-weight: bold">本月应收、应付情况计划</span>
          </div>
          <div class="pie">
            <div class="card-group">
              <div class="pie-group">
                <div style="margin-right: 80px">
                  <Echarts ref="chart" :legend="pieLegend" :chartStyle="chartStyle" :series="materialPieSeries"
                    :tooltip="pieTooltip"></Echarts>
                </div>
                <div class="info-message2">
                  <div class="info-message1">
                    <div class="pie-title">本月回款金额</div>
                    <div class="pie-info"><span class="pie-number">{{ receiveAmount }}</span>元 <span
                        class="pie-number">{{ receiveAmountPercentage }}</span>%</div>
                  </div>
                  <div class="info-message1">
                    <div class="pie-title">应收款金额</div>
                    <div class="pie-info"><span class="pie-number">{{ contractAmountMonth }}</span>元</div>
                  </div>
                </div>
              </div>
            </div>
            <div class="card-group">
              <div class="pie-group">
                <div style="margin-right: 80px">
                  <Echarts ref="chart" :options="options" :legend="pieLegend" :chartStyle="chartStyle"
                    :series="materialPieSeries1" :tooltip="pieTooltip1"></Echarts>
                </div>
                <div class="info-message2">
                  <div class="info-message1">
                    <div class="pie-title1">本月付款金额</div>
                    <div class="pie-info"><span class="pie-number1">{{ paymentAmount }}</span>元 <span
                        class="pie-number1">{{ payableAmountPercentage }}</span>%</div>
                  </div>
                  <div class="info-message1">
                    <div class="pie-title1">应付款金额</div>
                    <div class="pie-info"><span class="pie-number1">{{ payableAmount }}</span>元</div>
                  </div>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
      <div>
        <div class="card-top-right">
          <div class="title">
            <span style="font-weight: bold">客户合同金额TOP5统计</span>
          </div>
          <div>
            <Echarts ref="chart" :chartStyle="chartStyle1" :grid="grid" :legend="barLegend" :series="barSeries"
              :tooltip="tooltip" :xAxis="xAxis1" :yAxis="yAxis1" style="height: 42vh;"></Echarts>
          </div>
        </div>
      </div>
    </div>
    <div>
      <div>
        <div class="card-bottom">
          <div class="title">
            <span style="font-weight: bold">回款、开票近半年走势图</span>
          </div>
          <div>
            <Echarts ref="chart" :chartStyle="chartStyle1" :grid="grid" :legend="barLegend" :series="lineSeries"
              :tooltip="tooltipLine" :xAxis="xAxis2" :yAxis="yAxis2" style="height: 27vh;"></Echarts>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>
<script setup name="Index">
const { proxy } = getCurrentInstance()
import * as echarts from 'echarts';
import Echarts from "@/components/Echarts/echarts.vue";
import {
  getAmountHalfYear,
  getAmountMouth,
  getContractAmount,
  getInvoiceAmount,
  getReceiptAmount,
  getTopFiveList, paymentMonthList
} from "@/api/viewIndex.js";
const pieLegend = reactive({
  show: false,
})
const contractAmount = ref(0)
const invoiceAmount = ref(0)
const receiptAmount = ref(0)
const receiveAmount = ref(0)
const contractAmountMonth = ref(0)
const receiveAmountPercentage = ref(0)
const paymentAmount = ref(0)
const payableAmount = ref(0)
const payableAmountPercentage = ref(0)
const options = reactive({
  graphic: {
    type: 'circle',
    left: 'center',
    top: 'middle',
    shape: {
      r: '70%' // 圆形半径与饼图外圈相同
    },
  }
})
const pieTooltip = reactive({
  trigger: 'item',
  formatter: function (params) {
    // 动态生成提示信息,基于数据项的 name 属性
    const description = params.name === '本月回款金额' ? '本月回款金额' : '应收款金额';
    return `${description} ${formatNumber(params.value)}元 ${params.percent}%`;
  },
  position: 'right'
})
const pieTooltip1 = reactive({
  trigger: 'item',
  formatter: function (params) {
    // 动态生成提示信息,基于数据项的 name 属性
    const description = params.name === '本月付款金额' ? '本月付款金额' : '应付款金额';
    return `${description} ${formatNumber(params.value)}元 ${params.percent}%`;
  },
  position: 'right'
})
// 数字格式化函数,添加逗号作为千位分隔符
function formatNumber(num) {
  return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
}
const materialPieSeries = ref([
  {
    type: 'pie',
    radius: '92%',
    avoidLabelOverlap: false,
    itemStyle: {
      borderColor: '#fff',
      borderWidth: 2
    },
    label: {
      show: false
    },
    data: [
      { value: 0, name: '本月回款金额', itemStyle: { color: '#2D99FF' } },
      { value: 0, name: '应收款金额', itemStyle: { color: '#D4DDFF' } },
    ]
  }
])
const materialPieSeries1 = ref([
  {
    type: 'pie',
    radius: '92%',
    avoidLabelOverlap: false,
    itemStyle: {
      borderColor: '#fff',
      borderWidth: 2
    },
    label: {
      show: false
    },
    data: [
      { value: 0, name: '本月付款金额', itemStyle: { color: '#1EBFAC' } },
      { value: 0, name: '应付款金额', itemStyle: { color: '#D0EFE1' } },
    ]
  }
])
const chartStyle = {
  width: '150%',
  height: '120%' // 设置图表容器的高度
}
const chartStyle1 = {
  width: '100%',
  height: '100%' // 设置图表容器的高度
}
const grid = {
  left: '2%', // 增大左侧留白
  right: '10%',
  bottom: '15%',
  top: '10%',
  containLabel: true // 确保包含标签
}
const tooltip = {
  trigger: 'axis',
  axisPointer: {
    type: 'shadow'
  }
}
const tooltipLine = {
  trigger: 'axis',
}
const yAxis1 = ref([
  {
    type: 'value',
  }
])
const xAxis1 = ref([
  {
    type: 'category',
    data: []
  }
])
const yAxis2 = ref([
  {
    type: 'value',
  }
])
const xAxis2 = ref([
  {
    type: 'category',
    data: []
  }
])
const barLegend = reactive({})
const barSeries = ref([
  {
    type: 'bar',
    data: [],
    label: {
      show: true
    },
  },
])
const lineSeries = ref([
  {
    type: 'line',
    data: [],
    label: {
      show: true
    },
  },
])
// 合同金额
const getContractAmountNum = () => {
  getContractAmount().then((res) => {
    contractAmount.value = res.data
  })
}
// 开票金额
const getInvoiceAmountNum = () => {
  getInvoiceAmount().then((res) => {
    invoiceAmount.value = res.data
  })
}
// 回款金额
const getReceiptAmountNum = () => {
  getReceiptAmount().then((res) => {
    receiptAmount.value = res.data
  })
}
// 月回款金额饼图
const getAmountMouthNum = () => {
  getAmountMouth().then((res) => {
    receiveAmount.value = res.data.receiveAmount
    contractAmountMonth.value = res.data.contractAmount
    const percentage = (receiveAmount.value / contractAmountMonth.value) * 100;
    receiveAmountPercentage.value = percentage.toFixed(2)
    materialPieSeries.value[0].data[0].value = receiveAmount.value
    materialPieSeries.value[0].data[1].value = contractAmountMonth.value
  })
}
// 月付款金额饼图
const paymentMonthListNum = () => {
  paymentMonthList().then((res) => {
    paymentAmount.value = res.data.paymentAmount
    payableAmount.value = res.data.payableAmount
    const percentage = (paymentAmount.value / payableAmount.value) * 100;
    payableAmountPercentage.value = percentage.toFixed(2)
    materialPieSeries1.value[0].data[0].value = paymentAmount.value
    materialPieSeries1.value[0].data[1].value = payableAmount.value
  })
}
// 客户top5
const getTopFiveListNum = async () => {
  const res = await getTopFiveList()
  const customerName = []
  const totalAmount = []
  res.data.forEach(item => {
    customerName.push(item.customerName)
    totalAmount.push(item.totalAmount)
  })
  // 正确响应式赋值:创建新的 xAxis 和 series 对象
  xAxis1.value = [
    {
      type: 'category',
      data: customerName
    }
  ]
  barSeries.value = [
    {
      type: 'bar',
      data: totalAmount,
      itemStyle: {
        color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
          { offset: 0, color: '#F7D2FF' },
          { offset: 1, color: '#826AF9' }
        ])
      },
      emphasis: {
        itemStyle: {
          color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
            { offset: 1, color: '#826AF9' }
          ])
        }
      },
    }
  ]
}
// 线形图
const getAmountHalfYearNum = async () => {
  const res = await getAmountHalfYear()
  console.log(res)
  const monthName = []
  const receiptAmount = []
  const invoiceAmount = []
  res.data.forEach(item => {
    monthName.push(item.month)
    receiptAmount.push(item.receiptAmount)
    invoiceAmount.push(item.invoiceAmount)
  })
  // 正确响应式赋值:创建新的 xAxis 和 series 对象
  xAxis2.value = [
    {
      type: 'category',
      data: monthName
    }
  ]
  lineSeries.value = [
    {
      name: '开票',
      type: 'line',
      data: receiptAmount,
      smooth: true,
      stack: 'Total',
      areaStyle: {
        color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
          {
            offset: 0,
            color: 'rgba(131, 207, 255, 1)'
          },
          {
            offset: 1,
            color: 'rgba(186, 228, 255, 1)'
          }
        ])
      },
      // 设置小圆点的颜色
      itemStyle: {
        color: '#2D99FF', // 小圆点颜色设置为#2D99FF
        borderColor: '#2D99FF' // 如果需要的话,可以设置边框颜色
      },
      emphasis: {
        focus: 'series'
      },
      lineStyle: {
        width: 0
      },
      showSymbol: false,
    },
    {
      name: '回款',
      type: 'line',
      data: invoiceAmount,
      smooth: true,
      stack: 'Total',
      lineStyle: {
        width: 0
      },
      // 设置小圆点的颜色
      itemStyle: {
        color: '#83CFFF', // 小圆点颜色设置为#83CFFF
        borderColor: '#83CFFF' // 如果需要的话,可以设置边框颜色
      },
      showSymbol: false,
      areaStyle: {
        color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
          {
            offset: 0,
            color: 'rgba(54, 153, 255, 1)'
          },
          {
            offset: 1,
            color: 'rgba(89, 169, 254, 1)'
          }
        ])
      },
      emphasis: {
        focus: 'series'
      },
    }
  ]
}
getContractAmountNum()
getInvoiceAmountNum()
getReceiptAmountNum()
getTopFiveListNum()
getAmountMouthNum()
paymentMonthListNum()
getAmountHalfYearNum()
</script>
<style scoped>
.card-top-left {
  padding: 16px;
  background: #fff;
  height: 24vh;
  width: 56vw;
  margin-bottom: 20px;
}
.card-top-right {
  padding: 16px;
  background: #fff;
  height: 50.6vh;
  width: 28vw;
  margin-bottom: 20px;
  margin-left: 20px;
}
.card-bottom {
  padding: 16px;
  background: #fff;
  height: 34vh;
  width: 85.2vw;
  margin-bottom: 20px;
}
.title {
  position: relative;
  font-size: 18px;
  color: #333;
  font-weight: 400;
  padding-left: 10px;
  margin-bottom: 26px;
}
.title::before {
  position: absolute;
  left: 0;
  top: 4px;
  content: '';
  width: 4px;
  height: 18px;
  background-color: #002FA7;
  border-radius: 2px;
}
.card-group {
  display: flex;
}
.info-card {
  width: 300px;
  height: 126px;
  background-image: url("../assets/images/Rectangle 76@2x.png");
  background-size: 100% 100%;
  display: flex;
  justify-content: space-around;
  align-items: center;
}
.info-card1 {
  width: 300px;
  height: 126px;
  background-image: url("../assets/images/Rectangle 77@2x.png");
  background-size: 100% 100%;
  margin: 0 40px;
  display: flex;
  justify-content: space-around;
  align-items: center;
}
.info-card2 {
  width: 300px;
  height: 126px;
  background-image: url("../assets/images/Rectangle 77@2x(1).png");
  background-size: 100% 100%;
  display: flex;
  justify-content: space-around;
  align-items: center;
}
.info-message {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
}
.info-message1 {
  font-weight: bold;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: flex-start;
}
.info-message2 {
  font-weight: bold;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: space-around;
}
.info-number {
  font-weight: bold;
  font-size: 32px;
  color: #FFFFFF;
  margin-bottom: 10px;
}
.info-title {
  font-weight: bold;
  font-size: 18px;
  color: #FFFFFF;
}
.pie {
  display: flex;
  flex-direction: row;
  justify-content: space-around;
}
.pie-group {
  display: flex;
}
.pie-title {
  font-size: 14px;
  line-height: 24px;
  color: #2853FD;
  padding-left: 16px;
  position: relative;
}
.pie-title::before {
  content: '';
  width: 6px;
  /* 蓝点的宽度 */
  height: 6px;
  /* 蓝点的高度 */
  background-color: #2853FD;
  /* 蓝点的颜色 */
  border-radius: 50%;
  /* 将正方形变为圆形 */
  position: absolute;
  left: 0;
  /* 定位到左边 */
  top: 9px;
  /* 垂直居中对齐,根据行高调整 */
}
.pie-title1 {
  font-size: 14px;
  line-height: 24px;
  color: #1EBFAC;
  padding-left: 16px;
  position: relative;
}
.pie-title1::before {
  content: '';
  width: 6px;
  /* 蓝点的宽度 */
  height: 6px;
  /* 蓝点的高度 */
  background-color: #1EBFAC;
  /* 蓝点的颜色 */
  border-radius: 50%;
  /* 将正方形变为圆形 */
  position: absolute;
  left: 0;
  /* 定位到左边 */
  top: 9px;
  /* 垂直居中对齐,根据行高调整 */
}
.pie-info {
  padding-left: 16px;
  font-size: 14px;
  line-height: 24px;
}
.pie-number {
  color: #2853FD;
}
.pie-number1 {
  color: #1EBFAC;
}
</style>
<template>
    <div class="dashboard">
        <!-- 顶部横向两栏 -->
        <div class="dashboard-top">
            <!-- 左:企业信息+三大数据卡片(上下排列) -->
            <div class="top-left">
                <div class="company-info">
                    <div class="section-title">登陆信息</div>
                    <div style="display: flex;align-items: center;gap: 20px">
                        <img :src="userStore.avatar" class="avatar" alt=""/>
                        <div class="company-card">
                            <div class="company-name">{{userStore.name}}</div>
                            <div class="company-meta">{{userStore.roleName}}</div>
                        </div>
                        <div style="display: flex;align-items: center;gap: 8px">
                            <el-icon color="#5053B5" size="22"><Clock /></el-icon>
                            <span>登陆日期:{{userStore.currentLoginTime}}</span>
                        </div>
                    </div>
                </div>
                <div class="data-cards">
                    <div class="data-card sales">
                        <div class="data-title">销售数据</div>
                        <div class="data-num">
                            <div>
                                <div class="data-desc">本月销售额/元</div>
                                <div class="data-value">{{businessInfo.monthSaleMoney}}</div>
                            </div>
                            <div>
                                <div class="data-desc">未开票金额/元</div>
                                <div class="data-value">{{businessInfo.monthSaleHaveMoney}}</div>
                            </div>
                        </div>
                    </div>
                    <div class="data-card purchase">
                        <div class="data-title">采购数据</div>
                        <div class="data-num">
                            <div>
                                <div class="data-desc">本月采购额/元</div>
                                <div class="data-value">{{businessInfo.monthPurchaseMoney}}</div>
                            </div>
                            <div>
                                <div class="data-desc">待付款金额/元</div>
                                <div class="data-value">{{businessInfo.monthPurchaseHaveMoney}}</div>
                            </div>
                        </div>
                    </div>
                    <div class="data-card inventory">
                        <div class="data-title">库存数据</div>
                        <div class="data-num">
                            <div>
                                <div class="data-desc">当前库存总量/件</div>
                                <div class="data-value">{{businessInfo.inventoryNum}}</div>
                            </div>
                            <div>
                                <div class="data-desc">今日入库/件</div>
                                <div class="data-value">{{businessInfo.todayInventoryNum}}</div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
            <!-- 右:待办事项 -->
            <div class="todo-panel">
                <div class="section-title">待办事项</div>
                <ul class="todo-list" v-if="todoList.length > 0">
                    <li v-for="item in todoList" :key="item.id">
                        <div style="display: flex;flex-direction: column;justify-content: space-between;width: 100%;gap: 20px">
                            <div style="display: flex;justify-content: space-between;align-items: center;">
                                <div class="todo-title">流程编号:{{item.approveId}}</div>
                                <div class="todo-division">申请部门:{{item.approveDeptName}}</div>
                                <div class="todo-time">{{item.approveTime}}</div>
                            </div>
                            <div class="todo-division">审批事由:{{item.approveReason}}</div>
                        </div>
                    </li>
                </ul>
                <div v-else style="text-align: center">
                    暂无数据
                </div>
            </div>
        </div>
        <!-- 中部横向两栏 -->
        <div class="dashboard-row">
            <div class="main-panel">
                <div class="section-title">客户合同金额分析</div>
                <div class="contract-summary">
                    <div class="contract-info">
                        <img src="../assets/images/khtitle.png" alt="" style="width: 42px"/>
                        <div class="contract-card">
                            <div class="contract-name">总合同金额(元)</div>
                            <div class="contract-meta">
                                <div class="main-amount">{{sum}}</div>
                                <div>周同比: <span class="up">{{yny}}% </span> 日环比: <span class="up">{{chain}}% </span></div>
                            </div>
                        </div>
                    </div>
                </div>
                <div style="display: flex;align-items: center;gap: 20px;justify-content: space-evenly;height: 180px;margin-top: 20px">
                    <div>
                        <Echarts ref="chart" :legend="pieLegend" :chartStyle="chartStylePie"
                                         :series="materialPieSeries"
                                         :tooltip="pieTooltip"></Echarts>
                    </div>
                    <ul class="contract-list">
                        <li v-for="item in materialPieSeries[0].data" :key="item.name">
                            <div style="display: flex;align-items: center;justify-content: space-between;width: 100%">
                                <div class="line" :style="{color: item.itemStyle.color}">●{{item.name}}</div>
                                <div style="width: 70px">{{item.rate}}%</div>
                                <div>¥{{item.value}}</div>
                            </div>
                        </li>
                    </ul>
                </div>
            </div>
            <div class="main-panel">
                <div style="display: flex;justify-content: space-between;">
                    <div class="section-title">应收应付统计</div>
                    <el-radio-group v-model="radio1" size="large" @change="statisticsReceivable">
                        <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"
                                 :chartStyle="chartStyle"
                                 :grid="grid"
                                 :series="barSeries"
                                 :tooltip="tooltip"
                                 :xAxis="xAxis"
                                 :yAxis="yAxis"
                                 style="height: 260px"></Echarts>
            </div>
        </div>
        <!-- 底部横向两栏 -->
        <div class="dashboard-row">
            <div class="main-panel">
                <div class="section-title">质量统计</div>
                <div class="quality-cards">
                    <div class="quality-card one">原材料已检测数 <span>{{qualityStatisticsObject.supplierNum}}件</span></div>
                    <div class="quality-card two">过程检验数量 <span>{{qualityStatisticsObject.processNum}}件</span></div>
                    <div class="quality-card three">出厂已检数量 <span>{{qualityStatisticsObject.factoryNum}}件</span></div>
                </div>
                <Echarts ref="chart"
                                 :chartStyle="chartStyle"
                                 :grid="grid"
                                 :legend="barLegend"
                                 :series="barSeries1"
                                 :tooltip="tooltip"
                                 :xAxis="xAxis1"
                                 :yAxis="yAxis1"
                                 style="height: 260px"></Echarts>
            </div>
            <div class="main-panel">
                <div class="section-title">回款与开票分析</div>
                <Echarts ref="chart" :chartStyle="chartStyle" :grid="grid" :legend="lineLegend" :series="lineSeries"
                                 :tooltip="tooltipLine" :xAxis="xAxis2" :yAxis="yAxis2" style="height: 270px;"></Echarts>
            </div>
        </div>
    </div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import Echarts from "@/components/Echarts/echarts.vue";
import * as echarts from 'echarts';
import useUserStore from "@/store/modules/user.js";
import {
    analysisCustomerContractAmounts, getAmountHalfYear,
    getBusiness,
    homeTodos,
    qualityStatistics,
    statisticsReceivablePayable
} from "@/api/viewIndex.js";
const userStore = useUserStore()
const businessInfo = ref({
    inventoryNum: 0,
    monthPurchaseHaveMoney: 0,
    monthPurchaseMoney: 0,
    monthSaleHaveMoney: 0,
    monthSaleMoney: 0,
    todayInventoryNum: 0,
})
const qualityStatisticsObject = ref({
    supplierNum: 0,
    processNum: 0,
    factoryNum: 0,
})
const sum = ref(0)
const yny = ref(0)
const chain = ref(0)
const pieLegend = reactive({
    show: false,
})
const barSeries = ref([
    {
        type: 'bar',
        data: [],
        label: {
            show: true,
        }
    },
])
const barSeries1 = ref([
    {
        name: '原材料不合格数',
        type: 'bar',
        barGap: 0,
        emphasis: {
            focus: 'series'
        },
        data: []
    },
    {
        name: '过程不合格数',
        type: 'bar',
        emphasis: {
            focus: 'series'
        },
        data: []
    },
    {
        name: '出厂不合格数',
        type: 'bar',
        emphasis: {
            focus: 'series'
        },
        data: []
    },
])
const chartStyle = {
    width: '100%',
    height: '100%' // 设置图表容器的高度
}
const chartStylePie = {
    width: '140%',
    height: '140%' // 设置图表容器的高度
}
const grid = {
    left: '3%',
        right: '4%',
        bottom: '3%',
        containLabel: true
}
const barLegend = {
    show: true,
    data: ['原材料不合格数', '过程不合格数', '出厂不合格数']
}
const barLegend1 = {
    show: true,
    data: ['预付账款', '应付账款', '预收账款', '应收账款']
}
const lineLegend = {
    show: true,
    data: ['开票', '回款']
}
const tooltip = {
    trigger: 'axis',
    axisPointer: {
        type: 'shadow'
    }
}
const xAxis = [{
    type: 'value',
}]
const xAxis1 = ref([{
    type: 'category',
    axisTick: { show: false },
    data: []
}])
const yAxis = [{
    type: 'category',
    data: ['预付账款', '应付账款', '预收账款', '应收账款',]
}]
const yAxis1 = [{
    type: 'value'
}]
const pieTooltip = reactive({
    trigger: 'item',
    formatter: function (params) {
        // 动态生成提示信息,基于数据项的 name 属性
        const description = params.name === '本月回款金额' ? '本月回款金额' : '应收款金额';
        return `${description} ${formatNumber(params.value)}元 ${params.percent}%`;
    },
    position: 'right'
})
const materialPieSeries = ref([
    {
        type: 'pie',
        radius: ['66%', '90%'],
        avoidLabelOverlap: false,
        itemStyle: {
            borderColor: '#fff',
            borderWidth: 2
        },
        label: {
            show: false
        },
        data: []
    }
])
const lineSeries = ref([
    {
        type: 'line',
        data: [],
        label: {
            show: true
        },
    showSymbol: true, // 显示圆点
    },
])
const tooltipLine = {
    trigger: 'axis',
}
const yAxis2 = ref([
    {
        type: 'value',
    }
])
const xAxis2 = ref([
    {
        type: 'category',
        data: [],
        axisLabel: {
            interval: 0,
            formatter: function(value) {
                return value.replace(/~/g, '\n');
            },
        }
    }
])
// 合同金额分析数据
const contractList = [
    { name: '深圳科技有限公司', percent: 36, value: 4544, color: '#4fc3f7' },
    { name: '北京科技有限公司', percent: 20, value: 4000, color: '#81c784' },
    { name: '广州科技有限公司', percent: 16, value: 3113, color: '#ffb74d' },
    { name: '上海科技有限公司', percent: 10, value: 2341, color: '#ba68c8' },
    { name: '南京科技有限公司', percent: 9, value: 1231, color: '#e57373' },
    { name: '北京未来科技有限公司', percent: 9, value: 1231, color: '#64b5f6' }
]
// 待办事项
const todoList = ref([])
const radio1 = ref(1)
// 图表引用
const barChart = ref(null)
const lineChart = ref(null)
const barColors2 = ['#5181DB', '#D369E0', '#F2CA6D', '#60CCA8']
// 随机颜色生成函数
const getRandomColor = () => {
  return '#' + Math.floor(Math.random() * 0xffffff).toString(16).padStart(6, '0');
}
onMounted(() => {
    getBusinessData()
    analysisCustomer()
    todoInfoS()
    statisticsReceivable()
    qualityStatisticsInfo()
    getAmountHalfYearNum()
})
// 数据统计
const getBusinessData = () => {
    getBusiness().then((res) => {
        businessInfo.value = {...res.data}
    })
}
// 合同金额
const analysisCustomer = () => {
    analysisCustomerContractAmounts().then((res) => {
        sum.value = res.data.sum
        yny.value = res.data.yny
        chain.value = res.data.chain
    // 为每个数据项分配随机颜色
    materialPieSeries.value[0].data = res.data.item.map(item => ({
      ...item,
      itemStyle: { color: getRandomColor() }
    }))
    })
}
// 待办事项
const todoInfoS = () => {
    homeTodos().then((res) => {
        todoList.value = res.data
    })
}
// 应付应收统计
const statisticsReceivable = (type) => {
    console.log(type)
    statisticsReceivablePayable({type: radio1.value}).then((res) => {
        barSeries.value[0].data = [
            { value: res.data.prepayMoney, itemStyle: { color: barColors2[0] } },
            { value: res.data.payableMoney, itemStyle: { color: barColors2[1] } },
            { value: res.data.advanceMoney, itemStyle: { color: barColors2[2] } },
            { value: res.data.receivableMoney, itemStyle: { color: barColors2[3] } }
        ]
    })
}
// 质检统计
const qualityStatisticsInfo = () => {
    qualityStatistics().then((res) => {
        res.data.item.forEach(item => {
            xAxis1.value[0].data.push(item.date)
            barSeries1.value[0].data.push(item.supplierNum)
            barSeries1.value[1].data.push(item.processNum)
            barSeries1.value[2].data.push(item.factoryNum)
        })
        qualityStatisticsObject.value.supplierNum = res.data.supplierNum
        qualityStatisticsObject.value.processNum = res.data.processNum
        qualityStatisticsObject.value.factoryNum = res.data.factoryNum
    })
}
const getAmountHalfYearNum = async () => {
    const res = await getAmountHalfYear()
    console.log(res)
    const monthName = []
    const receiptAmount = []
    const invoiceAmount = []
    res.data.forEach(item => {
        monthName.push(item.month)
        receiptAmount.push(item.receiptAmount)
        invoiceAmount.push(item.invoiceAmount)
    })
    // 正确响应式赋值:创建新的 xAxis 和 series 对象
    xAxis2.value[0].data = monthName
    xAxis2.value[0].data = monthName.map(item => item.replace(/~/g, '\n~'));
    lineSeries.value = [
        {
            name: '开票',
            type: 'line',
            data: receiptAmount,
            stack: 'Total',
            areaStyle: {
                color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
                    {
                        offset: 0,
                        color: 'rgba(131, 207, 255, 1)'
                    },
                    {
                        offset: 1,
                        color: 'rgba(186, 228, 255, 1)'
                    }
                ])
            },
            itemStyle: {
                color: '#2D99FF',
                borderColor: '#2D99FF'
            },
            emphasis: {
                focus: 'series'
            },
            lineStyle: {
                width: 0
            },
            showSymbol: true,
        },
        {
            name: '回款',
            type: 'line',
            data: invoiceAmount,
            stack: 'Total',
            lineStyle: {
                width: 0
            },
            itemStyle: {
                color: '#83CFFF',
                borderColor: '#83CFFF'
            },
            showSymbol: true,
            areaStyle: {
                color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
                    {
                        offset: 0,
                        color: 'rgba(54, 153, 255, 1)'
                    },
                    {
                        offset: 1,
                        color: 'rgba(89, 169, 254, 1)'
                    }
                ])
            },
            emphasis: {
                focus: 'series'
            },
        }
    ]
}
</script>
<style scoped>
.dashboard {
    background: #f5f7fa;
    min-height: 100vh;
    padding: 20px;
    box-sizing: border-box;
}
.dashboard-top {
    display: flex;
    gap: 20px;
    margin-bottom: 20px;
}
.company-info {
    display: flex;
    flex-direction: column;
    gap: 8px;
    padding: 20px;
    min-width: 0;
    background-color: #EFF2FB; /* 使用指定的背景颜色 */
    background-image: url("../assets/images/denglu.png");
    background-size: cover;
    background-position: center;
    background-repeat: no-repeat;
    border-radius: 12px;
    height: 138px;
}
.avatar {
    width: 60px;
    height: 60px;
    border-radius: 50%;
    object-fit: contain;
    background: #fff;
    border: 1px solid #eee;
}
.company-card {
    display: flex;
    flex-direction: column;
    gap: 10px;
    position: relative;
    padding-right: 15px;
}
.company-card::after {
    content: '';
    position: absolute;
    right: 0;
    top: 0;
    bottom: 0;
    width: 1px;
    background-color: #C9C5C5;
    border-radius: 2px;
}
.company-name {
    font-weight: 400;
    font-size: 16px;
    color: #161A9A;
}
.company-meta {
    font-weight: 400;
    font-size: 12px;
    color: #818185;
}
.data-cards {
    display: flex;
    gap: 16px;
    justify-content: flex-start;
    background: #ffffff;
    border-radius: 12px;
    padding: 20px;
}
.data-title {
    font-weight: 700;
    font-size: 26px;
    color: #FFFFFF;
}
.data-num {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-top: 20px;
}
.data-card {
    background: #fff;
    border-radius: 12px;
    padding: 14px;
    min-width: 160px;
    box-shadow: 0 2px 8px #eee;
    display: flex;
    flex-direction: column;
    width: 32%;
    height: 140px;
}
.data-card.sales {
    background-image: url("../assets/images/xioashoushuju.png");
    background-size: cover;
    background-position: center;
    background-repeat: no-repeat;
}
.data-card.purchase {
    background-image: url("../assets/images/caigou.png");
    background-size: cover;
    background-position: center;
    background-repeat: no-repeat;
}
.data-card.inventory {
    background-image: url("../assets/images/kucun.png");
    background-size: cover;
    background-position: center;
    background-repeat: no-repeat;
}
.data-desc {
    font-weight: 500;
    font-size: 13px;
    color: #FFFFFF;
}
.data-value {
    font-size: 18px;
    font-weight: 500;
    margin: 10px 0;
    color: #FFFFFF;
}
.top-left {
    display: flex;
    flex-direction: column;
    gap: 20px;
    width: 50%;
}
.todo-panel {
    background: #fff;
    border-radius: 12px;
    padding: 20px;
    width: 50%;
}
.todo-list {
    list-style: none;
    padding: 0;
    margin: 0;
    font-size: 15px;
    overflow-y: auto;
    height: 260px;
}
.todo-list li {
    border-radius: 8px;
    margin-bottom: 12px;
    padding: 8px 20px;
    height: 74px;
    display: flex;
    justify-content: space-between;
    align-items: center;
    background: rgba(225,227,250,0.62);
}
.todo-title {
    font-weight: 400;
    font-size: 12px;
    color: #000000;
    position: relative;
}
.todo-title::before {
    content: ''; /* 必需,表示这里有一个内容 */
    position: absolute;
    left: -10px; /* 定位到左侧 */
    top: 50%; /* 垂直居中 */
    transform: translateY(-50%); /* 微调垂直居中 */
    width: 6px; /* 圆的直径 */
    height: 6px; /* 圆的直径 */
    background: #498CEB;
    border-radius: 50%; /* 让其变成圆形 */
}
.todo-division {
    font-weight: 400;
    font-size: 12px;
    color: #000000;
}
.todo-time {
    font-weight: 400;
    font-size: 12px;
    color: #000000;
}
.todo-meta {
    color: #888;
    font-size: 13px;
}
.dashboard-row {
    display: flex;
    gap: 20px;
    margin-bottom: 20px;
}
.main-panel {
    background: #fff;
    border-radius: 12px;
    padding: 20px;
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
}
.section-title {
    position: relative;
    font-size: 18px;
    color: #333;
    padding-left: 10px;
    margin-bottom: 10px;
    font-weight: 700;
}
.section-title::before {
    position: absolute;
    left: 0;
    top: 4px;
    content: '';
    width: 4px;
    height: 18px;
    background-color: #002FA7;
    border-radius: 2px;
}
.contract-info {
    display: flex;
    align-items: center;
    gap: 20px;
    height: 90px;
    background: rgba(245,245,245,0.59);
    width: 100%;
    border-radius: 10px;
    padding: 10px 30px;
}
.contract-summary {
    display: flex;
    align-items: center;
    gap: 30px;
}
.contract-card {
    display: flex;
    flex-direction: column;
    gap: 10px;
}
.contract-name {
    font-weight: 400;
    font-size: 14px;
    color: #050505;
}
.contract-meta {
    display: flex;
    align-items: center;
    width: 100%;
    gap: 80px;
}
.main-amount {
    font-size: 24px;
    color: rgba(51,50,50,0.85);
}
.up { color: #e57373; }
.contract-list {
    margin-top: 16px;
    font-size: 14px;
    color: #666;
    list-style: none;
    padding: 0;
}
.line {
    position: relative;
    width: 250px;
}
.line::after {
    content: '';
    position: absolute;
    right: 12px;
    top: 0;
    bottom: 0;
    width: 1px;
    background-color: #C9C5C5;
    border-radius: 2px;
}
.contract-list li {
    margin-top: 10px;
}
.quality-cards {
    display: flex;
    gap: 12px;
    margin-bottom: 12px;
}
.quality-card {
    border-radius: 8px;
    padding: 15px 10px 10px 50px;
    font-weight: 400;
    font-size: 12px;
    color: rgba(0,0,0,0.67);
    width: 236px;
    height: 49px;
    background-size: cover;
    background-position: center;
    background-repeat: no-repeat;
}
.quality-card.one {
    background-image: url("../assets/images/yuancailiao.png");
}
.quality-card.two {
    background-image: url("../assets/images/guocheng.png");
}
.quality-card.three {
    background-image: url("../assets/images/chuchang.png");
}
.quality-card span {
    color: #4fc3f7;
    font-weight: bold;
    margin-left: 6px;
}
.chart {
    width: 100%;
    height: 220px;
    margin-top: 10px;
}
</style>