From 11c0c969f05fd3c546b44898a0020eed9ba65fbe Mon Sep 17 00:00:00 2001
From: zouyu <2723363702@qq.com>
Date: 星期四, 22 一月 2026 13:15:24 +0800
Subject: [PATCH] 业务管理:IFS域字段显示

---
 src/views/statisticalCharts/qualificationRateStatistics/index.vue |  447 +++++++++++++++++++++++++++++++++++--------------------
 1 files changed, 286 insertions(+), 161 deletions(-)

diff --git a/src/views/statisticalCharts/qualificationRateStatistics/index.vue b/src/views/statisticalCharts/qualificationRateStatistics/index.vue
index 2e4996f..a1e8649 100644
--- a/src/views/statisticalCharts/qualificationRateStatistics/index.vue
+++ b/src/views/statisticalCharts/qualificationRateStatistics/index.vue
@@ -30,28 +30,22 @@
       </el-col>
     </el-row>
     <el-row :gutter="20">
-      <el-col :span="6">
+      <el-col :span="8">
         <div class="pie-card" :class="{ active: currentMaterialProp === '00Raw' }" @click="updateTitle('鍘熸潗鏂�')">
           <div class="title"><span style="color: #005BAC;font-weight: bold;">鍘熸潗鏂�</span>鍚堟牸鐜�</div>
           <Echarts ref="chart" :legend="pieLegend" :series="rawPieSeries" :tooltip="pieTooltip" style="height: 36vh;">
           </Echarts>
         </div>
       </el-col>
-      <el-col :span="6">
-        <div class="pie-card" :class="{ active: currentMaterialProp === '01Cu' }" @click="updateTitle('閾�')">
-          <div class="title"><span style="color: #005BAC;font-weight: bold;">閾�</span>鍚堟牸鐜�</div>
-          <Echarts ref="chart" :legend="pieLegend" :series="cuPieSeries" :tooltip="pieTooltip" style="height: 36vh;">
+      <el-col :span="8">
+        <div class="pie-card" :class="{ active: currentMaterialProp === '01Cu,02Al' }" @click="updateTitle('瀵间綋')">
+          <div class="title"><span style="color: #005BAC;font-weight: bold;">瀵间綋</span>鍚堟牸鐜�</div>
+          <Echarts ref="chart" :legend="pieLegend" :series="conductorPieSeries" :tooltip="pieTooltip"
+            style="height: 36vh;">
           </Echarts>
         </div>
       </el-col>
-      <el-col :span="6">
-        <div class="pie-card" :class="{ active: currentMaterialProp === '02Al' }" @click="updateTitle('閾�')">
-          <div class="title"><span style="color: #005BAC;font-weight: bold;">閾�</span>鍚堟牸鐜�</div>
-          <Echarts ref="chart" :legend="pieLegend" :series="alPieSeries" :tooltip="pieTooltip" style="height: 36vh;">
-          </Echarts>
-        </div>
-      </el-col>
-      <el-col :span="6">
+      <el-col :span="8">
         <div class="pie-card" :class="{ active: currentMaterialProp === '04Dlan' }" @click="updateTitle('鐢电紗')">
           <div class="title"><span style="color: #005BAC;font-weight: bold;">鐢电紗</span>鍚堟牸鐜�</div>
           <Echarts ref="chart" :legend="pieLegend" :series="dlanPieSeries" :tooltip="pieTooltip" style="height: 36vh;">
@@ -79,9 +73,16 @@
       </el-col>
       <el-col :span="12">
         <div class="inspection-card">
-          <div class="title"><span style="color: #005BAC;font-weight: bold;">{{ inspectionTitle }}</span>鏄庣粏鏁版嵁</div>
+          <div class="title" style="display:flex;align-items:center;justify-content:space-between;">
+            <div>
+              <span style="color: #005BAC;font-weight: bold;">{{ inspectionTitle }}</span>鏄庣粏鏁版嵁
+            </div>
+            <el-button type="text" icon="el-icon-download" @click="downloadTable">
+              涓嬭浇
+            </el-button>
+          </div>
           <lims-table :tableData="tableData" :column="tableColumn" :tableLoading="tableLoading"
