fix: 合格率统计调整:铜、铝合并为【导体】统计数据。明细表格字段更改和导出按钮
已修改3个文件
330 ■■■■■ 文件已修改
src/api/statisticalCharts/dataAnalysis.js 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/Table/lims-table.vue 20 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/statisticalCharts/qualificationRateStatistics/index.vue 300 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/statisticalCharts/dataAnalysis.js
@@ -96,3 +96,13 @@
    params: query,
  });
}
// 导出供应商合格率统计
export function exportSupplierExcel(data) {
  return request({
    url: "/dataAnalysis/exportSupplierExcel",
    method: "post",
    data: data,
    responseType: "blob"
  });
}
src/components/Table/lims-table.vue
@@ -4,7 +4,7 @@
    <el-table ref="multipleTable" v-loading="tableLoading" :border="border" :data="tableData"
      :header-cell-style="{ background: '#f8f8f9', color: '#515a6e' }" :height="height"
      :highlight-current-row="highlightCurrentRow" :row-class-name="rowClassName" :row-style="rowStyle"
      :row-key="rowKey" :span-method="spanMethod" stripe style="width: 100%" tooltip-effect="dark" @row-click="rowClick"
      :row-key="rowKey" :span-method="spanMethod" :show-summary="showSummary" :summary-method="summaryMethod" stripe style="width: 100%" tooltip-effect="dark" @row-click="rowClick"
      @current-change="currentChange" @selection-change="handleSelectionChange" class="lims-table">
      <el-table-column align="center" type="selection" width="55" v-if="isSelection" />
      <el-table-column align="center" label="序号" type="index" width="60" :index="indexMethod" />
@@ -260,6 +260,14 @@
        };
      },
    },
    showSummary: {
      type: Boolean,
      default: false
    },
    summaryMethod: {
      type: Function,
      default: null
    }
  },
  data() {
    return {
@@ -284,9 +292,13 @@
  watch: {
    tableData: {
      handler() {
        // 当表格数据变化时,初始化 uploadKeys
        this.tableData.forEach((_, index) => {
          this.$set(this.uploadKeys, index, Date.now());
        });
        this.$nextTick(() => {
          if (this.$refs.multipleTable) {
            this.$refs.multipleTable.doLayout();
          }
        });
      },
      immediate: true
@@ -471,7 +483,7 @@
</script>
<style scoped>
.el-table>>>.el-table__empty-text {
.el-table ::v-deep .el-table__empty-text {
  text-align: center;
}
@@ -479,7 +491,7 @@
  color: rgb(64, 158, 255);
  cursor: pointer;
}
>>>.cell {
::v-deep .cell {
  padding: 0 !important;
}
.cell {
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 {
@@ -243,40 +245,7 @@
          ]
        }
      ],
      cuPieSeries: [
        {
          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' } },
          ]
        }
      ],
      alPieSeries: [
      conductorPieSeries: [
        {
          name: 'Access From',
          type: 'pie',
@@ -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
          formatData: (val) => (val != null ? val + '%' : '0%')
          }
        },
        { label: '下发时间', prop: 'sendTime', minWidth: '150px' }
      ],
      rawPassRate: '',
      cuPassRate: '',
      alPassRate: '',
      conductorPassRate: '',
      dlanPassRate: '',
      sum: '',
    }
@@ -471,6 +424,8 @@
    },
    getBar() {
      const types = this.currentMaterialProp.split(',');
      const requests = types.map(t => {
      const params = {
        dateType: this.dateType,
        beginDate: this.beginDate,
@@ -478,20 +433,42 @@
        sampleName: this.sampleName,
        modelName: this.modelName,
        supplierName: this.supplierName,
        materialProp: this.currentMaterialProp,
          materialProp: t,
      };
        return getRawPassRateByBarChart(params);
      });
      getRawPassRateByBarChart(params).then((res) => {
      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 sortedDates = Object.keys(dateMap).sort();
        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);
        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;
@@ -505,12 +482,13 @@
    getRawPass() {
      const materials = [
        { type: '00Raw', series: 'rawPieSeries', rate: 'rawPassRate' },
        { type: '01Cu', series: 'cuPieSeries', rate: 'cuPassRate' },
        { type: '02Al', series: 'alPieSeries', rate: 'alPassRate' },
        { 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,
@@ -518,19 +496,32 @@
          sampleName: this.sampleName,
          modelName: this.modelName,
          supplierName: this.supplierName,
          materialProp: item.type
        }
        getRawPassRateByCake(params).then((res) => {
            materialProp: t
          };
          return getRawPassRateByCake(params);
        });
        Promise.all(requests).then((responses) => {
          let unQualified = 0;
          let qualified = 0;
          responses.forEach(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] = ''
              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 +581,7 @@
      const typeMap = {
        '原材料': '00Raw',
        '铜': '01Cu',
        '铝': '02Al',
        '导体': '01Cu,02Al',
        '电缆': '04Dlan',
      };
@@ -605,6 +595,8 @@
    getTableData() {
      this.tableLoading = true;
      const types = this.currentMaterialProp.split(',');
      const requests = types.map(t => {
      const params = {
        dateType: this.dateType,
        beginDate: this.beginDate,
@@ -612,17 +604,40 @@
        sampleName: this.sampleName,
        modelName: this.modelName,
        supplierName: this.supplierName,
        materialProp: this.currentMaterialProp,
          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 +654,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>