feat(inspection): 更新检测类型选项并移除湿度字段
- 将"委托试验"改为"委托检验",增加委托检测、实验室间比对、能力验证等检测类型
- 移除所有表单中的湿度相关字段及验证逻辑
- 在报告准备界面添加印章选择功能,支持MA章、报告章、骑缝章的位置选择
- 优化报告结果流程界面,增加报告下载功能和领报告相关信息字段
- 新增印章管理系统,支持印章的增删改查和图片上传功能
| | |
| | | params: query, |
| | | }); |
| | | } |
| | | |
| | | // ä¸è½½æ¹ååçæçæç« æ¥å |
| | | export function downloadStampedReport(id, format) { |
| | | return request({ |
| | | url: "/processReport/downloadStampedReport", |
| | | method: "get", |
| | | params: { id, format }, |
| | | responseType: "blob", |
| | | }); |
| | | } |
| | |
| | | <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;"> |
| | |
| | | <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;"> |
| | |
| | | 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;"> |
| | |
| | | <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> |
| | | |
| | |
| | | doProcessReport, |
| | | addProcessReport, |
| | | pageProcessReport, |
| | | delProcessReport |
| | | delProcessReport, |
| | | downloadStampedReport |
| | | } from '@/api/cnas/process/reportResults.js' |
| | | import { |
| | | selectUserCondition, |
| | |
| | | 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" }, |
| | |
| | | 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", |
| | |
| | | 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 => { |
| | |
| | | message: 'æä½æå!' |
| | | }); |
| | | this.refreshTable() |
| | | }).catch(err => { }) |
| | | }).catch(() => { |
| | | this.addLoading = false |
| | | }) |
| | | } else { |
| | | doProcessReport(this.addInfo).then(res => { |
| | | this.addLoading = false |
| | |
| | | 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: "ç¡®å®", |
| | |
| | | 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: 'å£åº¦æ£éª', value: 'Quarterly inspection', type: ''}, |
| | |
| | | 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" |
| | |
| | | numOptions: [], |
| | | temperatureOptions: [], |
| | | wareTableData: [], |
| | | // æ£æµæ¡ä»¶ææ¨¡æ¿ id åç»ï¼{ [templateId]: { temperature, humidity, detectionTime, detectionPlace, deviceIds } } |
| | | // æ£æµæ¡ä»¶ææ¨¡æ¿ id åç»ï¼{ [templateId]: { temperature, detectionTime, detectionPlace, deviceIds } } |
| | | otherForm: {}, |
| | | deviceOptions: [], |
| | | equipForm: { |
| | |
| | | currentOtherForm() { |
| | | return this.otherForm[this.currentTable] || { |
| | | temperature: null, |
| | | humidity: null, |
| | | detectionTime: null, |
| | | detectionPlace: null, |
| | | deviceIds: [], |
| | |
| | | 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] : [], |
| | |
| | | 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() |
| | |
| | | this.tableList = null; |
| | | this.tableList = [this.tableLists[0]] |
| | | this.currentTable = this.tableLists[0].templateId; |
| | | // è¡¥é½æ¯ä¸ªæ¨¡æ¿çæ¸©åº¦ãæ¹¿åº¦æ§½ä½ |
| | | // è¡¥é½æ¯ä¸ªæ¨¡æ¿çæ¸©åº¦æ§½ä½ |
| | | this.tableLists.forEach(m => this.ensureOtherForm(m.templateId)) |
| | | // å¤ç页é¢åè¡¨æ°æ® |
| | | this.handleTableData() |
| | |
| | | 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() |
| | |
| | | if (!this.otherForm[templateId]) { |
| | | this.$set(this.otherForm, templateId, { |
| | | temperature: null, |
| | | humidity: null, |
| | | detectionTime: null, |
| | | detectionPlace: null, |
| | | deviceIds: [], |
| | |
| | | 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) { |
| | |
| | | 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: "", |
| | |
| | | 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" |
| | |
| | | inspectionItem: 1, |
| | | inspectionItemSubclass: "20(常温)", |
| | | }, |
| | | // æ£æµæ¡ä»¶ä¸æ£éªç»è®ºææ¨¡æ¿ id åç»ï¼{ [templateId]: { temperature, humidity, detectionTime, detectionPlace, deviceIds, insResult } } |
| | | // æ£æµæ¡ä»¶ä¸æ£éªç»è®ºææ¨¡æ¿ id åç»ï¼{ [templateId]: { temperature, detectionTime, detectionPlace, deviceIds, insResult } } |
| | | otherForm: {}, |
| | | equipForm: { |
| | | value0: null, |
| | |
| | | return ( |
| | | this.otherForm[this.currentTable] || { |
| | | temperature: null, |
| | | humidity: null, |
| | | detectionTime: null, |
| | | detectionPlace: null, |
| | | deviceIds: [], |
| | |
| | | (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] : [], |
| | |
| | | if (!this.otherForm[templateId]) { |
| | | this.$set(this.otherForm, templateId, { |
| | | temperature: null, |
| | | humidity: null, |
| | | detectionTime: null, |
| | | detectionPlace: null, |
| | | deviceIds: [], |
| | |
| | | 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}ãçæ£æµå°ç¹`); |
| | |
| | | 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] : [], |
| | |
| | | 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" |
| | |
| | | currentOtherForm() { |
| | | return this.otherForm[this.currentTable] || { |
| | | temperature: null, |
| | | humidity: null, |
| | | detectionTime: null, |
| | | detectionPlace: null, |
| | | deviceIds: [], |
| | |
| | | (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] : [], |
| | |
| | | if (templateId == null || this.otherForm[templateId]) return; |
| | | this.$set(this.otherForm, templateId, { |
| | | temperature: null, |
| | | humidity: null, |
| | | detectionTime: null, |
| | | detectionPlace: null, |
| | | deviceIds: [], |
| | |
| | | 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}ãçæ£æµå°ç¹`); |
| | |
| | | @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> |
| | |
| | | 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', |
| | |
| | | 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, |
| | |
| | | {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: 'å£åº¦æ£éª', value: 'Quarterly inspection' }, |
| | |
| | | // æ¹åæé® |
| | | 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 === '') { |
| | |
| | | }, |
| | | // æ¹åéè¿ |
| | | 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('å·²æ¹å') |
| | |
| | | }).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() |
| | |
| | | ratifyReport({ |
| | | id: this.currentInfo.id, |
| | | isRatify: 0, |
| | | examineTell: this.reason |
| | | ratifyTell: this.reason |
| | | }).then(res => { |
| | | this.loadingApproveReason = false |
| | | this.$message.success('æä½æå') |
| | |
| | | this.approveReasonVisible = false; |
| | | }).catch(e => { |
| | | this.$message.error('æä½å¤±è´¥') |
| | | this.loadingIssuedReason = false; |
| | | this.loadingApproveReason = false; |
| | | }) |
| | | } |
| | | } |
| | |
| | | .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> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <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> |