-            :height="'calc(40vh - 40px)'"></lims-table>
+            :height="'calc(40vh - 40px)'" :show-summary="true" :summary-method="getSummaries"></lims-table>
         </div>
       </el-col>
     </el-row>
@@ -96,7 +97,8 @@
   getRawPassRateByBarChart,
   getRawPassRateByCake,
   getRawUpMonth,
-  getMaterialPropTable
+  getMaterialPropTable,
+  exportSupplierExcel
 } from "@/api/statisticalCharts/dataAnalysis";
 
 export default {
@@ -178,7 +180,7 @@
           data: [],
           label: {
             show: true,
-            formatter: (v) => v.value + '%'
+            formatter: (v) =>  v.value + '%'
           }
         }
       ],
@@ -201,7 +203,7 @@
         }
       },
       lineColors: ['#91A0FC'],
-      barColors: ['#13ce66', '#F56C6C'], // 鍚堟牸缁� / 涓嶅悎鏍肩孩
+      barColors: ['#9fe080', '#ff994d'], // 鍚堟牸缁� / 涓嶅悎鏍肩孩
       barColors2: ['#A4EEDA'],
       pieTooltip: {
         trigger: 'item'
@@ -212,7 +214,7 @@
       },
       rawPieSeries: [
         {
-          name: 'Access From',
+          name: '',
           type: 'pie',
           radius: '70%',
           center: ['50%', '50%'],
@@ -222,7 +224,7 @@
             borderWidth: 2
           },
           label: {
-            alignTo: 'edge',
+            alignTo: 'labelLine',
             formatter: '{name|{b}}\n{time|{c}}',
             edgeDistance: 10,
             lineHeight: 15,
@@ -238,14 +240,14 @@
             length2: 40
           },
           data: [
-            { value: 0, name: '涓嶅悎鏍兼暟閲�', itemStyle: { color: '#F56C6C' } },
-            { value: 0, name: '鍚堟牸鏁伴噺', itemStyle: { color: '#67C23A' } }
+            { value: 0, name: '涓嶅悎鏍兼暟閲�', itemStyle: {color: '#ff994d'} },
+            { value: 0, name: '鍚堟牸鏁伴噺', itemStyle: { color: '#9fe080' } }
           ]
         }
       ],
-      cuPieSeries: [
+      conductorPieSeries: [
         {
-          name: 'Access From',
+          name: '',
           type: 'pie',
           radius: '70%',
           center: ['50%', '50%'],
@@ -255,7 +257,7 @@
             borderWidth: 2
           },
           label: {
-            alignTo: 'edge',
+            alignTo: 'labelLine',
             formatter: '{name|{b}}\n{time|{c}}',
             edgeDistance: 10,
             lineHeight: 15,
@@ -271,47 +273,14 @@
             length2: 40,
           },
           data: [
-            { value: 0, name: '涓嶅悎鏍兼暟閲�', itemStyle: { color: '#F56C6C' } },
-            { value: 0, name: '鍚堟牸鏁伴噺', itemStyle: { color: '#67C23A' } },
-          ]
-        }
-      ],
-      alPieSeries: [
-        {
-          name: 'Access From',
-          type: 'pie',
-          radius: '70%',
-          center: ['50%', '50%'],
-          avoidLabelOverlap: false,
-          itemStyle: {
-            borderColor: '#fff',
-            borderWidth: 2
-          },
-          label: {
-            alignTo: 'edge',
-            formatter: '{name|{b}}\n{time|{c}}',
-            edgeDistance: 10,
-            lineHeight: 15,
-            rich: {
-              time: {
-                fontSize: 10,
-                color: '#999'
-              }
-            },
-          },
-          labelLine: {
-            length: 20,
-            length2: 40,
-          },
-          data: [
-            { value: 0, name: '涓嶅悎鏍兼暟閲�', itemStyle: { color: '#F56C6C' } },
-            { value: 0, name: '鍚堟牸鏁伴噺', itemStyle: { color: '#67C23A' } },
+            { value: 0, name: '涓嶅悎鏍兼暟閲�', itemStyle: { color: '#ff994d' } },
+            { value: 0, name: '鍚堟牸鏁伴噺', itemStyle: { color: '#9fe080' } },
           ]
         }
       ],
       dlanPieSeries: [
         {
-          name: 'Access From',
+          name: '',
           type: 'pie',
           radius: '70%',
           center: ['50%', '50%'],
@@ -321,7 +290,7 @@
             borderWidth: 2
           },
           label: {
-            alignTo: 'edge',
+            alignTo: 'labelLine',
             formatter: '{name|{b}}\n{time|{c}}',
             edgeDistance: 10,
             lineHeight: 15,
@@ -337,8 +306,8 @@
             length2: 40,
           },
           data: [
-            { value: 0, name: '涓嶅悎鏍兼暟閲�', itemStyle: { color: '#F56C6C' } },
-            { value: 0, name: '鍚堟牸鏁伴噺', itemStyle: { color: '#67C23A' } },
+            { value: 0, name: '涓嶅悎鏍兼暟閲�', itemStyle: { color: '#ff994d' } },
+            { value: 0, name: '鍚堟牸鏁伴噺', itemStyle: { color: '#9fe080' } },
           ]
         }
       ],
