| | |
| | | </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;"> |
| | |
| | | </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> |
| | |
| | | getRawPassRateByBarChart, |
| | | getRawPassRateByCake, |
| | | getRawUpMonth, |
| | | getMaterialPropTable |
| | | getMaterialPropTable, |
| | | exportSupplierExcel |
| | | } from "@/api/statisticalCharts/dataAnalysis"; |
| | | |
| | | export default { |
| | |
| | | ] |
| | | } |
| | | ], |
| | | 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', |
| | |
| | | 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: '', |
| | | } |
| | |
| | | }, |
| | | |
| | | getBar() { |
| | | const types = this.currentMaterialProp.split(','); |
| | | const requests = types.map(t => { |
| | | const params = { |
| | | dateType: this.dateType, |
| | | beginDate: this.beginDate, |
| | |
| | | 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; |
| | |
| | | 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, |
| | |
| | | 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] = ''; |
| | | } |
| | | }); |
| | | }); |
| | | }, |
| | | // 获取本月检验类型数量 |
| | |
| | | |
| | | const typeMap = { |
| | | '原材料': '00Raw', |
| | | '铜': '01Cu', |
| | | '铝': '02Al', |
| | | '导体': '01Cu,02Al', |
| | | '电缆': '04Dlan', |
| | | }; |
| | | |
| | |
| | | |
| | | getTableData() { |
| | | this.tableLoading = true; |
| | | const types = this.currentMaterialProp.split(','); |
| | | const requests = types.map(t => { |
| | | const params = { |
| | | dateType: this.dateType, |
| | | beginDate: this.beginDate, |
| | |
| | | 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; |
| | |
| | | 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> |