董国庆
8 天以前 add04dce2ad833ecbb7495a641f42fb835ddff62
新增实验调度逻辑
7个文件已修改
1个文件已删除
3个文件已添加
1400 ■■■■ 已修改文件
laboratory/src/components/AiEditor/index.vue 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
laboratory/src/components/DynamicComponent/addTableData.vue 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
laboratory/src/components/DynamicComponent/addTableHeader.vue 49 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
laboratory/src/components/DynamicComponent/index.vue 143 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
laboratory/src/components/SelectMember.vue 补丁 | 查看 | 原始文档 | blame | 历史
laboratory/src/components/SelectMemberSimple/index.vue 154 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
laboratory/src/views/dataManagement/dispatching/addDispatch.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
laboratory/src/views/dataManagement/schemeManagement/addPlan.vue 382 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
laboratory/src/views/dataManagement/schemeManagement/components/experimental-scheduling.vue 194 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
laboratory/src/views/dataManagement/schemeManagement/list.vue 415 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
laboratory/src/views/dataManagement/schemeManagement/service.js 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
laboratory/src/components/AiEditor/index.vue
@@ -72,6 +72,7 @@
  },
  mounted() {
    this.initEditor()
    console.log('editor instance:', this.editor)
  },
  beforeDestroy() {
    this.destroyEditor()
laboratory/src/components/DynamicComponent/addTableData.vue
@@ -38,6 +38,7 @@
                  <el-input
                    v-model="form[header.name]"
                    :placeholder="'请输入' + header.name"
                    :disabled="!checkEditPermission(header)"
                  />
                </el-form-item>
                <el-form-item
@@ -58,6 +59,7 @@
                    list-type="picture-card"
                    :on-change="handleSpectrumChange"
                    :on-remove="handleSpectrumRemove"
                    :disabled="!checkEditPermission(header)"
                  >
                    <i class="el-icon-plus"></i>
                  </el-upload>
@@ -77,6 +79,7 @@
                    type="datetime"
                    placeholder="选择日期时间"
                    value-format="yyyy-MM-dd HH:mm:ss"
                    :disabled="!checkEditPermission(header)"
                    :picker-options="{
                      shortcuts: [{
                        text: '今天',
@@ -116,6 +119,7 @@
                    multiple
                    filterable
                    placeholder="请选择用户"
                    :disabled="!checkEditPermission(header)"
                  >
                    <el-option
                      v-for="item in userOptions"
@@ -185,6 +189,10 @@
        this.$emit("update:visible", val);
      },
    },
    currentUserId() {
      const userInfo = JSON.parse(sessionStorage.getItem('userInfo') || '{}');
      return userInfo.userId;
    }
  },
  watch: {
    visible: {
@@ -232,6 +240,12 @@
    },
  },
  methods: {
    checkEditPermission(header) {
      if (!header.role || !Array.isArray(header.role)) {
        return true;
      }
      return header.role.includes(this.currentUserId);
    },
    initRules() {
      // 初始化校验规则
      const rules = {};
laboratory/src/components/DynamicComponent/addTableHeader.vue
@@ -12,7 +12,7 @@
          <el-form ref="form" :model="form" :rules="rules" label-position="top">
            <el-row :gutter="24">
              <el-col :span="24">
                <el-form-item label="表头名称" prop="sampleCode">
                <el-form-item label="表头名称" prop="name">
                  <el-input
                    v-model="form.name"
                    style="width: 100%"
@@ -21,7 +21,7 @@
                </el-form-item>
              </el-col>
              <el-col :span="24">
                <el-form-item label="表头类型" prop="sampleCode">
                <el-form-item label="表头类型" prop="type">
                  <el-radio-group v-model="form.type" style="width: 100%">
                    <el-radio-button  label="text">文本框</el-radio-button>
                    <el-radio-button label="image">图片上传</el-radio-button>
@@ -31,7 +31,7 @@
                </el-form-item>
              </el-col>
              <el-col :span="24">
                <el-form-item label="操作权限" prop="sampleCode">
                <el-form-item label="操作权限" prop="role">
                  <el-select v-model="form.role" placeholder="请选择" style="width: 100%" multiple>
                    <el-option
                      v-for="item in options"
@@ -43,8 +43,8 @@
                  </el-select>
                </el-form-item>
              </el-col>
              <el-col :span="24">
                <el-form-item label="提示文案" prop="sampleCode">
              <el-col :span="24" v-if="['text', 'date', 'user'].includes(form.type)">
                <el-form-item label="提示文案" prop="message">
                  <el-input
                    v-model="form.message"
                    style="width: 100%"
@@ -53,7 +53,7 @@
                </el-form-item>
              </el-col>
              <el-col :span="24">
                <el-form-item label="是否必填" prop="testTypes">
                <el-form-item label="是否必填" prop="required">
                  <el-radio-group v-model="form.required">
                    <el-radio label="true">是</el-radio>
                    <el-radio label="false">否</el-radio>
@@ -80,6 +80,10 @@
      type: Boolean,
      default: false,
    },
    participants: {
      type: Array,
      default: () => []
    }
  },
  data() {
    return {
@@ -102,13 +106,13 @@
          {
            type: "array",
            required: true,
            message: "请至少选择一种检测类型",
            message: "请至少选择一个操作人",
            trigger: "change",
          },
        ],
        message: [
          {
            required: true,
            required: false,
            message: "请输入提示文案",
            trigger: "blur",
          },
@@ -121,22 +125,6 @@
          },
        ],
      },
      options: [{
          value: '1',
          label: '黄金糕'
        }, {
          value: '2',
          label: '双皮奶'
        }, {
          value: '3',
          label: '蚵仔煎'
        }, {
          value: '4',
          label: '龙须面'
        }, {
          value: '5',
          label: '北京烤鸭'
        }],
        value: ''
    };
  },
