From 069ef85293bb4baa74dd9251e8b3fd8cc4355410 Mon Sep 17 00:00:00 2001 From: 董国庆 <364620639@qq.com> Date: 星期四, 15 五月 2025 15:21:10 +0800 Subject: [PATCH] 实验方案管理详情和中止 --- laboratory/src/views/dataManagement/schemeManagement/components/approvalDialog.vue | 738 +++++++++++++++++++++++++------------------------------- 1 files changed, 329 insertions(+), 409 deletions(-) diff --git a/laboratory/src/views/dataManagement/schemeManagement/components/approvalDialog.vue b/laboratory/src/views/dataManagement/schemeManagement/components/approvalDialog.vue index d31d6ae..64b6a8c 100644 --- a/laboratory/src/views/dataManagement/schemeManagement/components/approvalDialog.vue +++ b/laboratory/src/views/dataManagement/schemeManagement/components/approvalDialog.vue @@ -18,25 +18,34 @@ :rules="rules" inline label-position="top" - :disabled="type === 'view'" + > <div class="header-title" style="margin-bottom: 38px"> - <div class="header-title-left"> - <img src="@/assets/public/headercard.png" /> - <div>所属实验调度</div> + <div style="display: flex; align-items: center; gap: 13px"> + <div class="header-title-left"> + <img src="@/assets/public/headercard.png" /> + <div>所属实验调度</div> + </div> + <el-button @click="handleStopExperiment" type="danger"> + 申请终止实验</el-button> </div> </div> - <Table :data="groupTableData" :total="0" :height="null"> - <el-table-column - type="index" - label="序号" - width="80" - ></el-table-column> - <el-table-column - prop="groupName" - label="组别" - ></el-table-column> - <el-table-column prop="remark" label="备注"></el-table-column> + <Table :data="dispatchData" :total="0" :height="null" class="groupTable"> + <el-table-column type="index" label="序号" width="80"></el-table-column> + <el-table-column prop="projectName" label="所属项目课题方案"></el-table-column> + <el-table-column prop="experimentCode" label="实验编号"></el-table-column> + <el-table-column prop="experimentName" label="实验名称"></el-table-column> + <el-table-column prop="experimentDate" label="通知时间"></el-table-column> + <el-table-column prop="experimentStartTime" label="实验开始时间"></el-table-column> + <el-table-column prop="experimentEndTime" label="实验结束时间"></el-table-column> + <el-table-column prop="participantsName" label="参加人员"></el-table-column> + <el-table-column prop="status" label="状态"> + <template slot-scope="scope"> + <el-tag :type="getStatusType(scope.row.status)"> + {{ getStatusText(scope.row.status) }} + </el-tag> + </template> + </el-table-column> </Table> <div class="header-title" style="margin-bottom: 38px"> @@ -46,72 +55,37 @@ </div> </div> - <div class="add-group"> - <span>组别列表</span> - </div> - <Table - :data="groupTableData" - :total="0" - :height="null" - class="groupTable" - > - <el-table-column - type="index" - label="序号" - width="80" - ></el-table-column> - <el-table-column - prop="groupName" - label="组别" - ></el-table-column> - <el-table-column prop="remark" label="备注"></el-table-column> - </Table> + <template v-if="groupData && groupData.length > 0"> + <div class="add-group"> + <span>组别列表</span> + </div> + <Table :data="groupData" :total="0" :height="null" class="groupTable"> + <el-table-column type="index" label="序号" width="80"></el-table-column> + <el-table-column prop="groupName" label="组别"></el-table-column> + <el-table-column prop="remark" label="备注"></el-table-column> + </Table> + </template> - <div style="padding-left: 25px; margin-top: 20px"> - <el-form-item prop="testTime" label="试验时间"> - <el-date-picker - v-model="form.testTime" - type="datetime" - placeholder="选择日期时间" - value-format="yyyy-MM-dd HH:mm:ss" - /> + <div style="padding-left: 25px;margin-top: 28px;"> + <el-form-item prop="experimentDate" label="试验日期"> + <el-date-picker v-model="form.experimentDate" type="datetime" :disabled="true" placeholder="选择日期时间"> + </el-date-picker> </el-form-item> </div> + <div class="add-group"> - <div>*</div> <span>实验人员</span> </div> <div class="member-list"> - <div v-for="item in 3" :key="item" class="member-list-card"> + <div class="member-list-card"> <div class="member-item"> - <div class="member-title"> - {{ ["工艺工程师", "实验员", "化验师"][item - 1] }} - </div> - <div - :class=" - item == 1 || item == 2 || item == 3 - ? 'member-name-box' - : 'flex1' - " - > - <div - :class=" - item == 1 || item == 2 || item == 3 - ? 'member-name-box' - : 'member-name-box-2' - " - > - <div - v-for="i in memberList(item)" - :key="i" - class="member-name" - > - 张三 + <div class="member-title">实验员</div> + <div class="flex"> + <div class="member-name-box-2"> + <div v-for="i in selectedParticipants" :key="i.id" class="member-name"> + {{ i.nickName }} </div> </div> - </div> - <div class="member-change" v-if="type !== 'view'"> - <div class="member-change-btn">修改</div> </div> </div> </div> @@ -123,12 +97,15 @@ <div>一、实验目的</div> </div> </div> - <AiEditor - ref="purposeEditor" - v-model="form.purpose" - height="200px" - placeholder="请输入实验目的..." - /> + <div class="content-box"> + <AiEditor + ref="purposeEditor" + :readOnly="true" + :value="form.experimentObjective" + height="200px" + placeholder="请输入实验目的..." + /> + </div> <div class="header-title" style="margin-bottom: 38px"> <div class="header-title-left"> @@ -136,12 +113,15 @@ <div>二、工艺参数及路线</div> </div> </div> - <AiEditor - ref="processEditor" - v-model="form.process" - height="200px" - placeholder="请输入工艺参数及路线..." - /> + <div class="content-box"> + <AiEditor + ref="processEditor" + :readOnly="true" + :value="form.experimentParamRoute" + height="200px" + placeholder="请输入工艺参数及路线..." + /> + </div> <div class="header-title" style="margin-bottom: 38px"> <div class="header-title-left"> @@ -149,13 +129,19 @@ <div>三、实验材料及设备</div> </div> </div> - <ViewDynamicComponent + <DynamicComponent + ref="materialComponent" title="实验材料" - :components="form.materialsAndEquipment || []" + :dialogCanEdit="false" + :dataSource="form.experimentMaterial" + :editable="false" /> - <ViewDynamicComponent + <DynamicComponent + ref="equipmentComponent" title="实验所用设备" - :components="form.materialsAndEquipment || []" + :dialogCanEdit="false" + :dataSource="form.experimentDevice" + :editable="false" /> <div class="header-title" style="margin-bottom: 38px"> @@ -165,15 +151,17 @@ </div> </div> - <div class="step-list" v-for="(item, idx) in form.operationSteps" :key="idx"> + <div class="step-list" v-for="(item, idx) in stepList" :key="idx"> <div class="step-list-item"> <div class="step-list-item-title"> 步骤{{ idx + 1 }}:{{ item.stepName }} </div> </div> - <ViewDynamicComponent + <DynamicComponent + :dialogCanEdit="false" :ref="'stepContent' + idx" - :components="[item]" + :dataSource="item.content" + :editable="false" /> </div> </el-form> @@ -181,13 +169,10 @@ </Card> </div> <!-- 右侧审批流程 --> - <div class="approval-flow" v-if="type === 'view'"> + <div class="approval-flow" v-if="showApprovalFlow"> <div class="flow-content"> <approval-process - :status="form.status" - :submit-time="form.createTime" - :approver="form.approver" - :approve-time="form.approveTime" + :processData="approvalProcessData" /> </div> </div> @@ -203,15 +188,16 @@ <script> import ApprovalProcess from "@/components/approvalProcess"; import SignatureCanvas from "@/components/SignatureCanvas.vue"; -import ViewDynamicComponent from "@/components/DynamicComponent/ViewDynamicComponent.vue"; -import AiEditor from "@/components/AiEditor/index.vue"; +import DynamicComponent from "@/components/DynamicComponent"; +import AiEditor from "@/components/AiEditor"; +import { getDetail, getGroupByDispatchId } from "../service"; export default { name: "ApprovalDialog", components: { ApprovalProcess, SignatureCanvas, - ViewDynamicComponent, + DynamicComponent, AiEditor, }, props: { @@ -231,187 +217,43 @@ data() { return { form: { - planName: "", - planCode: "", - stage: "", - testDate: "", - testName: "", - testCode: "", - testTime: "", - creator: "", - createTime: "", - approvalComment: "", - status: "approved", - approver: "", - approveTime: "", - materialsAndEquipment: [ - { - id: 1, - type: "richText", - data: { - content: - "<p>1. 实验材料说明</p><p>2. 设备使用说明</p><p>3. 安全注意事项</p>", - }, - }, - { - id: 2, - type: "customTable", - data: { - headers: [ - { name: "材料名称", type: "text" }, - { name: "规格", type: "text" }, - { name: "数量", type: "text" }, - { name: "用途", type: "text" }, - ], - rows: [ - { - 材料名称: "催化剂A", - 规格: "工业级", - 数量: "100g", - 用途: "反应催化剂", - updateTime: "2024-01-01 12:00:00", - }, - { - 材料名称: "溶剂B", - 规格: "分析纯", - 数量: "500ml", - 用途: "反应溶剂", - updateTime: "2024-01-01 12:00:00", - }, - ], - }, - }, - { - id: 3, - type: "fileUpload", - data: { - fileList: [ - { - name: "材料安全说明书.pdf", - url: "https://example.com/msds.pdf", - }, - { - name: "设备操作手册.docx", - url: "https://example.com/manual.docx", - }, - ], - }, - }, - { - id: 4, - type: "imageGallery", - data: { - images: [ - { - url: "https://example.com/equipment1.jpg", - title: "实验设备1", - description: "主要反应设备", - }, - { - url: "https://example.com/equipment2.jpg", - title: "实验设备2", - description: "辅助设备", - }, - ], - }, - }, - ], - operationSteps: [ - { - id: 7, - type: "richText", - data: { - content: - "<p>1. 准备工作</p><p>2. 设备检查</p><p>3. 实验操作</p><p>4. 数据记录</p>", - }, - }, - { - id: 8, - type: "customTable", - data: { - headers: [ - { name: "步骤", type: "text" }, - { name: "操作内容", type: "text" }, - { name: "操作人", type: "user" }, - { name: "操作图片", type: "image" }, - ], - rows: [ - { - 步骤: "步骤1", - 操作内容: "称取催化剂", - 操作人: ["1"], - 操作图片: [{ url: "https://example.com/step1.jpg" }], - updateTime: "2024-01-01 12:00:00", - }, - { - 步骤: "步骤2", - 操作内容: "加入溶剂", - 操作人: ["2"], - 操作图片: [{ url: "https://example.com/step2.jpg" }], - updateTime: "2024-01-01 12:00:00", - }, - ], - }, - }, - ], + projectName: "", // 项目课题方案名称 + projectCode: "", // 项目课题方案编号 + experimentCode: "", // 实验编号 + experimentName: "", // 实验名称 + experimentDate: "", // 实验日期 + experimentMaterial: [], // 实验材料 + experimentDevice: [], // 实验设备 + experimentObjective: "", // 实验目的 + experimentParamRoute: "", // 工艺参数及路线 + createBy: "", // 创建人 + createTime: "", // 创建时间 + status: "", // 状态 + approver: "", // 审批人 + approveTime: "", // 审批时间 }, rules: { - planName: [ - { - required: true, - message: "请输入项目课题方案名称", - trigger: "blur", - }, - ], - planCode: [ - { - required: true, - message: "请输入项目课题方案编号", - trigger: "blur", - }, - ], - stage: [{ required: true, message: "请输入项目阶段", trigger: "blur" }], - testDate: [ - { required: true, message: "请选择试验日期", trigger: "change" }, - ], - testName: [ - { required: true, message: "请输入实验名称", trigger: "blur" }, - ], - testCode: [ - { required: true, message: "请输入实验编号", trigger: "blur" }, - ], testTime: [ { required: true, message: "请选择试验时间", trigger: "change" }, ], }, imgSrc: "", signatureDialogVisible: false, - status: "1", remark: "", - groupTableData: [], - taskTableData: [], + groupData: [], + dispatchData: [], // 实验调度数据 + stepList: [], + selectedParticipants: [], // 实验参与人员 + showApprovalFlow: false, + approvalProcessData: [], }; - }, - computed: { - dialogTitle() { - return this.type === "approve" ? "确认实验调度" : "实验调度详情"; - }, }, watch: { data: { handler(val) { - if (val) { - // 深拷贝数据,避免直接修改props - this.form = JSON.parse( - JSON.stringify({ - ...this.form, - ...val, - // 确保这些字段存在,如果不存在则使用默认值 - materialsAndEquipment: val.materialsAndEquipment || [], - operationSteps: val.operationSteps || [], - }) - ); - console.log("接收到的数据:", this.form); + if (val && val.id) { + // 当接收到数据且有ID时,调用获取详情接口 + this.getPlanDetail(val.id); } }, immediate: true, @@ -419,28 +261,35 @@ }, visible: { handler(val) { - if (val && this.type === "view") { - // 当弹窗打开且是查看模式时,获取详情数据 - this.getPlanDetail(); + if (val && this.data && this.data.id) { + // 弹窗打开时,确保数据已获取 + this.getPlanDetail(this.data.id); } }, immediate: true, }, }, methods: { - memberList(i) { - switch (i) { - case 1: - return [1]; - case 2: - return [1]; - case 3: - return [1, 2, 3, 4, 5, 6, 7, 8]; - case 4: - return [1, 2, 3, 4, 5, 6, 7, 8]; - default: - break; - } + handleStopExperiment() { + this.$router.push("/dataManagement/scheme-management/stop-experiment?id=" + this.data.id); + }, + getStatusType(status) { + const statusMap = { + "-1": "info", + "1": "warning", + "2": "success", + "3": "info" + }; + return statusMap[status] || "info"; + }, + getStatusText(status) { + const statusMap = { + "-1": "草稿箱", + "1": "待确认", + "2": "已确认", + "3": "已封存" + }; + return statusMap[status] || "未知"; }, handleClose() { this.$emit("update:visible", false); @@ -466,9 +315,6 @@ status: "rejected", }); }, - memberList(item) { - return item === 1 ? 2 : item === 2 ? 3 : 1; - }, openSignature() { this.signatureDialogVisible = true; }, @@ -476,129 +322,190 @@ console.log("imageData imageData", imageData); this.signatureDialogVisible = false; this.imgSrc = imageData; - - // 这里处理签名确认后的逻辑 - // this.$confirm('确认该实验调度吗?', '提示', { - // confirmButtonText: '确定', - // cancelButtonText: '取消', - // type: 'warning' - // }).then(() => { - // // 这里可以将签名图片数据(imageData)连同其他数据一起提交到后端 - // this.$message.success('确认成功'); - // this.signatureDialogVisible = false; - // this.getTableData(); - // }).catch(() => { - // this.signatureDialogVisible = false; - // }); }, // 获取方案详情 - async getPlanDetail() { + async getPlanDetail(id) { try { - // TODO: 替换为实际的接口调用 - // const { data } = await this.$api.getPlanDetail({ planCode: this.data.planCode }); + const res = await getDetail({ id }); + if (!res) { + this.$message.error('获取方案详情失败'); + this.handleClose(); + return; + } + if(res.stopReason){ + this.showApprovalFlow = true; + //中止实验申请 + let processData = []; + processData.push({ + type: "primary", + mode: "list", + fields: [ + { label: "提交人:", value: res.updateBy || "" }, + { label: "提交时间:", value: res.createTime || "" }, + ], + }); + if(res.status==4||res.status==3){ + processData.push({ + type: + res.auditStatus === 2 + ? "primary" + : res.auditStatus === 3 + ? "danger" + : "warning", + mode: "list", + fields: [ + { + label: "审核结果:", + value: + res.auditStatus === 2 + ? "通过" + : res.auditStatus === 3 + ? "驳回" + : "待审批", + }, + { label: "审批意见:", value: res.auditRemark || "" }, + { label: "审核人:", value: res.auditPersonName || "" }, + { label: "审核时间:", value: res.auditTime || "" }, + ], + }); + }else{ + processData.push({ + type: "warning", + mode: "list", + fields: [ + { label: "等待审核"}, + ], + }); + } + this.approvalProcessData = processData; + } - // 模拟接口返回数据 - const mockDetailData = { - planCode: this.data.planCode, - planName: "2024年度实验室设备升级方案", - stage: "设备升级实验", - testDate: "2024-03-15", - testTime: "2024-03-15 14:00:00", - tester: "张三", - creator: "张三", - createTime: "2024-03-15", - status: "pending", - approver: "李四", - approveTime: "2024-03-16", - materialsAndEquipment: [ - { - id: 1, - type: "richText", - data: { - content: - "<p>1. 实验材料说明</p><p>2. 设备使用说明</p><p>3. 安全注意事项</p>", - }, - }, - { - id: 2, - type: "customTable", - data: { - headers: [ - { name: "材料名称", type: "text" }, - { name: "规格", type: "text" }, - { name: "数量", type: "text" }, - { name: "用途", type: "text" }, - ], - rows: [ - { - 材料名称: "催化剂A", - 规格: "工业级", - 数量: "100g", - 用途: "反应催化剂", - updateTime: "2024-01-01 12:00:00", - }, - { - 材料名称: "溶剂B", - 规格: "分析纯", - 数量: "500ml", - 用途: "反应溶剂", - updateTime: "2024-01-01 12:00:00", - }, - ], - }, - }, - { - id: 3, - type: "fileUpload", - data: { - fileList: [ - { - name: "材料安全说明书.pdf", - url: "https://example.com/msds.pdf", - }, - { - name: "设备操作手册.docx", - url: "https://example.com/manual.docx", - }, - ], - }, - }, - { - id: 4, - type: "imageUpload", - data: { - images: [ - { - url: "https://example.com/equipment1.jpg", - title: "实验设备1", - description: "主要反应设备", - }, - { - url: "https://example.com/equipment2.jpg", - title: "实验设备2", - description: "辅助设备", - }, - ], - }, - }, - ], - operationSteps: [ - { - id: 4, - type: "richText", - data: { - content: - "<p>1. 准备工作</p><p>2. 设备检查</p><p>3. 实验操作</p><p>4. 数据记录</p>", - }, - }, - ], - }; - - // 更新表单数据 + // 填充基本表单数据 this.form = { ...this.form, - ...mockDetailData, + projectName: res.projectName, + projectCode: res.projectCode, + experimentCode: res.experimentCode, + experimentName: res.experimentName, + experimentDate: res.experimentDate, + createBy: res.createBy, + createTime: res.createTime, + status: res.status, + experimentObjective: res.experimentObjective || '', + experimentParamRoute: res.experimentParamRoute || '', }; + + // 构建实验调度数据 + if (res.experimentDispatch) { + this.dispatchData = [res.experimentDispatch]; + } + + // 填充组别数据 + if (res.dispatchId) { + try { + const groupRes = await getGroupByDispatchId({ dispatchId: res.dispatchId }); + if (groupRes) { + this.groupData = groupRes || []; + } + } catch (err) { + console.error('获取组别列表失败:', err); + } + } + + // 填充实验材料和设备 + if (res.experimentMaterial) { + try { + const materialData = typeof res.experimentMaterial === 'string' + ? JSON.parse(res.experimentMaterial) + : res.experimentMaterial; + this.form.experimentMaterial = materialData; + + // 为DynamicComponent设置初始数据 + // this.$nextTick(() => { + // if (this.$refs.materialComponent) { + // this.$refs.materialComponent.setInitialData(materialData); + // } + // }); + } catch (err) { + console.error('解析实验材料数据失败:', err); + } + } + + if (res.experimentDevice) { + try { + const deviceData = typeof res.experimentDevice === 'string' + ? JSON.parse(res.experimentDevice) + : res.experimentDevice; + this.form.experimentDevice = deviceData; + + // 为DynamicComponent设置初始数据 + this.$nextTick(() => { + // if (this.$refs.equipmentComponent) { + // this.$refs.equipmentComponent.setInitialData(deviceData); + // } + }); + } catch (err) { + console.error('解析实验设备数据失败:', err); + } + } + + // 填充实验步骤 + if (res.experimentStepRecord) { + try { + const stepsData = typeof res.experimentStepRecord === 'string' + ? JSON.parse(res.experimentStepRecord) + : res.experimentStepRecord; + + this.stepList = (stepsData || []).map(step => ({ + stepName: step.stepName, + content: step.content + })); + + // 设置步骤内容的初始数据 + this.$nextTick(() => { + // this.stepList.forEach((step, index) => { + // const stepContentRef = this.$refs['stepContent' + index]; + // if (stepContentRef && step.content) { + // const editor = Array.isArray(stepContentRef) ? stepContentRef[0] : stepContentRef; + // if (editor && typeof editor.setInitialData === 'function') { + // editor.setInitialData(step.content); + // } + // } + // }); + }); + } catch (err) { + console.error('解析实验步骤数据失败:', err); + this.stepList = []; + } + } + + // 设置实验人员 + if (res.experimentSchemePersons) { + try { + const participantsData = typeof res.experimentSchemePersons === 'string' + ? JSON.parse(res.experimentSchemePersons) + : res.experimentSchemePersons; + + this.selectedParticipants = participantsData || []; + } catch (err) { + console.error('解析实验人员数据失败:', err); + this.selectedParticipants = []; + } + } + + + + + // 更新编辑器内容 + this.$nextTick(() => { + if (this.$refs.purposeEditor) { + this.$refs.purposeEditor.setContent(this.form.experimentObjective); + } + if (this.$refs.processEditor) { + this.$refs.processEditor.setContent(this.form.experimentParamRoute); + } + }); + } catch (error) { console.error("获取方案详情失败:", error); this.$message.error("获取方案详情失败"); @@ -762,7 +669,7 @@ } .groupTable { - width: 65%; + width: 85%; padding-left: 40px; } @@ -779,8 +686,8 @@ margin-left: 38px; .member-list-card { - width: 280px; - height: 300px; + width: 340px; + height: 400px; border-radius: 8px; border: 1px solid #dcdfe6; @@ -891,11 +798,13 @@ .step-list { background: #eff8fa; padding: 20px; + .step-list-item { display: flex; justify-content: space-between; padding: 25px; background: #ffffff; + .step-list-item-title { font-weight: 500; font-size: 14px; @@ -907,13 +816,24 @@ } } -.dialog-footer { - align-items: center; +.content-box { + padding: 0 25px; + margin-bottom: 20px; + width: 65%; display: flex; - justify-content: center; - - button { - width: 150px; + + .content-box-left { + flex: 1; + div { + padding: 10px 0; + } + } + + .content-box-right { + flex: 1; + div { + padding: 10px 0; + } } } </style> \ No newline at end of file -- Gitblit v1.7.1