董国庆
2025-04-10 2977d97b59e336bd53baa14b8d1022b087c8fdc1
新建页面,打开表格的滚动条
6个文件已添加
2个文件已修改
1922 ■■■■■ 已修改文件
src/App.vue 67 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/router/index.js 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/dataManagement/sampleManage/addSample.vue 588 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/dataManagement/sampleManage/components/addTime.vue 102 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/dataManagement/sampleManage/components/approvalDialog.vue 402 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/dataManagement/sampleManage/components/experimental-scheduling.vue 81 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/dataManagement/sampleManage/components/sampleDialog.vue 329 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/dataManagement/sampleManage/list.vue 335 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/App.vue
@@ -5,57 +5,77 @@
</template>
<script>
import { mapState } from 'vuex'
import { mapState } from "vuex";
export default {
  name: 'App',
  name: "App",
  data() {
    return {
      windowWidth: window.innerWidth,
    }
    };
  },
  watch: {
    windowWidth(newWidth) {
      // 当窗口宽度小于某个值时,可以触发折叠
      if (newWidth < 1000) {
        this.$store.commit('SET_ISFOLD', true)
        this.$store.commit("SET_ISFOLD", true);
      } else if (newWidth >= 1000 && this.isFold) {
        this.$store.commit('SET_ISFOLD', false)
        this.$store.commit("SET_ISFOLD", false);
      }
    }
    },
  },
  computed: {
    ...mapState(['isFold'])
    ...mapState(["isFold"]),
  },
  created() {
    // 初始化时检查窗口大小
    this.handleResize()
    this.handleResize();
  },
  mounted() {
    // 监听窗口大小变化
    window.addEventListener('resize', this.handleResize)
    window.addEventListener("resize", this.handleResize);
  },
  beforeDestroy() {
    // 移除监听
    window.removeEventListener('resize', this.handleResize)
    window.removeEventListener("resize", this.handleResize);
  },
  methods: {
    handleResize() {
      if (window.innerWidth < 1000) {
        this.$store.commit('SET_ISFOLD', true)
        this.$store.commit("SET_ISFOLD", true);
      } else if (window.innerWidth >= 1000 && this.isFold) {
        this.$store.commit('SET_ISFOLD', false)
        this.$store.commit("SET_ISFOLD", false);
      }
      this.windowWidth = window.innerWidth
    }
      this.windowWidth = window.innerWidth;
    },
  },
}
};
</script>
<style lang="less">
::-webkit-scrollbar {
  display: none;
}
.table-container {
  ::-webkit-scrollbar {
    display: block !important;
    width: 6px;
    height: 6px;
  }
  ::-webkit-scrollbar-track {
    background: #f1f1f1;
    border-radius: 3px;
  }
  ::-webkit-scrollbar-thumb {
    background: #009688;
    border-radius: 3px;
  }
  ::-webkit-scrollbar-thumb:hover {
    background: #00796b;
  }
}
html,
body,
#app {
@@ -66,7 +86,7 @@
}
.selected {
  color: #049C9A !important;
  color: #049c9a !important;
}
.el-button--primary {
@@ -82,15 +102,14 @@
.el-button--default:hover {
  color: #009688 !important;
  border-color: #009688 !important;
  background: #E6FFFF !important;
  background: #e6ffff !important;
}
.card-custom {
  .el-form {
    .el-form-item__label {
      color: #222222;
      font-family: 'SourceHanSansCN-Medium';
      font-family: "SourceHanSansCN-Medium";
      line-height: 14px;
    }
@@ -103,7 +122,6 @@
  }
  .el-dialog {
    .el-form-item__label {
      line-height: 32px;
    }
@@ -118,7 +136,7 @@
  border-radius: 16px 16px 6px 6px;
  .el-dialog__header {
    font-family: 'Source Han Sans CN Bold Bold';
    font-family: "Source Han Sans CN Bold Bold";
    font-weight: bold;
    font-size: 20px;
    line-height: 20px;
@@ -165,8 +183,8 @@
  background-color: #009688;
}
.el-button+.el-button,
.el-checkbox.is-bordered+.el-checkbox.is-bordered {
.el-button + .el-button,
.el-checkbox.is-bordered + .el-checkbox.is-bordered {
  margin-left: 0;
}
@@ -178,7 +196,6 @@
}
.search-btn-box .el-form-item__content {
  margin-left: 63px;
}
</style>
src/router/index.js
@@ -181,6 +181,24 @@
                },
                component: () => import("../views/dataManagement/confirmation-sheet/components/add.vue"),
            },
            {
                path: "sampleManage",
                meta: {
                    title: "样品管理",
                    keepAlive: true,
                },
                component: () => import("../views/dataManagement/sampleManage/list.vue"),
            },
            {
                path: "addSample",
                name: "addPaddSamplelan",
                meta: {
                    title: "新增样品",
                    hide: true,
                    keepAlive: true,
                },
                component: () => import("../views/dataManagement/sampleManage/addSample"),
            },
        ],
    },
    {
src/views/dataManagement/sampleManage/addSample.vue
New file
@@ -0,0 +1,588 @@
<template>
  <Card>
    <div class="header-title-left">
      <img src="@/assets/public/headercard.png" />
      <div>所属实验调度</div>
      <el-button
        v-if="isEngineer"
        @click="showScheduling = true"
        class="el-icon-plus"
        type="primary"
      >
        选择实验调度</el-button
      >
    </div>
    <Table :data="schedulingData" :total="schedulingTotal" :height="null">
      <template>
        <el-table-column
          prop="planCode"
          label="所属项目课题方案"
        ></el-table-column>
        <el-table-column prop="planName" label="实验编号"></el-table-column>
        <el-table-column prop="planName" label="实验名称"></el-table-column>
        <el-table-column prop="stage" label="通知时间"></el-table-column>
        <el-table-column prop="stage" label="实验开始时间"></el-table-column>
        <el-table-column prop="stage" label="实验结束时间"></el-table-column>
        <el-table-column prop="stage" label="参加人员"></el-table-column>
        <el-table-column prop="creator" label="状态"></el-table-column>
      </template>
    </Table>
    <div class="header-title-left" style="margin-top: 38px">
      <img src="@/assets/public/headercard.png" />
      <span>基础信息</span>
    </div>
    <el-form
      ref="form"
      :model="form"
      :rules="rules"
      inline
      label-position="top"
    >
      <el-form-item label="取样单编号" prop="sampleCode">
        <el-input
          v-model="form.sampleCode"
          :disabled="!isExperimenter"
          :placeholder="isExperimenter ? '请输入取样单编号' : '自动生成'"
        />
      </el-form-item>
    </el-form>
    <div class="header-title-left" style="margin-top: 38px">
      <img src="@/assets/public/headercard.png" />
      <span>取样操作记录</span>
      <el-button
        v-if="isEngineer"
        type="primary"
        class="el-icon-plus"
        @click="showAddTime = true"
      >
        新增工艺时间
      </el-button>
      <el-button
        type="primary"
        class="el-icon-plus"
        @click="showAdditives = !showAdditives"
      >
        {{ showAdditives ? '收起辅料详情' : '展开辅料详情' }}
      </el-button>
    </div>
    <Table :data="sampleData" :total="sampleTotal" :height="null">
      <template>
        <el-table-column prop="index" label="序号" width="80"></el-table-column>
        <el-table-column prop="processTime" label="工艺时间" width="150"></el-table-column>
        <el-table-column prop="sampleName" label="取样名称" width="150"></el-table-column>
        <el-table-column prop="sampleCode" label="取样样品编号" width="150"></el-table-column>
        <el-table-column prop="temperature" label="温度" width="150"></el-table-column>
        <el-table-column prop="ph" label="PH" width="150"></el-table-column>
        <el-table-column prop="waterAmount" label="加水量" width="150"></el-table-column>
        <template v-if="showAdditives">
          <el-table-column prop="additive1" label="加辅1" width="150" ></el-table-column>
          <el-table-column prop="additive2" label="加辅2" width="150"></el-table-column>
          <el-table-column prop="additive3" label="加辅3" width="150"></el-table-column>
          <el-table-column prop="additive4" label="加辅4" width="150"></el-table-column>
          <el-table-column prop="additive5" label="加辅5" width="150"></el-table-column>
          <el-table-column prop="additive6" label="加辅6" width="150"></el-table-column>
          <el-table-column prop="additive7" label="加辅7" width="150"></el-table-column>
          <el-table-column prop="additive8" label="加辅8" width="150"></el-table-column>
          <el-table-column prop="additive9" label="加辅9" width="150"></el-table-column>
          <el-table-column prop="additive10" label="加辅10" width="150"></el-table-column>
        </template>
        <el-table-column prop="sampleAmount" label="取样量" width="150"></el-table-column>
        <el-table-column prop="sampleTime" label="取样时间" width="150"></el-table-column>
        <el-table-column prop="remark" label="拍照" width="150"></el-table-column>
        <el-table-column prop="operator" label="操作人员" width="150"></el-table-column>
        <el-table-column v-if="isExperimenter" fixed="right" label="操作" width="150">
          <template slot-scope="scope">
            <el-button
               type="text"
              @click="handleEdit(scope.$index, scope.row)"
            >编辑</el-button>
          </template>
        </el-table-column>
      </template>
    </Table>
    <div class="remark-section" v-if="isExperimenter">
      <el-form :model="form" label-position="top">
        <el-form-item label="备注">
          <el-input
            type="textarea"
            v-model="form.remark"
            :rows="4"
            placeholder="请输入备注信息"
          ></el-input>
        </el-form-item>
      </el-form>
    </div>
    <div class="add-project-footer">
      <el-button type="primary" class="save-btn">保存</el-button>
      <el-button>存草稿</el-button>
    </div>
    <experimentalScheduling :show="showScheduling" />
    <addTime :show.sync="showAddTime" @confirm="handleAddTime" />
    <sample-dialog
      :visible.sync="showSampleDialog"
      :data="currentSampleData"
      @submit="handleSampleSubmit"
    />
  </Card>
</template>
<script>
import experimentalScheduling from "./components/experimental-scheduling.vue";
import addTime from "./components/addTime.vue";
import SampleDialog from "./components/sampleDialog.vue";
export default {
  name: "AddConfirmationSheet",
  components: {
    experimentalScheduling,
    addTime,
    SampleDialog
  },
  props: {},
  data() {
    return {
      showScheduling: false,
      showAddTime: false,
      showAdditives: true,
      showSampleDialog: false,
      currentSampleData: {},
      currentEditIndex: -1,
      schedulingData: [], // 实验调度表格数据
      schedulingTotal: 0,
      sampleData: [
        {
          index: 1,
          processTime: '2h',
          sampleName: '样品A',
          sampleCode: 'SP001',
          temperature: '25℃',
          ph: '7.0',
          waterAmount: '500ml',
          additive1: '10g',
          additive2: '5g',
          additive3: '3g',
          additive4: '',
          additive5: '',
          additive6: '',
          additive7: '',
          additive8: '',
          additive9: '',
          additive10: '',
          sampleAmount: '100ml',
          sampleTime: '2024-03-20 10:00:00',
          remark: '',
          operator: '张三'
        },
        {
          index: 2,
          processTime: '4min',
          sampleName: '样品B',
          sampleCode: 'SP002',
          temperature: '30℃',
          ph: '6.5',
          waterAmount: '300ml',
          additive1: '8g',
          additive2: '4g',
          additive3: '',
          additive4: '',
          additive5: '',
          additive6: '',
          additive7: '',
          additive8: '',
          additive9: '',
          additive10: '',
          sampleAmount: '50ml',
          sampleTime: '2024-03-20 10:30:00',
          remark: '',
          operator: '李四'
        }
      ],
      sampleTotal: 2,
      form: {
        sampleCode: '',
        remark: '',
      },
      rules: {
        sampleCode: [
          { required: true, message: '请输入取样单编号', trigger: 'blur' }
        ],
      },
      menu: [],
      userRole: 'experimenter', // 用户角色,可以是 'engineer' 或 'experimenter'
    };
  },
  computed: {
    height() {
      return this.$baseTableHeight();
    },
    isEngineer() {
      return this.userRole === 'engineer';
    },
    isExperimenter() {
      return this.userRole === 'experimenter';
    }
  },
  watch: {},
  created() {},
  mounted() {},
  methods: {
    setSelectedIds(arr, selectKeyList) {
      function traverse(item) {
        item.selected = selectKeyList.includes(item.menuId);
        if (item.children && item.children.length > 0) {
          item.children.forEach(traverse);
        }
      }
      arr.forEach(traverse);
      return arr;
    },
    onSubmit() {
      this.$refs["form"].validate((valid) => {
        if (valid) {
          if (this.getSelectedIds(this.menu).length == 0) {
            this.msgwarning("请勾选操作权限");
            return;
          }
          let obj = {
            ...this.form,
            menuIds: this.getSelectedIds(this.menu),
          };
          if (this.$route.query && this.$route.query.roleId) {
            obj.roleId = this.$route.query.roleId;
            edit(obj).then(() => {
              this.msgsuccess("保存成功");
              this.$router.go(-1);
            });
          } else {
            add(obj).then(() => {
              this.msgsuccess("保存成功");
              this.form = {
                roleName: "",
                remark: "",
              };
              this.menu = [];
              this.$router.go(-1);
            });
          }
        }
      });
    },
    getSelectedIds(arr) {
      let result = [];
      function traverse(item) {
        if (item.selected) {
          result.push(item.menuId);
        }
        if (item.children && item.children.length > 0) {
          for (let children of item.children) {
            traverse(children);
          }
        }
      }
      for (let item of arr) {
        traverse(item);
      }
      return result;
    },
    setCheckStatus1(id, status) {
      //点击第1级
      if (!status) {
        this.menu = this.menu.map((item) => {
          if (item.menuId == id) {
            item.selected = status;
            if (item.children.length > 0) {
              item.children = item.children.map((item1) => {
                item1.selected = status;
                if (item1.children.length > 0) {
                  item1.children = item1.children.map((item2) => {
                    item2.selected = status;
                    return { ...item2 };
                  });
                }
                return { ...item1 };
              });
            }
          }
          return { ...item };
        });
      } else {
        this.menu = this.menu.map((item) => {
          if (item.menuId == id) {
            item.selected = true;
          }
          return { ...item };
        });
      }
    },
    setCheckStatus2(id, status, aId) {
      //点击第2级
      this.menu = this.menu.map((item) => {
        if (item.menuId == aId) {
          item.selected = true;
          if (item.children.length > 0) {
            item.children = item.children.map((item1) => {
              if (item1.menuId == id) {
                item1.selected = status;
              }
              return { ...item1 };
            });
          }
        }
        return { ...item };
      });
    },
    setCheckStatus3(id, status, bId, aId) {
      //点击第3级
      this.menu = this.menu.map((item) => {
        if (item.menuId == aId) {
          item.selected = true;
          if (item.children.length > 0) {
            item.children = item.children.map((item1) => {
              if (item1.menuId == bId) {
                item1.selected = true;
                if (item1.children.length > 0) {
                  item1.children = item1.children.map((item2) => {
                    if (item2.menuId == id) {
                      item2.selected = status;
                    }
                    return { ...item2 };
                  });
                }
              }
              return { ...item1 };
            });
          }
        }
        return { ...item };
      });
    },
    handleEdit(index, row) {
      this.currentEditIndex = index;
      this.currentSampleData = { ...row };
      this.showSampleDialog = true;
    },
    handleSampleSubmit(formData) {
      if (this.currentEditIndex > -1) {
        // 更新表格数据
        this.$set(this.sampleData, this.currentEditIndex, {
          ...this.sampleData[this.currentEditIndex],
          ...formData
        });
        this.currentEditIndex = -1;
      }
      this.showSampleDialog = false;
    },
    handleDelete(index, row) {
      this.$confirm('确认删除该记录吗?', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(() => {
        this.sampleData.splice(index, 1);
        // 重新计算序号
        this.sampleData.forEach((item, i) => {
          item.index = i + 1;
        });
        this.$message.success('删除成功');
      }).catch(() => {});
    },
    handleAddTime(processTime) {
      if (!this.isEngineer) return;
      const newRow = {
        index: this.sampleData.length + 1,
        processTime,
        sampleName: '',
        sampleCode: '',
        temperature: '',
        ph: '',
        waterAmount: '',
        additive1: '',
        additive2: '',
        additive3: '',
        additive4: '',
        additive5: '',
        additive6: '',
        additive7: '',
        additive8: '',
        additive9: '',
        additive10: '',
        sampleAmount: '',
        sampleTime: '',
        remark: '',
        operator: ''
      };
      this.sampleData.push(newRow);
      this.$message.success('添加工艺时间成功');
    },
  },
};
</script>
<style lang="less" scoped>
.title_box {
  display: flex;
  align-items: center;
  margin-bottom: 20px;
  font-weight: bold;
  div:first-child {
    width: 4px;
    height: 16px;
    background: #598dec;
    margin-right: 8px;
  }
}
.no-data {
  height: 100%;
  background-color: #fff;
  border-radius: 0 0 6px 6px;
  border-left: 1px solid #e8e8e8;
  border-right: 1px solid #e8e8e8;
  border-bottom: 1px solid #e8e8e8;
  box-sizing: border-box;
  display: flex;
  align-items: center;
  justify-content: center;
  color: #909399;
  font-size: 14px;
}
.add-project-footer {
  margin-top: 43px;
  button {
    width: 220px;
  }
  .save-btn {
    margin-right: 20px;
  }
}
.el-checkbox {
  display: flex;
  align-items: center;
}
.row,
.header {
  display: flex;
  align-items: center;
  border: 1px solid #e8e8e8;
  .w20 {
    width: 15%;
    padding: 8px 20px;
  }
  .sconed {
    flex: 1;
    .subpage {
      .title {
        border: 1px solid #e8e8e8;
        border-top: none;
        border-bottom: none;
      }
      .two {
        display: flex;
        align-items: center;
        border: 1px solid #e8e8e8;
        border-top: none;
        border-right: none;
        .left {
          width: 200px;
          padding: 13px 20px;
          border-right: 1px solid #e8e8e8;
        }
        .right {
          display: flex;
          flex: 1;
          div {
            padding: 13px 0 13px 20px;
          }
        }
      }
      .two:last-child {
        border-bottom: none;
      }
      .btns {
        display: flex;
        align-items: center;
        padding: 0 20px;
      }
    }
  }
}
.header {
  border-radius: 16px 16px 0 0;
  background-color: #fafafa;
  color: #909399;
  .subpage {
    display: flex;
  }
  .title {
    width: 200px;
    padding: 8px 20px;
  }
}
.header-title-left {
  display: flex;
  align-items: center;
  gap: 13px;
  margin-bottom: 20px;
  .el-button {
    margin-left: 0;
  }
  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";
  }
}
.remark-section {
  margin-top: 20px;
  .el-form-item {
    width: 100%;
    .el-textarea {
      width: 100%;
    }
  }
}
</style>
src/views/dataManagement/sampleManage/components/addTime.vue
New file
@@ -0,0 +1,102 @@
<template>
  <div>
    <el-dialog
      title="新增工艺时间"
      :visible.sync="dialogVisible"
      width="40%"
      @close="handleClose"
    >
      <el-form
        :model="form"
        ref="form"
        :rules="rules"
        label-width="100px"
        class="center-form"
        label-position="top"
      >
        <el-form-item label="工艺时间" prop="processTime">
          <el-input
            v-model="form.processTime"
            placeholder="请输入工艺时间"
            style="width: 300px;"
          ></el-input>
        </el-form-item>
      </el-form>
      <span slot="footer" class="dialog-footer">
        <el-button @click="handleClose">取 消</el-button>
        <el-button type="primary" @click="handleSubmit">确认新增</el-button>
      </span>
    </el-dialog>
  </div>
</template>
<script>
export default {
  props: {
    show: {
      type: Boolean,
      default: false,
    },
  },
  data() {
    return {
      dialogVisible: false,
      form: {
        processTime: "",
      },
      rules: {
        processTime: [
          { required: true, message: "请输入工艺时间", trigger: "blur" },
        ],
      },
    };
  },
  watch: {
    show: {
      immediate: true,
      handler(val) {
        this.dialogVisible = val;
      },
    },
  },
  methods: {
    handleClose() {
      this.resetForm();
      this.$emit("update:show", false);
    },
    resetForm() {
      if (this.$refs.form) {
        this.$refs.form.resetFields();
      }
      this.form.processTime = "";
    },
    handleSubmit() {
      this.$refs.form.validate((valid) => {
        if (valid) {
          this.$emit("confirm", this.form.processTime);
          this.handleClose();
        }
      });
    },
  },
};
</script>
<style lang="less" scoped>
.dialog-footer {
  display: flex;
  justify-content: center;
  gap: 10px;
}
.center-form {
  display: flex;
  flex-direction: column;
  align-items: center;
  :deep(.el-form-item__label) {
    text-align: left;
    width: 100%;
  }
}
</style>
src/views/dataManagement/sampleManage/components/approvalDialog.vue
New file
@@ -0,0 +1,402 @@
<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 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>
      </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>
        </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>
</template>
<script>
import ApprovalProcess from '@/components/approvalProcess'
export default {
  name: "ApprovalDialog",
  components: {
    ApprovalProcess
  },
  props: {
    visible: {
      type: Boolean,
      default: false,
    },
    type: {
      type: String,
      default: "approve", // approve-审批,view-查看
    },
    data: {
      type: Object,
      default: () => ({}),
    },
  },
  data() {
    return {
      form: {
        planName: "",
        planCode: "",
        stage: "",
        creator: "",
        createTime: "",
        approvalComment: "",
        status: "pending",
        approver: "",
        approveTime: ""
      },
      radio1: 1,
      rules: {},
      status: "1",
      remark: "",
    };
  },
  computed: {
    dialogTitle() {
      return this.type === "approve" ? "审批" : "审批详情";
    },
  },
  watch: {
    data: {
      handler(val) {
        if (val) {
          this.form = { ...val };
        }
      },
      immediate: true,
    },
  },
  methods: {
    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",
      });
    },
  },
};
</script>
<style scoped lang="less">
::v-deep .el-dialog__header {
  border-bottom: 1px solid #e4e7ed;
}
.approval-dialog {
  display: flex;
  height: 300px;
  .approval-content {
    flex: 1;
    margin-right: 20px;
    background: #ffffff;
    box-shadow: 0px 4px 12px 4px rgba(0, 0, 0, 0.08);
    border-radius: 10px;
  }
  .approval-flow {
    padding: 40px 20px;
    width: 405px;
    background: #ffffff;
    box-shadow: 0px 4px 12px 4px rgba(0, 0, 0, 0.08);
    border-radius: 10px;
    .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-content-card {
  height: calc(100% - 100px) !important;
  box-shadow: none !important;
}
.header-title {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 13px;
  .header-title-left {
    display: flex;
    align-items: center;
    gap: 13px;
    margin-top: 38px;
    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 {
  .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 {
  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;
  }
}
.dialog-footer{
    align-items: center;
    display: flex;
    justify-content: center;
    button{
        width: 150px;
    }
}
</style>
src/views/dataManagement/sampleManage/components/experimental-scheduling.vue
New file
@@ -0,0 +1,81 @@
<template>
    <div>
        <el-dialog title="选择项目组" :visible.sync="show" width="80%">
            <TableCustom :queryForm="form" :tableData="tableData" :total="total" :height="null">
                <template #search>
                    <el-form :model="form" label-width="140px" inline>
                        <el-form-item label="所属项目课题方案:">
                            <el-input v-model="form.planName" placeholder="请输入"></el-input>
                        </el-form-item>
                        <el-form-item label="实验编号:">
                            <el-input v-model="form.planCode" placeholder="请输入"></el-input>
                        </el-form-item>
                        <el-form-item label="实验名称:">
                            <el-input v-model="form.creator" 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"></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="已确认"></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 prop="planCode" label="所属项目课题方案"></el-table-column>
                    <el-table-column prop="planName" label="实验编号"></el-table-column>
                    <el-table-column prop="planName" label="实验名称"></el-table-column>
                    <el-table-column prop="stage" label="通知时间"></el-table-column>
                    <el-table-column prop="stage" label="实验开始时间"></el-table-column>
                    <el-table-column prop="stage" label="试验结束时间"></el-table-column>
                    <el-table-column prop="stage" label="参加人员"></el-table-column>
                    <el-table-column prop="creator" label="状态"></el-table-column>
                </template>
            </TableCustom>
            <span slot="footer" class="dialog-footer">
                <el-button @click="show = false">取 消</el-button>
                <el-button type="primary" @click="show = false">确 定</el-button>
            </span>
        </el-dialog>
    </div>
</template>
<script>
export default {
    props: ['show'],
    data() {
        return {
            form: {},
            tableData: [],
            total: 0
        }
    },
    methods: {
        resetForm() {
        },
        handleSearch() {
        }
    }
}
</script>
<style lang="less" scoped>
.dialog-footer {
    display: flex
;
    justify-content: center;
    gap: 10px;
}
</style>
src/views/dataManagement/sampleManage/components/sampleDialog.vue
New file
@@ -0,0 +1,329 @@
<template>
  <el-dialog
    title="取样样品"
    :visible.sync="visible"
    width="60%"
    :close-on-click-modal="false"
    @close="handleClose"
  >
    <div class="sample-dialog">
      <div class="sample-content">
        <div class="header-title">
          <div class="header-title-left">
            <span>工艺时间:{{ form.processTime }}</span>
          </div>
        </div>
        <div class="form-content">
          <el-form
            ref="form"
            :model="form"
            :rules="rules"
            label-position="top"
            style="margin-top: 38px"
          >
            <el-row :gutter="20">
              <el-col :span="12">
                <el-form-item label="取样名称" prop="sampleName">
                  <el-input
                    v-model="form.sampleName"
                    placeholder="请输入取样名称"
                  />
                </el-form-item>
              </el-col>
              <el-col :span="12">
                <el-form-item label="取样样品编号" prop="sampleCode">
                  <el-input
                    v-model="form.sampleCode"
                    placeholder="请输入取样样品编号"
                  />
                </el-form-item>
              </el-col>
            </el-row>
            <el-row :gutter="20">
              <el-col :span="8">
                <el-form-item label="温度" prop="temperature">
                  <el-input
                    v-model="form.temperature"
                    placeholder="请输入温度"
                  />
                </el-form-item>
              </el-col>
              <el-col :span="8">
                <el-form-item label="PH" prop="ph">
                  <el-input
                    v-model="form.ph"
                    placeholder="请输入PH值"
                  />
                </el-form-item>
              </el-col>
              <el-col :span="8">
                <el-form-item label="加水量" prop="waterAmount">
                  <el-input
                    v-model="form.waterAmount"
                    placeholder="请输入加水量"
                  />
                </el-form-item>
              </el-col>
            </el-row>
            <el-row :gutter="20">
              <el-col :span="4.6" v-for="i in 5" :key="'additive' + i">
                <el-form-item :label="'加辅' + i" :prop="'additive' + i">
                  <el-input v-model="form['additive' + i]" placeholder="请输入" />
                </el-form-item>
              </el-col>
            </el-row>
            <el-row :gutter="20">
              <el-col :span="4.6" v-for="i in 5" :key="'additive' + (i + 5)">
                <el-form-item
                  :label="'加辅' + (i + 5)"
                  :prop="'additive' + (i + 5)"
                >
                  <el-input
                    v-model="form['additive' + (i + 5)]"
                    placeholder="请输入"
                  />
                </el-form-item>
              </el-col>
            </el-row>
            <el-row :gutter="20">
              <el-col :span="24">
                <el-form-item
                  label="取样量"
                  prop="sampleAmount"
                  :rules="[
                    { required: true, message: '请输入取样量', trigger: 'blur' },
                  ]"
                >
                  <el-input
                    v-model="form.sampleAmount"
                    placeholder="请输入取样量"
                  />
                </el-form-item>
              </el-col>
            </el-row>
            <el-row :gutter="20">
              <el-col :span="24">
                <el-form-item
                  label="拍照"
                  prop="fileList"
                  :rules="[
                    { required: true, message: '请上传照片', trigger: 'change' },
                  ]"
                >
                  <el-upload
                    class="upload-demo"
                    action="#"
                    :file-list="fileList"
                    :auto-upload="false"
                    list-type="picture-card"
                  >
                    <i class="el-icon-plus"></i>
                  </el-upload>
                </el-form-item>
              </el-col>
            </el-row>
          </el-form>
        </div>
      </div>
    </div>
    <div slot="footer" class="dialog-footer">
      <el-button @click="handleClose">取 消</el-button>
      <el-button type="primary" @click="handleSubmit" style="margin-left: 20px;">确 定</el-button>
    </div>
  </el-dialog>
</template>
<script>
export default {
  name: "SampleDialog",
  props: {
    visible: {
      type: Boolean,
      default: false,
    },
    data: {
      type: Object,
      default: () => ({}),
    },
  },
  data() {
    return {
      form: {
        processTime: "",
        sampleName: "",
        sampleCode: "",
        temperature: "",
        ph: "",
        waterAmount: "",
        additive1: "",
        additive2: "",
        additive3: "",
        additive4: "",
        additive5: "",
        additive6: "",
        additive7: "",
        additive8: "",
        additive9: "",
        additive10: "",
        sampleAmount: "",
      },
      rules: {
        sampleName: [
          { required: true, message: "请输入取样名称", trigger: "blur" },
        ],
        sampleCode: [
          { required: true, message: "请输入取样样品编号", trigger: "blur" },
        ],
        temperature: [
          { required: true, message: "请输入温度", trigger: "blur" },
        ],
        ph: [{ required: true, message: "请输入PH值", trigger: "blur" }],
        waterAmount: [
          { required: true, message: "请输入加水量", trigger: "blur" },
        ],
      },
      fileList: [],
    };
  },
  watch: {
    data: {
      handler(val) {
        if (val) {
          this.form = { ...this.form, ...val };
        }
      },
      immediate: true,
    },
  },
  methods: {
    handleClose() {
      this.$emit("update:visible", false);
      this.form = this.$options.data().form;
      this.fileList = [];
    },
    handleSubmit() {
      this.$refs.form.validate((valid) => {
        if (valid) {
          const submitData = {
            ...this.form,
            sampleTime: new Date().toISOString(),
            operator: window._userInfo?.name || "",
            fileList: this.fileList,
          };
          this.$emit("submit", submitData);
          this.handleClose();
        }
      });
    },
  },
};
</script>
<style scoped lang="less">
::v-deep .el-dialog__header {
  border-bottom: 1px solid #e4e7ed;
  padding: 15px 20px;
}
::v-deep .el-dialog__body {
  padding: 0;
}
.sample-dialog {
  .sample-content {
    background: #ffffff;
    border-radius: 10px;
    padding: 20px;
    .form-content {
      max-height: calc(80vh - 180px);
      overflow-y: auto;
      padding: 0 10px;
      &::-webkit-scrollbar {
        width: 6px;
      }
      &::-webkit-scrollbar-thumb {
        background: #c0c4cc;
        border-radius: 3px;
      }
      &::-webkit-scrollbar-track {
        background: #f5f7fa;
      }
    }
  }
}
.sample-content-card {
  box-shadow: none !important;
}
.header-title {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 13px;
  padding: 0 10px;
  &:first-child {
    margin-top: 0;
  }
  .header-title-left {
    display: flex;
    align-items: center;
    gap: 13px;
    img {
      width: 12px;
      height: 19px;
    }
    span {
      flex-shrink: 0;
      font-weight: bold;
      font-size: 18px;
      color: #222222;
      line-height: 27px;
      font-family: "Source Han Sans CN Bold Bold";
    }
  }
}
.dialog-footer {
  align-items: center;
  display: flex;
  justify-content: center;
  padding: 15px 20px;
  border-top: 1px solid #e4e7ed;
  button {
    width: 150px;
  }
}
::v-deep .el-upload--picture-card {
  width: 120px;
  height: 120px;
  line-height: 120px;
}
.el-row {
  margin-bottom: 20px;
}
::v-deep .el-form-item--small.el-form-item {
  margin-bottom: 0;
}
::v-deep .el-form-item__label {
  padding-bottom: 8px;
}
</style>
src/views/dataManagement/sampleManage/list.vue
New file
@@ -0,0 +1,335 @@
<template>
  <div class="list">
    <TableCustom :queryForm="form" :tableData="tableData" :total="total">
      <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>
          <el-form-item label="实验编号:">
            <el-input v-model="form.planCode" 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"
            ></el-date-picker>
          </el-form-item>
          <el-form-item label="状态:">
            <el-input v-model="form.approver" placeholder="请输入"></el-input>
          </el-form-item>
          <el-form-item label="">
            <el-button type="default" @click="resetForm">重置</el-button>
            <el-button type="primary" @click="handleSearch">查询</el-button>
          </el-form-item>
        </el-form>
      </template>
      <template #setting>
        <div class="tableTitle">
          <div class="flex a-center">
            <div
            class="title"
              :class="{active:currentType === 'list'}"
              @click="handleTypeChange('list')"
            >取样操作记录列表</div>
            <div
            class="drafts"
              :class="{active:currentType === 'draft'}"
              @click="handleTypeChange('draft')"
            >草稿箱</div>
          </div>
          <el-button @click="handleAddSample" class="el-icon-plus" type="primary">
            新增取样操作记录</el-button
          >
        </div>
      </template>
      <template #table>
        <el-table-column
          prop="planCode"
          label="项目课题方案编号"
        ></el-table-column>
        <el-table-column
          prop="planName"
          label="项目课题方案名称"
        ></el-table-column>
        <el-table-column prop="stage" label="项目阶段"></el-table-column>
        <el-table-column prop="creator" label="创建人"></el-table-column>
        <el-table-column prop="createTime" 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>
        <el-table-column prop="approver" label="审批人"></el-table-column>
        <el-table-column prop="approveTime" label="审批时间"></el-table-column>
        <el-table-column label="操作" width="250">
          <template slot-scope="scope">
            <el-button
              v-if="scope.row.status === 'pending'"
              type="text"
              @click="handleApprove(scope.row)"
              >审批</el-button
            >
            <el-button
              v-if="scope.row.status === 'approved'"
              type="text"
              @click="handleRevokeApprove(scope.row)"
              >撤销审批</el-button
            >
            <el-button
              v-if="scope.row.status === 'rejected'"
              type="text"
              @click="handleEdit(scope.row)"
              >编辑</el-button
            >
            <el-button
              v-if="scope.row.status === 'rejected'"
              type="text"
              @click="handleDelete(scope.row)"
              >删除</el-button
            >
            <el-button type="text" @click="handleDetail(scope.row)"
              >详情</el-button
            >
          </template>
        </el-table-column>
      </template>
    </TableCustom>
    <!-- 审批弹窗 -->
    <approval-dialog
      :visible.sync="approvalDialogVisible"
      :type="approvalDialogType"
      :data="currentApprovalData"
      @approve="handleApproveSubmit"
      @reject="handleRejectSubmit"
    />
  </div>
</template>
<script>
import ApprovalDialog from './components/approvalDialog.vue'
export default {
  name: "ProjectList",
  components: {
    ApprovalDialog
  },
  data() {
    return {
      currentType: 'list', // 当前显示类型:list-列表,draft-草稿箱
      form: {
        planName: "",
        planCode: "",
        creator: "",
        createTime: [],
        approver: "",
        status: "",
      },
      tableData: [],
      total: 0,
      // 模拟数据
      mockListData: [
        {
          planCode: 'PLAN-2024-001',
          planName: '2024年度实验室设备升级方案',
          stage: '规划阶段',
          creator: '张三',
          createTime: '2024-03-15',
          status: 'pending',
          approver: '李四',
          approveTime: '2024-03-16'
        },
        {
          planCode: 'PLAN-2024-002',
          planName: '实验室安全管理制度更新方案',
          stage: '实施阶段',
          creator: '王五',
          createTime: '2024-03-14',
          status: 'approved',
          approver: '赵六',
          approveTime: '2024-03-15'
        },
        {
          planCode: 'PLAN-2024-003',
          planName: '实验室人员培训计划',
          stage: '准备阶段',
          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',
      currentApprovalData: null,
    };
  },
  created() {
    this.getTableData();
  },
  methods: {
    resetForm() {
      this.form = {
        planName: "",
        planCode: "",
        creator: "",
        createTime: [],
        approver: "",
        status: "",
      };
    },
    handleSearch() {
      // 实现查询逻辑
      console.log("查询条件:", this.form);
    },
    getStatusType(status) {
      const statusMap = {
        pending: "warning",
        rejected: "danger",
        approved: "success",
        archived: "info",
        draft: "info"
      };
      return statusMap[status] || "info";
    },
    getStatusText(status) {
      const statusMap = {
        pending: "待审批",
        rejected: "已驳回",
        approved: "已通过",
        archived: "已封存",
        draft: "草稿"
      };
      return statusMap[status] || "未知";
    },
    handleAddSample() {
      this.$router.push({
        path: "/dataManagement/addSample",
      });
    },
    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) {
      // 实现编辑逻辑
      console.log("编辑数据:", row);
    },
    handleDelete(row) {
      // 实现删除逻辑
      console.log("删除数据:", row);
    },
    handleDetail(row) {
      this.currentApprovalData = row;
      this.approvalDialogType = 'view';
      this.approvalDialogVisible = true;
    },
    handleTypeChange(type) {
      this.currentType = type;
      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;
      }
    },
  },
};
</script>
<style scoped lang="less">
.list {
  height: 100%;
}
.flex {
  display: flex;
  align-items: center;
}
.tableTitle {
  display: flex;
  padding-bottom: 20px;
  justify-content: space-between;
  align-items: center;
  .title {
    background: #fafafc;
    border-radius: 8px 8px 0px 0px;
    border: 1px solid #dcdfe6;
    padding: 16px 29px;
    font-weight: bold;
    font-size: 18px;
    color: #606266;
    width: unset;
    cursor: pointer;
  }
  .drafts {
    padding: 16px 65px;
    background: #fafafc;
    border-radius: 8px 8px 0px 0px;
    border: 1px solid #dcdfe6;
    font-weight: 400;
    font-size: 18px;
    color: #606266;
    margin-left: 16px;
    cursor: pointer;
  }
  .active{
    color: #049c9a;
    background: #ffffff;
    border-radius: 8px 8px 0px 0px;
    border: 1px solid #049c9a;
  }
}
</style>