From 0c9660562a03191d44fc779a889d3da0dc624b6d Mon Sep 17 00:00:00 2001 From: 董国庆 <364620639@qq.com> Date: 星期五, 25 七月 2025 10:47:19 +0800 Subject: [PATCH] 修改弹窗ui和客户反馈 --- laboratory/src/views/dataManagement/schemeManagement/components/approvalDialog.vue | 845 ++++++++++++++++++++++++++++++++++++++++++-------------- 1 files changed, 633 insertions(+), 212 deletions(-) diff --git a/laboratory/src/views/dataManagement/schemeManagement/components/approvalDialog.vue b/laboratory/src/views/dataManagement/schemeManagement/components/approvalDialog.vue index 8765f17..a44242e 100644 --- a/laboratory/src/views/dataManagement/schemeManagement/components/approvalDialog.vue +++ b/laboratory/src/views/dataManagement/schemeManagement/components/approvalDialog.vue @@ -1,146 +1,163 @@ <template> - <el-dialog - :title="dialogTitle" - :visible.sync="visible" - width="80%" - :close-on-click-modal="false" - @close="handleClose" - > - <div class="approval-dialog"> - <!-- 左侧审批内容 --> - <div class="approval-content"> - <Card class="approval-content-card"> - <template style="position: relative"> - <div class="header-title"> - <div class="header-title-left"> - <img src="@/assets/public/headercard.png" /> - <span>项目课题方案信息</span> - </div> - </div> - <el-form - ref="form" - :model="form" - :rules="rules" - inline - label-position="top" - style="margin-top: 38px" - > - <el-form-item prop="name" label="项目课题方案名称"> - <el-input v-model="form.name" placeholder="请输入" /> - </el-form-item> - <el-form-item prop="description" label="项目阶段"> - <el-input v-model="form.description" placeholder="请输入" /> - </el-form-item> - <el-form-item prop="description" label="项目课题方案编号"> - <el-input v-model="form.description" placeholder="请输入" /> - </el-form-item> - </el-form> - <div class="header-title"> - <div class="header-title-left"> - <img src="@/assets/public/headercard.png" /> - <div>一 、实验目的</div> - </div> - </div> - <div class="header-title"> - <div class="header-title-left"> - <img src="@/assets/public/headercard.png" /> - <div>二 、实验拆料和设备</div> - </div> - </div> - <div class="item-title"> - <span>1.实验材料</span> - </div> - <div class="item-title"> - <span>2.实验设备</span> - </div> + <div> + <el-dialog title="实验方案详情" :visible="dialogVisible" width="90%" top="5vh" :close-on-click-modal="false" @close="handleClose"> + <div class="approval-dialog"> + <!-- 左侧审批内容 --> + <div class="approval-content"> + <Card class="approval-content-card"> + <template style="position: relative"> + <el-form ref="form" :model="form" :rules="rules" inline label-position="top"> + <div class="header-title" style="margin-bottom: 38px"> + <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" + v-if='form.status != 2 && form.status != 3 && form.status != 4 && userRole == 3'> + 申请终止实验</el-button> + </div> + </div> + <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"> - <div class="header-title-left"> - <img src="@/assets/public/headercard.png" /> - <div>三 、检测方法及开发</div> - </div> - </div> - <div class="header-title"> - <div class="header-title-left"> - <img src="@/assets/public/headercard.png" /> - <div>四 、实验步骤</div> - </div> - </div> - <div class="header-title"> - <div class="header-title-left"> - <img src="@/assets/public/headercard.png" /> - <div>五 、数据采集及分析</div> - </div> - </div> - <div class="header-title"> - <div class="header-title-left"> - <img src="@/assets/public/headercard.png" /> - <div>六 、结果评估</div> - </div> - </div> - <div class="header-title"> - <div class="header-title-left"> - <img src="@/assets/public/headercard.png" /> - <span>注意事项</span> - </div> - </div> - </template> - <SelectMember ref="selectMember" /> - </Card> - </div> - <!-- 右侧审批流程 --> - <div class="approval-flow"> - <div class="flow-content"> - <approval-process - :status="form.status" - :submit-time="form.createTime" - :approver="form.approver" - :approve-time="form.approveTime" - /> + <div class="header-title" style="margin-bottom: 38px"> + <div class="header-title-left"> + <img src="@/assets/public/headercard.png" /> + <span>基础信息</span> + </div> + </div> + + <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: 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"> + <span>实验人员</span> + </div> + <div class="member-list"> + <div class="member-list-card"> + <div class="member-item"> + <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> + </div> + </div> + + <div class="header-title" style="margin-bottom: 38px"> + <div class="header-title-left"> + <img src="@/assets/public/headercard.png" /> + <div>一、实验目的</div> + </div> + </div> + <div class="content-box"> + <AiEditor ref="purposeEditor" :readOnly="true" :value="form.experimentObjective" height="400px" + placeholder="请输入实验目的..." /> + </div> + + <div class="header-title" style="margin-bottom: 38px"> + <div class="header-title-left"> + <img src="@/assets/public/headercard.png" /> + <div>二、工艺参数及路线</div> + </div> + </div> + <div class="content-box"> + <AiEditor ref="processEditor" :readOnly="true" :value="form.experimentParamRoute" height="400px" + placeholder="请输入工艺参数及路线..." /> + </div> + + <div class="header-title" style="margin-bottom: 38px"> + <div class="header-title-left"> + <img src="@/assets/public/headercard.png" /> + <div>三、实验材料及设备</div> + </div> + </div> + <DynamicComponent ref="materialComponent" title="实验材料" :dialogCanEdit="false" + :dataSource="form.experimentMaterial" :editable="false" /> + <DynamicComponent ref="equipmentComponent" title="实验所用设备" :dialogCanEdit="false" + :dataSource="form.experimentDevice" :editable="false" /> + + <div class="header-title" style="margin-bottom: 38px"> + <div class="header-title-left"> + <img src="@/assets/public/headercard.png" /> + <div>四、实验操作步骤记录</div> + </div> + </div> + + <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> + <DynamicComponent :dialogCanEdit="false" :ref="'stepContent' + idx" :dataSource="item.content" + :editable="false" /> + </div> + </el-form> + </template> + </Card> </div> - </div> - </div> - <div class="approval-dialog-approve"> - <div class="status"> - <div class="status-title">审批结果</div> - <div class="status-content"> - <div - class="resolve" - :class="status == '1' && 'activeStatus'" - @click.stop="status = 1" - > - 通过 - </div> - <div - class="reject" - :class="status == '2' && 'activeStatus'" - @click.stop="status = 2" - > - 驳回 + <!-- 右侧审批流程 --> + <div class="approval-flow" v-if="showApprovalFlow"> + <div class="flow-content"> + <approval-process :processData="approvalProcessData" /> </div> </div> </div> - <div class="remark"> - <div class="remark-title">审批意见</div> - <el-input type="textarea" v-model="remark" placeholder="请输入审批意见" /> - </div> - </div> - <div slot="footer" class="dialog-footer"> - <el-button @click="handleClose">取 消</el-button> - <el-button type="primary" @click="handleApprove" v-if="type === 'approve'" - >通过</el-button - > - </div> - </el-dialog> + </el-dialog> + <SignatureCanvas :visible="signatureDialogVisible" @confirm="handleSignatureConfirm" /> + </div> </template> <script> -import ApprovalProcess from '@/components/approvalProcess' +import ApprovalProcess from "@/components/approvalProcess"; +import SignatureCanvas from "@/components/SignatureCanvas.vue"; +import DynamicComponent from "@/components/DynamicComponent"; +import AiEditor from "@/components/AiEditor"; +import { getDetail, getGroupByDispatchId } from "../service"; export default { name: "ApprovalDialog", components: { - ApprovalProcess + ApprovalProcess, + SignatureCanvas, + DynamicComponent, + AiEditor, }, props: { visible: { @@ -158,42 +175,88 @@ }, data() { return { + dialogVisible: false, form: { - planName: "", - planCode: "", - stage: "", - creator: "", - createTime: "", - approvalComment: "", - status: "pending", - approver: "", - approveTime: "" + projectName: "", // 项目课题方案名称 + projectCode: "", // 项目课题方案编号 + experimentCode: "", // 实验编号 + experimentName: "", // 实验名称 + experimentDate: "", // 实验日期 + experimentMaterial: [], // 实验材料 + experimentDevice: [], // 实验设备 + experimentObjective: "", // 实验目的 + experimentParamRoute: "", // 工艺参数及路线 + createBy: "", // 创建人 + createTime: "", // 创建时间 + status: "", // 状态 + approver: "", // 审批人 + approveTime: "", // 审批时间 }, - radio1: 1, - rules: {}, - status: "1", + rules: { + testTime: [ + { required: true, message: "请选择试验时间", trigger: "change" }, + ], + }, + imgSrc: "", + signatureDialogVisible: false, remark: "", + groupData: [], + dispatchData: [], // 实验调度数据 + stepList: [], + selectedParticipants: [], // 实验参与人员 + showApprovalFlow: false, + approvalProcessData: [], + userRole: '', }; }, - computed: { - dialogTitle() { - return this.type === "approve" ? "审批" : "审批详情"; - }, - }, watch: { - data: { + visible: { handler(val) { - if (val) { - this.form = { ...val }; + this.dialogVisible = val; + if (val && this.data && this.data.id) { + // 弹窗打开时,确保数据已获取 + this.getPlanDetail(this.data.id); + const userInfo = JSON.parse(sessionStorage.getItem('userInfo') || '{}'); + this.userRole = userInfo.roleType || ''; } }, immediate: true, }, + data: { + handler(val) { + if (val && val.id) { + // 当接收到数据且有ID时,调用获取详情接口 + this.getPlanDetail(val.id); + } + }, + immediate: true, + deep: true, + }, }, methods: { + 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); - this.form.approvalComment = ""; + this.$emit('update:visible', false); }, handleApprove() { if (!this.form.approvalComment) { @@ -215,6 +278,197 @@ status: "rejected", }); }, + openSignature() { + this.signatureDialogVisible = true; + }, + handleSignatureConfirm(imageData) { + this.signatureDialogVisible = false; + this.imgSrc = imageData; + }, + // 获取方案详情 + async getPlanDetail(id) { + try { + 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: 'primary', + mode: "list", + fields: [ + { + label: "审核结果:", + value: + res.status == 3 + ? "通过" + : res.status == 4 + ? "驳回" + : "待审批", + }, + { label: "审批意见:", value: res.auditRemark || "" }, + { label: "审核人:", value: res.auditPersonName || "" }, + { label: "审核时间:", value: res.auditTime || "" }, + ], + }); + } else { + processData.push({ + type: "warning", + mode: "list", + fields: [ + { label: "等待审核" }, + ], + }); + } + this.approvalProcessData = processData; + } + + // 填充基本表单数据 + this.form = { + ...this.form, + 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("获取方案详情失败"); + this.handleClose(); + } + }, }, }; </script> @@ -224,24 +478,57 @@ border-bottom: 1px solid #e4e7ed; } +::v-deep .el-dialog__body { + // padding: 20px; + + max-height: 80vh; + overflow: hidden; +} + +@media screen and (max-width: 1200px) { + ::v-deep .el-dialog__body { + max-height: none; + overflow: auto; + } +} + .approval-dialog { display: flex; - height: 300px; + min-height: 60vh; + max-height: 78vh; + padding: 20px; + overflow: hidden; + + @media screen and (max-width: 1200px) { + flex-direction: column; + height: auto; + + .approval-content, + .approval-flow { + width: 100%; + margin-right: 0; + margin-bottom: 20px; + height: 50vh; + } + } .approval-content { - flex: 1; + flex: 7; margin-right: 20px; background: #ffffff; box-shadow: 0px 4px 12px 4px rgba(0, 0, 0, 0.08); border-radius: 10px; + overflow-y: auto; } .approval-flow { - padding: 40px 20px; - width: 405px; + flex: 3; + max-width: 305px; + padding: 20px 0px; background: #ffffff; box-shadow: 0px 4px 12px 4px rgba(0, 0, 0, 0.08); border-radius: 10px; + overflow-y: auto; .flow-title { font-size: 16px; @@ -261,8 +548,12 @@ } } +.approval-dialog-approve { + margin-top: 26px; +} + .approval-content-card { - height: calc(100% - 100px) !important; + height: calc(100% - 10px) !important; box-shadow: none !important; } @@ -271,12 +562,12 @@ align-items: center; flex-wrap: wrap; gap: 13px; + margin-top: 38px; .header-title-left { display: flex; align-items: center; gap: 13px; - margin-top: 38px; img { width: 12px; @@ -314,6 +605,8 @@ } .header-title:first-child { + margin-top: 0 !important; + .header-title-left { margin-top: 0; } @@ -340,63 +633,191 @@ } .approval-dialog-approve { - padding: 38px 20px; - display: flex; - align-content: center; - .status { - margin-right: 40px; - } - // align-items: center; - .status-title { - color: #222222; - font-family: "SourceHanSansCN-Medium"; - line-height: 14px; - margin-bottom: 16px; - } - .status-content { - display: flex; - align-items: center; - gap: 16px; - background: #ffffff; - border-radius: 10px; - border: 1px solid rgba(4, 156, 154, 0.5); - .resolve { - border-radius: 10px; - font-size: 16px; - padding: 5px 55px; - font-weight: 400; - color: #333333; - cursor: pointer; - } - .reject { - border-radius: 10px; - font-size: 16px; - padding: 5px 55px; - font-weight: 400; - color: #333333; - cursor: pointer; - } - .activeStatus { - background: #ebfefd; - color: #049c9a; - box-shadow: 0px 0px 6px 0px rgba(10, 109, 108, 0.25); - border-radius: 10px; - } - } - .remark-title { - color: #222222; - font-family: "SourceHanSansCN-Medium"; - line-height: 14px; - margin-bottom: 16px; + img { + border: 2px dashed #049c9a; } } -.dialog-footer{ - align-items: center; - display: flex; - justify-content: center; - button{ - width: 150px; +.add-group { + padding-left: 25px; + margin-top: 14px; + display: flex; + align-items: center; + margin-bottom: 19px; + + div { + color: #f56c6c; + } + + span { + font-weight: 500; + font-size: 14px; + color: #222222; + line-height: 21px; + margin: 0 32px 0 8px; + } +} + +.groupTable { + width: 85%; + padding-left: 40px; +} + +.rwuTable { + width: 85%; + padding-left: 40px; +} + +.member-list { + margin-top: 18px; + display: flex; + flex-wrap: wrap; + gap: 28px; + margin-left: 38px; + + .member-list-card { + width: 340px; + height: 400px; + border-radius: 8px; + border: 1px solid #dcdfe6; + + &:nth-child(1) { + background: linear-gradient(to bottom, + rgba(4, 156, 154, 0.2) 0%, + rgba(5, 242, 194, 0) 70%); } + + &:nth-child(2) { + background: linear-gradient(to bottom, + rgba(5, 160, 193, 0.2) 0%, + rgba(5, 242, 194, 0) 70%); + } + + &:nth-child(3) { + background: linear-gradient(to bottom, + rgba(255, 77, 79, 0.2) 0%, + rgba(255, 242, 194, 0) 70%); + } + + &:nth-child(4) { + background: linear-gradient(to bottom, + rgba(250, 199, 20, 0.21) 0%, + rgba(255, 242, 194, 0) 70%); + } + + .member-item { + height: 100%; + display: flex; + flex-direction: column; + + .member-title { + margin-top: 20px; + width: 100%; + font-family: "Source Han Sans CN Bold Bold"; + font-weight: bold; + font-size: 16px; + color: rgba(0, 0, 0, 0.8); + line-height: 16px; + text-align: center; + } + + .flex1 { + flex: 1; + } + + .member-name-box { + flex: 1; + display: flex; + align-items: center; + justify-content: center; + } + + .member-name-box-2 { + flex: 1; + padding: 0 20px; + padding-top: 40px; + display: grid; + grid-template-columns: repeat(4, 1fr); + gap: 20px; + justify-items: center; + align-items: start; + } + + .member-name { + width: 60px; + height: 60px; + background: #7d8b79; + border-radius: 50%; + text-align: center; + line-height: 60px; + font-weight: 500; + font-size: 16px; + color: #ffffff; + margin: 0; + } + + .member-change { + display: flex; + justify-content: center; + padding: 10px 0; + margin-top: auto; + cursor: pointer; + + .member-change-btn { + background: #fff1f0; + border-radius: 4px; + border: 1px solid #ffccc7; + padding: 1px 8px; + font-weight: 400; + font-size: 12px; + color: #ff4d4f; + } + } + } + } +} + +.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; + color: rgba(0, 0, 0, 0.8); + line-height: 20px; + flex-wrap: wrap; + flex: 1; + } + } +} + +.content-box { + padding: 0 25px; + margin-bottom: 20px; + width: 65%; + display: flex; + + .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