bonus-ui/src/views/material/report/reportQuery.vue

943 lines
32 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="分公司" prop="impUnitName">
<el-select
v-model="queryParams.impUnitName"
placeholder="请选择分公司"
clearable
filterable
style="width: 240px"
@change="handleImpUnitChange"
>
<el-option
v-for="item in impUnitOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="项目部" prop="departName">
<el-select
v-model="queryParams.departName"
placeholder="请选择项目部"
clearable
filterable
style="width: 240px"
@change="handleDepartChange"
>
<el-option
v-for="item in departOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="工程" prop="proName">
<el-select
v-model="queryParams.proName"
placeholder="请选择工程"
clearable
filterable
style="width: 240px"
>
<el-option
v-for="item in proOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="物资名称" prop="typeName">
<el-select
v-model="queryParams.typeName"
placeholder="请选择物资名称"
clearable
filterable
style="width: 240px"
@change="handleMaModel"
>
<el-option
v-for="item in materialNameList"
:key="item.value"
:label="item.label"
:value="item.label"
/>
</el-select>
</el-form-item>
<el-form-item label="规格型号" prop="typeModelName">
<el-select
v-model="queryParams.typeModelName"
placeholder="请选择规格型号"
clearable
filterable
style="width: 240px"
>
<el-option
v-for="item in materialModelList"
:key="item.value"
:label="item.label"
:value="item.label"
/>
</el-select>
</el-form-item>
<el-form-item label="机具类型" prop="jiJuType">
<el-select
v-model="queryParams.jiJuType"
placeholder="请选择机具类型"
clearable
style="width: 240px"
>
<el-option label="施工机具" :value="1" />
<el-option label="安全工器具" :value="2" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">查询</el-button>
<el-button icon="el-icon-download" size="mini" @click="handleBulkDownload">一键下载</el-button>
</el-form-item>
</el-form>
<el-table v-loading="loading" :data="tableList" ref="multipleTable" row-key="id" @selection-change="handleSelectionChange" border>
<el-table-column type="selection" width="55"></el-table-column>
<el-table-column label="序号" align="center" width="70" type="index">
<template slot-scope="scope">
<span>{{ (queryParams.pageNum - 1) * queryParams.pageSize + scope.$index + 1 }}</span>
</template>
</el-table-column>
<el-table-column label="分公司" align="center" prop="impUnitName" :show-overflow-tooltip="true"/>
<el-table-column label="项目部" align="center" prop="departName" :show-overflow-tooltip="true"/>
<el-table-column label="工程名称" align="center" prop="proName" :show-overflow-tooltip="true"/>
<el-table-column label="物资名称" align="center" prop="typeName" :show-overflow-tooltip="true"/>
<el-table-column label="规格型号" align="center" prop="typeModelName" :show-overflow-tooltip="true"/>
<el-table-column label="计量单位" align="center" prop="unit" :show-overflow-tooltip="true"/>
<el-table-column label="数量" align="center" prop="num" :show-overflow-tooltip="true"/>
<el-table-column label="编码" align="center" prop="maCode" :show-overflow-tooltip="true">
<template slot-scope="scope">
<span
v-if="scope.row.maCode"
:style="{
color: scope.row.qrCode ? '#3EA7F1' : '#000000',
cursor: scope.row.qrCode ? 'pointer' : 'default'
}"
@click="scope.row.qrCode && handleViewQrCode(scope.row)"
>
{{ scope.row.maCode }}
</span>
<span v-else>—</span>
</template>
</el-table-column>
<el-table-column label="领用日期" align="center" prop="leaseTime" :show-overflow-tooltip="true"/>
<el-table-column label="出库检验报告" align="center" :show-overflow-tooltip="true">
<template #default="scope">
<span
@click="handleReport(scope.row)"
style="color: #3EA7F1; text-decoration: none; cursor: pointer;"
>查看</span>
</template>
</el-table-column>
<el-table-column label="合格证" align="center" :show-overflow-tooltip="true">
<template #default="scope">
<span v-if="!scope.row.qualifiedUrl">无</span>
<span
v-else
@click="handleFileView(scope.row.qualifiedUrl)"
style="color: #3EA7F1; text-decoration: none; cursor: pointer;"
>查看</span>
</template>
</el-table-column>
<el-table-column label="型式试验报告" align="center" :show-overflow-tooltip="true">
<template #default="scope">
<span v-if="!scope.row.testReportUrl">无</span>
<span
v-else
@click="handleFileView(scope.row.testReportUrl)"
style="color: #3EA7F1; text-decoration: none; cursor: pointer;"
>查看</span>
</template>
</el-table-column>
<el-table-column label="第三方检测报告" align="center" :show-overflow-tooltip="true">
<template #default="scope">
<span v-if="!scope.row.thirdReportUrl">无</span>
<span
v-else
@click="handleFileView(scope.row.thirdReportUrl)"
style="color: #3EA7F1; text-decoration: none; cursor: pointer;"
>查看</span>
</template>
</el-table-column>
<el-table-column label="出厂检测报告" align="center" :show-overflow-tooltip="true">
<template #default="scope">
<span v-if="!scope.row.factoryReportUrl">无</span>
<span
v-else
@click="handleFileView(scope.row.factoryReportUrl)"
style="color: #3EA7F1; text-decoration: none; cursor: pointer;"
>查看</span>
</template>
</el-table-column>
<el-table-column label="其他" align="center" :show-overflow-tooltip="true">
<template #default="scope">
<span v-if="!scope.row.otherReportUrl">无</span>
<span
v-else
@click="handleFileView(scope.row.otherReportUrl)"
style="color: #3EA7F1; text-decoration: none; cursor: pointer;"
>查看</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" width="140">
<template slot-scope="scope">
<el-button size="mini" type="primary" @click="handleDownload(scope.row)">下载</el-button>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total>0"
:total="total"
:page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize"
@pagination="getList"
/>
<!-- 验收单弹窗 -->
<el-dialog :title="title" :visible.sync="openOutReport" width="70%" append-to-body>
<div style="height: 600px; overflow-y: scroll; padding: 0 20px">
<vue-easy-print tableShow ref="remarksPrintRefCheck" class="print" :printable="'checkId1'">
<div id="checkId1">
<div class="title" style="text-align: center; font-weight: 600; font-size: 16px">
施工机具设备出库检验记录表
</div>
<div class="info" style="margin-top: 10px; display: flex; flex-wrap: wrap">
<div class="item" style="width: 50%; flex-shrink: 0; margin-bottom: 5px; font-size: 14px">
<span>领用工程:</span>
{{ checkDataInfo.leaseProject }}
</div>
<div class="item" style="width: 50%; flex-shrink: 0; margin-bottom: 5px; font-size: 14px">
<span>使用单位:</span>
{{ checkDataInfo.leaseUnit }}
</div>
</div>
<table
class="print-table"
style="margin-top: 20px; width: 100%; border-collapse: collapse"
border="1"
>
<thead>
<tr>
<th style="text-align: center">机具名称</th>
<th style="text-align: center">规格型号</th>
<th style="text-align: center">单位</th>
<th style="text-align: center">数量</th>
<th style="text-align: center">设备编码</th>
<th style="text-align: center">额定载荷KN</th>
<th style="text-align: center">试验载荷KN</th>
<th style="text-align: center">持荷时间min</th>
<th style="text-align: center; width: 100px">试验日期</th>
<th style="text-align: center; width: 100px">下次试验日期</th>
<th style="text-align: center">检验结论</th>
<th style="text-align: center">备注</th>
</tr>
</thead>
<tbody>
<tr v-for="(item, index) in printTableData" :key="index">
<td style="text-align: center">{{ item.typeName }}</td>
<td style="text-align: center">{{ item.typeModelName }}</td>
<td style="text-align: center">{{ item.unit }}</td>
<td style="text-align: center">{{ item.num }}</td>
<td style="text-align: center">{{ item.maCode }}</td>
<td style="text-align: center">{{ item.ratedLoad }}</td>
<td style="text-align: center">{{ item.testLoad }}</td>
<td style="text-align: center">{{ item.holdingTime }}</td>
<td style="text-align: center">{{ item.testTime }}</td>
<td style="text-align: center">{{ item.nextTestTime }}</td>
<td style="text-align: center">{{ item.checkResult }}</td>
<td style="text-align: center">{{ item.remark }}</td>
</tr>
</tbody>
</table>
<div
class="fillIn"
style="margin-top: 20px; display: flex; align-items: center; justify-content: right"
>
<div class="item" style="width: 30%; align-items: center; position: relative">
<div>
<span>检验单位</span>
</div>
<div v-if="openOutReport">
<canvas
id="canvas"
width="165"
height="165"
style="
position: absolute;
top: 180%;
left: 90%;
transform: translate(-100%, -50%);
z-index: 9999;
"
></canvas>
</div>
</div>
</div>
</div>
</vue-easy-print>
</div>
<div slot="footer" class="dialog-footer" style="text-align: center">
<!-- <el-button type="primary" @click="handleExportCheck">导出</el-button>-->
<el-button type="primary" @click="printCheck"> </el-button>
<el-button @click="closeDialogAndRefresh"> </el-button>
</div>
</el-dialog>
<!-- 二维码下载对话框 -->
<el-dialog title="二维码查看" :visible.sync="uploadOpen" width="450px" append-to-body :close-on-click-modal="false">
<div style="text-align: center" ref="codeQr" v-if="uploadOpen">
<div class="uploadImg">
<div id="qrcode" class="qrcode" ref="codeItem"></div>
</div>
<div class="boxCode">{{ rowObj.maCode }}</div>
</div>
<div slot="footer" class="dialog-footer" style="text-align: center">
<el-button type="primary" @click="downloadQrCode(rowObj)"> </el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import vueEasyPrint from "vue-easy-print";
import {batchDownloadFileApi, downloadFileApi, getReportList} from "@/api/report/report";
import {getDepartListByImpUnitApi, getImpUnitListApi, getProListByDepartApi} from "@/api/materialsStation";
import {getDeviceType} from "@/api/ma/device";
import {getCheckInfo} from "@/api/lease/apply";
import printJS from "print-js";
import QRCode from "qrcodejs2";
import axios from 'axios'
import {downloadFile} from "@/utils/download";
export default {
name: "PartAccept",
dicts: ['part_task_status'],
components: { vueEasyPrint },
data() {
return {
// 遮罩层
loading: false,
// 非单个禁用
single: true,
// 非多个禁用
multiple: true,
// 显示搜索条件
showSearch: true,
showHouse: false,
dateRange:[],
ids: [],
infos: [],
// 总条数
total: 0,
//表格数据
tableList: [],
// 弹出层标题
title: "",
// 是否显示弹出层
open: false,
// 查询参数
queryParams: {
pageNum: 1,
pageSize: 10,
keyWord:undefined,
taskStatus:undefined,
},
impUnitOptions: [], // 分公司下拉
departOptions: [], // 项目部下拉
proOptions: [], // 工程下拉
materialNameList: [],
materialModelList: [],
openOutReport: false,
checkDataInfo: {},
printTableData: [],
uploadOpen: false,
rowObj: {},
};
},
created() {
this.getList();
this.getImpUnitOptions()
this.handleImpUnitChange()
this.handleDepartChange()
this.getDeviceType()
},
methods: {
/** 获取分公司下拉 */
async getImpUnitOptions() {
try {
const res = await getImpUnitListApi() // 调后台接口
this.impUnitOptions = res.data.map(item => ({
label: item.impUnitName, // 这里根据实际字段替换
value: item.impUnitName
}))
} catch (e) {
console.error('获取分公司下拉失败:', e)
}
},
/** 分公司选择变化,加载项目部 */
async handleImpUnitChange() {
this.queryParams.departName = null // 清空项目部已选
this.departOptions = [] // 清空原有下拉
try {
const params = {
impUnitName: this.queryParams.impUnitName, // 分公司名称
departName: this.queryParams.departName, // 项目部名称
proName: this.queryParams.proName,
teamName:this.queryParams.teamName,
subUnitName:this.queryParams.subUnitName,
}
const res = await getDepartListByImpUnitApi(params)
this.departOptions = res.data.map(item => ({
label: item.departName, // 项目部名称字段
value: item.departName
}))
} catch (e) {
console.error('获取项目部下拉失败:', e)
}
},
/** 项目部选择变化,加载工程 */
async handleDepartChange() {
this.queryParams.proName = null // 清空工程已选
this.proOptions = [] // 清空原有下拉
try {
// 同时传入分公司和项目部参数
const params = {
impUnitName: this.queryParams.impUnitName, // 分公司名称
departName: this.queryParams.departName, // 项目部名称
proName: this.queryParams.proName,
teamName:this.queryParams.teamName,
subUnitName:this.queryParams.subUnitName,
}
const res = await getProListByDepartApi(params)
this.proOptions = res.data.map(item => ({
label: item.proName, // 工程名称字段
value: item.proName
}))
} catch (e) {
console.error('获取工程下拉失败:', e)
}
},
getDeviceType() {
getDeviceType({ level: 3, skipPermission: 1 }).then(response => {
let matNameRes = response.data
this.materialNameList = matNameRes.map((item) => {
return {
label: item.typeName,
value: item.typeId
}
})
})
getDeviceType({ level: 4, skipPermission: 1 }).then(response => {
let matModelRes = response.data
this.materialModelList = matModelRes.map((item) => {
return {
label: item.typeName,
value: item.typeId
}
})
})
},
// change设备类型
handleMaModel(e) {
this.queryParams.typeModelName=null
this.materialModelList = []
let typeId = null
if (!e) {
typeId = null
} else {
typeId = this.materialNameList.find(item => item.label == e).value
}
console.log('🚀 ~ handleMaModel ~ typeId:', typeId)
getDeviceType({ level: 4, skipPermission: 1, typeId }).then(response => {
let matModelRes = response.data
this.materialModelList = matModelRes.map((item) => {
return {
label: item.typeName,
value: item.typeId
}
})
})
},
/** 查询列表 */
getList() {
this.loading = true;
if(this.dateRange.length>0){
this.queryParams.startTime=this.dateRange[0]
this.queryParams.endTime=this.dateRange[1]
}else{
this.queryParams.startTime=undefined
this.queryParams.endTime=undefined
}
this.queryParams.taskStage = 3
getReportList(this.queryParams).then(response => {
this.tableList = response.data.rows;
this.total = response.data.total;
this.loading = false;
});
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.dateRange=[]
this.queryParams.keyWord=null;
this.handleQuery();
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1;
this.getList();
},
// 多选框选中数据
handleSelectionChange(selection) {
this.ids = selection.map(item => item.id)
this.infos = selection.map(item => ({ id: item.id }))
this.single = selection.length != 1
this.multiple = !selection.length
},
//查看
handleView(row){
console.log(row)
let query = { Id:row.id,taskId: row.taskId,isView:"true" }
this.$tab.closeOpenPage({
path: '/part/partAcceptDetail',
query,
})
},
// 文件查看逻辑
handleFileView(url) {
if (!url) return;
// 获取文件后缀名(忽略大小写)
const fileExt = url.split('.').pop().toLowerCase();
// 定义不同类型的文件后缀
const imgExts = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp'];
const pdfExts = ['pdf'];
const docExts = ['doc', 'docx', 'xls', 'xlsx'];
if (imgExts.includes(fileExt)) {
// 图片预览
this.$viewerApi ? this.$viewerApi({ images: [url] }) : window.open(url);
} else if (pdfExts.includes(fileExt)) {
// PDF 预览
window.open(url);
} else if (docExts.includes(fileExt)) {
// Word、Excel 文件 → 下载
this.downloadFile(url);
} else {
// 其他文件,默认下载
this.downloadFile(url);
}
},
// 文件下载逻辑
downloadFile(url) {
const link = document.createElement('a');
link.href = url;
const fileName = url.substring(url.lastIndexOf('/') + 1);
link.setAttribute('download', fileName);
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
},
//查看验收单
async handleReport(row) {
this.checkDataInfo = {
leaseProject:row.proName,
leaseUnit:row.departName
}
this.printTableData = [
{
typeName:row.typeName,
typeModelName:row.typeModelName,
unit:row.unit,
num:row.num,
maCode:row.maCode,
ratedLoad:row.ratedLoad,
testLoad:row.testLoad,
holdingTime:row.holdingTime,
testTime:row.testTime,
nextTestTime:row.nextTestTime,
checkResult:row.checkResult,
remark: ''
}
]
setTimeout(() => {
this.chapter('检验专用章', '安徽送变电工程有限公司机具(物流)分公司')
}, 200)
this.openOutReport = true
this.title = '出库检验单'
},
//出库检验单打印
printCheck() {
// this.$refs.remarksPrintRefCheck.print()
// printJS({
// printable: 'checkId1',
// type: 'html',
// targetStyles: ['*']
// // 其他配置选项
// })
this.$nextTick(() => {
printJS({
printable: 'checkId1',
type: 'html',
style: `
@page { size: A4; margin: 0; }
body { margin: 0.5cm; font-family: SimSun; }
#passId { font-size: 18px; padding: 0 20px; }
`,
scanStyles: false
})
})
},
// 关闭弹窗
closeDialogAndRefresh() {
this.openOutReport = false
},
chapter(text, companyName) {
let canvas = document.getElementById('canvas')
let context = canvas.getContext('2d')
canvas.width = canvas.width
context.height = canvas.height
// // 清除画布内容
// context.clearRect(0, 0, canvas.width, canvas.height);
//let text = "XXX专用章";
//let companyName = "XXX科技股份有限公司";
// 绘制印章边框
let width = canvas.width / 2
let height = canvas.height / 2
context.lineWidth = 3
context.strokeStyle = '#f00'
context.beginPath()
context.arc(width, height, 80, 0, Math.PI * 2) //宽、高、半径
context.stroke()
//画五角星
this.create5star(context, width, height, 20, '#f00', 0)
// 绘制印章名称
context.font = '100 13px 宋体'
context.textBaseline = 'middle' //设置文本的垂直对齐方式
context.textAlign = 'center' //设置文本的水平对对齐方式
context.lineWidth = 1
context.strokeStyle = '#ff2f2f'
context.strokeText(text, width, height + 50)
// 绘制印章单位
context.translate(width, height) // 平移到此位置,
context.font = '100 13px 宋体'
let count = companyName.length // 字数
let angle = (4 * Math.PI) / (3 * (count - 1)) // 字间角度
let chars = companyName.split('')
let c
for (let i = 0; i < count; i++) {
c = chars[i] // 需要绘制的字符
if (i == 0) {
context.rotate((5 * Math.PI) / 6)
} else {
context.rotate(angle)
}
context.save()
context.translate(65, 0) // 平移到此位置,此时字和x轴垂直公司名称和最外圈的距离
context.rotate(Math.PI / 2) // 旋转90度,让字平行于x轴
context.strokeStyle = '#ff5050' // 设置印章单位字体颜色为较浅的红色
context.strokeText(c, 0, 0) // 此点为字的中心点
context.restore()
}
},
//绘制五角星
create5star(context, sx, sy, radius, color, rotato) {
context.save()
context.fillStyle = color
context.translate(sx, sy) //移动坐标原点
context.rotate(Math.PI + rotato) //旋转
context.beginPath() //创建路径
// let x = Math.sin(0);
// let y = Math.cos(0);
let dig = (Math.PI / 5) * 4
for (let i = 0; i < 5; i++) {
//画五角星的五条边
let x = Math.sin(i * dig)
let y = Math.cos(i * dig)
context.lineTo(x * radius, y * radius)
}
context.closePath()
context.stroke()
context.fill()
context.restore()
},
// 二维码查看
handleViewQrCode(row) {
console.log('🚀 ~ handleViewQrCode ~ row:', row)
this.rowObj = row
this.uploadOpen = true
this.qrCode = row.qrCode
let str = 'http://ahjj.jypxks.com:9988/imw/backstage/machine/qrCodePage?qrcode=' + row.qrCode
console.log('🚀 ~ handleViewQrCode ~ str:', str)
this.$nextTick(() => {
this.$refs.codeItem.innerHTML = ''
var qrcode = new QRCode(this.$refs.codeItem, {
text: str, //二维码内容
width: 256,
height: 256,
colorDark: '#000000',
colorLight: '#ffffff',
correctLevel: QRCode.CorrectLevel.H
})
}, 500)
},
downloadQrCode(row) {
const qrContainer = document.createElement('div')
document.body.appendChild(qrContainer)
const qrSize = 512 // 放大二维码
const padding = 20 // 边距也放大
const fontSize = 68 // 放大文字
const maxTextWidth = qrSize
const qrcode = new QRCode(qrContainer, {
text: 'http://ahjj.jypxks.com:9988/imw/backstage/machine/qrCodePage?qrcode=' + row.qrCode,
width: qrSize,
height: qrSize,
colorDark: '#000000',
colorLight: '#ffffff',
correctLevel: QRCode.CorrectLevel.H
})
setTimeout(() => {
const img = qrContainer.querySelector('img') || qrContainer.querySelector('canvas')
const text = row.maCode || ''
// 计算换行
const ctxMeasure = document.createElement('canvas').getContext('2d')
ctxMeasure.font = `${fontSize}px Arial`
const words = text.split('')
let line = ''
const lines = []
for (let i = 0; i < words.length; i++) {
const testLine = line + words[i]
if (ctxMeasure.measureText(testLine).width > maxTextWidth) {
lines.push(line)
line = words[i]
} else {
line = testLine
}
}
lines.push(line)
// 动态计算画布高度
const lineHeight = fontSize + 10
const qrCanvas = document.createElement('canvas')
qrCanvas.width = qrSize + padding * 2
qrCanvas.height = qrSize + padding * 2 + lines.length * lineHeight
const ctx = qrCanvas.getContext('2d')
ctx.fillStyle = '#fff'
ctx.fillRect(0, 0, qrCanvas.width, qrCanvas.height)
ctx.drawImage(img, padding, padding, qrSize, qrSize)
// 绘制文字
ctx.fillStyle = '#000'
ctx.font = `${fontSize}px Arial`
ctx.textAlign = 'center'
lines.forEach((ln, index) => {
ctx.fillText(ln, qrCanvas.width / 2, qrSize + padding + fontSize / 1.2 + index * lineHeight)
})
// 下载
const a = document.createElement('a')
a.href = qrCanvas.toDataURL('image/png')
a.download = text + '.png'
a.click()
document.body.removeChild(qrContainer)
}, 500)
},
/** 单条下载 */
handleDownload(row) {
// 构建要传给后端的完整数据结构
const payload = {
items: [
{
proName: row.proName || '',
departName: row.departName || '',
typeName: row.typeName || '',
typeModelName: row.typeModelName || '',
qualifiedUrl: row.qualifiedUrl || null,
testReportUrl: row.testReportUrl || null,
thirdReportUrl: row.thirdReportUrl || null,
factoryReportUrl: row.factoryReportUrl || null,
otherReportUrl: row.otherReportUrl || null,
unit:row.unit || '',
num:row.num || '',
maCode:row.maCode || '',
ratedLoad:row.ratedLoad || '',
testLoad:row.testLoad || '',
holdingTime:row.holdingTime || '',
testTime:row.testTime || '',
nextTestTime:row.nextTestTime || '',
checkResult:row.checkResult || '',
remark: ''
},
],
zipName: row.proName || 'report',
};
// 访问后端接口(封装调用)
downloadFileApi(payload)
.then((res) => {
downloadFile({
fileName: (row.proName || '文件档案下载') + '.zip',
fileData: res,
fileType: 'application/zip',
});
})
.catch((err) => {
console.error('单条下载失败:', err);
this.$message.error('下载失败');
});
},
/**
* 一键下载(多选)
*/
async handleBulkDownload() {
const grouped = {};
try {
if (!this.infos || !this.infos.length) {
this.$message.warning('请先勾选要下载的行');
return;
}
// 构建 items
const items = this.infos.map((x) => {
const r = this.tableList.find(t => t.id === x.id) || {};
return {
proName: r.proName || '',
departName: r.departName || '',
typeName: r.typeName || '',
typeModelName: r.typeModelName || '',
qualifiedUrl: r.qualifiedUrl || null,
testReportUrl: r.testReportUrl || null,
thirdReportUrl: r.thirdReportUrl || null,
factoryReportUrl: r.factoryReportUrl || null,
otherReportUrl: r.otherReportUrl || null,
unit: r.unit || '',
num: r.num || '',
maCode: r.maCode || '',
ratedLoad: r.ratedLoad || '',
testLoad: r.testLoad || '',
holdingTime: r.holdingTime || '',
testTime: r.testTime || '',
nextTestTime: r.nextTestTime || '',
checkResult: r.checkResult || '',
remark: ''
};
});
// 按工程 -> 领用日期 -> 类型-规格分组
items.forEach(item => {
const proName = item.proName || "";
const leaseDate = item.testTime || "";
const typeFolder = `${item.typeName || ""}-${item.typeModelName || ""}`;
if (!grouped[proName]) grouped[proName] = {};
if (!grouped[proName][leaseDate]) grouped[proName][leaseDate] = {};
if (!grouped[proName][leaseDate][typeFolder]) grouped[proName][leaseDate][typeFolder] = [];
grouped[proName][leaseDate][typeFolder].push(item);
});
// 拉平成后端需要的数组
const flatItems = [];
Object.keys(grouped).forEach(proName => {
Object.keys(grouped[proName]).forEach(leaseDate => {
Object.keys(grouped[proName][leaseDate]).forEach(typeFolder => {
grouped[proName][leaseDate][typeFolder].forEach(item => {
// 可以把 leaseDate 覆盖到 item.testTime这样后端就可以直接使用
flatItems.push({ ...item, testTime: leaseDate });
});
});
});
});
const payload = {
items: flatItems,
zipName: `报告下载_${(new Date()).toISOString().slice(0,10)}`,
};
// 调用统一的封装接口
const res = await batchDownloadFileApi(payload);
// 前端生成下载
downloadFile({
fileName: payload.zipName + '.zip',
fileData: res,
fileType: 'application/zip'
});
} catch (err) {
console.error('一键下载失败', err);
this.$message.error('一键下载失败');
}
}
}
};
</script>
<style lang="scss" scoped>
.uploadImg {
padding-top: 20px;
display: flex;
align-items: center;
justify-content: center;
}
.deviceCode {
margin-top: 10px;
padding-bottom: 20px;
font-size: 18px;
}
::v-deep.el-table .fixed-width .el-button--mini {
width: 60px !important;
margin-bottom: 10px;
}
//隐藏图片上传框的css
::v-deep.disabled {
.el-upload--picture-card {
display: none;
}
}
</style>