@@ -149,6 +137,13 @@
        this.$emit("update:visible", val);
      },
    },
    options() {
      // 将participants转换为select组件需要的格式
      return this.participants.map(participant => ({
        value: participant.userId ,
        label: participant.nickName
      }));
    }
  },
  mounted() {
    // 组件挂载时的初始化逻辑
@@ -179,6 +174,12 @@
      };
    },
    handleSubmit() {
      // 对于需要提示文案的类型,添加额外验证
      if (['text', 'date', 'user'].includes(this.form.type) && !this.form.message) {
        this.$message.error('请输入提示文案');
        return;
      }
      this.$refs.form.validate((valid) => {
        if (valid) {
          const submitData = {
laboratory/src/components/DynamicComponent/index.vue
@@ -30,7 +30,7 @@
        <div v-if="item.type == 'richText'">
          <AiEditor 
            :ref="`editor_${item.id}`"
            v-model="item.data.content"
           :value="item.data.content"
            height="200px"
            placeholder="请输入内容..." 
          />
@@ -98,20 +98,23 @@
        </div>
        <!-- 图片上传 -->
        <div v-else-if="item.type == 'imageUpload'">
          <div class="image-upload-container">
          <el-upload
            action="#"
            :file-list="item.data.imageList"
            :on-change="(file, fileList) => handleImageChange(idx, fileList)"
            :on-success="
              (res, file, fileList) =>
                handleImageSuccess(res, file, fileList, idx)
            "
              :on-success="(res, file, fileList) => handleImageSuccess(res, file, fileList, idx)"
              :auto-upload="true"
              :http-request="() => {}"
              :before-upload="beforeImageUpload"
            list-type="picture-card"
              class="image-uploader"
          >
            <i class="el-icon-plus"></i>
            <div class="upload-text">上传图片</div>
          </el-upload>
          <div class="uploaf-notice">支持.jpg .png格式</div>
          </div>
        </div>
        <img
          src="@/assets/public/delete.png"
@@ -140,6 +143,7 @@
    <addTableHeader
      :visible.sync="tableHeaderDialog.visible"
      :participants="participants"
      @confirm="confirmAddHeader"
    ></addTableHeader>
    <addTableData
@@ -174,6 +178,10 @@
      type: String,
      default: "",
    },
    participants: {
      type: Array,
      default: () => []
    }
  },
  data() {
    return {
@@ -197,6 +205,15 @@
  },
  methods: {
    addComponent(type) {
      // 如果是添加自定义表格,需要检查是否已选择实验调度
      if (type === "customTable") {
        if (!this.participants || this.participants.length === 0) {
          this.$message.warning('请先选择实验调度');
          this.showAddDialog = false;
          return;
        }
      }
      this.showAddDialog = false;
      const id = Date.now() + Math.random();
      let data = {};
@@ -207,6 +224,63 @@
      console.log(type, "111111111111111", this.components);
      this.components.push({ id, type, data });
    },
    submit() {
      // 获取所有组件的数据
      const data = this.components.map(component => {
        let componentData = null;
        switch (component.type) {
          case 'richText':
            const editorRef = this.$refs[`editor_${component.id}`];
            const editor = Array.isArray(editorRef) ? editorRef[0] : editorRef;
            console.log('editor ref:', editor);
            const content = editor ? editor.getContent() : '';
            componentData = content && content !== '<p></p>' ? content : '';
            break;
          case 'customTable':
            // 获取表格数据
            componentData = {
              headers: component.data.headers,
              rows: component.data.rows
            };
            break;
          case 'fileUpload':
            // 获取文件列表
            componentData = component.data.fileList;
            break;
          case 'imageUpload':
            // 获取图片列表
            componentData = component.data.imageList;
            break;
        }
        return {
          type: component.type,
          data: componentData
        };
      });
      // 触发 submit 事件,将数据传递给父组件
      this.$emit('submit', data);
    },
    confirmAddRow(formData) {
      const { idx, rowIndex, isEdit } = this.rowDialog;
      if (isEdit) {
        // 编辑模式
        this.components[idx].data.rows[rowIndex] = {
          ...formData,
          updateTime: new Date().toLocaleString()
        };
      } else {
        // 新增模式
        this.components[idx].data.rows.push({
          ...formData,
          updateTime: new Date().toLocaleString()
        });
      }
      this.rowDialog.visible = false;
      this.rowDialog.form = {};
    },
    removeComponent(idx) {
      this.components.splice(idx, 1);
@@ -299,15 +373,47 @@
        .catch(() => {});
    },
    handleFileChange(idx, fileList) {
      // 为每个文件添加默认的URL和名称
      fileList = fileList.map(file => {
        if (!file.url) {
          file.url = 'https://picsum.photos/200/200';
        }
        if (!file.name) {
          file.name = '默认文件.txt';
        }
        return file;
      });
      this.components[idx].data.fileList = fileList;
    },
    handleImageChange(idx, fileList) {
      // 为每个文件添加默认的URL
      fileList = fileList.map(file => {
        if (!file.url) {
          file.url = 'https://picsum.photos/200/200';
        }
        return file;
      });
      this.components[idx].data.imageList = fileList;
    },
    handleImageSuccess(res, file, fileList, idx) {
      // 假设后端返回的图片地址在 res.url
      file.url = res.url;
      // 使用默认图片URL
      file.url = 'https://picsum.photos/200/200';
      this.components[idx].data.imageList = fileList;
    },
    beforeImageUpload(file) {
      const isJPG = file.type === 'image/jpeg';
      const isPNG = file.type === 'image/png';
      const isLt2M = file.size / 1024 / 1024 < 2;
      if (!isJPG && !isPNG) {
        this.$message.error('上传图片只能是 JPG 或 PNG 格式!');
        return false;
      }
      if (!isLt2M) {
        this.$message.error('上传图片大小不能超过 2MB!');
        return false;
      }
      return true;
    },
  },
};
@@ -351,6 +457,29 @@
    cursor: pointer;
  }
}
.image-uploader{
  display: flex;
 ::v-deep .el-upload-list__item{
width: 104px !important;
height: 104px !important;
  }
}
.image-upload-container {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: 10px;
}
::v-deep .image-uploader {
  .el-upload--picture-card {
    margin: 0;
  }
  .el-upload-list--picture-card .el-upload-list__item {
    margin: 0 10px 10px 0;
  }
}
::v-deep .el-upload {
  width: 104px;
  height: 104px;
laboratory/src/components/SelectMember.vue
laboratory/src/components/SelectMemberSimple/index.vue
New file
@@ -0,0 +1,154 @@
<template>
    <el-dialog @open="openDialog" class="select-member" :visible.sync="visible" width="53.33%"
        :close-on-click-modal="false" :show-close="false">
        <template #title>
            <div>选择实验人员</div>
        </template>
        <div class="select-member-content">
            <div class="select-member-content-right">
                <div class="select-member-content-right-header">
                    <div class="select-member-content-right-header-text">人员列表</div>
                    <div class="select-member-content-right-header-search">
                        <el-input clearable v-model="searchKeyword" placeholder="请输入姓名/手机号" />
                    </div>
                </div>
                <Table ref="memberTable" :height="null" :row-key="row => row.userId" :data="filteredTableData"
                    :total="0" @selection-change="handleSelectionChange" :row-class-name="tableRowClassName">
                    <el-table-column type="selection" width="55" />
                    <el-table-column label="角色" prop="roleType" >
                        <template #default="{ row }">
                            {{ row.roleType === 3 ? '工艺工程师' : row.roleType === 4 ? '化验师' : '实验员' }}
                        </template>
                    </el-table-column>
                    <el-table-column label="姓名" prop="nickName" />
                    <el-table-column label="头像" prop="avatar" width="80">
                        <template #default="{ row }">
                            <el-avatar :size="32" :src="row.avatar" />
                        </template>
                    </el-table-column>
                    <el-table-column label="创建时间" prop="signTime" />
                </Table>
            </div>
        </div>
        <div class="select-member-footer">
            <el-button @click="close" type="default">关闭</el-button>
            <el-button type="primary" @click="submit">确认选择</el-button>
        </div>
    </el-dialog>
</template>
<script>
export default {
    props: {
        memberList: {
            type: Array,
            default: () => []
        }
    },
    data() {
        return {
            visible: false,
            searchKeyword: '',
            selectData: [],
            defaultSelected: [] // 默认选中的行
        }
    },
    computed: {
        filteredTableData() {
            if (!this.searchKeyword) {
                return this.memberList;
            }
            const keyword = this.searchKeyword.toLowerCase();
            return this.memberList.filter(item =>
                (item.nickName && item.nickName.toLowerCase().includes(keyword)) ||
                (item.phone && item.phone.includes(keyword))
            );
        }
    },
    methods: {
        setSelection(selected) {
            this.selectData = selected
            this.$nextTick(() => {
                // 设置新选中
                this.memberList.forEach(row => {
                    if (selected.some(i => i.userId === row.userId)) {
                        this.$refs.memberTable.toggleRowSelection(row, true)
                    }
                })
            })
        },
        openDialog() {
            this.setSelection(this.selectData);
        },
        handleSelectionChange(val) {
            this.selectData = val
        },
        open(data = [], defaultSelected = []) {
            this.memberList = data
            this.visible = true
            this.defaultSelected = defaultSelected
            // 在下一个tick中设置选中状态,确保表格已经渲染完成
            this.$nextTick(() => {
                this.setDefaultSelection();
            });
        },
        close() {
            this.visible = false
        },
        submit() {
            this.$emit('submit', this.selectData)
        },
        tableRowClassName({ row, rowIndex }) {
            if (this.selectData.findIndex(item => item.userId === row.userId) != -1) {
                return 'select-row';
            }
            return '';
        },
        setDefaultSelection() {
            if (this.defaultSelected && this.defaultSelected.length > 0) {
                this.defaultSelected.forEach(row => {
                    const targetRow = this.memberList.find(item => item.userId === row.userId);
                    if (targetRow) {
                        this.$refs.memberTable.toggleRowSelection(targetRow, true);
                    }
                });
            }
        }
    }
}
</script>
<style scoped lang="less">
.select-member-content {
    .select-member-content-right {
        margin-bottom: 31px;
        &-header {
            display: flex;
            align-items: center;
            margin-top: 5px;
            justify-content: space-between;
            margin-bottom: 21px;
            &-text {
                flex-shrink: 0;
                font-weight: 500;
                font-size: 16px;
                line-height: 16px;
                color: #222222;
                font-family: 'SourceHanSansCN-Medium';
                margin-right: 20px;
            }
            &-search {
                display: flex;
                align-items: center;
                ::v-deep .el-input {
                    margin-right: 12px;
                }
            }
        }
    }
}
</style>
laboratory/src/views/dataManagement/dispatching/addDispatch.vue
@@ -221,7 +221,7 @@
</template>
<script>
import SelectMember from '@/components/SelectMember'
import SelectMember from '@/components/SelectMember/index.vue'
import AddGroupDialog from './components/AddGroupDialog'
import AddTaskDialog from './components/AddTaskDialog'
import AIEditor from '@/components/AiEditor'
laboratory/src/views/dataManagement/schemeManagement/addPlan.vue
@@ -16,12 +16,18 @@
        </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>
          <el-table-column label="操作" width="200">
          <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-button type="text" @click="handleEditGroup(scope.row)">编辑</el-button>
              <el-button type="text" @click="handleDeleteGroup(scope.row)">移除</el-button>
              <el-tag :type="getStatusType(scope.row.status)">
                {{ getStatusText(scope.row.status) }}
              </el-tag>
            </template>
          </el-table-column>
        </Table>
@@ -38,15 +44,20 @@
          <!-- <el-button type="primary" class="el-icon-plus" @click="handleAddGroup">添加组别</el-button> -->
        </div>
        <Table :data="groupTableData" :total="0" :height="null" class="groupTable">
        <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>
        <div style="padding-left: 25px">
          <el-form-item prop="name" label="试验日期">
            <el-input v-model="form.name" placeholder="请输入" />
        <div style="padding-left: 25px;margin-top: 28px;">
          <el-form-item prop="experimentDate" label="试验日期">
            <el-date-picker
      v-model="form.experimentDate"
      type="datetime"
      placeholder="选择日期时间">
    </el-date-picker>
          </el-form-item>
        </div>
@@ -56,20 +67,18 @@
          <el-button type="primary" class="el-icon-plus" @click="addMember">选择参加人员</el-button>
        </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 ? 'member-name-box' : 'flex1'">
                <div :class="item == 1 || item == 2 ? '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">
                <div class="member-change-btn">修改</div>
                <div class="member-change-btn" @click="handleEditMember">修改</div>
              </div>
            </div>
          </div>
@@ -82,7 +91,7 @@
          </div>
        </div>
        <div class="content-box">
          <AiEditor ref="purposeEditor" v-model="editorContents.purpose" height="200px" placeholder="请输入实验目的..." />
          <AiEditor ref="purposeEditor" :value="editorContents.purpose" height="200px" placeholder="请输入实验目的..." />
        </div>
        <div class="header-title" style="margin-bottom: 38px">
@@ -92,7 +101,7 @@
          </div>
        </div>
        <div class="content-box">
          <AiEditor ref="processEditor" v-model="editorContents.process" height="200px" placeholder="请输入工艺参数及路线..." />
          <AiEditor ref="processEditor" :value="editorContents.process" height="200px" placeholder="请输入工艺参数及路线..." />
        </div>
        <div class="header-title" style="margin-bottom: 38px">
@@ -101,8 +110,8 @@
            <div>三、实验材料及设备</div>
          </div>
        </div>
        <DynamicComponent ref="materialComponent" title="实验材料" @submit="handleMaterialSubmit" />
        <DynamicComponent ref="equipmentComponent" title="实验所用设备" @submit="handleEquipmentSubmit" />
        <DynamicComponent ref="materialComponent" title="实验材料" :participants="participantsData" @submit="handleMaterialSubmit" />
        <DynamicComponent ref="equipmentComponent" title="实验所用设备" :participants="participantsData" @submit="handleEquipmentSubmit" />
        <div class="header-title" style="margin-bottom: 38px">
          <div class="header-title-left">
@@ -138,23 +147,26 @@
        </div>
      </el-form>
    </template>
    <SelectMember ref="selectMember" />
    <experimentalScheduling :show="showScheduling" />
    <SelectMemberSimple ref="selectMember" @submit="handleMemberSubmit" />
    <experimentalScheduling :show="showScheduling" @submit="handleSchedulingSubmit" @close="handleSchedulingClose" />
    <AddStep ref="addStepDialog" @submit="handleStepSubmit" />
  </Card>
</template>
<script>
import SelectMember from "@/components/SelectMember";
import experimentalScheduling from "../confirmation-sheet/components/experimental-scheduling.vue";
import SelectMemberSimple from "@/components/SelectMemberSimple/index.vue";
import experimentalScheduling from "./components/experimental-scheduling.vue";
import DynamicComponent from "@/components/DynamicComponent";
import AddStep from "./components/add-step.vue";
import AiEditor from "@/components/AiEditor";
import { getGroupByDispatchId,getParticipantsByDispatchId } from "./service";
import moment from 'moment';
import { add } from "./service";
export default {
  name: "AddProject",
  components: {
    SelectMember,
    SelectMemberSimple,
    experimentalScheduling,
    DynamicComponent,
    AddStep,
@@ -164,8 +176,16 @@
    return {
      showScheduling: false,
      form: {
        material: null,
        equipment: null,
        experimentDate: '', // 实验日期
        experimentMaterial: null,
        experimentDevice: null,
        experimentObjective: '', // 实验目的
        experimentParamRoute: '', // 工艺参数及路线
        experimentStepRecord: [], // 实验步骤记录
        experimentSchemePersons: [], // 实验方案人员
        dispatchId: '', // 实验调度id
        status: -1, // 状态:-1=草稿箱 1=已发送
        commitTime: '', // 提交时间
      },
      editorContents: {
        purpose: "",
@@ -174,24 +194,28 @@
      stepList: [],
      editingStepIndex: -1,
      rules: {
        name: [
          { required: true, message: "请输入项目组名称", trigger: "blur" },
        experimentDate: [
          { required: true, message: "请输入实验日期", trigger: "blur" },
        ],
        description: [
          { required: true, message: "请输入项目组描述", trigger: "blur" },
        ],
        material: [
        experimentMaterial: [
          { required: true, message: "请添加实验材料", trigger: "change" },
        ],
        equipment: [
        experimentDevice: [
          { required: true, message: "请添加实验设备", trigger: "change" },
        ],
      },
      groupTableData: [],
      groupData: [],
      taskTableData: [],
      participantsData: [],
      selectedParticipants: [],
    };
  },
  methods: {
    confirmAddRow() {
      // 处理添加行的逻辑
      console.log('添加行');
    },
    submitForm() {
      this.$refs.form.validate((valid) => {
        if (valid) {
@@ -200,21 +224,16 @@
      });
    },
    addMember() {
      this.$refs.selectMember.open();
      this.$refs.selectMember.open(this.participantsData, []);
    },
    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;
      }
      const roleTypes = {
        1: '工艺工程师',
        2: '化验师',
        3: '实验员'
      };
      return this.selectedParticipants.filter(member => member.roleType === i) || [];
    },
    handleAddGroup() {
      this.$refs.addGroupDialog.open();
@@ -279,39 +298,164 @@
      }
    },
    handleMaterialSubmit(data) {
      this.form.material = data;
      // 处理实验材料数据
      this.form.experimentMaterial = data;
    },
    handleEquipmentSubmit(data) {
      this.form.equipment = data;
      // 处理实验设备数据
      this.form.experimentDevice = data;
    },
    handleSave() {
      this.$refs.form.validate((valid) => {
        if (valid && this.validateContent()) {
      // 先获取所有动态组件的数据
          this.$refs.materialComponent.submit();
          this.$refs.equipmentComponent.submit();
      // 获取所有步骤内容 - 添加错误检查
      this.stepList.forEach((step, index) => {
        const stepContentRef = this.$refs['stepContent' + index];
        console.log('stepContentRef',stepContentRef)
        const editor = Array.isArray(stepContentRef) ? stepContentRef[0] : stepContentRef;
        if (editor && typeof editor.submit === 'function') {
          editor.submit();
        }
      });
      console.log('材料数据',this.form.experimentMaterial)
      console.log('设备数据',this.form.experimentDevice)
      console.log('步骤数据',this.form.stepList)
      // 然后进行表单校验
      this.$refs.form.validate((valid) => {
        if (valid && this.validateContent()) {
          // 构建提交数据
          const formData = {
            ...this.form,
            ...this.getAllEditorContent(),
            steps: this.stepList,
            // 实验日期,使用 moment 格式化为指定格式
            experimentDate: moment(this.form.experimentDate).format('YYYY-MM-DD HH:mm:ss'),
            // 实验调度ID,从选中的调度数据中获取
            dispatchId: this.groupTableData[0]?.id,
            // 实验设备,转换为JSON字符串
            experimentDevice: this.form.experimentDevice,
            // 实验材料,转换为JSON字符串
            experimentMaterial: this.form.experimentMaterial,
            // 实验目的,从富文本编辑器获取内容
            experimentObjective: this.$refs.purposeEditor.getContent(),
            // 工艺参数及路线,从富文本编辑器获取内容
            experimentParamRoute: this.$refs.processEditor.getContent(),
            // 实验方案人员,包含用户ID、昵称和角色类型
            experimentSchemePersons: this.selectedParticipants.map(person => ({
              userId: person.userId,    // 用户ID
              nickName: person.nickName, // 用户昵称
              roleType: person.roleType,  // 角色类型
              commitTime: moment().format('YYYY-MM-DD HH:mm:ss'),
            })),
            // 实验步骤记录,转换为JSON字符串,包含步骤名称和内容
            experimentStepRecord: this.stepList.map(step => ({
              stepName: step.stepName,  // 步骤名称
              content: step.content     // 步骤内容
            })),
            // 状态:1=已发送
            status: 1,
            // 提交时间,使用 moment 格式化为指定格式
            commitTime: moment().format('YYYY-MM-DD HH:mm:ss'),
          };
          console.log("提交的数据:", formData);
          this.$message.success("保存成功");
          console.log('formData 提交数据',formData)
          // 调用添加接口
          add(formData).then(res => {
            if (res.code === 200) {
              this.$message.success('保存成功');
              this.$router.go(-1)
              // 可以在这里添加跳转逻辑
            } else {
              this.$message.error(res.msg || '保存失败');
            }
          }).catch(err => {
            this.$message.error('保存失败');
            console.error('保存失败:', err);
          });
        } else {
          // 获取第一个错误字段
          const firstError = this.$refs.form.fields.find(field => field.validateState === 'error');
          if (firstError) {
            // 滚动到错误字段
            this.$nextTick(() => {
              firstError.$el.scrollIntoView({ behavior: 'smooth', block: 'center' });
            });
          }
        }
      });
    },
    handleSaveDraft() {
      // 先获取所有动态组件的数据
      this.$refs.materialComponent.submit();
      this.$refs.equipmentComponent.submit();
      // 获取所有步骤内容 - 添加错误检查
      this.stepList.forEach((step, index) => {
        const stepContentRef = this.$refs['stepContent' + index];
        if (stepContentRef && typeof stepContentRef.submit === 'function') {
          stepContentRef.submit();
        }
      });
      // 然后进行表单校验
      this.$refs.form.validate((valid) => {
        if (valid && this.validateContent()) {
          // 构建草稿数据
      const formData = {
        ...this.form,
        ...this.getAllEditorContent(),
        steps: this.stepList,
        status: "draft",
            // 实验日期,使用 moment 格式化为指定格式
            experimentDate: moment(this.form.experimentDate).format('YYYY-MM-DD HH:mm:ss'),
            // 实验调度ID,从选中的调度数据中获取
            dispatchId: this.groupTableData[0]?.id,
            // 实验设备,转换为JSON字符串
            experimentDevice: JSON.stringify(this.form.experimentDevice),
            // 实验材料,转换为JSON字符串
            experimentMaterial: JSON.stringify(this.form.experimentMaterial),
            // 实验目的,从富文本编辑器获取内容
            experimentObjective: this.$refs.purposeEditor.getContent(),
            // 工艺参数及路线,从富文本编辑器获取内容
            experimentParamRoute: this.$refs.processEditor.getContent(),
            // 实验方案人员,包含用户ID、昵称和角色类型
            experimentSchemePersons: this.selectedParticipants.map(person => ({
              userId: person.userId,    // 用户ID
              nickName: person.nickName, // 用户昵称
              roleType: person.roleType  // 角色类型
            })),
            // 实验步骤记录,转换为JSON字符串,包含步骤名称和内容
            experimentStepRecord: JSON.stringify(this.stepList.map(step => ({
              stepName: step.stepName,  // 步骤名称
              content: step.content     // 步骤内容
            }))),
            // 状态:-1=草稿箱
            status: -1,
            // 提交时间,使用 moment 格式化为指定格式
            commitTime: moment().format('YYYY-MM-DD HH:mm:ss'),
      };
      console.log("草稿数据:", formData);
      this.$message.success("草稿保存成功");
          // 调用添加接口
          add(formData).then(res => {
            if (res.code === 200) {
              this.$message.success('草稿保存成功');
              // 可以在这里添加跳转逻辑
            } else {
              this.$message.error(res.msg || '草稿保存失败');
            }
          }).catch(err => {
            this.$message.error('草稿保存失败');
            console.error('草稿保存失败:', err);
          });
        } else {
          // 获取第一个错误字段
          const firstError = this.$refs.form.fields.find(field => field.validateState === 'error');
          if (firstError) {
            // 滚动到错误字段
            this.$nextTick(() => {
              firstError.$el.scrollIntoView({ behavior: 'smooth', block: 'center' });
            });
          }
        }
      });
    },
    handleAddStep() {
      this.$refs.addStepDialog.open();
@@ -346,6 +490,7 @@
      this.$refs.addStepDialog.setStepName(this.stepList[index].stepName);
    },
    handleStepContentSubmit(index, content) {
      console.log('步骤内容',content)
      this.stepList[index].content = content;
    },
    getAllEditorContent() {
@@ -355,20 +500,123 @@
      };
    },
    validateContent() {
      const contents = this.getAllEditorContent();
      if (!contents.purpose) {
        this.$message.error("请填写实验目的");
      // // 校验实验调度
      // if (!this.groupTableData || this.groupTableData.length === 0) {
      //   this.$message.error("请选择实验调度");
      //   return false;
      // }
      // // 校验实验日期
      // if (!this.form.experimentDate) {
      //   this.$message.error("请填写实验日期");
      //   return false;
      // }
      // // 校验参与人员
      // if (!this.selectedParticipants || this.selectedParticipants.length === 0) {
      //   this.$message.error("请选择参与人员");
      //   return false;
      // }
      // // 校验实验目的
      // const purpose = this.$refs.purposeEditor.getContent();
      // if (!purpose || purpose === '<p></p>' || purpose.trim() === '<p></p>') {
      //   this.$message.error("请填写实验目的");
      //   return false;
      // }
      // // 校验工艺参数及路线
      // const process = this.$refs.processEditor.getContent();
      // if (!process || process === '<p></p>' || process.trim() === '<p></p>') {
      //   this.$message.error("请填写工艺参数及路线");
      //   return false;
      // }
      // 校验实验材料
      if (!this.form.experimentMaterial) {
        this.$message.error("请添加实验材料");
        return false;
      }
      if (!contents.process) {
        this.$message.error("请填写工艺参数及路线");
      // 校验实验设备
      if (!this.form.experimentDevice) {
        this.$message.error("请添加实验设备");
        return false;
      }
      // 校验实验步骤记录
      if (!this.stepList || this.stepList.length === 0) {
        this.$message.error("请添加实验操作步骤");
        return false;
      }
      // 校验每个步骤是否都有内容
      for (let i = 0; i < this.stepList.length; i++) {
        if (!this.stepList[i].content) {
          this.$message.error(`请完善第${i + 1}个步骤的内容`);
          return false;
        }
      }
      return true;
    },
    handleStopExperiment() {
      this.$router.push("/dataManagement/scheme-management/stop-experiment");
    },
    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] || "未知";
    },
    handleSchedulingSubmit(data) {
      this.groupTableData = data;
      if (data && data.length > 0) {
        getGroupByDispatchId({ dispatchId: data[0].id }).then(res => {
          if (res) {
            this.groupData = res || [];
          } else {
            this.$message.error(res.msg || '获取组别列表失败');
          }
        }).catch(err => {
          this.$message.error('获取组别列表失败');
          console.error('获取组别列表失败:', err);
        });
        getParticipantsByDispatchId({ dispatchId: data[0].id }).then(res => {
          console.log("获取参加人员列表:", res);
          if (res) {
            this.participantsData = res || [];
          } else {
            this.$message.error(res.msg || '获取参加人员列表失败');
          }
        }).catch(err => {
          this.$message.error('获取参加人员列表失败');
          console.error('获取参加人员列表失败:', err);
        });
      }
    },
    handleSchedulingClose() {
      this.showScheduling = false;
    },
    handleMemberSubmit(selectedMembers) {
      this.selectedParticipants = selectedMembers;
      this.$refs.selectMember.close();
    },
    handleEditMember() {
      this.$refs.selectMember.open(this.participantsData, this.selectedParticipants);
    },
  },
};
</script>
laboratory/src/views/dataManagement/schemeManagement/components/experimental-scheduling.vue
New file
@@ -0,0 +1,194 @@
<template>
    <div>
        <el-dialog title="选择实验调度" :visible.sync="show" width="80%" @close="$emit('close', false)">
            <TableCustom
            :queryForm="form" :tableData="tableData"
            :total="total" :height="null"
             @handleCurrentChange="handleCurrentChange"
             @selection-change="changeSelectedRows"
             @handleSizeChange="handleSizeChange">
                <template #search>
                    <el-form :model="form" label-width="140px" inline>
                        <el-form-item label="所属项目课题方案:">
                            <el-input v-model="form.projectName" placeholder="请输入"></el-input>
                        </el-form-item>
                        <el-form-item label="实验编号:">
                            <el-input v-model="form.experimentCode" placeholder="请输入"></el-input>
                        </el-form-item>
                        <el-form-item label="实验名称:">
                            <el-input v-model="form.experimentName" placeholder="请输入"></el-input>
                        </el-form-item>
                        <el-form-item label="通知时间:">
                            <el-date-picker v-model="form.createTime" type="daterange" range-separator="至"
                                start-placeholder="开始日期" end-placeholder="结束日期"
                                value-format="yyyy-MM-dd" @change="handleDateChange"></el-date-picker>
                        </el-form-item>
                        <el-form-item label="状态:">
                            <el-select v-model="form.status" placeholder="请选择">
                                <el-option label="全部" value=""></el-option>
                                <el-option label="待确认" value="1"></el-option>
                                <el-option label="已确认" value="2"></el-option>
                            </el-select>
                        </el-form-item>
                        <el-form-item label="" class="search-btn-box">
                            <el-button type="default" @click="resetForm">重置</el-button>
                            <el-button type="primary" @click="handleSearch">查询</el-button>
                        </el-form-item>
                    </el-form>
                </template>
                <template #table>
                    <el-table-column type="selection" width="55"></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>
                </template>
            </TableCustom>
            <span slot="footer" class="select-member-footer">
                <el-button @click="$emit('close', false)">取 消</el-button>
                <el-button type="primary" @click="handleConfirm">确 定</el-button>
            </span>
        </el-dialog>
    </div>
</template>
<script>
import { getDispatchList } from "../service";
export default {
    props: {
        show: {
            type: Boolean,
            default: false
        }
    },
    data() {
        return {
            form: {
                projectName: "",
                experimentCode: "",
                experimentName: "",
                startTime: "",
                endTime: "",
                status: "",
                pageNum: 1,
                pageSize: 10
            },
            tableData: [],
            total: 0,
            selectedRows: []
        }
    },
    watch: {
        show(val) {
            if (val) {
                this.getTableData();
            }
        }
    },
    methods: {
        handleDateChange(val) {
            if (val) {
                this.form.startTime = val[0];
                this.form.endTime = val[1];
            } else {
                this.form.startTime = "";
                this.form.endTime = "";
            }
        },
        resetForm() {
            this.form = {
                projectName: "",
                experimentCode: "",
                experimentName: "",
                startTime: "",
                endTime: "",
                status: "",
                pageNum: 1,
                pageSize: 10
            };
            this.getTableData();
        },
        handleSearch() {
            this.form.pageNum = 1;
            this.getTableData();
        },
        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] || "未知";
        },
        getTableData() {
            const params = {
                ...this.form
            };
            getDispatchList(params).then(res => {
                console.log('222222222222',res)
                if (res.code==200) {
                    this.tableData = res.data.records || [];
                    this.total = res.data.total || 0;
                }
            });
        },
        handleCurrentChange(val) {
            this.form.pageNum = val;
            this.getTableData();
        },
        handleSizeChange(val) {
            this.form.pageSize = val;
            this.form.pageNum = 1;
            this.getTableData();
        },
        changeSelectedRows(val) {
            console.log('val',val)
            this.selectedRows = val;
        },
        handleCancel() {
            this.$emit('update:show', false);
        },
        handleConfirm() {
            if (!this.selectedRows || this.selectedRows.length === 0) {
                this.$message.warning('请至少选择一条数据');
                return;
            }
            if (this.selectedRows.length != 1) {
                this.$message.warning('请选择一条数据');
                return;
            }
            this.$emit('submit', this.selectedRows);
            this.$emit('close', false);
        }
    }
}
</script>
<style lang="less" scoped>
.select-member-footer {
    display: flex;
    justify-content: flex-end;
}
</style>
laboratory/src/views/dataManagement/schemeManagement/list.vue
@@ -1,13 +1,13 @@
<template>
  <div class="list">
    <TableCustom :queryForm="form" :tableData="tableData" :total="total">
    <TableCustom :queryForm="form" :tableData="tableData" :total="total" @handlePageChange="handlePageChange" @handleSizeChange="handleSizeChange">
      <template #search>
        <el-form :model="form" labelWidth="auto" inline>
          <el-form-item label="项目项目课题方案名称:">
            <el-input v-model="form.planName" placeholder="请输入"></el-input>
          <el-form-item label="项目课题方案名称:">
            <el-input v-model="form.projectName" placeholder="请输入"></el-input>
          </el-form-item>
          <el-form-item label="实验编号:">
            <el-input v-model="form.planCode" placeholder="请输入"></el-input>
            <el-input v-model="form.experimentCode" placeholder="请输入"></el-input>
          </el-form-item>
          <el-form-item label="创建时间:">
            <el-date-picker
@@ -17,10 +17,18 @@
              start-placeholder="开始日期"
              end-placeholder="结束日期"
              value-format="yyyy-MM-dd"
              @change="handleDateChange"
            ></el-date-picker>
          </el-form-item>
          <el-form-item label="状态:">
            <el-input v-model="form.approver" placeholder="请输入"></el-input>
            <el-select v-model="form.status" placeholder="请选择">
              <el-option label="全部" value=""></el-option>
              <el-option label="已发送" :value="1"></el-option>
              <el-option label="申请中止待审核" :value="2"></el-option>
              <el-option label="申请中止已通过" :value="3"></el-option>
              <el-option label="申请中止已驳回" :value="4"></el-option>
              <el-option label="已封存" :value="5"></el-option>
            </el-select>
          </el-form-item>
          <el-form-item label="">
            <el-button type="default" @click="resetForm">重置</el-button>
@@ -37,30 +45,34 @@
              @click="handleTypeChange('list')"
            >项目课题方案列表</div>
            <div 
              v-if="userRole == '3'"
            class="drafts"
              :class="{active:currentType === 'draft'}"
              @click="handleTypeChange('draft')"
            >草稿箱</div>
          </div>
          <el-button @click="handleAddPlan" class="el-icon-plus" type="primary">
            新增实验方案</el-button
          >
          <el-button
            v-if="userRole == '3'"
            @click="handleAddPlan"
            class="el-icon-plus"
            type="primary"
          >新增实验方案</el-button>
        </div>
      </template>
      <template #table>
        <el-table-column
          prop="planName"
          prop="projectName"
          label="项目课题方案名称"
        ></el-table-column>
        <el-table-column
          prop="planCode"
          prop="experimentCode"
          label="实验编号"
        ></el-table-column>
        <el-table-column prop="stage" label="实验名称"></el-table-column>
        <el-table-column prop="testDate" label="试验日期"></el-table-column>
        <el-table-column prop="tester" 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="experimentSchemePersons" label="实验员"></el-table-column>
        <el-table-column prop="createTime" label="创建日期"></el-table-column>
        <el-table-column prop="creator" label="创建人"></el-table-column>
        <el-table-column prop="createBy" label="创建人"></el-table-column>
        <el-table-column prop="status" label="当前状态">
          <template slot-scope="scope">
            <el-tag :type="getStatusType(scope.row.status)">
@@ -70,15 +82,21 @@
        </el-table-column>
        <el-table-column label="操作" width="250">
          <template slot-scope="scope">
            <el-button
              v-if="scope.row.status === 'rejected'"
              type="text"
              @click="handleEdit(scope.row)"
              >编辑</el-button
            >
            <el-button type="text" @click="handleDetail(scope.row)"
              >详情</el-button
            >
            <!-- 超级管理员(1)和审批人(2) -->
            <template v-if="userRole == '1' || userRole == '2'">
              <el-button type="text" @click="handleDetail(scope.row)">详情</el-button>
            </template>
            <!-- 工艺工程师(3) -->
            <template v-if="userRole == '3'">
              <el-button type="text" @click="handleEdit(scope.row)" v-if="scope.row.status == '4'">编辑</el-button>
              <el-button type="text" @click="handleDetail(scope.row)">详情</el-button>
            </template>
            <!-- 实验员(5) -->
            <template v-if="userRole == '5'">
              <el-button type="text" @click="handleEdit(scope.row)" v-if="scope.row.status == '4'">编辑</el-button>
            </template>
          </template>
        </el-table-column>
      </template>
@@ -96,6 +114,7 @@
<script>
import ApprovalDialog from './components/approvalDialog.vue'
import {getList} from './service'
export default {
  name: "ProjectList",
@@ -106,331 +125,147 @@
    return {
      currentType: 'list', // 当前显示类型:list-列表,draft-草稿箱
      form: {
        planName: "",
        planCode: "",
        creator: "",
        projectName: "",
        experimentCode: "",
        createTime: [],
        approver: "",
        status: "",
        startTime: "",
        endTime: "",
        pageNum: 1,
        pageSize: 10
      },
      tableData: [],
      total: 0,
      // 模拟数据
      mockListData: [
        {
          planCode: 'PLAN-2024-001',
          planName: '2024年度实验室设备升级方案',
          stage: '设备升级实验',
          testDate: '2024-03-15',
          tester: '张三',
          creator: '张三',
          createTime: '2024-03-15',
          status: 'pending',
          approver: '李四',
          approveTime: '2024-03-16',
          purpose: '<p>1. 研究新型催化剂的性能</p><p>2. 优化反应条件</p><p>3. 提高产品收率</p>',
          processParameters: [
            {
              '工艺参数': '反应温度',
              '参数值': '25℃',
              '操作人员': ['1', '2'],
              updateTime: '2024-01-01 12:00:00'
            },
            {
              '工艺参数': '反应压力',
              '参数值': '1.0MPa',
              '操作人员': ['3'],
              updateTime: '2024-01-01 12:00:00'
            }
          ],
          materials: [
            {
              '材料名称': '催化剂A',
              '规格': '工业级',
              '数量': '100g',
              updateTime: '2024-01-01 12:00:00'
            },
            {
              '材料名称': '溶剂B',
              '规格': '分析纯',
              '数量': '500ml',
              updateTime: '2024-01-01 12:00:00'
            }
          ],
          steps: '<p>1. 准备工作</p><p>2. 设备检查</p><p>3. 实验操作</p><p>4. 数据记录</p>'
        },
        {
          planCode: 'PLAN-2024-002',
          planName: '实验室安全管理制度更新方案',
          stage: '安全测试实验',
          testDate: '2024-03-14',
          tester: '王五',
          creator: '王五',
          createTime: '2024-03-14',
          status: 'approved',
          approver: '赵六',
          approveTime: '2024-03-15',
          purpose: '<p>1. 评估现有安全管理制度</p><p>2. 制定新的安全规范</p><p>3. 进行安全培训</p>',
          processParameters: [
            {
              '工艺参数': '培训时间',
              '参数值': '2小时',
              '操作人员': ['1', '2', '3'],
              updateTime: '2024-01-01 12:00:00'
            }
          ],
          materials: [
            {
              '材料名称': '培训材料',
              '规格': 'A4',
              '数量': '50份',
              updateTime: '2024-01-01 12:00:00'
            }
          ],
          steps: '<p>1. 安全评估</p><p>2. 制度更新</p><p>3. 人员培训</p><p>4. 效果评估</p>'
        },
        {
          planCode: 'PLAN-2024-003',
          planName: '实验室人员培训计划',
          stage: '培训效果实验',
          testDate: '2024-03-13',
          tester: '孙七',
          creator: '孙七',
          createTime: '2024-03-13',
          status: 'rejected',
          approver: '周八',
          approveTime: '2024-03-14'
        }
      ],
      mockDraftData: [
        {
          planCode: 'DRAFT-2024-001',
          planName: '实验室设备采购计划(草稿)',
          stage: '规划阶段',
          creator: '张三',
          createTime: '2024-03-16',
          status: 'draft',
          approver: '',
          approveTime: ''
        },
        {
          planCode: 'DRAFT-2024-002',
          planName: '实验室改造方案(草稿)',
          stage: '准备阶段',
          creator: '李四',
          createTime: '2024-03-15',
          status: 'draft',
          approver: '',
          approveTime: ''
        }
      ],
      approvalDialogVisible: false,
      approvalDialogType: 'approve',
      approvalDialogType: 'view',
      currentApprovalData: null,
      userRole: '',
    };
  },
  created() {
    const userInfo = JSON.parse(sessionStorage.getItem('userInfo') || '{}');
    this.userRole = userInfo.roleType || '';
    if (this.userRole == '4') {
      this.$router.push('/403');
      return;
    }
    this.getTableData();
  },
  methods: {
    handlePageChange(page) {
      this.form.pageNum = page;
      this.getTableData();
    },
    handleSizeChange(size) {
      this.form.pageSize = size;
      this.form.pageNum = 1;
      this.getTableData();
    },
    resetForm() {
      this.form = {
        planName: "",
        planCode: "",
        creator: "",
        projectName: "",
        experimentCode: "",
        createTime: [],
        approver: "",
        status: "",
        startTime: "",
        endTime: "",
        pageNum: 1,
        pageSize: 10
      };
      this.getTableData();
    },
    handleSearch() {
      // 实现查询逻辑
      console.log("查询条件:", this.form);
      this.form.pageNum = 1;
      this.getTableData();
    },
    getStatusType(status) {
      const statusMap = {
        pending: "warning",
        rejected: "danger",
        approved: "success",
        archived: "info",
        draft: "info"
        '-1': "info",
        '1': "warning",
        '2': "warning",
        '3': "success",
        '4': "danger",
        '5': "info"
      };
      return statusMap[status] || "info";
    },
    getStatusText(status) {
      const statusMap = {
        pending: "待审批",
        rejected: "已驳回",
        approved: "已通过",
        archived: "已封存",
        draft: "草稿"
        '-1': "草稿",
        '1': "已发送",
        '2': "申请中止待审核",
        '3': "申请中止已通过",
        '4': "申请中止已驳回",
        '5': "已封存"
      };
      return statusMap[status] || "未知";
    },
    handleDateChange(val) {
      if (val) {
        this.form.startTime = val[0];
        this.form.endTime = val[1];
      } else {
        this.form.startTime = '';
        this.form.endTime = '';
      }
    },
    handleTypeChange(type) {
      this.currentType = type;
      this.form.status = type === 'draft' ? -1 : '';
      this.form.pageNum = 1;
      this.getTableData();
    },
    async getTableData() {
      try {
        const { data } = await getList(this.form);
        this.tableData = data.records || [];
        this.total = data.total || 0;
      } catch (error) {
        console.error('获取列表数据失败:', error);
        this.$message.error('获取列表数据失败');
      }
    },
    handleAddPlan() {
      this.$router.push({
        path: "/dataManagement/scheme-management/add",
      });
    },
    handleApprove(row) {
      this.currentApprovalData = row;
      this.approvalDialogType = 'approve';
      this.approvalDialogVisible = true;
    },
    handleApproveSubmit(data) {
      // 处理审批通过
      console.log('审批通过:', data);
      this.approvalDialogVisible = false;
      this.$message.success('审批通过成功');
      this.getTableData();
    },
    handleRejectSubmit(data) {
      // 处理审批驳回
      console.log('审批驳回:', data);
      this.approvalDialogVisible = false;
      this.$message.success('审批驳回成功');
      this.getTableData();
    },
    handleRevokeApprove(row) {
      // 实现撤销审批逻辑
      console.log("撤销审批数据:", row);
    },
    handleEdit(row) {
      this.$router.push({
        path: "/dataManagement/scheme-management/add",
        query: {
          id: row.planCode,
          id: row.id,
          type: 'edit'
        }
      });
    },
    handleDelete(row) {
      // 实现删除逻辑
      console.log("删除数据:", row);
    },
    handleDetail(row) {
      // 打开弹窗
      this.approvalDialogType = 'view';
      this.approvalDialogVisible = true;
      // 调用获取详情接口
      this.getPlanDetail(row.planCode);
      this.getPlanDetail(row.id);
    },
    // 获取方案详情
    async getPlanDetail(planCode) {
    async getPlanDetail(id) {
      try {
        // TODO: 替换为实际的接口调用
        // const { data } = await this.$api.getPlanDetail({ planCode });
        // 模拟接口返回数据
        const mockDetailData = {
          planCode: 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',
          experimentPurpose: [
            {
              id: 1,
              type: 'richText',
              data: {
                content: '<p>1. 研究新型催化剂的性能</p><p>2. 优化反应条件</p><p>3. 提高产品收率</p>'
              }
            }
          ],
          processParameters: [
            {
              id: 2,
              type: 'customTable',
              data: {
                headers: [
                  { name: '工艺参数', type: 'text' },
                  { name: '参数值', type: 'text' },
                  { name: '操作人员', type: 'user' }
                ],
                rows: [
                  {
                    '工艺参数': '反应温度',
                    '参数值': '25℃',
                    '操作人员': ['1', '2'],
                    updateTime: '2024-01-01 12:00:00'
                  },
                  {
                    '工艺参数': '反应压力',
                    '参数值': '1.0MPa',
                    '操作人员': ['3'],
                    updateTime: '2024-01-01 12:00:00'
                  }
                ]
              }
            }
          ],
          materialsAndEquipment: [
            {
              id: 3,
              type: 'customTable',
              data: {
                headers: [
                  { 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'
                  }
                ]
              }
            }
          ],
          operationSteps: [
            {
              id: 4,
              type: 'richText',
              data: {
                content: '<p>1. 准备工作</p><p>2. 设备检查</p><p>3. 实验操作</p><p>4. 数据记录</p>'
              }
            }
          ]
        };
        // 更新弹窗数据
        this.currentApprovalData = mockDetailData;
        const { data } = await this.$api.getDetail({ id });
        this.currentApprovalData = data;
      } catch (error) {
        console.error('获取方案详情失败:', error);
        this.$message.error('获取方案详情失败');
        this.approvalDialogVisible = false;
      }
    },
    handleTypeChange(type) {
      this.currentType = type;
    handleApproveSubmit(data) {
      this.approvalDialogVisible = false;
      this.$message.success('审批通过成功');
      this.getTableData();
    },
    getTableData() {
      // 根据currentType请求不同的数据
      if (this.currentType === 'list') {
        this.tableData = this.mockListData;
        this.total = this.mockListData.length;
      } else {
        this.tableData = this.mockDraftData;
        this.total = this.mockDraftData.length;
    handleRejectSubmit(data) {
      this.approvalDialogVisible = false;
      this.$message.success('审批驳回成功');
      this.getTableData();
      }
    },
  },
};
</script>
laboratory/src/views/dataManagement/schemeManagement/service.js
New file
@@ -0,0 +1,46 @@
import axios from '@/utils/request';
// 列表
export const getList = (data) => {
  return axios.post('/api/t-experiment-scheme/pageList', { ...data })
}
// 详情
export const getDetail = (data) => {
  return axios.get('/open/t-experiment-scheme/getDetailById', { params:data })
}
//添加
export const add = (data) => {
  return axios.post('/api/t-experiment-scheme/add', { ...data })
}
//修改
export const update = (data) => {
  return axios.post('/api/t-experiment-scheme/update', { ...data })
}
//删除
export const deleteById = (data) => {
  return axios.delete('/open/t-experiment-scheme/deleteById', { params:data })
}
//批量删除
export const deleteByIds = (data) => {
  return axios.delete('/open/t-experiment-scheme/deleteByIds', { params:data })
}
// 申请中止实验
export const applicationTermination = (data) => {
  return axios.post('/api/t-experiment-scheme/applicationTermination', { ...data })
}
// 通过实验调度查询查询组别列表
export const getGroupByDispatchId = (data) => {
  return axios.get('/open/t-experiment-scheme/getGroupByDispatchId', { params:data })
}
// 通过实验调度查询查询参加人员列表
export const getParticipantsByDispatchId = (data) => {
  return axios.get('/open/t-experiment-scheme/getParticipantsByDispatchId', { params:data })
}
// 申请中止实验审核
export const audit = (data) => {
  return axios.get('/api/t-experiment-scheme/audit', { params:data })
}
// 获取实验调度列表
export const getDispatchList = (data) => {
  return axios.post('/api/t-experiment-dispatch/pageList', { params:data })
}