|
|
@@ -238,7 +238,7 @@
|
|
|
</template>
|
|
|
</el-table-column>
|
|
|
</el-table>
|
|
|
-
|
|
|
+
|
|
|
<pagination
|
|
|
v-show="total>0"
|
|
|
:total="total"
|
|
|
@@ -549,14 +549,14 @@
|
|
|
</div>
|
|
|
</el-dialog>
|
|
|
|
|
|
-
|
|
|
+
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
<script>
|
|
|
import { listBatch, getBatch, delBatch, addBatch, updateBatch } from "@/api/base/batch"
|
|
|
import QRCode from 'qrcode'
|
|
|
-import { listReport, getReport, addReport, updateReport } from "@/api/base/report"
|
|
|
+import { listReport, getReport, addReport, updateReport,batchAddReport,delReport } from "@/api/base/report"
|
|
|
import { listCertificate, getCertificate, addCertificate, updateCertificate } from "@/api/base/certificate"
|
|
|
|
|
|
|
|
|
@@ -853,7 +853,7 @@ export default {
|
|
|
try {
|
|
|
// 动态导入 html2canvas
|
|
|
const html2canvas = (await import('html2canvas')).default
|
|
|
-
|
|
|
+
|
|
|
// 将整个卡片转换为图片
|
|
|
const canvas = await html2canvas(cardElement, {
|
|
|
backgroundColor: '#ffffff',
|
|
|
@@ -861,14 +861,14 @@ export default {
|
|
|
useCORS: true,
|
|
|
logging: false
|
|
|
})
|
|
|
-
|
|
|
+
|
|
|
// 转为图片并下载
|
|
|
const imgUrl = canvas.toDataURL('image/png')
|
|
|
const link = document.createElement('a')
|
|
|
link.download = `溯源卡片_${this.qrData.batchNo}.png`
|
|
|
link.href = imgUrl
|
|
|
link.click()
|
|
|
-
|
|
|
+
|
|
|
this.$message.success('下载成功')
|
|
|
} catch (error) {
|
|
|
console.error('下载失败:', error)
|
|
|
@@ -901,7 +901,7 @@ export default {
|
|
|
certStatus: cert.certStatus,
|
|
|
certFiles: []
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
// 解析 certFiles 字段(JSON 数组)
|
|
|
if (cert.certFiles) {
|
|
|
try {
|
|
|
@@ -927,7 +927,7 @@ export default {
|
|
|
this.certForm.certFiles = []
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
this.certTitle = "编辑合格证"
|
|
|
}
|
|
|
})
|
|
|
@@ -952,7 +952,7 @@ export default {
|
|
|
}
|
|
|
this.resetForm("certForm")
|
|
|
},
|
|
|
- /** 提交合格证表单 */
|
|
|
+
|
|
|
/** 提交合格证表单 */
|
|
|
submitCertForm() {
|
|
|
this.$refs["certForm"].validate(valid => {
|
|
|
@@ -1060,8 +1060,9 @@ export default {
|
|
|
})
|
|
|
},
|
|
|
|
|
|
+
|
|
|
/** 打开上传检测报告弹窗 */
|
|
|
- handleUploadReport(row) {
|
|
|
+ handleUploadReport(row) {
|
|
|
this.resetReport()
|
|
|
this.reportForm.batchId = row.id
|
|
|
this.reportForm.batchNo = row.batchNo
|
|
|
@@ -1070,12 +1071,60 @@ export default {
|
|
|
this.reportVisible = true
|
|
|
this.reportTitle = "上传检测报告"
|
|
|
|
|
|
- // TODO: 查询该批次是否已有检测报告数据
|
|
|
- // listReport({ batchId: row.id }).then(response => {
|
|
|
- // if (response.rows && response.rows.length > 0) {
|
|
|
- // // 已有检测报告,设置为编辑模式
|
|
|
- // }
|
|
|
- // })
|
|
|
+ // 查询该批次是否已有检测报告数据
|
|
|
+ listReport({ batchId: row.id }).then(response => {
|
|
|
+ if (response.rows && response.rows.length > 0) {
|
|
|
+ // 已有检测报告,设置为编辑模式
|
|
|
+ this.reportTitle = "编辑检测报告"
|
|
|
+
|
|
|
+ // 将已有数据填充到 reportItems 中
|
|
|
+ const reportItems = []
|
|
|
+ response.rows.forEach(report => {
|
|
|
+ // 解析 reportFiles 字段(注意:后端返回的是 reportFiles,不是 reportFile)
|
|
|
+ let reportFileValue = []
|
|
|
+
|
|
|
+ // 使用 reportFiles 字段(从响应数据看,字段名是 reportFiles)
|
|
|
+ const fileUrl = report.reportFiles
|
|
|
+
|
|
|
+ if (fileUrl) {
|
|
|
+ try {
|
|
|
+ // 尝试解析为 JSON 数组
|
|
|
+ const parsed = JSON.parse(fileUrl)
|
|
|
+ if (Array.isArray(parsed)) {
|
|
|
+ // 如果是 JSON 数组
|
|
|
+ reportFileValue = parsed.map(file => {
|
|
|
+ return typeof file === 'string'
|
|
|
+ ? { url: file, name: file.split('/').pop() || file }
|
|
|
+ : file
|
|
|
+ })
|
|
|
+ } else {
|
|
|
+ // 单个对象
|
|
|
+ const url = typeof parsed === 'string' ? parsed : parsed.url
|
|
|
+ reportFileValue = [{
|
|
|
+ url: url,
|
|
|
+ name: url.split('/').pop() || url
|
|
|
+ }]
|
|
|
+ }
|
|
|
+ } catch (e) {
|
|
|
+ // 不是 JSON,直接当作 URL 字符串处理
|
|
|
+ reportFileValue = [{
|
|
|
+ url: fileUrl,
|
|
|
+ name: fileUrl.split('/').pop() || fileUrl
|
|
|
+ }]
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ reportItems.push({
|
|
|
+ id: report.id,
|
|
|
+ reportNo: report.reportNo,
|
|
|
+ reportDate: report.reportDate,
|
|
|
+ reportFile: reportFileValue
|
|
|
+ })
|
|
|
+ })
|
|
|
+
|
|
|
+ this.reportForm.reportItems = reportItems
|
|
|
+ }
|
|
|
+ })
|
|
|
},
|
|
|
|
|
|
/** 取消检测报告 */
|
|
|
@@ -1111,25 +1160,68 @@ export default {
|
|
|
})
|
|
|
},
|
|
|
|
|
|
+
|
|
|
/** 删除一条检测报告 */
|
|
|
removeReportItem(index) {
|
|
|
- if (this.reportForm.reportItems.length === 1) {
|
|
|
- this.$message.warning("至少保留一条检测报告")
|
|
|
- return
|
|
|
+ const item = this.reportForm.reportItems[index]
|
|
|
+
|
|
|
+ // 如果是已有数据(有 id),需要调用后端删除接口
|
|
|
+ if (item.id) {
|
|
|
+ this.$modal.confirm(`是否确认删除第 ${index + 1} 条检测报告?删除后不可恢复!`).then(() => {
|
|
|
+ const loading = this.$modal.loading("正在删除检测报告,请稍候...")
|
|
|
+
|
|
|
+ delReport(item.id)
|
|
|
+ .then(response => {
|
|
|
+ this.$modal.closeLoading()
|
|
|
+ this.$modal.msgSuccess("删除成功")
|
|
|
+ // 从数组中移除
|
|
|
+ this.reportForm.reportItems.splice(index, 1)
|
|
|
+
|
|
|
+ // 如果删除后数组为空,添加一个空项
|
|
|
+ if (this.reportForm.reportItems.length === 0) {
|
|
|
+ this.addReportItem()
|
|
|
+ }
|
|
|
+ })
|
|
|
+ .catch(error => {
|
|
|
+ this.$modal.closeLoading()
|
|
|
+ console.error('删除失败:', error)
|
|
|
+ this.$message.error("删除失败,请重试")
|
|
|
+ })
|
|
|
+ }).catch(() => {
|
|
|
+ // 用户取消删除
|
|
|
+ })
|
|
|
+ } else {
|
|
|
+ // 如果是新增数据(没有 id),直接从前端移除
|
|
|
+ this.$modal.confirm(`是否确认删除第 ${index + 1} 条检测报告?`).then(() => {
|
|
|
+ this.reportForm.reportItems.splice(index, 1)
|
|
|
+
|
|
|
+ // 如果删除后数组为空,添加一个空项
|
|
|
+ if (this.reportForm.reportItems.length === 0) {
|
|
|
+ this.addReportItem()
|
|
|
+ }
|
|
|
+
|
|
|
+ this.$modal.msgSuccess("删除成功")
|
|
|
+ }).catch(() => {
|
|
|
+ // 用户取消删除
|
|
|
+ })
|
|
|
}
|
|
|
- this.reportForm.reportItems.splice(index, 1)
|
|
|
},
|
|
|
|
|
|
- /** 提交检测报告表单 */
|
|
|
+ /** 提交检测报告表单 */
|
|
|
submitReportForm() {
|
|
|
// 校验整个报告表单
|
|
|
this.$refs["reportFormRef"].validate(valid => {
|
|
|
if (valid) {
|
|
|
- // 校验每条报告项 - file-upload 组件返回逗号分隔的字符串
|
|
|
+ // 校验每条报告项
|
|
|
const hasEmptyItem = this.reportForm.reportItems.some(item => {
|
|
|
- // 报告编号和检测日期是字符串,直接判断非空
|
|
|
- // reportFile 是上传组件返回的字符串,上传后会有值(如 "url1,url2")
|
|
|
- const isReportFileEmpty = !item.reportFile || item.reportFile.trim() === ""
|
|
|
+ let isReportFileEmpty = true
|
|
|
+
|
|
|
+ if (Array.isArray(item.reportFile)) {
|
|
|
+ isReportFileEmpty = item.reportFile.length === 0
|
|
|
+ } else if (typeof item.reportFile === 'string') {
|
|
|
+ isReportFileEmpty = !item.reportFile || item.reportFile.trim() === ""
|
|
|
+ }
|
|
|
+
|
|
|
return !item.reportNo || !item.reportDate || isReportFileEmpty
|
|
|
})
|
|
|
|
|
|
@@ -1140,44 +1232,141 @@ export default {
|
|
|
|
|
|
// 前端兜底校验:检查文件类型和大小
|
|
|
const allowedTypes = ['jpg', 'jpeg', 'png', 'pdf']
|
|
|
- const maxSize = 2 * 1024 * 1024 // 2MB
|
|
|
|
|
|
for (let i = 0; i < this.reportForm.reportItems.length; i++) {
|
|
|
const item = this.reportForm.reportItems[i]
|
|
|
- const files = item.reportFile
|
|
|
-
|
|
|
- // 如果有上传文件,进行兜底校验
|
|
|
- if (files && files.trim()) {
|
|
|
- const fileUrls = files.split(',')
|
|
|
- for (const fileUrl of fileUrls) {
|
|
|
- const trimmedUrl = fileUrl.trim()
|
|
|
- if (!trimmedUrl) continue
|
|
|
-
|
|
|
- // 获取文件扩展名(小写)
|
|
|
- const ext = trimmedUrl.split('.').pop().toLowerCase()
|
|
|
-
|
|
|
- // 检查文件类型
|
|
|
- if (!allowedTypes.includes(ext)) {
|
|
|
- this.$message.warning(`第 ${i + 1} 条检测报告的文件格式不正确,仅支持 jpg/jpeg/png/pdf 格式`)
|
|
|
- return
|
|
|
- }
|
|
|
+ let fileUrls = []
|
|
|
+
|
|
|
+ if (Array.isArray(item.reportFile)) {
|
|
|
+ fileUrls = item.reportFile.map(file => typeof file === 'string' ? file : file.url)
|
|
|
+ } else if (typeof item.reportFile === 'string') {
|
|
|
+ fileUrls = item.reportFile.split(',').map(url => url.trim())
|
|
|
+ }
|
|
|
|
|
|
- // 注意:这里无法直接获取文件大小进行校验
|
|
|
- // 文件大小由上传组件在上传前进行限制
|
|
|
- // 此处只能通过文件扩展名进行类型兜底校验
|
|
|
+ for (const fileUrl of fileUrls) {
|
|
|
+ const trimmedUrl = fileUrl || ''
|
|
|
+ if (!trimmedUrl) continue
|
|
|
+
|
|
|
+ const ext = trimmedUrl.split('.').pop().toLowerCase()
|
|
|
+ if (!allowedTypes.includes(ext)) {
|
|
|
+ this.$message.warning(`第 ${i + 1} 条检测报告的文件格式不正确,仅支持 jpg/jpeg/png/pdf 格式`)
|
|
|
+ return
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- // 前端校验通过,打印数据
|
|
|
- console.log("检测报告表单数据:", this.reportForm)
|
|
|
- this.$message.success("前端校验通过,待接接口")
|
|
|
+ // 前端校验通过,准备提交数据
|
|
|
+ // 将 reportItems 转换为后端需要的格式
|
|
|
+ const reportItemsData = this.reportForm.reportItems.map(item => {
|
|
|
+ // 处理 reportFile:转为单个 URL(取第一个)
|
|
|
+ let reportFileUrl = ""
|
|
|
+
|
|
|
+ if (Array.isArray(item.reportFile) && item.reportFile.length > 0) {
|
|
|
+ const firstFile = item.reportFile[0]
|
|
|
+ reportFileUrl = typeof firstFile === 'string' ? firstFile : (firstFile.url || "")
|
|
|
+ } else if (typeof item.reportFile === 'string' && item.reportFile.trim()) {
|
|
|
+ const urls = item.reportFile.split(',').map(url => url.trim())
|
|
|
+ reportFileUrl = urls[0] || ""
|
|
|
+ }
|
|
|
+
|
|
|
+ // 构建报告项数据(使用 reportFiles 作为 key)
|
|
|
+ const reportData = {
|
|
|
+ reportNo: item.reportNo,
|
|
|
+ reportDate: item.reportDate,
|
|
|
+ reportFiles: reportFileUrl
|
|
|
+ }
|
|
|
|
|
|
- // TODO: 接接口时的提交逻辑
|
|
|
- // const data = {
|
|
|
- // batchId: this.reportForm.batchId,
|
|
|
- // reportItems: this.reportForm.reportItems
|
|
|
- // }
|
|
|
+ if (item.id) {
|
|
|
+ reportData.id = item.id
|
|
|
+ }
|
|
|
+
|
|
|
+ return reportData
|
|
|
+ })
|
|
|
+
|
|
|
+ // 打印调试信息(查看提交的数据是否正确)
|
|
|
+ console.log('=== 提交检测报告数据 ===')
|
|
|
+ console.log('reportItemsData:', reportItemsData)
|
|
|
+ console.log('当前 reportForm.reportItems:', this.reportForm.reportItems)
|
|
|
+
|
|
|
+ // 判断操作类型
|
|
|
+ const hasExistingItems = this.reportForm.reportItems.some(item => item.id)
|
|
|
+ const hasNewItems = this.reportForm.reportItems.some(item => !item.id)
|
|
|
+
|
|
|
+ const loading = this.$modal.loading("正在保存检测报告,请稍候...")
|
|
|
+
|
|
|
+ // 情况 1:如果所有项都是新增的(没有 id),使用批量新增接口
|
|
|
+ if (hasNewItems && !hasExistingItems) {
|
|
|
+ const batchData = {
|
|
|
+ batchId: this.reportForm.batchId,
|
|
|
+ batchNo: this.reportForm.batchNo,
|
|
|
+ productName: this.reportForm.productName,
|
|
|
+ farmName: this.reportForm.farmName,
|
|
|
+ reportItems: reportItemsData
|
|
|
+ }
|
|
|
+
|
|
|
+ console.log('批量新增数据:', batchData)
|
|
|
+
|
|
|
+ batchAddReport(batchData)
|
|
|
+ .then(response => {
|
|
|
+ this.$modal.closeLoading()
|
|
|
+ this.$modal.msgSuccess("上传成功")
|
|
|
+ this.reportVisible = false
|
|
|
+ this.resetReport()
|
|
|
+ })
|
|
|
+ .catch(error => {
|
|
|
+ this.$modal.closeLoading()
|
|
|
+ console.error('上传失败:', error)
|
|
|
+ this.$message.error("上传失败,请重试")
|
|
|
+ })
|
|
|
+
|
|
|
+ // 情况 2:如果有已有数据(有 id),需要逐条处理(新增或修改)
|
|
|
+ } else {
|
|
|
+ // 分离新增和修改的数据
|
|
|
+ const newItems = reportItemsData.filter(item => !item.id)
|
|
|
+ const updateItems = reportItemsData.filter(item => item.id)
|
|
|
+
|
|
|
+ const promises = []
|
|
|
+
|
|
|
+ // 处理新增数据
|
|
|
+ if (newItems.length > 0) {
|
|
|
+ const newBatchData = {
|
|
|
+ batchId: this.reportForm.batchId,
|
|
|
+ batchNo: this.reportForm.batchNo,
|
|
|
+ productName: this.reportForm.productName,
|
|
|
+ farmName: this.reportForm.farmName,
|
|
|
+ reportItems: newItems
|
|
|
+ }
|
|
|
+ console.log('批量新增数据:', newBatchData)
|
|
|
+ promises.push(batchAddReport(newBatchData))
|
|
|
+ }
|
|
|
+
|
|
|
+ // 处理修改数据(逐条调用更新接口)
|
|
|
+ updateItems.forEach(item => {
|
|
|
+ const updateData = {
|
|
|
+ id: item.id,
|
|
|
+ batchId: this.reportForm.batchId,
|
|
|
+ reportNo: item.reportNo,
|
|
|
+ reportDate: item.reportDate,
|
|
|
+ reportFiles: item.reportFiles
|
|
|
+ }
|
|
|
+ console.log('更新数据:', updateData)
|
|
|
+ promises.push(updateReport(updateData))
|
|
|
+ })
|
|
|
+
|
|
|
+ // 等待所有请求完成
|
|
|
+ Promise.all(promises)
|
|
|
+ .then(() => {
|
|
|
+ this.$modal.closeLoading()
|
|
|
+ this.$modal.msgSuccess("保存成功")
|
|
|
+ this.reportVisible = false
|
|
|
+ this.resetReport()
|
|
|
+ })
|
|
|
+ .catch(error => {
|
|
|
+ this.$modal.closeLoading()
|
|
|
+ console.error('保存失败:', error)
|
|
|
+ this.$message.error("保存失败,请重试")
|
|
|
+ })
|
|
|
+ }
|
|
|
}
|
|
|
})
|
|
|
},
|
|
|
@@ -1485,4 +1674,4 @@ export default {
|
|
|
width: 100%;
|
|
|
border-style: dashed;
|
|
|
}
|
|
|
-</style>
|
|
|
+</style>
|