From a6fdbed4f31530c080b591d78c141e561db78967 Mon Sep 17 00:00:00 2001
From: liding <756868258@qq.com>
Date: 星期一, 28 九月 2026 09:35:01 +0800
Subject: [PATCH] feat(inspection): 更新检测类型选项并移除湿度字段 - 将"委托试验"改为"委托检验",增加委托检测、实验室间比对、能力验证等检测类型 - 移除所有表单中的湿度相关字段及验证逻辑 - 在报告准备界面添加印章选择功能,支持MA章、报告章、骑缝章的位置选择 - 优化报告结果流程界面,增加报告下载功能和领报告相关信息字段 - 新增印章管理系统,支持印章的增删改查和图片上传功能
---
src/views/business/inspectionTask/components/InspectionWord.vue | 17 -
src/views/structural/sealManage/index.vue | 314 ++++++++++++++++++++++++++
src/views/business/inspectionReview/index.vue | 5
src/views/business/reportPreparation/index.vue | 173 ++++++++++++++
src/api/cnas/process/reportResults.js | 10
src/views/CNAS/process/reportResults/index.vue | 130 ++++++++--
src/views/business/inspectionView/index.vue | 9
src/views/business/inspectionTask/inspection.vue | 12
src/views/business/inspectionTask/index.vue | 20 +
9 files changed, 623 insertions(+), 67 deletions(-)
diff --git a/src/api/cnas/process/reportResults.js b/src/api/cnas/process/reportResults.js
index 1b1011f..4902f6d 100644
--- a/src/api/cnas/process/reportResults.js
+++ b/src/api/cnas/process/reportResults.js
@@ -46,3 +46,13 @@
params: query,
});
}
+
+// 涓嬭浇鎵瑰噯鍚庣敓鎴愮殑鏈夌珷鎶ュ憡
+export function downloadStampedReport(id, format) {
+ return request({
+ url: "/processReport/downloadStampedReport",
+ method: "get",
+ params: { id, format },
+ responseType: "blob",
+ });
+}
diff --git a/src/views/CNAS/process/reportResults/index.vue b/src/views/CNAS/process/reportResults/index.vue
index 88b1c7c..1752de6 100644
--- a/src/views/CNAS/process/reportResults/index.vue
+++ b/src/views/CNAS/process/reportResults/index.vue
@@ -21,27 +21,13 @@
<lims-table :tableData="tableData" :column="column" :tableLoading="tableLoading" :height="'calc(100vh - 270px)'"
:page="page" @pagination="pagination"></lims-table>
</div>
- <el-dialog :title="title" :visible.sync="addDialogVisible" width="400px" top="6vh">
+ <el-dialog :title="title" :visible.sync="addDialogVisible" width="520px" top="6vh" :close-on-click-modal="false">
<el-row>
<el-col :span="24" style="margin-bottom: 16px;">
<div class="search_thing">
<div class="search_label">妫�楠屾姤鍛婄紪鍙凤細</div>
<div class="search_input"><el-input size="small" placeholder="璇疯緭鍏�" clearable
v-model="addInfo.insReportCode"></el-input></div>
- </div>
- </el-col>
- <el-col :span="24" style="margin-bottom: 16px;">
- <div class="search_thing">
- <div class="search_label">椤垫暟锛�</div>
- <div class="search_input"><el-input size="small" placeholder="璇疯緭鍏�" clearable
- v-model="addInfo.pages"></el-input></div>
- </div>
- </el-col>
- <el-col :span="24" style="margin-bottom: 16px;">
- <div class="search_thing">
- <div class="search_label">鍙戦�佷唤鏁帮細</div>
- <div class="search_input"><el-input size="small" placeholder="璇疯緭鍏�" clearable
- v-model="addInfo.number"></el-input></div>
</div>
</el-col>
<el-col :span="24" style="margin-bottom: 16px;">
@@ -55,7 +41,7 @@
<div class="search_thing">
<div class="search_label">鍙戦�佹柟寮忥細</div>
<div class="search_input"><el-input size="small" placeholder="璇疯緭鍏�" clearable
- v-model="addInfo.method"></el-input></div>
+ v-model="addInfo.method" :disabled="!!addInfo.inspectionOrderId"></el-input></div>
</div>
</el-col>
<el-col :span="24" style="margin-bottom: 16px;">
@@ -66,6 +52,23 @@
value-format="yyyy-MM-dd" style="width: 100%;">
</el-date-picker>
</div>
+ </div>
+ </el-col>
+ <el-col :span="24" style="margin-bottom: 16px;">
+ <div class="search_thing">
+ <div class="search_label">棰嗘姤鍛婃棩鏈燂細</div>
+ <div class="search_input">
+ <el-date-picker v-model="addInfo.receiveDate" type="date" size="small" placeholder="閫夋嫨鏃ユ湡"
+ format="yyyy-MM-dd" value-format="yyyy-MM-dd" style="width: 100%;">
+ </el-date-picker>
+ </div>
+ </div>
+ </el-col>
+ <el-col :span="24" style="margin-bottom: 16px;">
+ <div class="search_thing">
+ <div class="search_label">棰嗘姤鍛婁汉锛�</div>
+ <div class="search_input"><el-input size="small" placeholder="璇疯緭鍏�" clearable
+ v-model="addInfo.receiveUser"></el-input></div>
</div>
</el-col>
<el-col :span="24" style="margin-bottom: 16px;">
@@ -99,6 +102,22 @@
<el-button type="primary" @click="handleAdd" :loading="addLoading">纭� 瀹�</el-button>
</span>
</el-dialog>
+ <el-dialog title="涓嬭浇鏈夌珷鎶ュ憡" :visible.sync="downloadDialogVisible" width="420px"
+ :close-on-click-modal="false" @closed="closeDownloadDialog">
+ <el-form label-width="90px">
+ <el-form-item label="鏂囦欢鏍煎紡锛�">
+ <el-radio-group v-model="selectedDownloadFormat">
+ <el-radio label="pdf">PDF锛堝惈楠戠紳绔狅級</el-radio>
+ <el-radio label="word">Word</el-radio>
+ </el-radio-group>
+ </el-form-item>
+ </el-form>
+ <span slot="footer" class="dialog-footer">
+ <el-button @click="downloadDialogVisible = false">鍙� 娑�</el-button>
+ <el-button type="primary" :loading="downloadingReportId !== null"
+ @click="downloadSelectedReport">涓� 杞�</el-button>
+ </span>
+ </el-dialog>
</div>
</template>
@@ -109,7 +128,8 @@
doProcessReport,
addProcessReport,
pageProcessReport,
- delProcessReport
+ delProcessReport,
+ downloadStampedReport
} from '@/api/cnas/process/reportResults.js'
import {
selectUserCondition,
@@ -129,20 +149,21 @@
addDialogVisible: false,
addInfo: {},
addLoading: false,
+ downloadingReportId: null,
+ downloadDialogVisible: false,
+ downloadRow: null,
+ selectedDownloadFormat: 'pdf',
queryParams: {
insReportCode: ''
},
tableData: [],
column: [
{ label: "妫�楠屾姤鍛婄紪鍙�", prop: "insReportCode" },
- { label: "椤垫暟", prop: "pages", width: "120px" },
- {
- label: "鍙戦�佷唤鏁�",
- prop: "number",
- },
{ label: "鍙戝線浣曞", prop: "send" },
{ label: "鍙戦�佹柟寮�", prop: "method" },
{ label: "鍙戦�佹棩鏈�", prop: "sendTime" },
+ { label: "棰嗘姤鍛婃棩鏈�", prop: "receiveDate", width: "120px" },
+ { label: "棰嗘姤鍛婁汉", prop: "receiveUser", width: "110px" },
{ label: "鍙戦�佷汉", prop: "sendUserName" },
{ label: "绛炬敹浜�", prop: "signatory" },
{ label: "澶囨敞", prop: "remark" },
@@ -150,7 +171,16 @@
dataType: "action",
fixed: "right",
label: "鎿嶄綔",
+ width: "180px",
operation: [
+ {
+ name: "涓嬭浇",
+ type: "text",
+ disabled: (row) => this.downloadingReportId === row.id,
+ clickFun: (row) => {
+ this.handleDownload(row);
+ },
+ },
{
name: "缂栬緫",
type: "text",
@@ -242,7 +272,7 @@
delete this.addInfo.createTime
delete this.addInfo.createUser
delete this.addInfo.updateTime
- delete this.addInfo.updateUserer
+ delete this.addInfo.updateUser
delete this.addInfo.sendUserName
if (this.title == '鏂板') {
addProcessReport(this.addInfo).then(res => {
@@ -253,7 +283,9 @@
message: '鎿嶄綔鎴愬姛!'
});
this.refreshTable()
- }).catch(err => { })
+ }).catch(() => {
+ this.addLoading = false
+ })
} else {
doProcessReport(this.addInfo).then(res => {
this.addLoading = false
@@ -263,15 +295,59 @@
message: '鎿嶄綔鎴愬姛!'
});
this.refreshTable()
- }).catch(err => { })
+ }).catch(() => {
+ this.addLoading = false
+ })
}
},
handleWork(row) {
this.title = '淇敼'
- this.addInfo = row
- console.log(this.addInfo)
+ this.addInfo = { ...row }
this.addDialogVisible = true;
},
+ handleDownload(row) {
+ this.downloadRow = row
+ this.selectedDownloadFormat = 'pdf'
+ this.downloadDialogVisible = true
+ },
+ closeDownloadDialog() {
+ if (this.downloadingReportId === null) {
+ this.downloadRow = null
+ this.selectedDownloadFormat = 'pdf'
+ }
+ },
+ downloadSelectedReport() {
+ if (!this.downloadRow) {
+ return
+ }
+ const row = this.downloadRow
+ const format = this.selectedDownloadFormat
+ this.downloadingReportId = row.id
+ downloadStampedReport(row.id, format).then(res => {
+ if (res.type && res.type.includes('application/json')) {
+ const reader = new FileReader()
+ reader.onload = () => {
+ try {
+ this.$message.error(JSON.parse(reader.result).msg || '鏈夌珷鎶ュ憡涓嬭浇澶辫触')
+ } catch (e) {
+ this.$message.error('鏈夌珷鎶ュ憡涓嬭浇澶辫触')
+ }
+ }
+ reader.readAsText(res)
+ return
+ }
+ const extension = format === 'word' ? 'docx' : 'pdf'
+ this.$download.saveAs(res, `${row.insReportCode || '鏈夌珷鎶ュ憡'}.${extension}`)
+ this.downloadDialogVisible = false
+ }).catch(() => {
+ this.$message.error('鏈夌珷鎶ュ憡涓嬭浇澶辫触')
+ }).finally(() => {
+ this.downloadingReportId = null
+ if (!this.downloadDialogVisible) {
+ this.downloadRow = null
+ }
+ })
+ },
handleDelete(row) {
this.$confirm("鏄惁鍒犻櫎璇ユ潯鏁版嵁?", "鎻愮ず", {
confirmButtonText: "纭畾",
diff --git a/src/views/business/inspectionReview/index.vue b/src/views/business/inspectionReview/index.vue
index a054dbc..ccc5887 100644
--- a/src/views/business/inspectionReview/index.vue
+++ b/src/views/business/inspectionReview/index.vue
@@ -273,7 +273,10 @@
fullscreen: false,
option: null,
orderTypeList: [
- {label: '濮旀墭璇曢獙', value: 'Customer-ordered test', type: 'success'},
+ {label: '濮旀墭妫�娴�', value: 'Commissioned testing', type: 'success'},
+ {label: '瀹為獙瀹ら棿姣斿', value: 'Inter-laboratory comparison', type: 'warning'},
+ {label: '鑳藉姏楠岃瘉', value: 'Proficiency testing', type: 'primary'},
+ {label: '濮旀墭妫�楠�', value: 'Customer-ordered test', type: 'success'},
{label: '鎶芥', value: '鎶芥', type: 'danger'},
{label: '杩涘巶妫�楠�', value: '杩涘巶妫�楠�', type: 'info'},
{label: '瀛e害妫�楠�', value: 'Quarterly inspection', type: ''},
diff --git a/src/views/business/inspectionTask/components/InspectionWord.vue b/src/views/business/inspectionTask/components/InspectionWord.vue
index d9ff99f..f8dbf8e 100644
--- a/src/views/business/inspectionTask/components/InspectionWord.vue
+++ b/src/views/business/inspectionTask/components/InspectionWord.vue
@@ -33,11 +33,6 @@
style="width: 90px" @change="m => subOtherForm(m, 'temperature')"></el-input>
<span class="template-panel__unit">鈩�</span>
</el-form-item>
- <el-form-item label="婀垮害:">
- <el-input v-model="currentOtherForm.humidity" :disabled="state > 1" placeholder="" size="small"
- style="width: 90px" @change="m => subOtherForm(m, 'humidity')"></el-input>
- <span class="template-panel__unit">%RH</span>
- </el-form-item>
<el-form-item label="妫�娴嬫椂闂�:">
<el-date-picker v-model="currentOtherForm.detectionTime" :disabled="state > 1" type="date"
value-format="yyyy-MM-dd" placeholder="璇烽�夋嫨鏃ユ湡" size="small" style="width: 160px"
@@ -383,7 +378,7 @@
numOptions: [],
temperatureOptions: [],
wareTableData: [],
- // 妫�娴嬫潯浠舵寜妯℃澘 id 鍒嗙粍锛歿 [templateId]: { temperature, humidity, detectionTime, detectionPlace, deviceIds } }
+ // 妫�娴嬫潯浠舵寜妯℃澘 id 鍒嗙粍锛歿 [templateId]: { temperature, detectionTime, detectionPlace, deviceIds } }
otherForm: {},
deviceOptions: [],
equipForm: {
@@ -465,7 +460,6 @@
currentOtherForm() {
return this.otherForm[this.currentTable] || {
temperature: null,
- humidity: null,
detectionTime: null,
detectionPlace: null,
deviceIds: [],
@@ -512,7 +506,6 @@
conditionList.forEach(item => {
this.$set(this.otherForm, item.templateId, {
temperature: item.temperature != null ? item.temperature : null,
- humidity: item.humidity != null ? item.humidity : null,
detectionTime: item.detectionTime != null ? item.detectionTime : null,
detectionPlace: item.detectionPlace != null ? item.detectionPlace : null,
deviceIds: Array.isArray(item.deviceIds) ? [...item.deviceIds] : [],
@@ -577,7 +570,7 @@
if (this.tableLists && this.tableLists.length > 0) {
this.tableList = null;
this.tableList = this.tableLists.filter(m => m.templateId == val1)
- // 妯℃澘闆嗗悎鍙兘鍙樺寲锛岃ˉ榻愭俯搴︺�佹箍搴︽Ы浣�
+ // 妯℃澘闆嗗悎鍙兘鍙樺寲锛岃ˉ榻愭俯搴︽Ы浣�
this.tableLists.forEach(m => this.ensureOtherForm(m.templateId))
// 瀵规ā鏉胯繘琛屽鐞�
this.handleTableData()
@@ -910,7 +903,7 @@
this.tableList = null;
this.tableList = [this.tableLists[0]]
this.currentTable = this.tableLists[0].templateId;
- // 琛ラ綈姣忎釜妯℃澘鐨勬俯搴︺�佹箍搴︽Ы浣�
+ // 琛ラ綈姣忎釜妯℃澘鐨勬俯搴︽Ы浣�
this.tableLists.forEach(m => this.ensureOtherForm(m.templateId))
// 澶勭悊椤甸潰鍒楄〃鏁版嵁
this.handleTableData()
@@ -941,7 +934,7 @@
this.tableList = [this.tableLists[0]]
this.currentTable = this.tableLists[0].templateId;
this.currentSample.insProduct = this.HaveJson(list)
- // 琛ラ綈姣忎釜妯℃澘鐨勬俯搴︺�佹箍搴︽Ы浣�
+ // 琛ラ綈姣忎釜妯℃澘鐨勬俯搴︽Ы浣�
this.tableLists.forEach(m => this.ensureOtherForm(m.templateId))
// 澶勭悊椤甸潰鍒楄〃鏁版嵁
this.handleTableData()
@@ -1428,7 +1421,6 @@
if (!this.otherForm[templateId]) {
this.$set(this.otherForm, templateId, {
temperature: null,
- humidity: null,
detectionTime: null,
detectionPlace: null,
deviceIds: [],
@@ -1462,7 +1454,6 @@
this.tableLists.forEach(m => {
const form = this.otherForm[m.templateId] || {}
if (!form.temperature) missing.push(`璇峰~鍐欍��${m.templateName}銆嶇殑娓╁害`)
- if (!form.humidity) missing.push(`璇峰~鍐欍��${m.templateName}銆嶇殑婀垮害`)
if (!form.detectionTime) missing.push(`璇烽�夋嫨銆�${m.templateName}銆嶇殑妫�娴嬫椂闂碻)
if (!form.detectionPlace) missing.push(`璇峰~鍐欍��${m.templateName}銆嶇殑妫�娴嬪湴鐐筦)
if (!form.deviceIds || form.deviceIds.length === 0) {
diff --git a/src/views/business/inspectionTask/index.vue b/src/views/business/inspectionTask/index.vue
index 5cd38be..68b91cf 100644
--- a/src/views/business/inspectionTask/index.vue
+++ b/src/views/business/inspectionTask/index.vue
@@ -304,11 +304,29 @@
orderTypeList: [
{
value: "Customer-ordered test",
- label: "濮旀墭璇曢獙",
+ label: "濮旀墭妫�楠�",
type: "success",
effect: "plain",
},
{
+ value: "Commissioned testing",
+ label: "濮旀墭妫�娴�",
+ type: "success",
+ effect: "plain",
+ },
+ {
+ value: "Inter-laboratory comparison",
+ label: "瀹為獙瀹ら棿姣斿",
+ type: "warning",
+ effect: "plain",
+ },
+ {
+ value: "Proficiency testing",
+ label: "鑳藉姏楠岃瘉",
+ type: "primary",
+ effect: "plain",
+ },
+ {
value: "鎶芥",
label: "鎶芥",
type: "",
diff --git a/src/views/business/inspectionTask/inspection.vue b/src/views/business/inspectionTask/inspection.vue
index 64d1911..a660bcd 100644
--- a/src/views/business/inspectionTask/inspection.vue
+++ b/src/views/business/inspectionTask/inspection.vue
@@ -184,11 +184,6 @@
style="width: 90px" @change="(m) => subOtherForm(m, 'temperature')"></el-input>
<span class="template-panel__unit">鈩�</span>
</el-form-item>
- <el-form-item v-if="insOrder.ifsOrderType!=='02wg'" label="婀垮害:">
- <el-input v-model="currentOtherForm.humidity" :disabled="state > 1" placeholder="" size="small"
- style="width: 90px" @change="(m) => subOtherForm(m, 'humidity')"></el-input>
- <span class="template-panel__unit">%RH</span>
- </el-form-item>
<el-form-item label="妫�娴嬫椂闂�:">
<el-date-picker v-model="currentOtherForm.detectionTime" :disabled="state > 1" type="date"
value-format="yyyy-MM-dd" placeholder="璇烽�夋嫨鏃ユ湡" size="small" style="width: 160px"
@@ -702,7 +697,7 @@
inspectionItem: 1,
inspectionItemSubclass: "20(甯告俯)",
},
- // 妫�娴嬫潯浠朵笌妫�楠岀粨璁烘寜妯℃澘 id 鍒嗙粍锛歿 [templateId]: { temperature, humidity, detectionTime, detectionPlace, deviceIds, insResult } }
+ // 妫�娴嬫潯浠朵笌妫�楠岀粨璁烘寜妯℃澘 id 鍒嗙粍锛歿 [templateId]: { temperature, detectionTime, detectionPlace, deviceIds, insResult } }
otherForm: {},
equipForm: {
value0: null,
@@ -861,7 +856,6 @@
return (
this.otherForm[this.currentTable] || {
temperature: null,
- humidity: null,
detectionTime: null,
detectionPlace: null,
deviceIds: [],
@@ -1152,7 +1146,6 @@
(this.insOrder.templateConditions || []).forEach((item) => {
this.$set(this.otherForm, item.templateId, {
temperature: item.temperature != null ? item.temperature : null,
- humidity: item.humidity != null ? item.humidity : null,
detectionTime: item.detectionTime != null ? item.detectionTime : null,
detectionPlace: item.detectionPlace != null ? item.detectionPlace : null,
deviceIds: Array.isArray(item.deviceIds) ? [...item.deviceIds] : [],
@@ -1669,7 +1662,6 @@
if (!this.otherForm[templateId]) {
this.$set(this.otherForm, templateId, {
temperature: null,
- humidity: null,
detectionTime: null,
detectionPlace: null,
deviceIds: [],
@@ -2265,7 +2257,6 @@
const form = this.otherForm[m.templateId] || {};
if (this.insOrder.ifsOrderType !== "02wg") {
if (!form.temperature) missing.push(`璇峰~鍐欍��${m.templateName}銆嶇殑娓╁害`);
- if (!form.humidity) missing.push(`璇峰~鍐欍��${m.templateName}銆嶇殑婀垮害`);
}
if (!form.detectionTime) missing.push(`璇烽�夋嫨銆�${m.templateName}銆嶇殑妫�娴嬫椂闂碻);
if (!form.detectionPlace) missing.push(`璇峰~鍐欍��${m.templateName}銆嶇殑妫�娴嬪湴鐐筦);
@@ -2393,7 +2384,6 @@
id: this.insOrder.id,
templateId: template.templateId,
temperature: form.temperature == null ? null : form.temperature,
- humidity: form.humidity == null ? null : form.humidity,
detectionTime: form.detectionTime || null,
detectionPlace: form.detectionPlace || null,
deviceIds: Array.isArray(form.deviceIds) ? [...form.deviceIds] : [],
diff --git a/src/views/business/inspectionView/index.vue b/src/views/business/inspectionView/index.vue
index ca8bc0e..259c9ea 100644
--- a/src/views/business/inspectionView/index.vue
+++ b/src/views/business/inspectionView/index.vue
@@ -144,11 +144,6 @@
style="width: 90px" @change="m => subOtherForm(m, 'temperature')" />
<span class="template-panel__unit">鈩�</span>
</el-form-item>
- <el-form-item v-if="insOrder.ifsOrderType !== '02wg'" label="婀垮害:">
- <el-input v-model="currentOtherForm.humidity" :disabled="state > 1" size="small"
- style="width: 90px" @change="m => subOtherForm(m, 'humidity')" />
- <span class="template-panel__unit">%RH</span>
- </el-form-item>
<el-form-item label="妫�娴嬫椂闂�:">
<el-date-picker v-model="currentOtherForm.detectionTime" :disabled="state > 1" type="date"
value-format="yyyy-MM-dd" placeholder="璇烽�夋嫨鏃ユ湡" size="small" style="width: 160px"
@@ -753,7 +748,6 @@
currentOtherForm() {
return this.otherForm[this.currentTable] || {
temperature: null,
- humidity: null,
detectionTime: null,
detectionPlace: null,
deviceIds: [],
@@ -950,7 +944,6 @@
(this.insOrder.templateConditions || []).forEach((item) => {
this.$set(this.otherForm, item.templateId, {
temperature: item.temperature != null ? item.temperature : null,
- humidity: item.humidity != null ? item.humidity : null,
detectionTime: item.detectionTime != null ? item.detectionTime : null,
detectionPlace: item.detectionPlace != null ? item.detectionPlace : null,
deviceIds: Array.isArray(item.deviceIds) ? [...item.deviceIds] : [],
@@ -1464,7 +1457,6 @@
if (templateId == null || this.otherForm[templateId]) return;
this.$set(this.otherForm, templateId, {
temperature: null,
- humidity: null,
detectionTime: null,
detectionPlace: null,
deviceIds: [],
@@ -2116,7 +2108,6 @@
const form = this.otherForm[item.templateId] || {};
if (this.insOrder.ifsOrderType !== "02wg") {
if (!form.temperature) missing.push(`璇峰~鍐欍��${item.templateName}銆嶇殑娓╁害`);
- if (!form.humidity) missing.push(`璇峰~鍐欍��${item.templateName}銆嶇殑婀垮害`);
}
if (!form.detectionTime) missing.push(`璇烽�夋嫨銆�${item.templateName}銆嶇殑妫�娴嬫椂闂碻);
if (!form.detectionPlace) missing.push(`璇峰~鍐欍��${item.templateName}銆嶇殑妫�娴嬪湴鐐筦);
diff --git a/src/views/business/reportPreparation/index.vue b/src/views/business/reportPreparation/index.vue
index ccce7c9..d7b3314 100644
--- a/src/views/business/reportPreparation/index.vue
+++ b/src/views/business/reportPreparation/index.vue
@@ -130,8 +130,42 @@
@click="fullscreen = true;"></i>
<img v-else alt="" src="@/assets/images/no-full.svg" style="cursor: pointer;" @click="fullscreen = false;">
</div>
- <div v-if="approveVisible" style="height: 70vh;">
+ <div v-if="approveVisible" class="approve-preview">
<onlyoffice ref="onlyoffice" :options="option" style="width: 100%;height: 100%;"/>
+ </div>
+ <div class="seal-select-block" v-loading="sealLoading">
+ <div class="seal-select-title">
+ <span>鎸変綅缃�夋嫨鍗扮珷</span>
+ <span class="seal-select-tip">鑷姩鍖归厤 MA绔� / 1鍙风珷 / 楠戠紳绔狅紝姣忛」鍙崟鐙皟鏁存垨娓呯┖</span>
+ </div>
+ <div v-if="sealOptions.length" class="seal-position-grid">
+ <div v-for="position in sealPositions" :key="position.key" class="seal-position-card">
+ <div class="seal-position-name">{{ position.label }}</div>
+ <div class="seal-position-desc">{{ position.description }}</div>
+ <el-select
+ v-model="selectedSealPositions[position.key]"
+ clearable
+ placeholder="涓嶇洊绔�"
+ style="width: 100%"
+ >
+ <el-option
+ v-for="seal in sealOptions"
+ :key="seal.id"
+ :label="seal.name"
+ :value="Number(seal.id)"
+ />
+ </el-select>
+ <div v-if="getSelectedSeal(position.key)" class="seal-position-preview">
+ <img
+ :src="javaApi + '/img/' + getSelectedSeal(position.key).address"
+ :alt="getSelectedSeal(position.key).name"
+ >
+ <span>{{ getSelectedSeal(position.key).name }}</span>
+ </div>
+ <div v-else class="seal-position-empty">璇ヤ綅缃笉鐩栫珷</div>
+ </div>
+ </div>
+ <el-empty v-if="!sealLoading && sealOptions.length === 0" description="鏆傛棤鍚敤鍗扮珷锛岃鍏堝湪鍗扮珷绠$悊涓淮鎶�" :image-size="54" />
</div>
<span slot="footer" class="dialog-footer">
<el-button :disabled="loadingApprove" @click="approveReasonVisible = true">涓嶆壒鍑�</el-button>
@@ -249,6 +283,7 @@
import {selectUserCondition} from "@/api/business/inspectionTask";
import limsTable from "@/components/Table/lims-table.vue";
import {delFile, downFile, getFileList} from "@/api/business/rawMaterialOrder";
+import {selectEnabledSeals} from "@/api/structural/sealConfig";
export default {
name: 'ReportPreparation',
@@ -276,6 +311,18 @@
issuedReasonVisible: false,
approveVisible: false,
approveReasonVisible: false,
+ sealOptions: [],
+ sealPositions: [
+ { key: 'maSealId', label: 'MA鏍囧織绔�', description: 'PDF 绗�1椤靛乏涓婅' },
+ { key: 'reportSealId', label: '鎶ュ憡绔�', description: 'Word 妯℃澘鍚勯〉 seal1 / seal2 棰勭暀浣嶇疆' },
+ { key: 'seamSealId', label: '楠戠紳绔�', description: 'PDF 姣忛〉鍙充晶涓棿浣嶇疆' }
+ ],
+ selectedSealPositions: {
+ maSealId: null,
+ reportSealId: null,
+ seamSealId: null
+ },
+ sealLoading: false,
fullscreen: false,
loadingApproveReason: false,
loadingApprove: false,
@@ -307,7 +354,10 @@
{label: '鍘熸潗鏂欎笅鍗�', value: 1},
],
orderTypeList: [
- {label: '濮旀墭璇曢獙', value: 'Customer-ordered test'},
+ {label: '濮旀墭妫�娴�', value: 'Commissioned testing'},
+ {label: '瀹為獙瀹ら棿姣斿', value: 'Inter-laboratory comparison'},
+ {label: '鑳藉姏楠岃瘉', value: 'Proficiency testing'},
+ {label: '濮旀墭妫�楠�', value: 'Customer-ordered test'},
{label: '鎶芥', value: '鎶芥'},
// { label: '杩涘巶妫�楠�', value: '杩涘巶妫�楠�' },
// { label: '瀛e害妫�楠�', value: 'Quarterly inspection' },
@@ -986,6 +1036,7 @@
// 鎵瑰噯鎸夐挳
handleApprove(row) {
this.currentInfo = row;
+ this.loadEnabledSeals(row);
let fileName = (row.urlS === null || row.urlS === '') ? row.url : row.urlS
let fileType = "docx"
if (row.tempUrlPdf != null || row.tempUrlPdf === '') {
@@ -1009,10 +1060,16 @@
},
// 鎵瑰噯閫氳繃
subApprove() {
+ const selectedIds = Object.values(this.selectedSealPositions).filter(id => id != null)
+ if (!selectedIds.length) {
+ return this.$message.error('璇疯嚦灏戜负涓�涓綅缃�夋嫨鍗扮珷')
+ }
this.loadingApprove = true;
ratifyReport({
id: this.currentInfo.id,
- isRatify: 1
+ isRatify: 1,
+ ...this.selectedSealPositions,
+ sealIds: [...new Set(selectedIds)].join(',')
}).then(res => {
this.loadingApprove = false;
this.$message.success('宸叉壒鍑�')
@@ -1022,6 +1079,39 @@
}).catch(() => {
this.loadingApprove = false;
})
+ },
+ loadEnabledSeals(row) {
+ this.sealLoading = true
+ this.sealOptions = []
+ this.selectedSealPositions = { maSealId: null, reportSealId: null, seamSealId: null }
+ selectEnabledSeals().then(res => {
+ this.sealOptions = res.data || []
+ const availableIds = this.sealOptions.map(item => Number(item.id))
+ const legacyIds = String((row && row.sealIds) || '')
+ .split(',')
+ .map(item => Number(item.trim()))
+ .filter(id => availableIds.includes(id))
+ const legacyId = legacyIds[0] || null
+ const maSeal = this.sealOptions.find(item => String(item.name).toUpperCase().includes('MA'))
+ const reportSeal = this.sealOptions.find(item => item.name === '1鍙风珷')
+ || this.sealOptions.find(item => !String(item.name).toUpperCase().includes('MA') && !String(item.name).includes('楠戠紳'))
+ || this.sealOptions[0]
+ const seamSeal = this.sealOptions.find(item => String(item.name).includes('楠戠紳'))
+ this.selectedSealPositions = {
+ maSealId: availableIds.includes(Number(row && row.maSealId))
+ ? Number(row.maSealId) : (maSeal ? Number(maSeal.id) : null),
+ reportSealId: availableIds.includes(Number(row && row.reportSealId))
+ ? Number(row.reportSealId) : (legacyId || (reportSeal ? Number(reportSeal.id) : null)),
+ seamSealId: availableIds.includes(Number(row && row.seamSealId))
+ ? Number(row.seamSealId) : (seamSeal ? Number(seamSeal.id) : legacyId)
+ }
+ }).finally(() => {
+ this.sealLoading = false
+ })
+ },
+ getSelectedSeal(positionKey) {
+ const sealId = this.selectedSealPositions[positionKey]
+ return this.sealOptions.find(item => Number(item.id) === Number(sealId))
},
handle(row) {
this.getAuthorizedPerson()
@@ -1080,7 +1170,7 @@
ratifyReport({
id: this.currentInfo.id,
isRatify: 0,
- examineTell: this.reason
+ ratifyTell: this.reason
}).then(res => {
this.loadingApproveReason = false
this.$message.success('鎿嶄綔鎴愬姛')
@@ -1091,7 +1181,7 @@
this.approveReasonVisible = false;
}).catch(e => {
this.$message.error('鎿嶄綔澶辫触')
- this.loadingIssuedReason = false;
+ this.loadingApproveReason = false;
})
}
}
@@ -1156,4 +1246,77 @@
.fullscreen {
height: 82vh
}
+
+.approve-preview {
+ height: 52vh;
+}
+
+.seal-select-block {
+ margin-top: 16px;
+ padding: 14px 16px;
+ border: 1px solid #ebeef5;
+ border-radius: 6px;
+ background: #fafafa;
+}
+
+.seal-select-title {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ margin-bottom: 12px;
+ color: #303133;
+ font-weight: 600;
+}
+
+.seal-select-tip {
+ color: #909399;
+ font-size: 12px;
+ font-weight: normal;
+}
+
+.seal-position-grid {
+ display: grid;
+ grid-template-columns: repeat(3, minmax(0, 1fr));
+ gap: 14px;
+}
+
+.seal-position-card {
+ padding: 14px;
+ border: 1px solid #dcdfe6;
+ border-radius: 6px;
+}
+
+.seal-position-name {
+ color: #303133;
+ font-weight: 600;
+}
+
+.seal-position-desc {
+ margin: 4px 0 10px;
+ color: #909399;
+ font-size: 12px;
+}
+
+.seal-position-preview {
+ display: flex;
+ align-items: center;
+ margin-top: 12px;
+ color: #606266;
+}
+
+.seal-position-preview img {
+ width: 70px;
+ height: 70px;
+ margin-right: 10px;
+ object-fit: contain;
+}
+
+.seal-position-empty {
+ height: 70px;
+ margin-top: 12px;
+ color: #c0c4cc;
+ font-size: 12px;
+ line-height: 70px;
+ text-align: center;
+}
</style>
diff --git a/src/views/structural/sealManage/index.vue b/src/views/structural/sealManage/index.vue
new file mode 100644
index 0000000..e9e3e2b
--- /dev/null
+++ b/src/views/structural/sealManage/index.vue
@@ -0,0 +1,314 @@
+<template>
+ <div class="app-container seal-manage">
+ <div class="search-bar">
+ <el-form ref="queryForm" :inline="true" :model="queryParams" size="small">
+ <el-form-item label="鍗扮珷鍚嶇О" prop="name">
+ <el-input v-model="queryParams.name" clearable placeholder="璇疯緭鍏�" @keyup.enter.native="refreshTable" />
+ </el-form-item>
+ <el-form-item label="鐘舵��" prop="status">
+ <el-select v-model="queryParams.status" clearable placeholder="鍏ㄩ儴">
+ <el-option label="鍚敤" :value="1" />
+ <el-option label="鍋滅敤" :value="0" />
+ </el-select>
+ </el-form-item>
+ <el-form-item>
+ <el-button type="primary" size="mini" @click="refreshTable">鏌ヨ</el-button>
+ <el-button size="mini" @click="resetQuery">閲嶇疆</el-button>
+ </el-form-item>
+ </el-form>
+ <div class="toolbar">
+ <el-button type="primary" size="small" icon="el-icon-plus" @click="openForm()">鏂板</el-button>
+ <el-button type="primary" size="small" icon="el-icon-download" :loading="exportLoading" @click="handleExport">
+ 瀵煎嚭
+ </el-button>
+ </div>
+ </div>
+
+ <lims-table
+ :table-data="tableData"
+ :column="columns"
+ :page="page"
+ :table-loading="tableLoading"
+ height="calc(100vh - 245px)"
+ @pagination="pagination"
+ />
+
+ <el-dialog
+ :title="formTitle"
+ :visible.sync="formVisible"
+ width="620px"
+ :close-on-click-modal="false"
+ @closed="resetSealForm"
+ >
+ <el-form ref="sealForm" :model="form" :rules="rules" label-width="100px">
+ <el-row :gutter="20">
+ <el-col :span="24">
+ <el-form-item label="鍗扮珷鍚嶇О" prop="name">
+ <el-input v-model="form.name" maxlength="64" placeholder="璇疯緭鍏ュ嵃绔犲悕绉�" />
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <el-form-item label="鍗扮珷鍥剧墖" prop="address">
+ <el-upload
+ ref="sealUpload"
+ class="seal-uploader"
+ :action="uploadAction"
+ :headers="uploadHeader"
+ accept="image/jpeg,image/png"
+ :show-file-list="false"
+ :before-upload="beforeUpload"
+ :on-success="handleUploadSuccess"
+ :on-error="handleUploadError"
+ >
+ <img v-if="form.address" :src="sealImageUrl(form.address)" class="seal-preview" alt="鍗扮珷鍥剧墖">
+ <div v-else class="seal-upload-placeholder">
+ <i class="el-icon-upload" />
+ <span>鐐瑰嚮涓婁紶鍗扮珷鍥剧墖</span>
+ </div>
+ </el-upload>
+ <div class="upload-tip">浠呮敮鎸� jpg / png锛屽ぇ灏忎笉瓒呰繃 2MB</div>
+ </el-form-item>
+ <el-form-item label="浣跨敤鑼冨洿" prop="useScope">
+ <el-input v-model="form.useScope" maxlength="255" placeholder="璇疯緭鍏ヤ娇鐢ㄨ寖鍥�" />
+ </el-form-item>
+ <el-form-item label="鐘舵��" prop="status">
+ <el-radio-group v-model="form.status">
+ <el-radio :label="1">鍚敤</el-radio>
+ <el-radio :label="0">鍋滅敤</el-radio>
+ </el-radio-group>
+ </el-form-item>
+ <el-form-item label="澶囨敞" prop="remark">
+ <el-input v-model="form.remark" type="textarea" :rows="3" maxlength="255" show-word-limit />
+ </el-form-item>
+ </el-form>
+ <span slot="footer" class="dialog-footer">
+ <el-button @click="formVisible = false">鍙� 娑�</el-button>
+ <el-button type="primary" :loading="submitLoading" @click="submitForm">纭� 瀹�</el-button>
+ </span>
+ </el-dialog>
+ </div>
+</template>
+
+<script>
+import limsTable from '@/components/Table/lims-table.vue'
+import { addSeal, delSeal, exportSeal, pageSeal, updateSeal } from '@/api/structural/sealConfig'
+
+function emptyForm() {
+ return {
+ id: null,
+ name: '',
+ address: '',
+ useScope: '',
+ status: 1,
+ remark: ''
+ }
+}
+
+export default {
+ name: 'SealManage',
+ components: { limsTable },
+ data() {
+ return {
+ queryParams: {
+ name: '',
+ status: null
+ },
+ page: {
+ total: 0,
+ size: 20,
+ current: 1
+ },
+ tableData: [],
+ tableLoading: false,
+ exportLoading: false,
+ formVisible: false,
+ submitLoading: false,
+ form: emptyForm(),
+ rules: {
+ name: [{ required: true, message: '璇疯緭鍏ュ嵃绔犲悕绉�', trigger: 'blur' }],
+ address: [{ required: true, message: '璇蜂笂浼犲嵃绔犲浘鐗�', trigger: 'change' }]
+ },
+ columns: [
+ { label: '鍗扮珷缂栧彿', prop: 'code', width: '110px' },
+ { label: '鍗扮珷鍚嶇О', prop: 'name', width: '130px' },
+ { label: '鍗扮珷鍥剧墖', prop: 'address', dataType: 'image', width: '100px' },
+ { label: '浣跨敤鑼冨洿', prop: 'useScope' },
+ {
+ label: '鐘舵��',
+ prop: 'status',
+ width: '90px',
+ dataType: 'tag',
+ formatData: value => Number(value) === 1 ? '鍚敤' : '鍋滅敤',
+ formatType: value => Number(value) === 1 ? 'success' : 'info'
+ },
+ { label: '澶囨敞', prop: 'remark' },
+ { label: '鍒涘缓鏃堕棿', prop: 'createTime', width: '165px' },
+ {
+ label: '鎿嶄綔',
+ dataType: 'action',
+ fixed: 'right',
+ width: '140px',
+ operation: [
+ { name: '缂栬緫', type: 'text', clickFun: row => this.openForm(row) },
+ { name: '鍒犻櫎', type: 'text', clickFun: row => this.handleDelete(row) }
+ ]
+ }
+ ]
+ }
+ },
+ computed: {
+ formTitle() {
+ return this.form.id ? '缂栬緫鍗扮珷' : '鏂板鍗扮珷'
+ },
+ uploadAction() {
+ return this.javaApi + '/sealConfig/upload'
+ }
+ },
+ mounted() {
+ this.getList()
+ },
+ methods: {
+ getList() {
+ this.tableLoading = true
+ const params = { ...this.queryParams, current: this.page.current, size: this.page.size }
+ pageSeal(params).then(res => {
+ this.tableData = res.data.records || []
+ this.page.total = res.data.total || 0
+ }).finally(() => {
+ this.tableLoading = false
+ })
+ },
+ refreshTable() {
+ this.page.current = 1
+ this.getList()
+ },
+ resetQuery() {
+ this.resetForm('queryForm')
+ this.page.current = 1
+ this.getList()
+ },
+ pagination({ page, limit }) {
+ this.page.current = page
+ this.page.size = limit
+ this.getList()
+ },
+ openForm(row) {
+ this.form = row ? { ...row } : emptyForm()
+ this.formVisible = true
+ this.$nextTick(() => this.$refs.sealForm && this.$refs.sealForm.clearValidate())
+ },
+ resetSealForm() {
+ this.form = emptyForm()
+ if (this.$refs.sealUpload) this.$refs.sealUpload.clearFiles()
+ if (this.$refs.sealForm) this.$refs.sealForm.clearValidate()
+ },
+ submitForm() {
+ this.$refs.sealForm.validate(valid => {
+ if (!valid) return
+ this.submitLoading = true
+ const request = this.form.id ? updateSeal : addSeal
+ request(this.form).then(() => {
+ this.$message.success(this.form.id ? '淇敼鎴愬姛' : '鏂板鎴愬姛')
+ this.formVisible = false
+ this.getList()
+ }).finally(() => {
+ this.submitLoading = false
+ })
+ })
+ },
+ handleDelete(row) {
+ this.$confirm(`纭鍒犻櫎鍗扮珷鈥�${row.name}鈥濆悧锛焋, '鎻愮ず', {
+ confirmButtonText: '纭畾',
+ cancelButtonText: '鍙栨秷',
+ type: 'warning'
+ }).then(() => delSeal(row.id)).then(() => {
+ this.$message.success('鍒犻櫎鎴愬姛')
+ if (this.tableData.length === 1 && this.page.current > 1) this.page.current -= 1
+ this.getList()
+ }).catch(() => {})
+ },
+ handleExport() {
+ this.exportLoading = true
+ exportSeal(this.queryParams).then(data => {
+ this.$download.saveAs(new Blob([data]), '鍗扮珷绠$悊.xlsx')
+ }).finally(() => {
+ this.exportLoading = false
+ })
+ },
+ beforeUpload(file) {
+ const validType = ['image/jpeg', 'image/png'].includes(file.type)
+ const validSize = file.size / 1024 / 1024 <= 2
+ if (!validType) this.$message.error('浠呮敮鎸� jpg / png 鍥剧墖')
+ if (validType && !validSize) this.$message.error('鍥剧墖澶у皬涓嶈兘瓒呰繃 2MB')
+ return validType && validSize
+ },
+ handleUploadSuccess(response) {
+ if (response.code !== 200) {
+ this.$message.error(response.msg || '涓婁紶澶辫触')
+ return
+ }
+ this.form.address = typeof response.data === 'string' ? response.data : response.data.url
+ this.$nextTick(() => this.$refs.sealForm.validateField('address'))
+ },
+ handleUploadError() {
+ this.$message.error('鍗扮珷鍥剧墖涓婁紶澶辫触')
+ },
+ sealImageUrl(address) {
+ return this.javaApi + '/img/' + address
+ }
+ }
+}
+</script>
+
+<style scoped>
+.search-bar {
+ display: flex;
+ align-items: flex-start;
+ justify-content: space-between;
+}
+
+.toolbar {
+ flex: none;
+ margin-left: 16px;
+}
+
+.seal-uploader ::v-deep .el-upload {
+ overflow: hidden;
+ width: 150px;
+ height: 150px;
+ border: 1px dashed #d9d9d9;
+ border-radius: 6px;
+}
+
+.seal-uploader ::v-deep .el-upload:hover {
+ border-color: #409eff;
+}
+
+.seal-preview {
+ display: block;
+ width: 150px;
+ height: 150px;
+ object-fit: contain;
+}
+
+.seal-upload-placeholder {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ width: 100%;
+ height: 100%;
+ color: #909399;
+}
+
+.seal-upload-placeholder i {
+ margin-bottom: 8px;
+ font-size: 30px;
+}
+
+.upload-tip {
+ color: #909399;
+ font-size: 12px;
+ line-height: 20px;
+}
+</style>
--
Gitblit v1.9.3