@@ -420,34 +389,18 @@
       tableData: [],
       tableLoading: false,
       tableColumn: [
-        { label: '鎵瑰彿', prop: 'updateBatchNo', minWidth: '120px' },
-        { label: '鎶佃揪鎴愬搧鏁伴噺', prop: 'qtyArrived', minWidth: '100px' },
-        { label: '闆朵欢鎻忚堪', prop: 'partDesc', minWidth: '200px' },
+        { label: '渚涘簲鍟嗗悕绉�', prop: 'supplierName', minWidth: '200px' },
+        { label: '鍒拌揣鎵规', prop: 'totalBatch', minWidth: '100px' },
+        { label: '涓嶅悎鏍兼壒娆�', prop: 'unqualifiedBatch', minWidth: '100px' },
         {
-          dataType: 'tag',
-          label: '妫�楠岀姸鎬�',
-          prop: 'inspectStatus',
+          label: '鍚堟牸鐜�(%)',
+          prop: 'passRate',
           minWidth: '100px',
-          formatData: (params) => {
-            if (params == 0) return '妫�楠屼腑'
-            if (params == 1) return '鍚堟牸'
-            if (params == 2) return '涓嶅悎鏍�'
-            if (params == 3) return '鏈笅鍗�'
-            if (params == 4) return '璁╂鏀捐'
-            return null
-          },
-          formatType: (params) => {
-            if (params == 1 || params == 4) return 'success'
-            if (params == 0 || params == 2) return 'danger'
-            if (params == 3) return 'warning'
-            return null
-          }
-        },
-        { label: '涓嬪彂鏃堕棿', prop: 'sendTime', minWidth: '150px' }
+          formatData: (val) => (val != null ? val + '%' : '0%')
+        }
       ],
       rawPassRate: '',
-      cuPassRate: '',
-      alPassRate: '',
+      conductorPassRate: '',
       dlanPassRate: '',
       sum: '',
     }
