<template>
|
<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"
|
|
>
|
<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">
|
申请终止实验</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" 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="200px"
|
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="200px"
|
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 class="approval-flow" v-if="showApprovalFlow">
|
<div class="flow-content">
|
<approval-process
|
:processData="approvalProcessData"
|
/>
|
</div>
|
</div>
|
</div>
|
</el-dialog>
|
<SignatureCanvas
|
:visible="signatureDialogVisible"
|
@confirm="handleSignatureConfirm"
|
/>
|
</div>
|
</template>
|
|
<script>
|
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,
|
SignatureCanvas,
|
DynamicComponent,
|
AiEditor,
|
},
|
props: {
|
visible: {
|
type: Boolean,
|
default: false,
|
},
|
type: {
|
type: String,
|
default: "approve", // approve-审批,view-查看
|
},
|
data: {
|
type: Object,
|
default: () => ({}),
|
},
|
},
|
data() {
|
return {
|
form: {
|
projectName: "", // 项目课题方案名称
|
projectCode: "", // 项目课题方案编号
|
experimentCode: "", // 实验编号
|
experimentName: "", // 实验名称
|
experimentDate: "", // 实验日期
|
experimentMaterial: [], // 实验材料
|
experimentDevice: [], // 实验设备
|
experimentObjective: "", // 实验目的
|
experimentParamRoute: "", // 工艺参数及路线
|
createBy: "", // 创建人
|
createTime: "", // 创建时间
|
status: "", // 状态
|
approver: "", // 审批人
|
approveTime: "", // 审批时间
|
},
|
rules: {
|
testTime: [
|
{ required: true, message: "请选择试验时间", trigger: "change" },
|
],
|
},
|
imgSrc: "",
|
signatureDialogVisible: false,
|
remark: "",
|
groupData: [],
|
dispatchData: [], // 实验调度数据
|
stepList: [],
|
selectedParticipants: [], // 实验参与人员
|
showApprovalFlow: false,
|
approvalProcessData: [],
|
};
|
},
|
watch: {
|
data: {
|
handler(val) {
|
if (val && val.id) {
|
// 当接收到数据且有ID时,调用获取详情接口
|
this.getPlanDetail(val.id);
|
}
|
},
|
immediate: true,
|
deep: true,
|
},
|
visible: {
|
handler(val) {
|
if (val && this.data && this.data.id) {
|
// 弹窗打开时,确保数据已获取
|
this.getPlanDetail(this.data.id);
|
}
|
},
|
immediate: 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 = "";
|
},
|
handleApprove() {
|
if (!this.form.approvalComment) {
|
this.$message.warning("请输入审批意见");
|
return;
|
}
|
this.$emit("approve", {
|
...this.form,
|
status: "approved",
|
});
|
},
|
handleReject() {
|
if (!this.form.approvalComment) {
|
this.$message.warning("请输入审批意见");
|
return;
|
}
|
this.$emit("reject", {
|
...this.form,
|
status: "rejected",
|
});
|
},
|
openSignature() {
|
this.signatureDialogVisible = true;
|
},
|
handleSignatureConfirm(imageData) {
|
console.log("imageData imageData", 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:
|
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;
|
}
|
|
// 填充基本表单数据
|
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>
|
|
<style scoped lang="less">
|
::v-deep .el-dialog__header {
|
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: 60vh;
|
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: 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 {
|
flex: 3;
|
min-width: 350px;
|
padding: 40px 20px;
|
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;
|
font-weight: bold;
|
margin-bottom: 20px;
|
color: #303133;
|
}
|
|
.flow-content {
|
height: calc(100% - 40px);
|
overflow-y: auto;
|
|
.el-form--inline .el-form-item {
|
margin-right: 83px;
|
}
|
}
|
}
|
}
|
|
.approval-dialog-approve {
|
margin-top: 26px;
|
}
|
|
.approval-content-card {
|
height: calc(100% - 100px) !important;
|
box-shadow: none !important;
|
}
|
|
.header-title {
|
display: flex;
|
align-items: center;
|
flex-wrap: wrap;
|
gap: 13px;
|
margin-top: 38px;
|
|
.header-title-left {
|
display: flex;
|
align-items: center;
|
gap: 13px;
|
|
img {
|
width: 12px;
|
height: 19px;
|
}
|
|
div {
|
flex-shrink: 0;
|
font-weight: bold;
|
font-size: 18px;
|
color: #222222;
|
line-height: 27px;
|
font-family: "Source Han Sans CN Bold Bold";
|
|
&:before {
|
content: "*";
|
color: #f56c6c;
|
margin-right: 4px;
|
}
|
}
|
|
span {
|
flex-shrink: 0;
|
font-weight: bold;
|
font-size: 18px;
|
color: #222222;
|
line-height: 27px;
|
font-family: "Source Han Sans CN Bold Bold";
|
}
|
}
|
|
.header-title-left :first-child {
|
margin-top: 0;
|
}
|
}
|
|
.header-title:first-child {
|
margin-top: 0 !important;
|
|
.header-title-left {
|
margin-top: 0;
|
}
|
}
|
|
.item-title {
|
padding-left: 25px;
|
|
span {
|
flex-shrink: 0;
|
font-weight: bold;
|
font-size: 14px;
|
color: #222222;
|
line-height: 27px;
|
font-family: "Source Han Sans CN Bold Bold";
|
margin: 18px 0;
|
|
&:before {
|
content: "*";
|
color: #f56c6c;
|
margin-right: 4px;
|
}
|
}
|
}
|
|
.approval-dialog-approve {
|
img {
|
border: 2px dashed #049c9a;
|
}
|
}
|
|
.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>
|