From b7ec20b3ec22c858f2db3d9285c5e9d38bd8a48f Mon Sep 17 00:00:00 2001 From: 13404089107 <puwei@sinata.cn> Date: 星期四, 08 五月 2025 16:17:19 +0800 Subject: [PATCH] Merge branch 'main' of http://120.76.84.145:10101/gitblit/r/H5/leshan-laboratory --- laboratory/src/views/dataManagement/schemeManagement/components/approvalDialog.vue | 901 ++++++++++++++++++++++++++++++++++++++++++++------------ 1 files changed, 709 insertions(+), 192 deletions(-) diff --git a/laboratory/src/views/dataManagement/schemeManagement/components/approvalDialog.vue b/laboratory/src/views/dataManagement/schemeManagement/components/approvalDialog.vue index 8765f17..d31d6ae 100644 --- a/laboratory/src/views/dataManagement/schemeManagement/components/approvalDialog.vue +++ b/laboratory/src/views/dataManagement/schemeManagement/components/approvalDialog.vue @@ -1,146 +1,218 @@ <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.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"> + <el-form + ref="form" + :model="form" + :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> + </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> - <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> + + <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> + + <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" + /> + </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-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> + </div> + </div> + <div class="member-change" v-if="type !== 'view'"> + <div class="member-change-btn">修改</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> + <AiEditor + ref="purposeEditor" + v-model="form.purpose" + height="200px" + placeholder="请输入实验目的..." + /> + + <div class="header-title" style="margin-bottom: 38px"> + <div class="header-title-left"> + <img src="@/assets/public/headercard.png" /> + <div>二、工艺参数及路线</div> + </div> + </div> + <AiEditor + ref="processEditor" + v-model="form.process" + height="200px" + placeholder="请输入工艺参数及路线..." + /> + + <div class="header-title" style="margin-bottom: 38px"> + <div class="header-title-left"> + <img src="@/assets/public/headercard.png" /> + <div>三、实验材料及设备</div> + </div> + </div> + <ViewDynamicComponent + title="实验材料" + :components="form.materialsAndEquipment || []" + /> + <ViewDynamicComponent + title="实验所用设备" + :components="form.materialsAndEquipment || []" + /> + + <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 form.operationSteps" :key="idx"> + <div class="step-list-item"> + <div class="step-list-item-title"> + 步骤{{ idx + 1 }}:{{ item.stepName }} + </div> + </div> + <ViewDynamicComponent + :ref="'stepContent' + idx" + :components="[item]" + /> + </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="type === 'view'"> + <div class="flow-content"> + <approval-process + :status="form.status" + :submit-time="form.createTime" + :approver="form.approver" + :approve-time="form.approveTime" + /> </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 ViewDynamicComponent from "@/components/DynamicComponent/ViewDynamicComponent.vue"; +import AiEditor from "@/components/AiEditor/index.vue"; export default { name: "ApprovalDialog", components: { - ApprovalProcess + ApprovalProcess, + SignatureCanvas, + ViewDynamicComponent, + AiEditor, }, props: { visible: { @@ -162,35 +234,214 @@ planName: "", planCode: "", stage: "", + testDate: "", + testName: "", + testCode: "", + testTime: "", creator: "", createTime: "", approvalComment: "", - status: "pending", + status: "approved", approver: "", - approveTime: "" + 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", + }, + ], + }, + }, + ], }, - radio1: 1, - rules: {}, + 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: [], }; }, computed: { dialogTitle() { - return this.type === "approve" ? "审批" : "审批详情"; + return this.type === "approve" ? "确认实验调度" : "实验调度详情"; }, }, watch: { data: { handler(val) { if (val) { - this.form = { ...val }; + // 深拷贝数据,避免直接修改props + this.form = JSON.parse( + JSON.stringify({ + ...this.form, + ...val, + // 确保这些字段存在,如果不存在则使用默认值 + materialsAndEquipment: val.materialsAndEquipment || [], + operationSteps: val.operationSteps || [], + }) + ); + console.log("接收到的数据:", this.form); + } + }, + immediate: true, + deep: true, + }, + visible: { + handler(val) { + if (val && this.type === "view") { + // 当弹窗打开且是查看模式时,获取详情数据 + this.getPlanDetail(); } }, 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; + } + }, handleClose() { this.$emit("update:visible", false); this.form.approvalComment = ""; @@ -215,6 +466,145 @@ status: "rejected", }); }, + memberList(item) { + return item === 1 ? 2 : item === 2 ? 3 : 1; + }, + openSignature() { + this.signatureDialogVisible = true; + }, + handleSignatureConfirm(imageData) { + 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() { + try { + // TODO: 替换为实际的接口调用 + // const { data } = await this.$api.getPlanDetail({ planCode: this.data.planCode }); + + // 模拟接口返回数据 + 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, + }; + } catch (error) { + console.error("获取方案详情失败:", error); + this.$message.error("获取方案详情失败"); + this.handleClose(); + } + }, }, }; </script> @@ -226,7 +616,7 @@ .approval-dialog { display: flex; - height: 300px; + height: 60vh; .approval-content { flex: 1; @@ -261,6 +651,10 @@ } } +.approval-dialog-approve { + margin-top: 26px; +} + .approval-content-card { height: calc(100% - 100px) !important; box-shadow: none !important; @@ -271,12 +665,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 +708,8 @@ } .header-title:first-child { + margin-top: 0 !important; + .header-title-left { margin-top: 0; } @@ -340,63 +736,184 @@ } .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: 65%; + 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: 280px; + height: 300px; + 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; + } + } +} + +.dialog-footer { + align-items: center; + display: flex; + justify-content: center; + + button { + width: 150px; + } } </style> \ No newline at end of file -- Gitblit v1.7.1