@@ -465,52 +418,12 @@
   // 鏂规硶闆嗗悎
   methods: {
     // 鑾峰彇鍚堟牸鐜囧浘琛ㄦ暟鎹�
-
     setBarChartTitle() {
       this.echartsOptions.title.text = `${this.inspectionTitle}鍚堟牸鐜囪秼鍔縛;
     },
-
     getBar() {
-      const params = {
-        dateType: this.dateType,
-        beginDate: this.beginDate,
-        endDate: this.endDate,
-        sampleName: this.sampleName,
-        modelName: this.modelName,
-        supplierName: this.supplierName,
-        materialProp: this.currentMaterialProp,
-      };
-
-      getRawPassRateByBarChart(params).then((res) => {
-        let qualifiedData = [];
-        let unQualifiedData = [];
-        let lineData = [];
-        let xAxis = [];
-
-        res.data.forEach(item => {
-          qualifiedData.push(item.qualified || 0);
-          unQualifiedData.push(item.unQualified || 0);
-          lineData.push(item.passRate || 0);
-          xAxis.push(item.searchTime);
-        });
-
-        this.echartsSeries[0].data = qualifiedData;
-        this.echartsSeries[1].data = unQualifiedData;
-        this.echartsSeries[2].data = lineData;
-        this.xAxis[0].data = xAxis;
-      });
-    },
-
-    // 鑾峰彇鐗╂枡灞炴�у悎鏍肩巼鍥捐〃鏁版嵁
-    getRawPass() {
-      const materials = [
-        { type: '00Raw', series: 'rawPieSeries', rate: 'rawPassRate' },
-        { type: '01Cu', series: 'cuPieSeries', rate: 'cuPassRate' },
-        { type: '02Al', series: 'alPieSeries', rate: 'alPassRate' },
-        { type: '04Dlan', series: 'dlanPieSeries', rate: 'dlanPassRate' }
-      ];
-
-      materials.forEach(item => {
+      const types = this.currentMaterialProp.split(',');
+      const requests = types.map(t => {
         const params = {
           dateType: this.dateType,
           beginDate: this.beginDate,
@@ -518,19 +431,128 @@
           sampleName: this.sampleName,
           modelName: this.modelName,
           supplierName: this.supplierName,
-          materialProp: item.type
-        }
-        getRawPassRateByCake(params).then((res) => {
-          if (res.data) {
-            this[item.series][0].data[0].value = res.data.unQualified || 0
-            this[item.series][0].data[1].value = res.data.qualified || 0
-            this[item.rate] = res.data.passRate != null ? res.data.passRate + '%' : ''
-          } else {
-            this[item.series][0].data[0].value = 0
-            this[item.series][0].data[1].value = 0
-            this[item.rate] = ''
+          materialProp: t,
+        };
+        return getRawPassRateByBarChart(params);
+      });
+
+      Promise.all(requests).then((responses) => {
+        let dateMap = {}; // { date: { qualified, unQualified } }
+
+        responses.forEach(res => {
+          if (res.data && Array.isArray(res.data)) {
+            res.data.forEach(item => {
+              const date = item.searchTime;
+              if (!dateMap[date]) {
+                dateMap[date] = { qualified: 0, unQualified: 0 };
+              }
+              dateMap[date].qualified += (item.qualified || 0);
+              dateMap[date].unQualified += (item.unQualified || 0);
+            });
           }
-        })
+        });
+
+        const weekOrderMap = {
+          "鏄熸湡涓�": 0,
+          "鏄熸湡浜�": 1,
+          "鏄熸湡涓�": 2,
+          "鏄熸湡鍥�": 3,
+          "鏄熸湡浜�": 4,
+          "鏄熸湡鍏�": 5,
+          "鏄熸湡鏃�": 6
+        };
+        const yearOrderMap = {
+          "1鏈�": 0,
+          "2鏈�": 1,
+          "3鏈�": 2,
+          "4鏈�": 3,
+          "5鏈�": 4,
+          "6鏈�": 5,
+          "7鏈�": 6,
+          "8鏈�": 7,
+          "9鏈�": 8,
+          "10鏈�": 9,
+          "11鏈�": 10,
+          "12鏈�": 11
+        };
+        const sortedDates = Object.keys(dateMap).sort((a, b) => {
+          if(a.includes("鏄熸湡") && b.includes("鏄熸湡")) {
+            return weekOrderMap[a] - weekOrderMap[b];
+          } else if(a.includes("鏈�") && b.includes("鏈�")) {
+            return yearOrderMap[a] - yearOrderMap[b];
+          } else {
+            return new Date(a) - new Date(b);
+          }
+        });
+        let qualifiedData = [];
+        let unQualifiedData = [];
+        let lineData = [];
+        let xAxis = [];
+
+        console.log(sortedDates)
+        sortedDates.forEach(date => {
+          const { qualified, unQualified } = dateMap[date];
+          const total = qualified + unQualified;
+          const passRate = total > 0 ? (qualified / total * 100).toFixed(2) : 0;
+
+          xAxis.push(date);
+          qualifiedData.push(qualified);
+          unQualifiedData.push(unQualified);
+          lineData.push(parseFloat(passRate));
+        });
+
+        this.echartsSeries[0].data = qualifiedData;
+        this.echartsSeries[1].data = unQualifiedData;
+        this.echartsSeries[2].data = lineData;
+        this.xAxis[0].data = xAxis;
+        console.log(xAxis)
+      });
+    },
+
+    // 鑾峰彇鐗╂枡灞炴�у悎鏍肩巼鍥捐〃鏁版嵁
+    getRawPass() {
+      const materials = [
+        { type: '00Raw', series: 'rawPieSeries', rate: 'rawPassRate' },
+        { type: '01Cu,02Al', series: 'conductorPieSeries', rate: 'conductorPassRate' },
+        { type: '04Dlan', series: 'dlanPieSeries', rate: 'dlanPassRate' }
+      ];
+
+      materials.forEach(item => {
+        const types = item.type.split(',');
+        const requests = types.map(t => {
+          const params = {
+            dateType: this.dateType,
+            beginDate: this.beginDate,
+            endDate: this.endDate,
+            sampleName: this.sampleName,
+            modelName: this.modelName,
+            supplierName: this.supplierName,
+            materialProp: t
+          };
+          return getRawPassRateByCake(params);
+        });
+
+        Promise.all(requests).then((responses) => {
+          let unQualified = 0;
+          let qualified = 0;
+          responses.forEach(res => {
+            if (res.data) {
+              unQualified += (res.data.unQualified || 0);
+              qualified += (res.data.qualified || 0);
+            }
+          });
+
+          if (qualified + unQualified > 0) {
+            this[item.series][0].data[0].value = unQualified;
+            this[item.series][0].data[1].value = qualified;
+            const passRate = (qualified / (qualified + unQualified) * 100).toFixed(2);
+            this[item.rate] = passRate + '%';
+          } else {
+            this[item.series][0].data[0].value = 0;
+            this[item.series][0].data[1].value = 0;
+            this[item.rate] = '';
+          }
+        });
       });
     },
     // 鑾峰彇鏈湀妫�楠岀被鍨嬫暟閲�
@@ -590,8 +612,7 @@
 
       const typeMap = {
         '鍘熸潗鏂�': '00Raw',
-        '閾�': '01Cu',
-        '閾�': '02Al',
+        '瀵间綋': '01Cu,02Al',
         '鐢电紗': '04Dlan',
       };
 
@@ -605,24 +626,49 @@
 
     getTableData() {
       this.tableLoading = true;
-      const params = {
-        dateType: this.dateType,
-        beginDate: this.beginDate,
-        endDate: this.endDate,
-        sampleName: this.sampleName,
-        modelName: this.modelName,
-        supplierName: this.supplierName,
-        materialProp: this.currentMaterialProp,
-      };
+      const types = this.currentMaterialProp.split(',');
+      const requests = types.map(t => {
+        const params = {
+          dateType: this.dateType,
+          beginDate: this.beginDate,
+          endDate: this.endDate,
+          sampleName: this.sampleName,
+          modelName: this.modelName,
+          supplierName: this.supplierName,
+          materialProp: t,
+        };
+        return getMaterialPropTable(params);
+      });
 
-      getMaterialPropTable(params).then((res) => {
-        if (Array.isArray(res.data)) {
-          this.tableData = res.data;
-        } else if (res.data && res.data.records) {
-          this.tableData = res.data.records;
-        } else {
-          this.tableData = [];
-        }
+      Promise.all(requests).then((responses) => {
+        let supplierMap = {}; // { supplierName: { totalBatch, unqualifiedBatch } }
+
+        responses.forEach(res => {
+          const data = Array.isArray(res.data) ? res.data : (res.data && res.data.records ? res.data.records : []);
+          data.forEach(item => {
+            const name = item.supplierName || '鏈煡渚涘簲鍟�';
+            if (!supplierMap[name]) {
+              supplierMap[name] = { totalBatch: 0, unqualifiedBatch: 0 };
+            }
+            supplierMap[name].totalBatch += (item.totalBatch || 0);
+            supplierMap[name].unqualifiedBatch += (item.unqualifiedBatch || 0);
+          });
+        });
+
+        const tableData = Object.keys(supplierMap).map(name => {
+          const { totalBatch, unqualifiedBatch } = supplierMap[name];
+          const passRate = totalBatch > 0 ? ((totalBatch - unqualifiedBatch) / totalBatch * 100).toFixed(2) : 0;
+          return {
+            supplierName: name,
+            totalBatch,
+            unqualifiedBatch,
+            passRate
+          };
+        });
+
+        // Sort by totalBatch descending
+        tableData.sort((a, b) => b.totalBatch - a.totalBatch);
+        this.tableData = tableData;
         this.tableLoading = false;
       }).catch(() => {
         this.tableLoading = false;
@@ -639,6 +685,85 @@
       const title = `${this.inspectionTitle}_鍚堟牸鐜囪秼鍔縛;
       this.$refs.barChart.downloadImage(title);
     },
+
+    // 涓嬭浇琛ㄦ牸鏁版嵁
+    downloadTable() {
+      if (!this.tableData || this.tableData.length === 0) {
+        this.$message.warning("娌℃湁鍙鍑虹殑鏁版嵁");
+        return;
+      }
+
+      exportSupplierExcel(this.tableData)
+        .then(res => {
+          if (res.type && res.type.includes("application/json")) {
+            const fileReader = new FileReader();
+            fileReader.onload = () => {
+              const msg = JSON.parse(fileReader.result);
+              this.$message.error(msg.msg || "瀵煎嚭澶辫触");
+            };
+            fileReader.readAsText(res);
+            return;
+          }
+
+          const blob = new Blob([res], {
+            type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
+          });
+
+          const fileName = `${this.inspectionTitle}鍚堟牸鐜囩粺璁�.xlsx`;
+
+          if (window.navigator && window.navigator.msSaveOrOpenBlob) {
+            window.navigator.msSaveOrOpenBlob(blob, fileName);
+          } else {
+            const link = document.createElement("a");
+            link.href = window.URL.createObjectURL(blob);
+            link.download = fileName;
+            document.body.appendChild(link);
+            link.click();
+            document.body.removeChild(link);
+            window.URL.revokeObjectURL(link.href);
+          }
+        })
+        .catch(() => {
+          this.$message.error("瀵煎嚭澶辫触");
+        });
+    },
+
+    getSummaries(param) {
+      const { columns, data } = param;
+      const sums = [];
+      columns.forEach((column, index) => {
+        if (index === 0) {
+          sums[index] = '鍚堣';
+          return;
+        }
+
+        const property = column.property;
+        if (property === 'supplierName') {
+          sums[index] = '';
+          return;
+        }
+
+        if (property === 'totalBatch' || property === 'unqualifiedBatch') {
+          const values = data.map(item => Number(item[property]));
+          sums[index] = values.reduce((prev, curr) => {
+            const value = Number(curr);
+            if (!isNaN(value)) {
+              return prev + curr;
+            } else {
+              return prev;
+            }
+          }, 0);
+        } else if (property === 'passRate') {
+          const totalBatch = data.reduce((prev, curr) => prev + (Number(curr.totalBatch) || 0), 0);
+          const unqualifiedBatch = data.reduce((prev, curr) => prev + (Number(curr.unqualifiedBatch) || 0), 0);
+          sums[index] = totalBatch > 0 ? ((totalBatch - unqualifiedBatch) / totalBatch * 100).toFixed(2) + '%' : '0%';
+        } else {
+          sums[index] = '';
+        }
+      });
+
+      return sums;
+    },
   },
 }
 </script>

--
Gitblit v1.9.3