pyt
2025-04-23 6f83ace91841d7c46894583410dfcd5cad7dbe55
Merge branch 'main' of http://120.76.84.145:10101/gitblit/r/H5/leshan-laboratory
2 文件已重命名
14个文件已修改
46个文件已添加
10269 ■■■■■ 已修改文件
pages/index/index.vue 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/font/Source Han Sans CN Normal.ttf 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/font/font.css 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/public/active.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/public/modalImg1.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/public/modalImg2.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/public/modalImg3.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/public/modalImg4.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/public/noActive.png 补丁 | 查看 | 原始文档 | blame | 历史
src/components/AddComponentDialog/index.vue 210 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/AiEditor/index.vue 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/EvaluateTable/index.vue 327 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/Table/index.vue 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/TableSlot/index.vue 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main.js 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/router/index.js 206 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/store/index.js 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/dataManagement/SampleDeliveryRecord/components/receiveConfirmDialog.vue 149 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/dataManagement/SampleDeliveryRecord/deliveryRecord.vue 440 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/dataManagement/SampleDeliveryRecord/list.vue 185 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/dataManagement/approvalPlan/addPlan.vue 162 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/dataManagement/dispatching/addDispatch.vue 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/dataManagement/inspectionReport/components/addDialog.vue 424 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/dataManagement/inspectionReport/detail.vue 312 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/dataManagement/inspectionReport/list.vue 202 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/dataManagement/originalRecordTest/components/addDialog.vue 424 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/dataManagement/originalRecordTest/detail.vue 388 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/dataManagement/originalRecordTest/list.vue 186 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/dataManagement/sampleManage/addSample.vue 834 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/dataManagement/sampleManage/components/addTime.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/dataManagement/sampleManage/components/approvalDialog.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/dataManagement/sampleManage/components/receiveConfirmDialog.vue 149 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/dataManagement/sampleManage/list.vue 219 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/dataManagement/sampleRecordList/changeRecord.vue 327 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/dataManagement/sampleRecordList/components/confirmDialog.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/dataManagement/sampleRecordList/components/sampleDialog.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/dataManagement/sampleRecordList/list.vue 297 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/dataManagement/sampleSubmissionList/components/receiveConfirmDialog.vue 149 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/dataManagement/sampleSubmissionList/list.vue 199 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/dataManagement/sampleSubmissionList/submission.vue 279 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/dataManagement/testResultReport/components/checkout-result.vue 108 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/dataManagement/testResultReport/components/evaluation-dialog.vue 84 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/dataManagement/testResultReport/components/experimental-scheduling.vue 75 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/dataManagement/testResultReport/detail.vue 783 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/dataManagement/testResultReport/list.vue 344 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/deliveryAssessment/assayTaskList/components/AssessmentDialog.vue 208 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/deliveryAssessment/assayTaskList/components/CraftDialog.vue 231 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/deliveryAssessment/assayTaskList/index.vue 154 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/deliveryAssessment/clinicalTrial/components/detail.vue 115 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/deliveryAssessment/clinicalTrial/index.vue 127 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/deliveryAssessment/experimentResults/components/detail.vue 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/deliveryAssessment/experimentResults/index.vue 143 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/deliveryAssessment/processEngineerEvaluate/index.vue 157 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/deliveryAssessment/restsTask/components/detail.vue 104 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/deliveryAssessment/restsTask/index.vue 128 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/deliveryAssessment/taskList/components/AssessmentDialog.vue 244 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/deliveryAssessment/taskList/index.vue 171 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/deliveryAssessment/testerWorkerEvaluate/add.vue 200 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/deliveryAssessment/testerWorkerEvaluate/index.vue 217 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/deliveryAssessment/testingAndEvaluation/components/AssessmentDialog.vue 208 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/deliveryAssessment/testingAndEvaluation/index.vue 212 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/projectList/addProject.vue 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages/index/index.vue
New file
@@ -0,0 +1,65 @@
<view class="image-box">
    <template v-for="(item, index) in banner" :key="index">
        <!-- 平铺图 -->
        <view v-if="item.position === 3" class="full-row">
            <image :src="item.fileUrl" mode="widthFix" class="image" />
        </view>
        <!-- 左图 -->
        <view v-else-if="item.position === 1" class="row">
            <view class="left-half">
                <image :src="item.fileUrl" mode="widthFix" class="image" />
            </view>
            <!-- 如果下一个是右图,则显示右图 -->
            <view v-if="index + 1 < banner.length && banner[index + 1].position === 2" class="right-half">
                <image :src="banner[index + 1].fileUrl" mode="widthFix" class="image" />
            </view>
            <!-- 如果下一个不是右图,则显示空白 -->
            <view v-else class="right-half empty"></view>
        </view>
        <!-- 右图(只有当它不是左图的配对时才显示) -->
        <view v-else-if="item.position === 2 && (index === 0 || banner[index - 1].position !== 1)" class="row">
            <view class="left-half empty"></view>
            <view class="right-half">
                <image :src="item.fileUrl" mode="widthFix" class="image" />
            </view>
        </view>
    </template>
</view>
<style>
.image-box {
    width: 100%;
}
.full-row {
    width: 100%;
}
.row {
    width: 100%;
    display: flex;
}
.left-half, .right-half {
    width: 50%;
}
.left-half {
    padding-right: 5px;
}
.right-half {
    padding-left: 5px;
}
.image {
    width: 100%;
    display: block;
}
.empty {
    background-color: transparent;
}
</style>
src/assets/font/Source Han Sans CN Normal.ttf
Binary files differ
src/assets/font/font.css
@@ -24,4 +24,11 @@
  src: url('./SF Compact Display Black.ttf');
  font-weight: normal;
  font-style: normal;
}
@font-face {
  font-family: 'Source Han Sans CN Normal';
  src: url('./Source Han Sans CN Normal.ttf');
  font-weight: normal;
  font-style: normal;
}
src/assets/public/active.png
src/assets/public/modalImg1.png
src/assets/public/modalImg2.png
src/assets/public/modalImg3.png
src/assets/public/modalImg4.png
src/assets/public/noActive.png
src/components/AddComponentDialog/index.vue
New file
@@ -0,0 +1,210 @@
<template>
  <el-dialog
    title="新增组件"
    :visible.sync="visible"
    width="70%"
    :close-on-click-modal="false"
    custom-class="add-component-dialog"
    @close="handleClose"
  >
    <div class="component-cards">
      <div
        v-for="(item, index) in componentTypes"
        :key="index"
        class="component-card"
        :class="{ active: selectedType === item.type }"
        :data-type="item.type"
        @click="handleSelect(item)"
      >
        <div class="card-icon">
          <img :src="item.icon" :alt="item.title" />
        </div>
        <div class="card-content">
          <div class="card-title">{{ item.title }}</div>
          <div class="card-desc">{{ item.description }}</div>
        </div>
      </div>
    </div>
    <div slot="footer" class="dialog-footer select-member-footer">
      <el-button @click="handleClose">取消</el-button>
      <el-button type="primary" @click="handleConfirm">保存</el-button>
    </div>
  </el-dialog>
</template>
<script>
export default {
  name: 'AddComponentDialog',
  props: {
    visible: {
      type: Boolean,
      default: false
    }
  },
  data() {
    return {
      selectedType: '',
      componentTypes: [
        {
          type: 'richText',
          title: '富文本',
          icon: require('@/assets/public/modalImg1.png'),
          description: '富文本组件可以支持文本格式化(如字体、颜色、大小等)、图片插入、链接添加等多种功能。'
        },
        {
          type: 'customTable',
          title: '自定义表格',
          icon: require('@/assets/public/modalImg2.png'),
          description: '自定义表格组件可以支持自定义表头(包括配置表头类型、默认值、权限等)'
        },
        {
          type: 'fileUpload',
          title: '文件上传',
          icon: require('@/assets/public/modalImg3.png'),
          description: '支持上传.docx/.pdf/.xlsx/.txt文件'
        },
        {
          type: 'imageUpload',
          title: '图片上传',
          icon: require('@/assets/public/modalImg4.png'),
          description: '富文本组件可以支持文本格式化(如字体、颜色、大小等)、图片插入、链接添加等多种功能。'
        }
      ]
    }
  },
  methods: {
    handleSelect(item) {
      this.selectedType = item.type
    },
    handleClose() {
      this.$emit('update:visible', false)
      this.selectedType = ''
    },
    handleConfirm() {
      if (!this.selectedType) {
        this.$message.warning('请选择要添加的组件类型')
        return
      }
      this.$emit('confirm', this.selectedType)
      this.handleClose()
    }
  }
}
</script>
<style lang="less" scoped>
.add-component-dialog {
  ::v-deep .el-dialog__header {
    border-bottom: 1px solid #e4e7ed;
    padding: 20px;
    margin: 0;
  }
  ::v-deep .el-dialog__body {
    padding: 30px;
  }
  .component-cards {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
    gap: 20px;
    padding: 10px;
    .component-card {
      display: flex;
      flex-direction: column;
      align-items: center;
      padding: 30px 20px;
      background: #fff;
      border-radius: 10px;
      border: 1px solid #e4e7ed;
      cursor: pointer;
      transition: all 0.3s;
      &[data-type="richText"] {
        background: linear-gradient(180deg, #FFF1DE 0%, rgba(255,249,241,0) 100%);
        border: 1px solid rgba(249,171,20,0.5);
      }
      &[data-type="customTable"] {
        background: linear-gradient(180deg, rgba(5,112,255,0.14) 1%, rgba(234,243,255,0) 100%);
        border: 1px solid rgba(71,145,255,0.5);
      }
      &[data-type="fileUpload"] {
        background: linear-gradient(180deg, #E0FAEA 0%, rgba(224,250,234,0) 100%);
        border: 1px solid rgba(87,180,131,0.5);
      }
      &[data-type="imageUpload"] {
        background: linear-gradient(180deg, #F5EDFF 0%, rgba(234,243,255,0) 100%);
        border: 1px solid rgba(115,83,247,0.5);
      }
      &.active {
        transform: translateY(-4px);
        box-shadow: 0 4px 16px 0 rgba(0, 0, 0, 0.15);
        &[data-type="richText"] {
          border-color: rgba(249,171,20,0.8);
          background: linear-gradient(180deg, #FFF1DE 0%, rgba(255,249,241,0.5) 100%);
        }
        &[data-type="customTable"] {
          border-color: rgba(71,145,255,0.8);
          background: linear-gradient(180deg, rgba(5,112,255,0.2) 1%, rgba(234,243,255,0.5) 100%);
        }
        &[data-type="fileUpload"] {
          border-color: rgba(87,180,131,0.8);
          background: linear-gradient(180deg, #E0FAEA 0%, rgba(224,250,234,0.5) 100%);
        }
        &[data-type="imageUpload"] {
          border-color: rgba(115,83,247,0.8);
          background: linear-gradient(180deg, #F5EDFF 0%, rgba(234,243,255,0.5) 100%);
        }
      }
      .card-icon {
        width: 92px;
        height: 92px;
        margin-bottom: 16px;
        img {
          width: 100%;
          height: 100%;
          object-fit: contain;
        }
      }
      .card-content {
        text-align: center;
        .card-title {
          font-size: 16px;
          font-weight: 500;
          color: #303133;
          margin-bottom: 8px;
        }
        .card-desc {
          font-size: 14px;
          color: #909399;
          line-height: 1.4;
        }
      }
    }
  }
  .dialog-footer {
    text-align: center;
    padding-top: 20px;
    .el-button {
      min-width: 120px;
    }
  }
}
</style>
src/components/AiEditor/index.vue
@@ -54,7 +54,7 @@
  watch: {
    value: {
      handler(newVal) {
        if (this.editor && newVal !== this.editor.getContent()) {
        if (this.editor && newVal !== this.editor.getHtml()) {
          this.editor.setContent(newVal)
        }
      },
@@ -95,7 +95,9 @@
        // 禁用 AI 功能
        ai: false,
        // 禁用调整大小功能
        resize: false
        resize: false,
        // 添加唯一标识
        id: `editor-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`
      }
      try {
@@ -112,7 +114,7 @@
    },
    // 获取编辑器内容
    getContent() {
      return this.editor ? this.editor.getContent() : ''
      return this.editor ? this.editor.getHtml() : ''
    },
    // 设置编辑器内容
    setContent(content) {
src/components/EvaluateTable/index.vue
New file
@@ -0,0 +1,327 @@
<template>
    <div>
        <div class="evaluateTable-header">
            <el-row>
                <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="18">
                    <el-row style="width: 100%;">
                        <el-col style="color: transparent;cursor: default;" :xs="11" :sm="11" :md="11" :lg="11"
                            :xl="11">
                            占位字
                        </el-col>
                        <el-col :offset="1" :xs="12" :sm="12" :md="12" :lg="12" :xl="12">
                            <el-row style="width: 100%;">
                                <el-col :xs="8" :sm="8" :md="8" :lg="8" :xl="8">
                                    <div class="evaluateTable-header-text">良好</div>
                                </el-col>
                                <el-col :xs="8" :sm="8" :md="8" :lg="8" :xl="8">
                                    <div class="evaluateTable-header-text">正确</div>
                                </el-col>
                                <el-col :xs="8" :sm="8" :md="8" :lg="8" :xl="8">
                                    <div class="evaluateTable-header-text">失误</div>
                                </el-col>
                            </el-row>
                        </el-col>
                    </el-row>
                </el-col>
                <el-col style="color: transparent;cursor: default;" :xs="24" :sm="24" :md="24" :lg="24" :xl="6">
                    占位字
                </el-col>
            </el-row>
        </div>
        <div class="evaluateTable-body">
            <el-row>
                <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="18">
                    <div class="evaluateTable-body-left">
                        <div v-for="(item, index) in list" :key="index" class="evaluateTable-body-left-item">
                            <el-row style="width: 100%;">
                                <el-col :xs="11" :sm="11" :md="11" :lg="11" :xl="11">
                                    <div class="evaluateTable-body-left-item-text">
                                        {{ item }}
                                    </div>
                                </el-col>
                                <el-col :offset="1" :xs="12" :sm="12" :md="12" :lg="12" :xl="12">
                                    <div class="evaluateTable-body-left-item-score">
                                        <el-row v-if="index != 5" style="width: 100%;">
                                            <el-col :xs="8" :sm="8" :md="8" :lg="8" :xl="8">
                                                <div class="evaluateTable-body-left-item-score-num"
                                                    :class="yesOrNoActive(sort(2, index)) && 'activeScore'">
                                                    <img v-if="yesOrNoActive(sort(2, index))"
                                                        src="@/assets/public/active.png" />
                                                    <img v-else @click="selectNum(index, 2, sort(2, index))"
                                                        src="@/assets/public/noActive.png" />
                                                    <div>2分</div>
                                                </div>
                                            </el-col>
                                            <el-col :xs="8" :sm="8" :md="8" :lg="8" :xl="8">
                                                <div class="evaluateTable-body-left-item-score-num"
                                                    :class="yesOrNoActive(sort(1, index)) && 'activeScore'">
                                                    <img v-if="yesOrNoActive(sort(1, index))"
                                                        src="@/assets/public/active.png" />
                                                    <img v-else @click="selectNum(index, 1, sort(1, index))"
                                                        src="@/assets/public/noActive.png" />
                                                    <div>1分</div>
                                                </div>
                                            </el-col>
                                            <el-col :xs="8" :sm="8" :md="8" :lg="8" :xl="8">
                                                <div class="evaluateTable-body-left-item-score-num"
                                                    :class="yesOrNoActive(sort(0, index)) && 'activeScore'">
                                                    <img v-if="yesOrNoActive(sort(0, index))"
                                                        src="@/assets/public/active.png" />
                                                    <img v-else @click="selectNum(index, 0, sort(0, index))"
                                                        src="@/assets/public/noActive.png" />
                                                    <div>0分</div>
                                                </div>
                                            </el-col>
                                        </el-row>
                                        <el-row v-else style="width: 100%;">
                                            <el-col :xs="16" :sm="16" :md="16" :lg="16" :xl="16">
                                                <div class="evaluateTable-body-left-item-score-num"
                                                    :class="yesOrNoActive(sort(2, index)) && 'activeScore'">
                                                    <img v-if="yesOrNoActive(sort(2, index))"
                                                        src="@/assets/public/active.png" />
                                                    <img v-else @click="selectNum(index, -3, sort(2, index))"
                                                        src="@/assets/public/noActive.png" />
                                                    <div>是(-3分)</div>
                                                </div>
                                            </el-col>
                                            <el-col :xs="8" :sm="8" :md="8" :lg="8" :xl="8">
                                                <div class="evaluateTable-body-left-item-score-num"
                                                    :class="yesOrNoActive(sort(1, index)) && 'activeScore'">
                                                    <img v-if="yesOrNoActive(sort(1, index))"
                                                        src="@/assets/public/active.png" />
                                                    <img v-else @click="selectNum(index, 0, sort(1, index))"
                                                        src="@/assets/public/noActive.png" />
                                                    <div>否(0分)</div>
                                                </div>
                                            </el-col>
                                        </el-row>
                                    </div>
                                </el-col>
                            </el-row>
                        </div>
                    </div>
                </el-col>
                <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="6">
                    <div class="evaluateTable-body-right">
                        <div class="evaluateTable-body-right-title">总分</div>
                        <div class="evaluateTable-body-right-num">
                            <div>{{ total != null ? total : '[自动计算]' }}</div>
                        </div>
                    </div>
                </el-col>
            </el-row>
        </div>
    </div>
</template>
<script>
import store from "../../store";
export default {
    props: {
        type: {
            type: Number,
            default: 1 //1 化验师 2 实验员 3 工艺工程师
        },
        isReadonly: {
            type: Boolean,
            default: false // 是否只读
        },
        viewJson: {
            type: String,
            default: '' //回显
        }
    },
    computed: {
        list() {
            if (this.type == 1) return store.state.assay
            if (this.type == 2) return store.state.experiment
            if (this.type == 3) return store.state.craft
            return store.state.assay
        },
    },
    watch: {
        activeIndex: {
            handler(newVal, oldVal) {
                this.total = null
                this.activeIndex.map(item => {
                    this.total += item.score
                })
            },
            deep: true
        },
        viewJson: {
            handler(newVal, oldVal) {
                console.log("viewJson changed:", newVal)
                if (newVal && this.isReadonly) {
                    try {
                        const parsedData = JSON.parse(newVal)
                        this.activeIndex = parsedData
                        console.log("activeIndex updated:", this.activeIndex)
                    } catch (e) {
                        console.error("Failed to parse viewJson:", e)
                    }
                }
            },
            immediate: true
        }
    },
    data() {
        return {
            total: null,
            activeIndex: [
                {
                    num: null,
                    score: 0
                },
                {
                    num: null,
                    score: 0
                },
                {
                    num: null,
                    score: 0
                },
                {
                    num: null,
                    score: 0
                },
                {
                    num: null,
                    score: 0
                },
                {
                    num: null,
                    score: 0
                },
            ]
        }
    },
    methods: {
        selectNum(index, score, num) {
            if (this.isReadonly) return
            this.activeIndex[index].score = score
            this.activeIndex[index].num = num
        },
        yesOrNoActive(num) {
            let arr = this.activeIndex.filter(item => item.num == num)
            return arr.length
        },
        sort(num, index) {
            return (index + 1) * 3 - num
        }
    }
}
</script>
<style lang="less" scoped>
.evaluateTable-header {
    font-weight: 500;
    font-size: 20px;
    color: #FFFFFF;
    height: 70px;
    background: linear-gradient(270deg, #0ACBCA 0%, #049C9A 100%);
    box-shadow: 0px 6px 10px 0px rgba(4, 156, 154, 0.09);
    border-radius: 10px;
    font-family: 'SourceHanSansCN-Medium';
    padding: 0 23px 0 17px;
    &-text {
        line-height: 70px;
    }
}
.evaluateTable-body {
    background: #FFFFFF;
    box-shadow: 0px 6px 10px 0px rgba(4, 156, 154, 0.09);
    border-radius: 10px;
    margin-top: -10px;
    padding: 0 23px 27px 17px;
    height: 100%;
    &-left {
        margin-top: 21px;
        &-item {
            background: #D7EFF4;
            box-shadow: 0px 6px 10px 0px rgba(4, 156, 154, 0.09);
            border-radius: 29px;
            margin-bottom: 20px;
            &-text {
                flex-shrink: 0;
                line-height: 44px;
                background: radial-gradient(0% 50% at 50% 50%, #0ACBCA 0%, #049C9A 100%);
                box-shadow: 0px 6px 10px 0px rgba(4, 156, 154, 0.09);
                border-radius: 29px;
                text-align: center;
                font-weight: bold;
                font-family: 'Source Han Sans CN Bold Bold';
                font-size: 16px;
                color: #FFFFFF;
                letter-spacing: 1px;
            }
            &-score {
                line-height: 44px;
                display: flex;
                align-items: center;
                .activeScore {
                    color: #049C9A;
                }
                &-num {
                    display: flex;
                    align-items: center;
                    img {
                        width: 16px;
                        height: 16px;
                        margin-right: 13px;
                        cursor: pointer;
                    }
                    div {
                        flex-shrink: 0;
                    }
                }
            }
        }
        &-item:last-child {
            margin-bottom: unset;
        }
    }
    .evaluateTable-body-right {
        margin-top: 21px;
        background: linear-gradient(to top, #0ACBCA 0%, #049C9A 100%);
        border-radius: 10px;
        padding-top: 35px;
        min-height: 329px;
        display: flex;
        align-items: center;
        flex-direction: column;
        margin-left: 16px;
        &-title {
            font-family: 'Source Han Sans CN Normal';
            font-weight: 400;
            font-size: 24px;
            color: #FFFFFF;
            line-height: 24px;
        }
        &-num {
            flex: 1;
            display: flex;
            flex-direction: column;
            justify-content: center;
            font-family: 'SourceHanSansCN-Medium';
            font-weight: 500;
            font-size: 44px;
            color: #FFFFFF;
        }
    }
}
</style>
src/components/Table/index.vue
@@ -25,7 +25,7 @@
        },
        total: {
            type: Number,
            default: 20
            default: 0
        },
        queryForm: {
            type: Object,
@@ -90,6 +90,14 @@
}
::v-deep .el-table__expanded-cell {
    padding: 0;
}
::v-deep .el-table .el-table__expand-icon {
    display: none;
}
.pagination {
    display: flex;
    justify-content: flex-end;
src/components/TableSlot/index.vue
@@ -12,11 +12,14 @@
                </div>
            </template>
            <template v-if="$slots.table">
                <Table :data="tableData" :total="total" :height="height" :queryForm="queryForm" @currentChange="handleCurrentChange"
                    @sizeChange="handleSizeChange">
                <Table :data="tableData" :total="total" :height="height" :queryForm="queryForm"
                    @currentChange="handleCurrentChange" @sizeChange="handleSizeChange">
                    <slot name="table"></slot>
                </Table>
            </template>
            <template v-if="$slots.tableCustom">
                <slot name="tableCustom"></slot>
            </template>
        </div>
    </div>
</template>
src/main.js
@@ -10,6 +10,7 @@
import Card from '@/components/Card/index.vue'
import ShowDelConfirm from '@/components/showDelConfirm/index.vue'
import SelectMember from '@/components/SelectMember/index.vue'
import EvaluateTable from '@/components/EvaluateTable/index.vue'
import "aieditor/dist/style.css"
import './assets/tailwind.css'
import './styles/element-variables.less'
@@ -21,6 +22,7 @@
Vue.component('Card', Card)
Vue.component('ShowDelConfirm', ShowDelConfirm)
Vue.component('SelectMember', SelectMember)
Vue.component('EvaluateTable', EvaluateTable)
Vue.prototype.msgsuccess = function (msg) {
  this.$message({
src/router/index.js
@@ -182,22 +182,139 @@
                component: () => import("../views/dataManagement/confirmation-sheet/components/add.vue"),
            },
            {
                path: "sampleManage",
                path: "/sampleManage",
                meta: {
                    title: "样品管理",
                    keepAlive: true,
                    // keepAlive: true,
                },
                component: () => import("../views/dataManagement/sampleManage/list.vue"),
                component: Parent,
                children: [
                    {
                        path: "manage",
                        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"),
                    }, {
                        path: "record",
                        meta: {
                            title: "取样操作记录列表",
                            keepAlive: true,
                        },
                        component: () => import("../views/dataManagement/sampleRecordList/list.vue"),
                    },
                    {
                        path: "changeRecord",
                        name: "changeRecord",
                        meta: {
                            title: "填写取样操作记录",
                            hide: true,
                            keepAlive: true,
                        },
                        component: () => import("../views/dataManagement/sampleRecordList/changeRecord"),
                    }, {
                        path: "submissionList",
                        meta: {
                            title: "送样单列表",
                            keepAlive: true,
                        },
                        component: () => import("../views/dataManagement/sampleSubmissionList/list.vue"),
                    },
                    {
                        path: "submission",
                        name: "submission",
                        meta: {
                            title: "送样单详情",
                            hide: true,
                            keepAlive: true,
                        },
                        component: () => import("../views/dataManagement/sampleSubmissionList/submission"),
                    },
                ]
            },
            {
                path: "addSample",
                name: "addPaddSamplelan",
                path: "deliveryRecord",
                meta: {
                    title: "新增样品",
                    title: "取样送样记录",
                    keepAlive: true,
                },
                component: () => import("../views/dataManagement/SampleDeliveryRecord/list.vue"),
            },
            {
                path: "delivery",
                name: "delivery",
                meta: {
                    title: "送样详情",
                    hide: true,
                    keepAlive: true,
                },
                component: () => import("../views/dataManagement/sampleManage/addSample"),
                component: () => import("../views/dataManagement/SampleDeliveryRecord/deliveryRecord"),
            },
            {
                path: "originalRecordTest",
                meta: {
                    title: "原始检验记录",
                    keepAlive: true,
                },
                component: () => import("../views/dataManagement/originalRecordTest/list.vue"),
            },
            {
                path: "originalRecordTest/detail",
                name: "OriginalRecordTestDetail",
                meta: {
                    title: "原始检验记录详情",
                    hide: true,
                    keepAlive: true,
                },
                component: () => import("../views/dataManagement/originalRecordTest/detail.vue"),
            },
            {
                path: "inspectionReport",
                meta: {
                    title: "检验报告管理",
                    keepAlive: true,
                },
                component: () => import("../views/dataManagement/inspectionReport/list.vue"),
            },
            {
                path: "inspectionReport/detail",
                name: "inspectionReportDetail",
                meta: {
                    title: "检验报告详情",
                    hide: true,
                    keepAlive: true,
                },
                component: () => import("../views/dataManagement/inspectionReport/detail.vue"),
            },
            {
                path: "testResultReport",
                meta: {
                    title: "实验结果汇报",
                    keepAlive: true,
                },
                component: () => import("../views/dataManagement/testResultReport/list.vue"),
            },
            {
                path: "testResultReport/detail",
                name: "testResultReportDetail",
                meta: {
                    title: "实验结果详情",
                    hide: true,
                    keepAlive: true,
                },
                component: () => import("../views/dataManagement/testResultReport/detail.vue"),
            },
        ],
    },
@@ -281,6 +398,81 @@
                },
                component: () => import("../views/deliveryAssessment/projectTeamIntegral/detail.vue"),
            },
            {
                // 超级管理员
                path: "taskList",
                meta: {
                    title: "课题列表",
                },
                component: () => import("../views/deliveryAssessment/taskList"),
            },
            {
                // 工艺工程师 审批人 实验员 超级管理员
                path: "restsTask",
                meta: {
                    title: "实验员其他任务",
                },
                component: () => import("../views/deliveryAssessment/restsTask"),
            },
            {
                // 超级管理员 工艺工程师
                path: "clinicalTrial",
                meta: {
                    title: "临床试验积分列表",
                },
                component: () => import("../views/deliveryAssessment/clinicalTrial"),
            },
            {
                // 化验师 审批人
                path: "testingAndEvaluation",
                meta: {
                    title: "检测项评定列表",
                },
                component: () => import("../views/deliveryAssessment/testingAndEvaluation"),
            },
            {
                // 化验师 实验员
                path: "experimentResults",
                meta: {
                    title: "实验结果评定",
                },
                component: () => import("../views/deliveryAssessment/experimentResults"),
            },
            {
                // 化验师 工艺工程师 审批人
                path: "assayTaskList",
                meta: {
                    title: "课题评定列表",
                },
                component: () => import("../views/deliveryAssessment/assayTaskList"),
            },
            {
                // 工艺工程师
                path: "processEngineerEvaluate",
                meta: {
                    title: "工艺工程师工作评定详情",
                },
                component: () => import("../views/deliveryAssessment/processEngineerEvaluate"),
            },
            {
                // 工艺工程师
                path: "testerWorkerEvaluate",
                meta: {
                    title: "实验员工作评定",
                },
                component: () => import("../views/deliveryAssessment/testerWorkerEvaluate"),
            },
            {
                // 工艺工程师
                path: "addTesterWorkerEvaluate",
                name: 'AddTesterWorkerEvaluate',
                meta: {
                    title: "新增实验员工作评定",
                    hide: true,
                    keepAlive: true,
                },
                component: () => import("../views/deliveryAssessment/testerWorkerEvaluate/add"),
            },
        ]
    }
];
src/store/index.js
@@ -8,6 +8,30 @@
    keepAliveList: localStorage.getItem('keepAliveList') ? JSON.parse(localStorage.getItem('keepAliveList')) : [],//缓存页面
    tagList: localStorage.getItem('tagList') ? JSON.parse(localStorage.getItem('tagList')) : [],//标签列表
    isFold: false,//是否折叠
    assay: [
      '检验方法正确',
      '实验检验相关记录完整',
      '检验数据准确',
      '检验过程异常问题的优化处理',
      '检验时效完成度,安全操作',
      '操作失败',
    ],//化验师评定项
    experiment: [
      '实验方案熟悉度、设备及材料准备完整度',
      '实验操作、取样、送样准确性',
      '实验记录清晰完整度',
      '实验过程异常问题的优化处理',
      '实验操作时效完成度、安全操作',
      '实验操作失败',
    ],//实验员评定项
    craft: [
      '实验工作调度安排完成度',
      '全程实验、化验异常问题处理',
      '实验方案准确度、检验方法准确度',
      '检查实验数据记录准确性',
      '实验分析报告完成度',
      '实验损失与失败',
    ],//工艺工程师评定项
  },
  mutations: {
    SET_MENUS(state, data) {
src/views/dataManagement/SampleDeliveryRecord/components/receiveConfirmDialog.vue
New file
@@ -0,0 +1,149 @@
<template>
  <el-dialog
    title="发送签字确认"
    :visible.sync="visible"
    width="500px"
    :close-on-click-modal="false"
    @close="handleClose"
  >
    <div class="receive-dialog">
      <div class="receive-tip">
        您已选中 <span class="sample-count">{{ sampleCount }}</span> 条样品发送,请签字确认
      </div>
      <div class="approval-dialog-approve">
        <div class="add-group">
          <div>*</div>
          <span>签字确认</span>
          <el-button type="primary" class="el-icon-plus" @click="openSignature">签名</el-button>
        </div>
        <img
          v-if="imgSrc"
          style="width: 200px; height: 100px; margin-left: 25px"
          :src="imgSrc"
          fit="fit"
        />
      </div>
    </div>
    <div slot="footer" class="dialog-footer select-member-footer">
      <el-button @click="handleClose">取 消</el-button>
      <el-button type="primary" @click="handleConfirm" :disabled="!imgSrc">
        确认发送
      </el-button>
    </div>
    <SignatureCanvas
      :visible="signatureDialogVisible"
      @confirm="handleSignatureConfirm"
      @close="signatureDialogVisible = false"
    />
  </el-dialog>
</template>
<script>
import SignatureCanvas from "@/components/SignatureCanvas.vue";
export default {
  name: "ReceiveConfirmDialog",
  components: {
    SignatureCanvas,
  },
  props: {
    visible: {
      type: Boolean,
      default: false,
    },
    sampleCount: {
      type: Number,
      default: 1,
    },
  },
  data() {
    return {
      signatureDialogVisible: false,
      imgSrc: "",
    };
  },
  methods: {
    handleClose() {
      this.$emit("update:visible", false);
      this.imgSrc = "";
    },
    openSignature() {
      this.signatureDialogVisible = true;
    },
    handleSignatureConfirm(imageData) {
      this.signatureDialogVisible = false;
      this.imgSrc = imageData;
    },
    handleConfirm() {
      if (!this.imgSrc) {
        this.$message.warning("请先完成签名确认");
        return;
      }
      this.$emit("confirm", this.imgSrc);
      this.handleClose();
    },
  },
};
</script>
<style lang="less" scoped>
::v-deep .el-dialog__header {
  border-bottom: 1px solid #e4e7ed;
}
.receive-dialog {
  padding: 20px;
  .receive-tip {
    font-size: 14px;
    color: #333;
    margin-bottom: 20px;
    .sample-count {
      color: #409EFF;
      font-weight: bold;
    }
  }
}
.approval-dialog-approve {
  margin-top: 26px;
  img {
    border: 2px dashed #049c9a;
  }
}
.add-group {
  padding-left: 25px;
  margin-top: 14px;
  display: flex;
  align-items: center;
  margin-bottom: 19px;
  div {
    color: #f56c6c;
  }
  span {
    font-weight: 500;
    font-size: 14px;
    color: #222222;
    line-height: 21px;
    margin: 0 32px 0 8px;
  }
}
.dialog-footer {
  align-items: center;
  display: flex;
  justify-content: center;
  button {
    width: 150px;
  }
}
</style>
src/views/dataManagement/SampleDeliveryRecord/deliveryRecord.vue
New file
@@ -0,0 +1,440 @@
<template>
  <Card>
    <div class="header-title-left">
      <img src="@/assets/public/headercard.png" />
      <div>所属实验调度</div>
    </div>
    <Table :data="schedulingData" :total="schedulingTotal" :height="null">
      <template>
        <el-table-column
          prop="planCode"
          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="notifyTime" label="通知时间"></el-table-column>
        <el-table-column
          prop="startTime"
          label="实验开始时间"
        ></el-table-column>
        <el-table-column prop="endTime" label="实验结束时间"></el-table-column>
        <el-table-column prop="participants" label="参加人员"></el-table-column>
        <el-table-column prop="status" label="状态"></el-table-column>
      </template>
    </Table>
    <!-- 送样-收样记录部分 -->
    <div class="sample-record-section">
      <div class="header-title-left" style="margin-top: 38px">
        <img src="@/assets/public/headercard.png" />
        <span>送样-收样记录</span>
        <el-button
          type="primary"
          class="el-icon-plus"
          @click="showAdditives = !showAdditives"
        >
          {{ showAdditives ? "收起辅料详情" : "展开辅料详情" }}
        </el-button>
      </div>
      <div class="sample-code">
        取样单编号:{{ form.sampleCode || "DD-25010BS01" }}
      </div>
      <div class="sample-code">待收样列表</div>
      <div class="sample-table">
        <Table
          :data="sampleList"
          :total="sampleTotal"
          :height="null"
          style="width: 100%"
        >
          <template>
            <el-table-column
              prop="index"
              label="序号"
              width="80"
            ></el-table-column>
            <el-table-column
              prop="processTime"
              label="工艺时间间/h"
              width="120"
            ></el-table-column>
            <el-table-column
              prop="sampleName"
              label="取样名称"
              min-width="150"
            ></el-table-column>
            <el-table-column
              prop="sampleCode"
              label="取样样品编号"
              min-width="150"
            ></el-table-column>
            <el-table-column
              prop="temperature"
              label="温度"
              width="100"
            ></el-table-column>
            <el-table-column prop="ph" label="PH" width="100"></el-table-column>
            <el-table-column
              prop="waterAmount"
              label="加水量"
              width="100"
            ></el-table-column>
            <template v-if="showAdditives">
              <el-table-column
                prop="additive1"
                label="加辅1"
                min-width="150"
              ></el-table-column>
              <el-table-column
                prop="additive2"
                label="加辅2"
                min-width="150"
              ></el-table-column>
              <el-table-column
                prop="additive3"
                label="加辅3"
                min-width="150"
              ></el-table-column>
              <el-table-column
                prop="additive4"
                label="加辅4"
                min-width="150"
              ></el-table-column>
              <el-table-column
                prop="additive5"
                label="加辅5"
                min-width="150"
              ></el-table-column>
              <el-table-column
                prop="additive6"
                label="加辅6"
                min-width="150"
              ></el-table-column>
              <el-table-column
                prop="additive7"
                label="加辅7"
                min-width="150"
              ></el-table-column>
              <el-table-column
                prop="additive8"
                label="加辅8"
                min-width="150"
              ></el-table-column>
              <el-table-column
                prop="additive9"
                label="加辅9"
                min-width="150"
              ></el-table-column>
              <el-table-column
                prop="additive10"
                label="加辅10"
                min-width="150"
              ></el-table-column>
            </template>
            <el-table-column
              prop="sampleAmount"
              label="取样量"
              width="100"
            ></el-table-column>
            <el-table-column
              prop="sampleTime"
              label="取样时间"
              min-width="150"
            ></el-table-column>
            <el-table-column prop="photo" label="拍照" width="100">
              <template slot-scope="scope">
                <img
                  v-if="scope.row.photo"
                  :src="scope.row.photo"
                  class="sample-photo"
                />
              </template>
            </el-table-column>
            <el-table-column
              prop="operator"
              label="操作人"
              width="100"
            ></el-table-column>
            <el-table-column
              prop="status"
              label="状态"
              width="100"
            ></el-table-column>
          </template>
        </Table>
      </div>
      <div class="received-samples">
        <div style="display: flex; align-items: center">
          <div class="sample-code">已收样列表</div>
          <el-button
            type="primary"
            class="el-icon-plus"
            style="margin-left: 16px"
            @click="showReceivedColumns = !showReceivedColumns"
          >
            {{ showReceivedColumns ? "收起辅料详情" : "展开辅料详情" }}
          </el-button>
        </div>
        <Table
          :data="receivedSamples"
          :total="receivedSamplesTotal"
          :height="null"
          style="width: 100%"
        >
          <template>
            <el-table-column
              prop="index"
              label="序号"
              width="80"
            ></el-table-column>
            <el-table-column
              prop="sampleName"
              label="取样名称"
              min-width="150"
            ></el-table-column>
            <el-table-column
              prop="sampleCode"
              label="取样样品编号"
              min-width="150"
            ></el-table-column>
            <el-table-column
              prop="temperature"
              label="温度"
              width="100"
            ></el-table-column>
            <el-table-column
              prop="waterAmount"
              label="加水量"
              width="100"
            ></el-table-column>
            <el-table-column prop="ph" label="PH" width="100"></el-table-column>
            <template v-if="showReceivedColumns">
              <el-table-column
                prop="additive1"
                label="加辅1"
                min-width="150"
              ></el-table-column>
              <el-table-column
                prop="additive2"
                label="加辅2"
                min-width="150"
              ></el-table-column>
              <el-table-column
                prop="additive3"
                label="加辅3"
                min-width="150"
              ></el-table-column>
              <el-table-column
                prop="additive4"
                label="加辅4"
                min-width="150"
              ></el-table-column>
              <el-table-column
                prop="additive5"
                label="加辅5"
                min-width="150"
              ></el-table-column>
            </template>
            <el-table-column
              prop="sampleAmount"
              label="取样量"
              width="100"
            ></el-table-column>
            <el-table-column
              prop="sendTime"
              label="送样时间"
              min-width="150"
            ></el-table-column>
            <el-table-column
              prop="sender"
              label="送样人"
              width="100"
            ></el-table-column>
            <el-table-column
              prop="receiveTime"
              label="收样时间"
              min-width="150"
            ></el-table-column>
            <el-table-column
              prop="receiver"
              label="收样人"
              width="100"
            ></el-table-column>
            <el-table-column
              prop="status"
              label="状态"
              width="100"
              fixed="right"
            ></el-table-column>
          </template>
        </Table>
      </div>
    </div>
  </Card>
</template>
<script>
export default {
  name: "SampleSubmission",
  data() {
    return {
      showAdditives: true,
      showReceivedColumns: true,
      schedulingData: [
        {
          planCode: "DD-EX001",
          experimentCode: "DD-EX001",
          experimentName: "名称名称名称",
          notifyTime: "2025-1-2 14:50:19",
          startTime: "2025-02-27",
          endTime: "2025-06-06",
          participants: "张弘, 李天浩, 陈三, 李四",
          status: "已确认",
        },
      ],
      schedulingTotal: 1,
      form: {
        sampleCode: "DD-25010BS01",
      },
      sampleList: [
        {
          index: 1,
          processTime: "2",
          sampleName: "名称名称名称",
          sampleCode: "314234321",
          temperature: "34",
          ph: "34",
          waterAmount: "34",
          additive1: "10g",
          additive2: "5g",
          additive3: "3g",
          additive4: "",
          additive5: "",
          additive6: "",
          additive7: "",
          additive8: "",
          additive9: "",
          additive10: "",
          sampleAmount: "34",
          sampleTime: "2025-1-9 15:26:08",
          photo: "@/assets/sample-photos/photo1.jpg",
          operator: "张三",
          status: "待发送",
        },
        {
          index: 2,
          processTime: "4",
          sampleName: "名称名称名称",
          sampleCode: "314234321",
          temperature: "23",
          ph: "23",
          waterAmount: "23",
          additive1: "8g",
          additive2: "4g",
          additive3: "",
          additive4: "",
          additive5: "",
          additive6: "",
          additive7: "",
          additive8: "",
          additive9: "",
          additive10: "",
          sampleAmount: "23",
          sampleTime: "2025-1-9 15:26:08",
          photo: "@/assets/sample-photos/photo2.jpg",
          operator: "李四",
          status: "待接收",
        },
        {
          index: 3,
          processTime: "8",
          sampleName: "名称名称名称",
          sampleCode: "314234321",
          temperature: "23",
          ph: "23",
          waterAmount: "23",
          additive1: "",
          additive2: "",
          additive3: "",
          additive4: "",
          additive5: "",
          additive6: "",
          additive7: "",
          additive8: "",
          additive9: "",
          additive10: "",
          sampleAmount: "23",
          sampleTime: "2025-1-9 15:26:08",
          photo: "@/assets/sample-photos/photo3.jpg",
          operator: "王五",
          status: "已接收",
        },
      ],
      sampleTotal: 3,
    };
  },
};
</script>
<style lang="less" scoped>
.header-title-left {
  display: flex;
  align-items: center;
  gap: 13px;
  margin-bottom: 20px;
  img {
    width: 12px;
    height: 19px;
  }
  div,
  span {
    flex-shrink: 0;
    font-weight: bold;
    font-size: 18px;
    color: #222222;
    line-height: 27px;
    font-family: "Source Han Sans CN Bold Bold";
  }
  div:before {
    content: "*";
    color: #f56c6c;
    margin-right: 4px;
  }
  .el-button {
    margin-left: 16px;
  }
}
.sample-record-section {
  .sample-code {
    font-family: PingFangSC, PingFang SC;
    font-weight: 400;
    font-size: 14px;
    color: rgba(0, 0, 0, 0.88);
    margin: 20px 0;
  }
  .table-header {
    margin-bottom: 16px;
    text-align: left;
  }
  .sample-photo {
    width: 60px;
    height: 60px;
    object-fit: cover;
  }
}
</style>
src/views/dataManagement/SampleDeliveryRecord/list.vue
New file
@@ -0,0 +1,185 @@
<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-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-input v-model="form.planCode" 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">待接收样品列表</div>
          </div>
        </div>
      </template>
      <template #table>
        <el-table-column
          prop="planName"
          label="所属项目课题方案"
        ></el-table-column>
        <el-table-column prop="experimentNo" label="实验编号"></el-table-column>
        <el-table-column
          prop="experimentName"
          label="实验名称"
        ></el-table-column>
        <el-table-column prop="sampleNo" 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="sampleStatus"
          label="样品总数/待接收/已接收"
        ></el-table-column>
        <el-table-column label="操作" width="100">
          <template slot-scope="scope">
            <el-button type="text" @click="handleDetail(scope.row)"
              >详情</el-button
            >
          </template>
        </el-table-column>
      </template>
    </TableCustom>
  </div>
</template>
<script>
export default {
  name: "ProjectList",
  data() {
    return {
      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",
        },
      ],
    };
  },
  created() {
    this.getTableData();
  },
  methods: {
    resetForm() {
      this.form = {
        planName: "",
        planCode: "",
        creator: "",
        createTime: [],
        approver: "",
        status: "",
      };
    },
    handleSearch() {
      // 实现查询逻辑
      console.log("查询条件:", this.form);
      this.getTableData();
    },
    handleDetail(row) {
      this.$router.push({
        path: "/dataManagement/delivery",
        query: {
          id: row.id,
          type: "view",
        },
      });
    },
    getTableData() {
      // 获取列表数据
      this.tableData = this.mockListData;
      this.total = this.mockListData.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: #ffffff;
    border-radius: 8px 8px 0px 0px;
    border: 1px solid #049c9a;
    padding: 16px 29px;
    font-weight: bold;
    font-size: 18px;
    color: #049c9a;
    width: unset;
  }
}
</style>
src/views/dataManagement/approvalPlan/addPlan.vue
@@ -31,6 +31,12 @@
          <div>一 、实验目的</div>
        </div>
      </div>
      <AiEditor
        ref="purposeEditor"
        v-model="editorContents.purpose"
        height="200px"
        placeholder="请输入实验目的..."
      />
      <div class="header-title">
        <div class="header-title-left">
          <img src="@/assets/public/headercard.png" />
@@ -40,43 +46,84 @@
      <div class="item-title">
        <span>1.实验材料</span>
      </div>
      <AiEditor
        ref="materialEditor"
        v-model="editorContents.material"
        height="200px"
        placeholder="请输入实验材料..."
      />
      <div class="item-title">
        <span>2.实验设备</span>
      </div>
      <AiEditor
        ref="equipmentEditor"
        v-model="editorContents.equipment"
        height="200px"
        placeholder="请输入实验设备..."
      />
      <div class="header-title">
        <div class="header-title-left">
          <img src="@/assets/public/headercard.png" />
          <div>三 、检测方法及开发</div>
        </div>
      </div>
      <AiEditor
        ref="methodEditor"
        v-model="editorContents.method"
        height="200px"
        placeholder="请输入检测方法及开发..."
      />
      <div class="header-title">
        <div class="header-title-left">
          <img src="@/assets/public/headercard.png" />
          <div>四 、实验步骤</div>
        </div>
      </div>
      <AiEditor
        ref="stepsEditor"
        v-model="editorContents.steps"
        height="200px"
        placeholder="请输入实验步骤..."
      />
      <div class="header-title">
        <div class="header-title-left">
          <img src="@/assets/public/headercard.png" />
          <div>五 、数据采集及分析</div>
        </div>
      </div>
      <AiEditor
        ref="dataAnalysisEditor"
        v-model="editorContents.dataAnalysis"
        height="200px"
        placeholder="请输入数据采集及分析..."
      />
      <div class="header-title">
        <div class="header-title-left">
          <img src="@/assets/public/headercard.png" />
          <div>六 、结果评估</div>
        </div>
      </div>
      <AiEditor
        ref="evaluationEditor"
        v-model="editorContents.evaluation"
        height="200px"
        placeholder="请输入结果评估..."
      />
      <div class="header-title">
        <div class="header-title-left">
          <img src="@/assets/public/headercard.png" />
          <span>注意事项</span>
        </div>
      </div>
      <AiEditor
        ref="notesEditor"
        v-model="editorContents.notes"
        height="200px"
        placeholder="请输入注意事项..."
      />
      <div class="add-project-footer">
        <el-button type="primary" class="save-btn">保存</el-button>
        <el-button>存草稿</el-button>
        <el-button type="primary" class="save-btn" @click="handleSave">保存</el-button>
        <el-button @click="handleSaveDraft">存草稿</el-button>
      </div>
    </template>
    <SelectMember ref="selectMember" />
@@ -84,11 +131,38 @@
</template>
<script>
import AiEditor from '@/components/AiEditor'
// import { savePlan, saveDraft } from '@/api/approvalPlan'
export default {
  name: "AddProject",
  components: {
    AiEditor
  },
  data() {
    return {
      form: {},
      form: {
        name: '',
        description: '',
        purpose: '',
        material: '',
        equipment: '',
        method: '',
        steps: '',
        dataAnalysis: '',
        evaluation: '',
        notes: ''
      },
      editorContents: {
        purpose: '',
        material: '',
        equipment: '',
        method: '',
        steps: '',
        dataAnalysis: '',
        evaluation: '',
        notes: ''
      },
      rules: {
        name: [
          { required: true, message: "请输入项目组名称", trigger: "blur" },
@@ -96,33 +170,69 @@
        description: [
          { required: true, message: "请输入项目组描述", trigger: "blur" },
        ],
        purpose: [
          { required: true, message: "请输入实验目的", trigger: "blur" },
        ],
        material: [
          { required: true, message: "请输入实验材料", trigger: "blur" },
        ],
        equipment: [
          { required: true, message: "请输入实验设备", trigger: "blur" },
        ],
        method: [
          { required: true, message: "请输入检测方法及开发", trigger: "blur" },
        ],
        steps: [
          { required: true, message: "请输入实验步骤", trigger: "blur" },
        ],
        dataAnalysis: [
          { required: true, message: "请输入数据采集及分析", trigger: "blur" },
        ],
        evaluation: [
          { required: true, message: "请输入结果评估", trigger: "blur" },
        ],
        notes: [
          { required: true, message: "请输入注意事项", trigger: "blur" },
        ],
      },
    };
  },
  methods: {
    submitForm() {
    // 获取所有编辑器的内容
    getAllEditorContent() {
      return {
        purpose: this.$refs.purposeEditor.getContent(),
        material: this.$refs.materialEditor.getContent(),
        equipment: this.$refs.equipmentEditor.getContent(),
        method: this.$refs.methodEditor.getContent(),
        steps: this.$refs.stepsEditor.getContent(),
        dataAnalysis: this.$refs.dataAnalysisEditor.getContent(),
        evaluation: this.$refs.evaluationEditor.getContent(),
        notes: this.$refs.notesEditor.getContent()
      }
    },
    // 保存
    handleSave() {
      this.$refs.form.validate((valid) => {
        if (valid) {
          console.log("submit!");
          const formData = {
            ...this.form,
            ...this.getAllEditorContent()
          }
          console.log('保存数据:', formData)
          this.$message.success('保存成功')
        }
      });
      })
    },
    addMember() {
      this.$refs.selectMember.open();
    },
    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;
    // 保存草稿
    handleSaveDraft() {
      const formData = {
        ...this.form,
        ...this.getAllEditorContent(),
        status: 'draft'
      }
      console.log('草稿数据:', formData)
      this.$message.success('草稿保存成功')
    },
  },
};
@@ -212,4 +322,12 @@
    margin-right: 20px;
  }
}
.ai-editor-container {
  width: 75%;
  margin: 20px 0;
  border: 1px solid #dcdfe6;
  border-radius: 4px;
  background: #fff;
}
</style>
src/views/dataManagement/dispatching/addDispatch.vue
@@ -340,6 +340,7 @@
}
.header-title:first-child {
  margin-top: 0px;
  .header-title-left {
    margin-top: 0;
  }
src/views/dataManagement/inspectionReport/components/addDialog.vue
New file
@@ -0,0 +1,424 @@
<template>
  <el-dialog
    title="新增检测数据"
    :visible.sync="dialogVisible"
    width="60%"
    :close-on-click-modal="false"
    @close="handleClose"
  >
    <div class="sample-dialog">
      <div class="sample-content">
        <div class="form-content">
          <el-form
            ref="form"
            :model="form"
            :rules="rules"
            label-position="top"
          >
            <el-row :gutter="20">
              <el-col :span="12">
                <el-form-item label="检测样编号" prop="sampleCode">
                  <el-input
                    v-model="form.sampleCode"
                    placeholder="请输入检测样编号"
                  />
                </el-form-item>
              </el-col>
              <el-col :span="12">
                <el-form-item label="检测数据" prop="testData">
                  <el-input
                    v-model="form.testData"
                    placeholder="请输入检测数据"
                  />
                </el-form-item>
              </el-col>
            </el-row>
            <el-row :gutter="20">
              <el-col :span="24">
                <el-form-item label="检测类型" prop="testTypes">
                  <el-checkbox-group v-model="form.testTypes">
                    <el-checkbox label="photo">拍照</el-checkbox>
                    <el-checkbox label="spectrum">图谱</el-checkbox>
                  </el-checkbox-group>
                </el-form-item>
              </el-col>
            </el-row>
            <el-row v-if="showPhotoUpload" :gutter="20">
              <el-col :span="24">
                <el-form-item label="检测拍照" prop="photos">
                  <div v-if="isIPad">
                    <el-button type="primary" @click="handleIPadPhoto">拍照</el-button>
                    <el-button type="primary" @click="handleIPadUpload">上传</el-button>
                  </div>
                  <el-upload
                    v-else
                    class="upload-demo"
                    action="#"
                    :file-list="photoList"
                    :auto-upload="false"
                    list-type="picture-card"
                    :on-change="handlePhotoChange"
                  >
                    <i class="el-icon-plus"></i>
                    <!-- <div class="el-upload__tip" slot="tip">支持 jpg、png 格式文件</div> -->
                  </el-upload>
                </el-form-item>
              </el-col>
            </el-row>
            <el-row v-if="showSpectrumUpload" :gutter="20">
              <el-col :span="24">
                <el-form-item label="检测图谱" prop="spectrums">
                  <div v-if="isIPad">
                    <el-button type="primary" @click="handleIPadSpectrum">选择图谱</el-button>
                  </div>
                  <el-upload
                    v-else
                    class="upload-file"
                    action="#"
                    :file-list="spectrumList"
                    :auto-upload="false"
                    :on-change="handleSpectrumChange"
                    :on-remove="handleSpectrumRemove"
                  >
                    <el-button type="primary">
                      <i class="el-icon-upload"></i> 选择文件
                    </el-button>
                    <div class="el-upload__tip" slot="tip">支持 jpg、png、pdf 格式文件</div>
                  </el-upload>
                </el-form-item>
              </el-col>
            </el-row>
          </el-form>
        </div>
      </div>
    </div>
    <div slot="footer" class="dialog-footer select-member-footer">
      <el-button @click="handleClose">取 消</el-button>
      <el-button type="primary" @click="handleSubmit">确 定</el-button>
    </div>
  </el-dialog>
</template>
<script>
export default {
  name: "AddDialog",
  props: {
    visible: {
      type: Boolean,
      default: false
    }
  },
  data() {
    return {
      isIPad: /iPad/i.test(navigator.userAgent),
      form: {
        sampleCode: "",
        testData: "",
        testTypes: ["photo"], // 默认选中拍照
        photos: [],
        spectrums: []
      },
      rules: {
        sampleCode: [
          { required: true, message: "请输入检测样编号", trigger: "blur" }
        ],
        testData: [
          { required: true, message: "请输入检测数据", trigger: "blur" }
        ],
        testTypes: [
          {
            type: 'array',
            required: true,
            message: "请至少选择一种检测类型",
            trigger: "change"
          }
        ],
        photos: [
          {
            required: true,
            message: "请上传检测照片",
            trigger: "change",
            validator: (rule, value, callback) => {
              if (this.form.testTypes.includes('photo') && (!this.photoList || !this.photoList.length)) {
                callback(new Error('请上传检测照片'));
              } else {
                callback();
              }
            }
          }
        ],
        spectrums: [
          {
            required: true,
            message: "请上传检测图谱",
            trigger: "change",
            validator: (rule, value, callback) => {
              if (this.form.testTypes.includes('spectrum') && (!this.spectrumList || !this.spectrumList.length)) {
                callback(new Error('请上传检测图谱'));
              } else {
                callback();
              }
            }
          }
        ]
      },
      photoList: [],
      spectrumList: []
    };
  },
  computed: {
    dialogVisible: {
      get() {
        return this.visible;
      },
      set(val) {
        this.$emit("update:visible", val);
      }
    },
    showPhotoUpload() {
      return this.form.testTypes.includes('photo');
    },
    showSpectrumUpload() {
      return this.form.testTypes.includes('spectrum');
    }
  },
  methods: {
    setFormData(data) {
      // 设置基础表单数据
      this.form.sampleCode = data.sampleCode;
      this.form.testData = data.testData;
      this.form.testTypes = data.testTypes;
      // 设置照片列表
      if (data.photos && data.photos.length) {
        this.photoList = data.photos.map(photo => ({
          name: photo.name,
          url: photo.url,
          status: 'success'
        }));
      } else {
        this.photoList = [];
      }
      // 设置图谱列表
      if (data.spectrums && data.spectrums.length) {
        this.spectrumList = data.spectrums.map(spectrum => ({
          name: spectrum.name,
          url: spectrum.url,
          status: 'success'
        }));
      } else {
        this.spectrumList = [];
      }
      // 重置表单校验状态
      this.$nextTick(() => {
        this.$refs.form?.clearValidate();
      });
    },
    handleClose() {
      this.dialogVisible = false;
      this.$refs.form?.resetFields();
      this.photoList = [];
      this.spectrumList = [];
      this.form = {
        sampleCode: "",
        testData: "",
        testTypes: ["photo"],
        photos: [],
        spectrums: []
      };
    },
    handleSubmit() {
      this.$refs.form.validate((valid) => {
        if (valid) {
          const submitData = {
            ...this.form,
            photos: this.photoList,
            spectrums: this.spectrumList
          };
          this.$emit("success", submitData);
        }
      });
    },
    handlePhotoChange(file, fileList) {
      this.photoList = fileList;
      this.$refs.form.validateField('photos');
    },
    handleSpectrumChange(file, fileList) {
      this.spectrumList = fileList;
      this.$refs.form.validateField('spectrums');
    },
    handleSpectrumRemove(file) {
      // 处理文件移除逻辑
    },
    // iPad 相关方法
    handleIPadPhoto() {
      // TODO: 调用 iPad 拍照功能
      console.log('调用 iPad 拍照功能');
    },
    handleIPadUpload() {
      // TODO: 调用 iPad 上传功能
      console.log('调用 iPad 上传功能');
    },
    handleIPadSpectrum() {
      // TODO: 调用 iPad 选择图谱功能
      console.log('调用 iPad 选择图谱功能');
    }
  }
};
</script>
<style scoped lang="less">
::v-deep .el-dialog__body {
  padding: 0;
  max-height: calc(100vh - 200px);  // 设置最大高度
}
::v-deep .el-dialog {
  margin-top: 5vh !important;  // 调整弹窗位置
  max-height: 90vh;  // 设置弹窗最大高度
  display: flex;
  flex-direction: column;
  .el-dialog__body {
    flex: 1;
    overflow: hidden;  // 防止出现双滚动条
  }
}
.sample-dialog {
  height: 100%;
  .sample-content {
    background: #ffffff;
    border-radius: 10px;
    padding: 20px;
    height: 100%;
    display: flex;
    flex-direction: column;
    .form-content {
      flex: 1;
      overflow-y: auto;
      padding: 0 10px;
      max-height: calc(90vh - 250px);  // 设置内容区域最大高度
      &::-webkit-scrollbar {
        width: 6px;
      }
      &::-webkit-scrollbar-thumb {
        background: #c0c4cc;
        border-radius: 3px;
      }
      &::-webkit-scrollbar-track {
        background: #f5f7fa;
      }
    }
  }
}
.dialog-footer {
  align-items: center;
  display: flex;
  justify-content: center;
  padding: 15px 20px;
  border-top: 1px solid #e4e7ed;
  margin-top: auto;  // 保持在底部
  button {
    width: 150px;
  }
}
.upload-demo {
  ::v-deep {
    .el-upload--picture-card {
      width: 120px;
      height: 120px;
      line-height: 120px;
    }
    .el-upload-list--picture-card {
      display: flex;
      flex-wrap: wrap;
      gap: 8px;
      .el-upload-list__item {
        width: 120px;
        height: 120px;
        margin: 0;
      }
    }
    // 让上传按钮始终显示在列表最后
    .el-upload--picture-card {
      order: 9999;
      margin: 0;
    }
  }
  // 包裹容器也使用flex布局
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}
.el-row {
  margin-bottom: 20px;
  &:last-child {
    margin-bottom: 0;  // 最后一行不要margin
  }
}
::v-deep .el-form-item--small.el-form-item {
  margin-bottom: 0;
}
::v-deep .el-form-item__label {
  padding-bottom: 8px;
}
// 优化表单布局
::v-deep .el-form {
  .el-form-item {
    margin-bottom: 15px;  // 减小表单项间距
    &:last-child {
      margin-bottom: 0;
    }
  }
}
// 优化上传区域布局
::v-deep .el-upload-list--picture-card {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;  // 设置图片间距
}
.upload-file {
  ::v-deep {
    .el-upload-list {
      margin-top: 10px;
    }
    .el-upload-list__item {
      transition: all .3s;
      &:hover {
        background-color: #f5f7fa;
      }
    }
    .el-upload__tip {
      color: #909399;
      font-size: 12px;
      margin-top: 8px;
    }
  }
}
</style>
src/views/dataManagement/inspectionReport/detail.vue
New file
@@ -0,0 +1,312 @@
<template>
  <Card>
    <el-form
      ref="form"
      :model="form"
      :rules="rules"
      inline
      label-position="top"
    >
      <div class="header-title" style="margin-bottom: 38px">
        <div class="header-title-left">
          <img src="@/assets/public/headercard.png" />
          <div>所属实验调度</div>
        </div>
        <el-button type="primary" class="el-icon-plus" @click="handleAddTask">选择实验调度</el-button>
      </div>
      <Table :data="taskTableData" :total="0" :height="null" class="rwuTable">
        <el-table-column prop="index" label="所属项目课题方案" width="200"></el-table-column>
        <el-table-column prop="sampleCode" label="实验编号"></el-table-column>
        <el-table-column prop="testData" label="实验名称"></el-table-column>
        <el-table-column prop="testData" label="通知时间"></el-table-column>
        <el-table-column prop="testData" label="实验开始时间"></el-table-column>
        <el-table-column prop="testData" label="实验结束时间"></el-table-column>
        <el-table-column prop="testData" label="参加人员"></el-table-column>
        <el-table-column prop="testData" label="状态"></el-table-column>
      </Table>
      <div class="header-title">
        <div class="header-title-left">
          <img src="@/assets/public/headercard.png" />
          <div>本次检验结果总表</div>
        </div>
      </div>
      <div class="header-title">
        <div class="header-title-left">
          <img src="@/assets/public/headercard.png" />
          <span>本次检验结果</span>
        </div>
      </div>
      <div class="add-project-footer">
        <el-button type="primary" class="save-btn">发送</el-button>
        <el-button>存草稿</el-button>
      </div>
    </el-form>
    <!-- 添加检测数据弹窗 -->
    <add-dialog
      ref="addDialog"
      :visible.sync="dialogVisible"
      @success="handleTaskSubmit"
    />
  </Card>
</template>
<script>
import AddDialog from './components/addDialog.vue'
import moment from 'moment'
export default {
  name: "AddProject",
  components: {
    AddDialog
  },
  data() {
    return {
      dialogVisible: false,
      isEdit: false,
      currentEditIndex: -1,
      form: {
        recordNo: '',
        testItemName: '',
        testItemNo: '',
        testMethodName: '',
        testMethodNo: ''
      },
      rules: {
        recordNo: [{ required: true, message: "请输入原始检验记录编号", trigger: "blur" }],
        testItemName: [{ required: true, message: "请输入检测项名字", trigger: "blur" }],
        testItemNo: [{ required: true, message: "请输入检测项编号", trigger: "blur" }],
        testMethodName: [{ required: true, message: "请输入检测方法名字", trigger: "blur" }],
        testMethodNo: [{ required: true, message: "请输入检测方法编号", trigger: "blur" }]
      },
      taskTableData: []
    };
  },
  methods: {
    handleAddTask() {
      this.isEdit = false;
      this.currentEditIndex = -1;
      this.dialogVisible = true;
    },
    handleEditTask(row) {
      this.isEdit = true;
      this.currentEditIndex = this.taskTableData.findIndex(item => item === row);
      const editData = {
        sampleCode: row.sampleCode,
        testData: row.testData,
        testTypes: [],
        photos: [],
        spectrums: []
      };
      if (row.photos && row.photos.length > 0) {
        editData.testTypes.push('photo');
        editData.photos = row.photos.map(photo => ({
          name: photo.name,
          url: photo.url,
          raw: null
        }));
      }
      if (row.spectrums && row.spectrums.length > 0) {
        editData.testTypes.push('spectrum');
        editData.spectrums = row.spectrums.map(spectrum => ({
          name: spectrum.name,
          url: spectrum.url,
          raw: null
        }));
      }
      this.$nextTick(() => {
        this.dialogVisible = true;
        this.$refs.addDialog.setFormData(editData);
      });
    },
    handleDeleteTask(row) {
      this.$confirm("确认删除该检测数据吗?", "提示", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning",
      })
        .then(() => {
          const index = this.taskTableData.findIndex((item) => item === row);
          if (index > -1) {
            const item = this.taskTableData[index];
            if (item.photos) {
              item.photos.forEach(photo => {
                if (photo.url.startsWith('blob:')) {
                  URL.revokeObjectURL(photo.url);
                }
              });
            }
            if (item.spectrums) {
              item.spectrums.forEach(spectrum => {
                if (spectrum.url.startsWith('blob:')) {
                  URL.revokeObjectURL(spectrum.url);
                }
              });
            }
            this.taskTableData.splice(index, 1);
            this.$message.success("删除成功");
          }
        })
        .catch(() => {});
    },
    handleTaskSubmit(formData) {
      const photos = formData.photos.map(file => ({
        name: file.name,
        url: file.url || URL.createObjectURL(file.raw)
      }));
      const spectrums = formData.spectrums.map(file => ({
        name: file.name,
        url: file.url || URL.createObjectURL(file.raw)
      }));
      const newData = {
        sampleCode: formData.sampleCode,
        testData: formData.testData,
        testResult: '',
        photos,
        spectrums,
        createTime: this.isEdit ? this.taskTableData[this.currentEditIndex].createTime : moment().format('YYYY-MM-DD HH:mm:ss')
      };
      if (this.isEdit && this.currentEditIndex > -1) {
        const oldData = this.taskTableData[this.currentEditIndex];
        if (oldData.photos) {
          oldData.photos.forEach(photo => {
            if (photo.url.startsWith('blob:') && !photos.find(p => p.url === photo.url)) {
              URL.revokeObjectURL(photo.url);
            }
          });
        }
        if (oldData.spectrums) {
          oldData.spectrums.forEach(spectrum => {
            if (spectrum.url.startsWith('blob:') && !spectrums.find(s => s.url === spectrum.url)) {
              URL.revokeObjectURL(spectrum.url);
            }
          });
        }
        this.taskTableData.splice(this.currentEditIndex, 1, newData);
        this.$message.success('更新成功');
      } else {
        this.taskTableData.push(newData);
        this.$message.success('添加成功');
      }
      this.dialogVisible = false;
      this.isEdit = false;
      this.currentEditIndex = -1;
    },
    getPhotoUrls(photos) {
      return photos.map(photo => photo.url);
    }
  }
};
</script>
<style scoped lang="less">
.el-form--inline .el-form-item {
  margin-right: 83px;
}
.header-title {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 13px;
  margin-top: 38px;
  .header-title-left {
    display: flex;
    align-items: center;
    gap: 13px;
    img {
      width: 12px;
      height: 19px;
    }
    span {
      flex-shrink: 0;
      font-weight: bold;
      font-size: 18px;
      color: #222222;
      line-height: 27px;
      font-family: "Source Han Sans CN Bold Bold";
    }
    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;
      }
    }
  }
}
.rwuTable {
  width: 85%;
  padding-left: 40px;
}
.add-project-footer {
  margin-top: 43px;
  button {
    width: 220px;
  }
  .save-btn {
    margin-right: 20px;
  }
}
.image-preview {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  .preview-image {
    width: 50px;
    height: 50px;
    border-radius: 4px;
    cursor: pointer;
  }
  .image-slot {
    display: flex;
    justify-content: center;
    align-items: center;
    width: 100%;
    height: 100%;
    background: #f5f7fa;
    color: #909399;
  }
}
.spectrum-link {
  display: block;
  margin-bottom: 5px;
  &:last-child {
    margin-bottom: 0;
  }
}
</style>
src/views/dataManagement/inspectionReport/list.vue
New file
@@ -0,0 +1,202 @@
<template>
  <div class="list">
    <TableCustom :queryForm="form" :tableData="tableData" :total="total" :height="null">
      <template #search>
        <el-form :model="form" labelWidth="auto" inline>
          <el-form-item label="项目课题方案名称:">
            <el-input v-model="form.projectPlan" placeholder="请输入"></el-input>
          </el-form-item>
          <el-form-item label="所属实验编号:">
            <el-input v-model="form.experimentNo" 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-input v-model="form.experimentName" placeholder="请输入"></el-input>
          </el-form-item>
          <el-form-item label="">
            <el-button type="default" @click="resetForm">重置</el-button>
            <el-button type="primary" style="margin-left: 10px;" @click="handleSearch">查询</el-button>
          </el-form-item>
        </el-form>
      </template>
      <template #setting>
        <div class="tableTitle" @click="handleAdd">
          <div class="flex a-center">
            <div class="title active">新增检验报告</div>
          </div>
        </div>
      </template>
      <template #table>
        <el-table-column prop="projectPlan" label="所属项目课题方案" ></el-table-column>
        <el-table-column prop="experimentNo" label="所属实验编号" ></el-table-column>
        <el-table-column prop="experimentName" 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="status" label="状态" >
          <template slot-scope="scope">
            <el-tag :type="scope.row.status === '待提交' ? 'warning' : 'success'">
              {{ scope.row.status }}
            </el-tag>
          </template>
        </el-table-column>
        <el-table-column label="操作">
          <template slot-scope="scope">
            <el-button type="text" @click="handleSubmit(scope.row)">提交</el-button>
            <el-button type="text" @click="handleEdit(scope.row)">编辑</el-button>
          </template>
        </el-table-column>
      </template>
    </TableCustom>
  </div>
</template>
<script>
import store from '../../../store';
export default {
  name: "OriginalRecordTest",
  data() {
    return {
      form: {
        projectPlan: "",
        experimentNo: "",
        creator: "",
        createTime: [],
        experimentName: ""
      },
      tableData: [
        {
          id: '1',
          projectPlan: '名称名称名称',
          experimentNo: '31423764',
          experimentName: '名称名称名称',
          creator: '张三',
          createTime: '2024-01-08',
          status: '待提交'
        }
      ],
      total: 5
    };
  },
  created() {
    this.getTableData();
  },
  methods: {
    resetForm() {
      this.form = {
        projectPlan: "",
        experimentNo: "",
        creator: "",
        createTime: [],
        experimentName: ""
      };
    },
    handleSearch() {
      this.getTableData();
    },
    handleAdd(){
      this.$router.push({
        path: "/dataManagement/inspectionReport/detail",
        query: {
          type: 'add'
        }
      });
    },
    handleEdit(row) {
      this.$router.push({
        path: "/dataManagement/inspectionReport/detail",
        query: {
          id: row.id,
          type: 'edit'
        }
      });
    },
    handleSubmit(row) {
      this.$confirm('确认提交该检验报告?', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(() => {
        // TODO: 调用提交接口
        this.$message({
          type: 'success',
          message: '提交成功!'
        });
        this.getTableData();
      }).catch(() => {
        this.$message({
          type: 'info',
          message: '已取消提交'
        });
      });
    },
    getTableData() {
      this.tableData = [
        {
          id: '1',
          projectPlan: '名称名称名称',
          experimentNo: '31423764',
          experimentName: '名称名称名称',
          creator: '张三',
          createTime: '2024-01-08',
          status: '待提交'
        },
        {
          id: '2',
          projectPlan: '项目方案2',
          experimentNo: '31423765',
          experimentName: '实验名称2',
          creator: '李四',
          createTime: '2024-01-09',
          status: '已提交'
        }
      ];
      this.total = this.tableData.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;
  cursor: pointer;
  .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;
  }
  .active {
    color: #049c9a;
    background: #ffffff;
    border-radius: 8px 8px 0px 0px;
    border: 1px solid #049c9a;
  }
}
</style>
src/views/dataManagement/originalRecordTest/components/addDialog.vue
New file
@@ -0,0 +1,424 @@
<template>
  <el-dialog
    title="新增检测数据"
    :visible.sync="dialogVisible"
    width="60%"
    :close-on-click-modal="false"
    @close="handleClose"
  >
    <div class="sample-dialog">
      <div class="sample-content">
        <div class="form-content">
          <el-form
            ref="form"
            :model="form"
            :rules="rules"
            label-position="top"
          >
            <el-row :gutter="20">
              <el-col :span="12">
                <el-form-item label="检测样编号" prop="sampleCode">
                  <el-input
                    v-model="form.sampleCode"
                    placeholder="请输入检测样编号"
                  />
                </el-form-item>
              </el-col>
              <el-col :span="12">
                <el-form-item label="检测数据" prop="testData">
                  <el-input
                    v-model="form.testData"
                    placeholder="请输入检测数据"
                  />
                </el-form-item>
              </el-col>
            </el-row>
            <el-row :gutter="20">
              <el-col :span="24">
                <el-form-item label="检测类型" prop="testTypes">
                  <el-checkbox-group v-model="form.testTypes">
                    <el-checkbox label="photo">拍照</el-checkbox>
                    <el-checkbox label="spectrum">图谱</el-checkbox>
                  </el-checkbox-group>
                </el-form-item>
              </el-col>
            </el-row>
            <el-row v-if="showPhotoUpload" :gutter="20">
              <el-col :span="24">
                <el-form-item label="检测拍照" prop="photos">
                  <div v-if="isIPad">
                    <el-button type="primary" @click="handleIPadPhoto">拍照</el-button>
                    <el-button type="primary" @click="handleIPadUpload">上传</el-button>
                  </div>
                  <el-upload
                    v-else
                    class="upload-demo"
                    action="#"
                    :file-list="photoList"
                    :auto-upload="false"
                    list-type="picture-card"
                    :on-change="handlePhotoChange"
                  >
                    <i class="el-icon-plus"></i>
                    <!-- <div class="el-upload__tip" slot="tip">支持 jpg、png 格式文件</div> -->
                  </el-upload>
                </el-form-item>
              </el-col>
            </el-row>
            <el-row v-if="showSpectrumUpload" :gutter="20">
              <el-col :span="24">
                <el-form-item label="检测图谱" prop="spectrums">
                  <div v-if="isIPad">
                    <el-button type="primary" @click="handleIPadSpectrum">选择图谱</el-button>
                  </div>
                  <el-upload
                    v-else
                    class="upload-file"
                    action="#"
                    :file-list="spectrumList"
                    :auto-upload="false"
                    :on-change="handleSpectrumChange"
                    :on-remove="handleSpectrumRemove"
                  >
                    <el-button type="primary">
                      <i class="el-icon-upload"></i> 选择文件
                    </el-button>
                    <div class="el-upload__tip" slot="tip">支持 jpg、png、pdf 格式文件</div>
                  </el-upload>
                </el-form-item>
              </el-col>
            </el-row>
          </el-form>
        </div>
      </div>
    </div>
    <div slot="footer" class="dialog-footer select-member-footer">
      <el-button @click="handleClose">取 消</el-button>
      <el-button type="primary" @click="handleSubmit">确 定</el-button>
    </div>
  </el-dialog>
</template>
<script>
export default {
  name: "AddDialog",
  props: {
    visible: {
      type: Boolean,
      default: false
    }
  },
  data() {
    return {
      isIPad: /iPad/i.test(navigator.userAgent),
      form: {
        sampleCode: "",
        testData: "",
        testTypes: ["photo"], // 默认选中拍照
        photos: [],
        spectrums: []
      },
      rules: {
        sampleCode: [
          { required: true, message: "请输入检测样编号", trigger: "blur" }
        ],
        testData: [
          { required: true, message: "请输入检测数据", trigger: "blur" }
        ],
        testTypes: [
          {
            type: 'array',
            required: true,
            message: "请至少选择一种检测类型",
            trigger: "change"
          }
        ],
        photos: [
          {
            required: true,
            message: "请上传检测照片",
            trigger: "change",
            validator: (rule, value, callback) => {
              if (this.form.testTypes.includes('photo') && (!this.photoList || !this.photoList.length)) {
                callback(new Error('请上传检测照片'));
              } else {
                callback();
              }
            }
          }
        ],
        spectrums: [
          {
            required: true,
            message: "请上传检测图谱",
            trigger: "change",
            validator: (rule, value, callback) => {
              if (this.form.testTypes.includes('spectrum') && (!this.spectrumList || !this.spectrumList.length)) {
                callback(new Error('请上传检测图谱'));
              } else {
                callback();
              }
            }
          }
        ]
      },
      photoList: [],
      spectrumList: []
    };
  },
  computed: {
    dialogVisible: {
      get() {
        return this.visible;
      },
      set(val) {
        this.$emit("update:visible", val);
      }
    },
    showPhotoUpload() {
      return this.form.testTypes.includes('photo');
    },
    showSpectrumUpload() {
      return this.form.testTypes.includes('spectrum');
    }
  },
  methods: {
    setFormData(data) {
      // 设置基础表单数据
      this.form.sampleCode = data.sampleCode;
      this.form.testData = data.testData;
      this.form.testTypes = data.testTypes;
      // 设置照片列表
      if (data.photos && data.photos.length) {
        this.photoList = data.photos.map(photo => ({
          name: photo.name,
          url: photo.url,
          status: 'success'
        }));
      } else {
        this.photoList = [];
      }
      // 设置图谱列表
      if (data.spectrums && data.spectrums.length) {
        this.spectrumList = data.spectrums.map(spectrum => ({
          name: spectrum.name,
          url: spectrum.url,
          status: 'success'
        }));
      } else {
        this.spectrumList = [];
      }
      // 重置表单校验状态
      this.$nextTick(() => {
        this.$refs.form?.clearValidate();
      });
    },
    handleClose() {
      this.dialogVisible = false;
      this.$refs.form?.resetFields();
      this.photoList = [];
      this.spectrumList = [];
      this.form = {
        sampleCode: "",
        testData: "",
        testTypes: ["photo"],
        photos: [],
        spectrums: []
      };
    },
    handleSubmit() {
      this.$refs.form.validate((valid) => {
        if (valid) {
          const submitData = {
            ...this.form,
            photos: this.photoList,
            spectrums: this.spectrumList
          };
          this.$emit("success", submitData);
        }
      });
    },
    handlePhotoChange(file, fileList) {
      this.photoList = fileList;
      this.$refs.form.validateField('photos');
    },
    handleSpectrumChange(file, fileList) {
      this.spectrumList = fileList;
      this.$refs.form.validateField('spectrums');
    },
    handleSpectrumRemove(file) {
      // 处理文件移除逻辑
    },
    // iPad 相关方法
    handleIPadPhoto() {
      // TODO: 调用 iPad 拍照功能
      console.log('调用 iPad 拍照功能');
    },
    handleIPadUpload() {
      // TODO: 调用 iPad 上传功能
      console.log('调用 iPad 上传功能');
    },
    handleIPadSpectrum() {
      // TODO: 调用 iPad 选择图谱功能
      console.log('调用 iPad 选择图谱功能');
    }
  }
};
</script>
<style scoped lang="less">
::v-deep .el-dialog__body {
  padding: 0;
  max-height: calc(100vh - 200px);  // 设置最大高度
}
::v-deep .el-dialog {
  margin-top: 5vh !important;  // 调整弹窗位置
  max-height: 90vh;  // 设置弹窗最大高度
  display: flex;
  flex-direction: column;
  .el-dialog__body {
    flex: 1;
    overflow: hidden;  // 防止出现双滚动条
  }
}
.sample-dialog {
  height: 100%;
  .sample-content {
    background: #ffffff;
    border-radius: 10px;
    padding: 20px;
    height: 100%;
    display: flex;
    flex-direction: column;
    .form-content {
      flex: 1;
      overflow-y: auto;
      padding: 0 10px;
      max-height: calc(90vh - 250px);  // 设置内容区域最大高度
      &::-webkit-scrollbar {
        width: 6px;
      }
      &::-webkit-scrollbar-thumb {
        background: #c0c4cc;
        border-radius: 3px;
      }
      &::-webkit-scrollbar-track {
        background: #f5f7fa;
      }
    }
  }
}
.dialog-footer {
  align-items: center;
  display: flex;
  justify-content: center;
  padding: 15px 20px;
  border-top: 1px solid #e4e7ed;
  margin-top: auto;  // 保持在底部
  button {
    width: 150px;
  }
}
.upload-demo {
  ::v-deep {
    .el-upload--picture-card {
      width: 120px;
      height: 120px;
      line-height: 120px;
    }
    .el-upload-list--picture-card {
      display: flex;
      flex-wrap: wrap;
      gap: 8px;
      .el-upload-list__item {
        width: 120px;
        height: 120px;
        margin: 0;
      }
    }
    // 让上传按钮始终显示在列表最后
    .el-upload--picture-card {
      order: 9999;
      margin: 0;
    }
  }
  // 包裹容器也使用flex布局
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}
.el-row {
  margin-bottom: 20px;
  &:last-child {
    margin-bottom: 0;  // 最后一行不要margin
  }
}
::v-deep .el-form-item--small.el-form-item {
  margin-bottom: 0;
}
::v-deep .el-form-item__label {
  padding-bottom: 8px;
}
// 优化表单布局
::v-deep .el-form {
  .el-form-item {
    margin-bottom: 15px;  // 减小表单项间距
    &:last-child {
      margin-bottom: 0;
    }
  }
}
// 优化上传区域布局
::v-deep .el-upload-list--picture-card {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;  // 设置图片间距
}
.upload-file {
  ::v-deep {
    .el-upload-list {
      margin-top: 10px;
    }
    .el-upload-list__item {
      transition: all .3s;
      &:hover {
        background-color: #f5f7fa;
      }
    }
    .el-upload__tip {
      color: #909399;
      font-size: 12px;
      margin-top: 8px;
    }
  }
}
</style>
src/views/dataManagement/originalRecordTest/detail.vue
New file
@@ -0,0 +1,388 @@
<template>
  <Card>
    <el-form
      ref="form"
      :model="form"
      :rules="rules"
      inline
      label-position="top"
    >
      <div style="padding-left: 25px">
        <el-form-item prop="recordNo" label="原始检验记录编号">
          <el-input v-model="form.recordNo" placeholder="请输入" />
        </el-form-item>
      </div>
      <div class="header-title" style="margin-bottom: 38px">
        <div class="header-title-left">
          <img src="@/assets/public/headercard.png" />
          <span>检测项信息</span>
        </div>
      </div>
      <div style="padding-left: 25px">
        <el-form-item prop="testItemName" label="检测项名字">
          <el-input v-model="form.testItemName" placeholder="请输入" />
        </el-form-item>
        <el-form-item prop="testItemNo" label="检测项编号">
          <el-input v-model="form.testItemNo" placeholder="请输入" />
        </el-form-item>
        <el-form-item prop="testMethodName" label="检测方法名字">
          <el-input v-model="form.testMethodName" placeholder="请输入" />
        </el-form-item>
        <el-form-item prop="testMethodNo" label="检测方法编号">
          <el-input v-model="form.testMethodNo" placeholder="请输入" />
        </el-form-item>
      </div>
      <div class="header-title" style="margin-bottom: 38px">
        <div class="header-title-left">
          <img src="@/assets/public/headercard.png" />
          <span>一 、检测标准</span>
        </div>
      </div>
      <div class="header-title" style="margin-bottom: 38px">
        <div class="header-title-left">
          <img src="@/assets/public/headercard.png" />
          <span>二 、检测仪器</span>
        </div>
      </div>
      <div class="header-title" style="margin-bottom: 38px">
        <div class="header-title-left">
          <img src="@/assets/public/headercard.png" />
          <span>三 、检测试剂</span>
        </div>
      </div>
      <div class="header-title" style="margin-bottom: 38px">
        <div class="header-title-left">
          <img src="@/assets/public/headercard.png" />
          <span>四 、检测步骤</span>
        </div>
      </div>
      <div class="header-title" style="margin-bottom: 38px">
        <div class="header-title-left">
          <img src="@/assets/public/headercard.png" />
          <span>五 、检测数据及参照</span>
        </div>
        <el-button type="primary" class="el-icon-plus" @click="handleAddTask">添加检测数据</el-button>
      </div>
      <Table :data="taskTableData" :total="0" :height="null" class="rwuTable">
        <el-table-column type="index" label="序号" width="80"></el-table-column>
        <el-table-column prop="sampleCode" label="检测样编号"></el-table-column>
        <el-table-column prop="testData" label="检测数据"></el-table-column>
        <el-table-column label="检测图片" width="200">
          <template slot-scope="scope">
            <div class="image-preview" v-if="scope.row.photos && scope.row.photos.length">
              <el-image
                v-for="(photo, index) in scope.row.photos"
                :key="index"
                :src="photo.url"
                :preview-src-list="getPhotoUrls(scope.row.photos)"
                fit="cover"
                class="preview-image"
              >
                <div slot="error" class="image-slot">
                  <i class="el-icon-picture-outline"></i>
                </div>
              </el-image>
            </div>
            <span v-else>无图片</span>
          </template>
        </el-table-column>
        <el-table-column label="检测图谱" width="200">
          <template slot-scope="scope">
            <div v-if="scope.row.spectrums && scope.row.spectrums.length">
              <el-link
                v-for="(spectrum, index) in scope.row.spectrums"
                :key="index"
                type="primary"
                :href="spectrum.url"
                target="_blank"
                class="spectrum-link"
              >
                {{ spectrum.name }}
              </el-link>
            </div>
            <span v-else>无图谱</span>
          </template>
        </el-table-column>
        <el-table-column prop="createTime" label="添加时间" width="180"></el-table-column>
        <el-table-column label="操作" width="150">
          <template slot-scope="scope">
            <el-button type="text" @click="handleEditTask(scope.row)">编辑</el-button>
            <el-button type="text" @click="handleDeleteTask(scope.row)">删除</el-button>
          </template>
        </el-table-column>
      </Table>
      <div class="header-title">
        <div class="header-title-left">
          <img src="@/assets/public/headercard.png" />
          <span>六 、检测结果预算</span>
        </div>
      </div>
      <Table :data="taskTableData" :total="0" :height="null" class="rwuTable">
        <el-table-column type="index" label="序号" width="80"></el-table-column>
        <el-table-column prop="sampleCode" label="检测样编号"></el-table-column>
        <el-table-column prop="testData" label="检测数据"></el-table-column>
        <el-table-column label="检测结果" >
          <template slot-scope="scope">
            <el-input v-model="scope.row.testResult" placeholder="请输入检测结果"></el-input>
          </template>
        </el-table-column>
      </Table>
      <div class="add-project-footer">
        <el-button type="primary" class="save-btn">发送</el-button>
        <el-button>存草稿</el-button>
      </div>
    </el-form>
    <!-- 添加检测数据弹窗 -->
    <add-dialog
      ref="addDialog"
      :visible.sync="dialogVisible"
      @success="handleTaskSubmit"
    />
  </Card>
</template>
<script>
import AddDialog from './components/addDialog.vue'
import moment from 'moment'
export default {
  name: "AddProject",
  components: {
    AddDialog
  },
  data() {
    return {
      dialogVisible: false,
      isEdit: false,
      currentEditIndex: -1,
      form: {
        recordNo: '',
        testItemName: '',
        testItemNo: '',
        testMethodName: '',
        testMethodNo: ''
      },
      rules: {
        recordNo: [{ required: true, message: "请输入原始检验记录编号", trigger: "blur" }],
        testItemName: [{ required: true, message: "请输入检测项名字", trigger: "blur" }],
        testItemNo: [{ required: true, message: "请输入检测项编号", trigger: "blur" }],
        testMethodName: [{ required: true, message: "请输入检测方法名字", trigger: "blur" }],
        testMethodNo: [{ required: true, message: "请输入检测方法编号", trigger: "blur" }]
      },
      taskTableData: []
    };
  },
  methods: {
    handleAddTask() {
      this.isEdit = false;
      this.currentEditIndex = -1;
      this.dialogVisible = true;
    },
    handleEditTask(row) {
      this.isEdit = true;
      this.currentEditIndex = this.taskTableData.findIndex(item => item === row);
      const editData = {
        sampleCode: row.sampleCode,
        testData: row.testData,
        testTypes: [],
        photos: [],
        spectrums: []
      };
      if (row.photos && row.photos.length > 0) {
        editData.testTypes.push('photo');
        editData.photos = row.photos.map(photo => ({
          name: photo.name,
          url: photo.url,
          raw: null
        }));
      }
      if (row.spectrums && row.spectrums.length > 0) {
        editData.testTypes.push('spectrum');
        editData.spectrums = row.spectrums.map(spectrum => ({
          name: spectrum.name,
          url: spectrum.url,
          raw: null
        }));
      }
      this.$nextTick(() => {
        this.dialogVisible = true;
        this.$refs.addDialog.setFormData(editData);
      });
    },
    handleDeleteTask(row) {
      this.$confirm("确认删除该检测数据吗?", "提示", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning",
      })
        .then(() => {
          const index = this.taskTableData.findIndex((item) => item === row);
          if (index > -1) {
            const item = this.taskTableData[index];
            if (item.photos) {
              item.photos.forEach(photo => {
                if (photo.url.startsWith('blob:')) {
                  URL.revokeObjectURL(photo.url);
                }
              });
            }
            if (item.spectrums) {
              item.spectrums.forEach(spectrum => {
                if (spectrum.url.startsWith('blob:')) {
                  URL.revokeObjectURL(spectrum.url);
                }
              });
            }
            this.taskTableData.splice(index, 1);
            this.$message.success("删除成功");
          }
        })
        .catch(() => {});
    },
    handleTaskSubmit(formData) {
      const photos = formData.photos.map(file => ({
        name: file.name,
        url: file.url || URL.createObjectURL(file.raw)
      }));
      const spectrums = formData.spectrums.map(file => ({
        name: file.name,
        url: file.url || URL.createObjectURL(file.raw)
      }));
      const newData = {
        sampleCode: formData.sampleCode,
        testData: formData.testData,
        testResult: '',
        photos,
        spectrums,
        createTime: this.isEdit ? this.taskTableData[this.currentEditIndex].createTime : moment().format('YYYY-MM-DD HH:mm:ss')
      };
      if (this.isEdit && this.currentEditIndex > -1) {
        const oldData = this.taskTableData[this.currentEditIndex];
        if (oldData.photos) {
          oldData.photos.forEach(photo => {
            if (photo.url.startsWith('blob:') && !photos.find(p => p.url === photo.url)) {
              URL.revokeObjectURL(photo.url);
            }
          });
        }
        if (oldData.spectrums) {
          oldData.spectrums.forEach(spectrum => {
            if (spectrum.url.startsWith('blob:') && !spectrums.find(s => s.url === spectrum.url)) {
              URL.revokeObjectURL(spectrum.url);
            }
          });
        }
        this.taskTableData.splice(this.currentEditIndex, 1, newData);
        this.$message.success('更新成功');
      } else {
        this.taskTableData.push(newData);
        this.$message.success('添加成功');
      }
      this.dialogVisible = false;
      this.isEdit = false;
      this.currentEditIndex = -1;
    },
    getPhotoUrls(photos) {
      return photos.map(photo => photo.url);
    }
  }
};
</script>
<style scoped lang="less">
.el-form--inline .el-form-item {
  margin-right: 83px;
}
.header-title {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 13px;
  margin-top: 38px;
  .header-title-left {
    display: flex;
    align-items: center;
    gap: 13px;
    img {
      width: 12px;
      height: 19px;
    }
    span {
      flex-shrink: 0;
      font-weight: bold;
      font-size: 18px;
      color: #222222;
      line-height: 27px;
      font-family: "Source Han Sans CN Bold Bold";
    }
  }
}
.rwuTable {
  width: 85%;
  padding-left: 40px;
}
.add-project-footer {
  margin-top: 43px;
  button {
    width: 220px;
  }
  .save-btn {
    margin-right: 20px;
  }
}
.image-preview {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  .preview-image {
    width: 50px;
    height: 50px;
    border-radius: 4px;
    cursor: pointer;
  }
  .image-slot {
    display: flex;
    justify-content: center;
    align-items: center;
    width: 100%;
    height: 100%;
    background: #f5f7fa;
    color: #909399;
  }
}
.spectrum-link {
  display: block;
  margin-bottom: 5px;
  &:last-child {
    margin-bottom: 0;
  }
}
</style>
src/views/dataManagement/originalRecordTest/list.vue
New file
@@ -0,0 +1,186 @@
<template>
  <div class="list">
    <TableCustom :queryForm="form" :tableData="tableData" :total="total" :height="null">
      <template #search>
        <el-form :model="form" labelWidth="auto" inline>
          <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-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 active">原始记录检测列表</div>
          </div>
        </div>
      </template>
      <template #table>
        <el-table-column prop="projectPlan" label="所属项目课题方案"></el-table-column>
        <el-table-column prop="experimentNo" label="实验编号"></el-table-column>
        <el-table-column prop="experimentName" 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="scope.row.status === '已填写' ? 'success' : 'warning'">
              {{ scope.row.status }}
            </el-tag>
          </template>
        </el-table-column>
        <el-table-column label="操作" width="150">
          <template slot-scope="scope">
            <el-button type="text" @click="handleEdit(scope.row)">编辑</el-button>
            <el-button type="text" @click="handleDetail(scope.row.id)">详情</el-button>
          </template>
        </el-table-column>
      </template>
    </TableCustom>
  </div>
</template>
<script>
export default {
  name: "OriginalRecordTest",
  data() {
    return {
      form: {
        planCode: "",
        creator: "",
        createTime: [],
        approver: ""
      },
      tableData: [
        {
          id: '1',
          projectPlan: 'DD250218-JL01',
          experimentNo: '31423764',
          experimentName: '化学成分分析实验',
          creator: '张三',
          createTime: '2025-1-8 17:56:11',
          status: '未填写'
        },
        {
          id: '2',
          projectPlan: 'DD250218-JL02',
          experimentNo: '31423765',
          experimentName: '物理性能测试实验',
          creator: '李四',
          createTime: '2025-1-8 18:00:00',
          status: '未填写'
        },
        {
          id: '3',
          projectPlan: 'DD250218-JL03',
          experimentNo: '31423766',
          experimentName: '材料强度测试',
          creator: '王五',
          createTime: '2025-1-8 18:30:00',
          status: '已填写'
        },
        {
          id: '4',
          projectPlan: 'DD250218-JL04',
          experimentNo: '31423767',
          experimentName: '耐久性测试实验',
          creator: '赵六',
          createTime: '2025-1-8 19:00:00',
          status: '已填写'
        },
        {
          id: '5',
          projectPlan: 'DD250218-JL05',
          experimentNo: '31423768',
          experimentName: '环境适应性测试',
          creator: '孙七',
          createTime: '2025-1-8 19:30:00',
          status: '未填写'
        }
      ],
      total: 5
    };
  },
  created() {
    this.getTableData();
  },
  methods: {
    resetForm() {
      this.form = {
        planCode: "",
        creator: "",
        createTime: [],
        approver: ""
      };
    },
    handleSearch() {
      this.getTableData();
    },
    handleEdit(row) {
      console.log("编辑数据:", row);
    },
    handleDetail(id) {
      this.$router.push({
        path: "/dataManagement/originalRecordTest/detail",
        query: { id }
      });
    },
    getTableData() {
      // TODO: 调用接口获取数据
    //   this.tableData = [];
    //   this.total = 0;
    }
  }
};
</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;
  }
  .active {
    color: #049c9a;
    background: #ffffff;
    border-radius: 8px 8px 0px 0px;
    border: 1px solid #049c9a;
  }
}
</style>
src/views/dataManagement/sampleManage/addSample.vue
@@ -3,21 +3,12 @@
    <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
      >
      <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="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>
@@ -27,218 +18,168 @@
        <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
      v-if="!isExperimenter"
      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 v-else class="number">取样单编号:213421241</div>
    <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
            >
    <!-- 化验师专用部分 -->
    <div v-if="isAssayer" class="assayer-section">
      <div class="header-title-left" style="margin-top: 38px">
        <img src="@/assets/public/headercard.png" />
        <span>送样-收样记录</span>
        <el-button type="primary" class="el-icon-plus" @click="showPendingColumns = !showPendingColumns">
          {{ showPendingColumns ? "收起待收样详情" : "展开待收样详情" }}
        </el-button>
        <el-button type="primary" class="el-icon-plus" @click="showReceivedColumns = !showReceivedColumns">
          {{ showReceivedColumns ? "收起已收样详情" : "展开已收样详情" }}
        </el-button>
      </div>
      <div class="sample-code">
        取样单编号:{{ form.sampleCode || "DD-25010BS01" }}
      </div>
      <!-- 待收样列表 -->
      <div class="pending-samples">
        <div class="sub-title">待收样列表</div>
        <el-button type="primary" class="batch-receive" @click="handleBatchReceive">批量接收</el-button>
        <Table :data="pendingSamples" :total="pendingSamplesTotal" :height="null"
          @selection-change="handleSelectionChange">
          <template>
            <el-table-column type="selection" width="55"></el-table-column>
            <el-table-column prop="index" label="序号" width="80"></el-table-column>
            <el-table-column prop="sampleName" label="取样名称"></el-table-column>
            <el-table-column prop="sampleCode" label="取样样品编号"></el-table-column>
            <el-table-column prop="ph" label="PH"></el-table-column>
            <el-table-column prop="sampleAmount" label="取样量"></el-table-column>
            <el-table-column prop="sendTime" label="送样时间"></el-table-column>
            <el-table-column prop="sender" label="送样人"></el-table-column>
            <el-table-column prop="status" label="状态"></el-table-column>
            <template v-if="showPendingColumns">
              <el-table-column prop="temperature" label="温度" width="150"></el-table-column>
              <el-table-column prop="waterAmount" label="加水量" width="150"></el-table-column>
              <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>
            </template>
            <el-table-column label="操作" width="100" fixed="right">
              <template slot-scope="scope">
                <el-button type="text" @click="handleReceive(scope.row)">接收</el-button>
              </template>
            </el-table-column>
          </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>
        </Table>
      </div>
      <!-- 已收样列表 -->
      <div class="received-samples">
        <div class="sub-title">已收样列表</div>
        <Table :data="receivedSamples" :total="receivedSamplesTotal" :height="null">
          <template>
            <el-table-column prop="index" label="序号" width="80"></el-table-column>
            <el-table-column prop="sampleName" label="取样名称"></el-table-column>
            <el-table-column prop="sampleCode" label="取样样品编号"></el-table-column>
            <el-table-column prop="temperature" label="温度" width="150"></el-table-column>
            <el-table-column prop="waterAmount" label="加水量" width="150"></el-table-column>
            <el-table-column prop="ph" label="PH"></el-table-column>
            <template v-if="showReceivedColumns">
              <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>
            </template>
            <el-table-column prop="sampleAmount" label="取样量"></el-table-column>
            <el-table-column prop="sendTime" label="送样时间"></el-table-column>
            <el-table-column prop="sender" label="送样人"></el-table-column>
            <el-table-column prop="receiveTime" label="收样时间"></el-table-column>
            <el-table-column prop="receiver" label="收样人"></el-table-column>
            <el-table-column prop="status" label="状态" fixed="right"></el-table-column>
          </template>
        </Table>
      </div>
    </div>
    <!-- 工程师部分 -->
    <div v-if="isEngineer">
      <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="true" placeholder="自动生成" />
        </el-form-item>
      </el-form>
      <div class="header-title-left" style="margin-top: 38px">
        <img src="@/assets/public/headercard.png" />
        <span>取样操作记录</span>
        <el-button 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>
        </template>
      </Table>
      <div class="add-project-footer">
        <el-button type="primary" class="save-btn" @click="handleSave">保存</el-button>
        <el-button>存草稿</el-button>
      </div>
    </div>
    <div class="add-project-footer">
      <el-button type="primary" class="save-btn" @click="handleSave">保存</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"
    />
    <confirm-dialog
      :visible.sync="showConfirmDialog"
      :form-data="form"
      :sample-data="sampleData"
      @confirm="handleConfirm"
    />
    <receive-confirm-dialog :visible.sync="showReceiveConfirm" :sample-count="selectedSamples.length"
      @confirm="confirmReceive" />
  </Card>
</template>
<script>
import experimentalScheduling from "./components/experimental-scheduling.vue";
import addTime from "./components/addTime.vue";
import SampleDialog from "./components/sampleDialog.vue";
import ConfirmDialog from './components/confirmDialog.vue';
import ReceiveConfirmDialog from "./components/receiveConfirmDialog.vue";
export default {
  name: "AddConfirmationSheet",
  name: "AddSample",
  components: {
    experimentalScheduling,
    addTime,
    SampleDialog,
    ConfirmDialog,
    ReceiveConfirmDialog,
  },
  props: {},
  data() {
    return {
      showScheduling: false,
      showAddTime: false,
      showAdditives: true,
      showSampleDialog: false,
      showConfirmDialog: false,
      currentSampleData: {},
      currentEditIndex: -1,
      schedulingData: [], // 实验调度表格数据
      showReceiveConfirm: false,
      showPendingColumns: true,
      showReceivedColumns: true,
      schedulingData: [],
      schedulingTotal: 0,
      sampleData: [
        {
@@ -291,197 +232,99 @@
      sampleTotal: 2,
      form: {
        sampleCode: "",
        remark: "",
      },
      rules: {
        sampleCode: [
          { required: true, message: "请输入取样单编号", trigger: "blur" },
        ],
      },
      menu: [],
      userRole: "experimenter", // 用户角色,可以是 'engineer' 或 'experimenter'
      userRole: "assayer", // engineer默认设置为工程师assayer化验师
      // 化验师相关数据
      pendingSamples: [
        {
          index: 1,
          sampleName: "名称名称名称",
          sampleCode: "314234321",
          ph: "5",
          sampleAmount: "34",
          sendTime: "2025-1-9 15:26:08",
          sender: "李四",
          status: "待接收",
        },
        {
          index: 2,
          sampleName: "名称名称名称",
          sampleCode: "314234321",
          ph: "6",
          sampleAmount: "23",
          sendTime: "2025-1-9 15:26:08",
          sender: "李四",
          status: "待接收",
        },
        {
          index: 3,
          sampleName: "名称名称名称",
          sampleCode: "314234321",
          ph: "7",
          sampleAmount: "23",
          sendTime: "2025-1-13 16:38:17",
          sender: "李四",
          status: "待接收",
        },
      ],
      pendingSamplesTotal: 3,
      receivedSamples: [
        {
          index: 1,
          sampleName: "名称名称名称",
          sampleCode: "314234321",
          ph: "5",
          sampleAmount: "34",
          sendTime: "2025-1-9 15:26:08",
          sender: "李四",
          receiveTime: "2025-1-15 14:00:50",
          receiver: "段向蒙",
          status: "已接收",
        },
        {
          index: 2,
          sampleName: "名称名称名称",
          sampleCode: "314234321",
          ph: "6",
          sampleAmount: "23",
          sendTime: "2025-1-9 15:26:08",
          sender: "李四",
          receiveTime: "2025-1-15 14:00:50",
          receiver: "傅彦涛",
          status: "已接收",
        },
        {
          index: 3,
          sampleName: "名称名称名称",
          sampleCode: "314234321",
          ph: "7",
          sampleAmount: "23",
          sendTime: "2025-1-13 16:38:17",
          sender: "李四",
          receiveTime: "2025-1-15 14:00:50",
          receiver: "王乐康",
          status: "已接收",
        },
      ],
      receivedSamplesTotal: 3,
      selectedSamples: [],
    };
  },
  computed: {
    height() {
      return this.$baseTableHeight();
    },
    isEngineer() {
      return this.userRole === "engineer";
    },
    isExperimenter() {
      return this.userRole === "experimenter";
    isAssayer() {
      return this.userRole === "assayer";
    },
  },
  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,
@@ -509,147 +352,59 @@
      this.$message.success("添加工艺时间成功");
    },
    handleSave() {
      if (this.isExperimenter) {
        this.showConfirmDialog = true;
      } else {
        this.submitForm();
      }
      this.submitForm();
    },
    handleConfirm(signature) {
      this.submitForm(signature);
    },
    submitForm(signature = null) {
    submitForm() {
      // 在这里实现表单提交逻辑
      console.log('提交表单数据', { signature });
      this.$message.success('保存成功');
      this.showConfirmDialog = false;
      console.log("提交表单数据");
      this.$message.success("保存成功");
    },
    handleSelectionChange(selection) {
      this.selectedSamples = selection;
    },
    handleReceive(row) {
      this.selectedSamples = [row];
      this.showReceiveConfirm = true;
    },
    handleBatchReceive() {
      if (this.selectedSamples.length === 0) {
        this.$message.warning("请先选择要接收的样品");
        return;
      }
      this.showReceiveConfirm = true;
    },
    confirmReceive(signature) {
      // 这里处理接收确认逻辑
      const sampleIds = this.selectedSamples.map((item) => item.id);
      console.log("接收样品", { sampleIds, signature });
      // 模拟接收成功
      this.$message.success(`成功接收 ${this.selectedSamples.length} 个样品`);
      // 更新数据
      this.selectedSamples.forEach((sample) => {
        const index = this.pendingSamples.findIndex(
          (item) => item.id === sample.id
        );
        if (index > -1) {
          this.pendingSamples.splice(index, 1);
          this.receivedSamples.unshift({
            ...sample,
            receiveTime: new Date().toLocaleString(),
            receiver: "当前用户",
            status: "已接收",
          });
        }
      });
      // 清空选择
      this.selectedSamples = [];
    },
  },
};
</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;
}
.number{
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 14px;
color: rgba(0,0,0,0.88);
}
.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;
@@ -679,6 +434,7 @@
      margin-right: 4px;
    }
  }
  span {
    flex-shrink: 0;
    font-weight: bold;
@@ -689,39 +445,43 @@
  }
}
.remark-section {
  margin-top: 20px;
.add-project-footer {
  margin-top: 43px;
  .el-form-item {
    width: 100%;
  button {
    width: 220px;
  }
    .el-textarea {
      width: 100%;
    }
  .save-btn {
    margin-right: 20px;
  }
}
.signature-section {
  margin-top: 26px;
}
.add-group {
  padding-left: 25px;
  margin-top: 14px;
  display: flex;
  align-items: center;
  margin-bottom: 19px;
  div {
    color: #f56c6c;
  }
  span {
    font-weight: 500;
.assayer-section {
  .sample-code {
    font-family: PingFangSC, PingFang SC;
    font-weight: 400;
    font-size: 14px;
    color: #222222;
    line-height: 21px;
    margin: 0 32px 0 8px;
    color: rgba(0, 0, 0, 0.88);
    margin: 20px 0;
  }
  .sub-title {
    font-size: 16px;
    font-weight: 500;
    color: #333;
    margin: 20px 0;
    display: inline-block;
  }
  .batch-receive {
    float: right;
    margin-bottom: 16px;
  }
  .pending-samples,
  .received-samples {
    // margin-top: 20px;
  }
}
</style>
src/views/dataManagement/sampleManage/components/addTime.vue
@@ -22,7 +22,7 @@
          ></el-input>
        </el-form-item>
      </el-form>
      <span slot="footer" class="dialog-footer">
      <span slot="footer" class="dialog-footer select-member-footer">
        <el-button @click="handleClose">取 消</el-button>
        <el-button type="primary" @click="handleSubmit">确认新增</el-button>
      </span>
src/views/dataManagement/sampleManage/components/approvalDialog.vue
@@ -125,7 +125,7 @@
        <el-input type="textarea" v-model="remark" placeholder="请输入审批意见"   />
      </div>
    </div>
    <div slot="footer" class="dialog-footer">
    <div slot="footer" class="dialog-footer select-member-footer">
      <el-button @click="handleClose">取 消</el-button>
      <el-button type="primary" @click="handleApprove" v-if="type === 'approve'"
        >通过</el-button
src/views/dataManagement/sampleManage/components/receiveConfirmDialog.vue
New file
@@ -0,0 +1,149 @@
<template>
  <el-dialog
    title="接收签字确认"
    :visible.sync="visible"
    width="500px"
    :close-on-click-modal="false"
    @close="handleClose"
  >
    <div class="receive-dialog">
      <div class="receive-tip">
        您已选中 <span class="sample-count">{{ sampleCount }}</span> 条样品接收,请签字确认
      </div>
      <div class="approval-dialog-approve">
        <div class="add-group">
          <div>*</div>
          <span>签字确认</span>
          <el-button type="primary" class="el-icon-plus" @click="openSignature">签名</el-button>
        </div>
        <img
          v-if="imgSrc"
          style="width: 200px; height: 100px; margin-left: 25px"
          :src="imgSrc"
          fit="fit"
        />
      </div>
    </div>
    <div slot="footer" class="dialog-footer select-member-footer">
      <el-button @click="handleClose">取 消</el-button>
      <el-button type="primary" @click="handleConfirm" :disabled="!imgSrc">
        确认接收
      </el-button>
    </div>
    <SignatureCanvas
      :visible="signatureDialogVisible"
      @confirm="handleSignatureConfirm"
      @close="signatureDialogVisible = false"
    />
  </el-dialog>
</template>
<script>
import SignatureCanvas from "@/components/SignatureCanvas.vue";
export default {
  name: "ReceiveConfirmDialog",
  components: {
    SignatureCanvas,
  },
  props: {
    visible: {
      type: Boolean,
      default: false,
    },
    sampleCount: {
      type: Number,
      default: 1,
    },
  },
  data() {
    return {
      signatureDialogVisible: false,
      imgSrc: "",
    };
  },
  methods: {
    handleClose() {
      this.$emit("update:visible", false);
      this.imgSrc = "";
    },
    openSignature() {
      this.signatureDialogVisible = true;
    },
    handleSignatureConfirm(imageData) {
      this.signatureDialogVisible = false;
      this.imgSrc = imageData;
    },
    handleConfirm() {
      if (!this.imgSrc) {
        this.$message.warning("请先完成签名确认");
        return;
      }
      this.$emit("confirm", this.imgSrc);
      this.handleClose();
    },
  },
};
</script>
<style lang="less" scoped>
::v-deep .el-dialog__header {
  border-bottom: 1px solid #e4e7ed;
}
.receive-dialog {
  padding: 20px;
  .receive-tip {
    font-size: 14px;
    color: #333;
    margin-bottom: 20px;
    .sample-count {
      color: #409EFF;
      font-weight: bold;
    }
  }
}
.approval-dialog-approve {
  margin-top: 26px;
  img {
    border: 2px dashed #049c9a;
  }
}
.add-group {
  padding-left: 25px;
  margin-top: 14px;
  display: flex;
  align-items: center;
  margin-bottom: 19px;
  div {
    color: #f56c6c;
  }
  span {
    font-weight: 500;
    font-size: 14px;
    color: #222222;
    line-height: 21px;
    margin: 0 32px 0 8px;
  }
}
.dialog-footer {
  align-items: center;
  display: flex;
  justify-content: center;
  button {
    width: 150px;
  }
}
</style>
src/views/dataManagement/sampleManage/list.vue
@@ -9,6 +9,9 @@
          <el-form-item label="实验编号:">
            <el-input v-model="form.planCode" placeholder="请输入"></el-input>
          </el-form-item>
          <el-form-item label="创建人:" v-if="isChemist">
            <el-input v-model="form.creator" placeholder="请输入"></el-input>
          </el-form-item>
          <el-form-item label="创建时间:">
            <el-date-picker
              v-model="form.createTime"
@@ -19,8 +22,11 @@
              value-format="yyyy-MM-dd"
            ></el-date-picker>
          </el-form-item>
          <el-form-item label="状态:">
          <el-form-item label="状态:" v-if="!isChemist">
            <el-input v-model="form.approver" placeholder="请输入"></el-input>
          </el-form-item>
          <el-form-item label="实验名称:" v-if="isChemist">
            <el-input v-model="form.experimentName" placeholder="请输入"></el-input>
          </el-form-item>
          <el-form-item label="">
            <el-button type="default" @click="resetForm">重置</el-button>
@@ -49,54 +55,14 @@
      </template>
      <template #table>
        <el-table-column
          prop="planCode"
          label="项目课题方案编号"
          v-for="(column, index) in currentColumns"
          :key="index"
          :prop="column.prop"
          :label="column.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="审批状态">
        <el-table-column label="操作" width="100">
          <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
            >
            <el-button type="text" @click="handleDetail(scope.row.id)">详情</el-button>
          </template>
        </el-table-column>
      </template>
@@ -122,6 +88,7 @@
  },
  data() {
    return {
      userRole: 'chemist', // 用户角色
      currentType: 'list', // 当前显示类型:list-列表,draft-草稿箱
      form: {
        planName: "",
@@ -130,62 +97,111 @@
        createTime: [],
        approver: "",
        status: "",
        experimentName: "",
        sampleCount: "",
        receivedStatus: ""
      },
      tableData: [],
      total: 0,
      // 不同角色的表头配置
      columnConfig: {
        default: [
          { prop: 'projectPlan', label: '所属项目课题方案' },
          { prop: 'experimentNo', label: '实验编号' },
          { prop: 'experimentName', label: '实验名称' },
          { prop: 'sampleNo', label: '取样单编号' },
          { prop: 'creator', label: '创建人' },
          { prop: 'createTime', label: '创建时间' },
          { prop: 'sendTo', label: '发送对象' },
          { prop: 'sendTime', label: '发送时间' }
        ],
        chemist: [
          { prop: 'projectPlan', label: '所属项目课题方案' },
          { prop: 'experimentNo', label: '实验编号' },
          { prop: 'experimentName', label: '实验名称' },
          { prop: 'sampleNo', label: '取样单编号' },
          { prop: 'creator', label: '创建人' },
          { prop: 'createTime', label: '创建时间' },
          { prop: 'sampleCount', label: '样品总数' },
          { prop: 'receivedCount', label: '待接收' },
          { prop: 'completedCount', label: '已接收' }
        ]
      },
      // 模拟数据
      mockListData: [
        {
          planCode: 'PLAN-2024-001',
          planName: '2024年度实验室设备升级方案',
          stage: '规划阶段',
          id: '1',
          projectPlan: '2024年度实验室设备性能评估方案',
          experimentNo: 'EXP20240315001',
          experimentName: '设备性能测试实验A',
          sampleNo: 'SAMPLE20240315001',
          creator: '张三',
          createTime: '2024-03-15',
          status: 'pending',
          approver: '李四',
          approveTime: '2024-03-16'
          createTime: '2024-03-15 09:30:00',
          sendTo: '质检部门',
          sendTime: '2024-03-15 10:00:00',
          sampleCount: 5,
          receivedCount: 2,
          completedCount: 3
        },
        {
          planCode: 'PLAN-2024-002',
          planName: '实验室安全管理制度更新方案',
          stage: '实施阶段',
          id: '2',
          projectPlan: '新型材料研究项目方案',
          experimentNo: 'EXP20240315002',
          experimentName: '材料强度测试实验',
          sampleNo: 'SAMPLE20240315002',
          creator: '李四',
          createTime: '2024-03-15 11:30:00',
          sendTo: '研发部门',
          sendTime: '2024-03-15 14:00:00',
          sampleCount: 3,
          receivedCount: 1,
          completedCount: 2
        },
        {
          id: '3',
          projectPlan: '环境监测系统优化方案',
          experimentNo: 'EXP20240315003',
          experimentName: '空气质量检测实验',
          sampleNo: 'SAMPLE20240315003',
          creator: '王五',
          createTime: '2024-03-14',
          status: 'approved',
          approver: '赵六',
          approveTime: '2024-03-15'
          createTime: '2024-03-15 15:30:00',
          sendTo: '环境监测组',
          sendTime: '2024-03-15 16:00:00'
        },
        {
          planCode: 'PLAN-2024-003',
          planName: '实验室人员培训计划',
          stage: '准备阶段',
          creator: '孙七',
          createTime: '2024-03-13',
          status: 'rejected',
          approver: '周八',
          approveTime: '2024-03-14'
          id: '4',
          projectPlan: '生物样本分析项目',
          experimentNo: 'EXP20240315004',
          experimentName: '细胞活性测试',
          sampleNo: 'SAMPLE20240315004',
          creator: '赵六',
          createTime: '2024-03-15 16:30:00',
          sendTo: '生物实验室',
          sendTime: '2024-03-15 17:00:00'
        }
      ],
      mockDraftData: [
        {
          planCode: 'DRAFT-2024-001',
          planName: '实验室设备采购计划(草稿)',
          stage: '规划阶段',
          creator: '张三',
          createTime: '2024-03-16',
          status: 'draft',
          approver: '',
          approveTime: ''
          id: '5',
          projectPlan: '化学反应监测方案',
          experimentNo: 'EXP20240316001',
          experimentName: '催化剂效率测试',
          sampleNo: 'SAMPLE20240316001',
          creator: '孙七',
          createTime: '2024-03-16 09:30:00',
          sendTo: '化学实验室',
          sendTime: '2024-03-16 10:00:00'
        },
        {
          planCode: 'DRAFT-2024-002',
          planName: '实验室改造方案(草稿)',
          stage: '准备阶段',
          creator: '李四',
          createTime: '2024-03-15',
          status: 'draft',
          approver: '',
          approveTime: ''
          id: '6',
          projectPlan: '药物稳定性研究方案',
          experimentNo: 'EXP20240316002',
          experimentName: '药物降解测试',
          sampleNo: 'SAMPLE20240316002',
          creator: '周八',
          createTime: '2024-03-16 11:30:00',
          sendTo: '药物研究部',
          sendTime: '2024-03-16 14:00:00'
        }
      ],
      approvalDialogVisible: false,
@@ -193,10 +209,27 @@
      currentApprovalData: null,
    };
  },
  computed: {
    // 获取当前角色的表头配置
    currentColumns() {
      return this.columnConfig[this.userRole] || this.columnConfig.default;
    },
    // 判断是否为化验师角色
    isChemist() {
      return this.userRole === 'chemist';
    }
  },
  created() {
    this.getUserRole();
    this.getTableData();
  },
  methods: {
    // 获取用户角色
    async getUserRole() {
      // TODO: 从用户信息或接口中获取角色
      // 示例:this.userRole = await getUserRole();
      this.userRole = 'chemist'; // 临时写死,实际应该从接口获取
    },
    resetForm() {
      this.form = {
        planName: "",
@@ -205,6 +238,9 @@
        createTime: [],
        approver: "",
        status: "",
        experimentName: "",
        sampleCount: "",
        receivedStatus: ""
      };
    },
    handleSearch() {
@@ -233,7 +269,7 @@
    },
    handleAddSample() {
      this.$router.push({
        path: "/dataManagement/addSample",
        path: "/sampleManage/addSample",
      });
    },
    handleApprove(row) {
@@ -267,10 +303,13 @@
      // 实现删除逻辑
      console.log("删除数据:", row);
    },
    handleDetail(row) {
      this.currentApprovalData = row;
      this.approvalDialogType = 'view';
      this.approvalDialogVisible = true;
    handleDetail(id) {
      this.$router.push({
        path: "/sampleManage/addSample",
        query: {
          id: id
        }
      });
    },
    handleTypeChange(type) {
      this.currentType = type;
src/views/dataManagement/sampleRecordList/changeRecord.vue
New file
@@ -0,0 +1,327 @@
<template>
  <Card>
    <div class="header-title-left" style="margin-top: 38px">
      <img src="@/assets/public/headercard.png" />
      <span>基础信息</span>
    </div>
    <div class="number">取样单编号:213421241</div>
    <div class="header-title-left" style="margin-top: 38px">
      <img src="@/assets/public/headercard.png" />
      <span>取样操作记录</span>
      <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="120"
        ></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="100"
        ></el-table-column>
        <el-table-column prop="ph" label="PH" width="100"></el-table-column>
        <el-table-column
          prop="waterAmount"
          label="加水量"
          width="100"
        ></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="100"
        ></el-table-column>
        <el-table-column
          prop="sampleTime"
          label="取样时间"
          width="150"
        ></el-table-column>
        <el-table-column
          prop="remark"
          label="拍照"
          width="100"
        ></el-table-column>
        <el-table-column
          prop="operator"
          label="操作人员"
          width="100"
        ></el-table-column>
        <el-table-column
          fixed="right"
          label="操作"
          width="100"
        >
          <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">
      <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" @click="handleSave">保存</el-button>
      <el-button>存草稿</el-button>
    </div>
    <sample-dialog
      :visible.sync="showSampleDialog"
      :data="currentSampleData"
      @submit="handleSampleSubmit"
    />
    <confirm-dialog
      :visible.sync="showConfirmDialog"
      :form-data="form"
      :sample-data="sampleData"
      @confirm="handleConfirm"
    />
  </Card>
</template>
<script>
import SampleDialog from "./components/sampleDialog.vue";
import ConfirmDialog from './components/confirmDialog.vue';
export default {
  name: "ChangeRecord",
  components: {
    SampleDialog,
    ConfirmDialog
  },
  data() {
    return {
      showSampleDialog: false,
      showConfirmDialog: false,
      currentSampleData: {},
      currentEditIndex: -1,
      showAdditives: true,
      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: {
        remark: "",
      },
    };
  },
  methods: {
    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;
    },
    handleSave() {
      this.showConfirmDialog = true;
    },
    handleConfirm(signature) {
      this.submitForm(signature);
    },
    submitForm(signature = null) {
      // 在这里实现表单提交逻辑
      console.log('提交表单数据', { signature });
      this.$message.success('保存成功');
      this.showConfirmDialog = false;
    }
  }
};
</script>
<style lang="less" scoped>
.header-title-left {
  display: flex;
  align-items: center;
  gap: 13px;
  margin-bottom: 20px;
  img {
    width: 12px;
    height: 19px;
  }
  div, span {
    flex-shrink: 0;
    font-weight: bold;
    font-size: 18px;
    color: #222222;
    line-height: 27px;
    font-family: "Source Han Sans CN Bold Bold";
  }
  div:before {
    content: "*";
    color: #f56c6c;
    margin-right: 4px;
  }
  .el-button {
    margin-left: 16px;
  }
}
.add-project-footer {
  margin-top: 43px;
  button {
    width: 220px;
  }
  .save-btn {
    margin-right: 20px;
  }
}
.number {
  font-family: PingFangSC, PingFang SC;
  font-weight: 400;
  font-size: 14px;
  color: rgba(0,0,0,0.88);
}
.remark-section {
  margin-top: 20px;
  .el-form-item {
    width: 100%;
    .el-textarea {
      width: 100%;
    }
  }
}
</style>
src/views/dataManagement/sampleRecordList/components/confirmDialog.vue
File was renamed from src/views/dataManagement/sampleManage/components/confirmDialog.vue
@@ -177,7 +177,7 @@
      />
    </div>
    <div slot="footer" class="dialog-footer">
    <div slot="footer" class="dialog-footer select-member-footer">
      <el-button @click="handleClose">取 消</el-button>
      <el-button type="primary" @click="handleConfirm" :disabled="!imgSrc"
        >确 认</el-button
src/views/dataManagement/sampleRecordList/components/sampleDialog.vue
File was renamed from src/views/dataManagement/sampleManage/components/sampleDialog.vue
@@ -131,7 +131,7 @@
        </div>
      </div>
    </div>
    <div slot="footer" class="dialog-footer">
    <div slot="footer" class="dialog-footer select-member-footer">
      <el-button @click="handleClose">取 消</el-button>
      <el-button type="primary" @click="handleSubmit" style="margin-left: 20px;">确 定</el-button>
    </div>
src/views/dataManagement/sampleRecordList/list.vue
New file
@@ -0,0 +1,297 @@
<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-input v-model="form.planCode" placeholder="请输入"></el-input>
          </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')"
            >{{ type === 'draft' ? '草稿箱' : '取样操作记录列表' }}</div>
          </div>
        </div>
      </template>
      <template #table>
        <el-table-column
          prop="planName"
          label="所属项目课题方案"
        ></el-table-column>
        <el-table-column
          prop="experimentNo"
          label="实验编号"
        ></el-table-column>
        <el-table-column
          prop="experimentName"
          label="实验名称"
        ></el-table-column>
        <el-table-column
          prop="sampleNo"
          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 label="操作" width="150">
          <template slot-scope="scope">
            <el-button
              type="text"
              @click="handleEdit(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 {
      type: 'list',
      currentType: 'list',
      form: {
        planName: "",
        planCode: "",
        creator: "",
        createTime: [],
        approver: "",
        status: "",
      },
      tableData: [],
      total: 0,
      // 模拟数据
      mockListData: [
        {
          planName: '2024年度实验室设备升级方案',
          experimentNo: '31423764',
          experimentName: '设备性能测试',
          sampleNo: '2024-05-28',
          creator: '刘大大',
          createTime: '2025-1-2 16:27:17',
          status: 'pending'
        }
      ],
      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: "/sampleManage/changeRecord",
      });
    },
    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: "/sampleManage/changeRecord",
        query: {
          type: 'edit',
          id: row.id
        }
      });
    },
    handleDelete(row) {
      // 实现删除逻辑
      console.log("删除数据:", row);
    },
    handleDetail(row) {
      this.$router.push({
        path: "/sampleManage/changeRecord",
        query: {
          type: 'view',
          id: row.id
        }
      });
    },
    handleTypeChange(type) {
      this.currentType = type;
      this.type = 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>
src/views/dataManagement/sampleSubmissionList/components/receiveConfirmDialog.vue
New file
@@ -0,0 +1,149 @@
<template>
  <el-dialog
    title="发送签字确认"
    :visible.sync="visible"
    width="500px"
    :close-on-click-modal="false"
    @close="handleClose"
  >
    <div class="receive-dialog">
      <div class="receive-tip">
        您已选中 <span class="sample-count">{{ sampleCount }}</span> 条样品发送,请签字确认
      </div>
      <div class="approval-dialog-approve">
        <div class="add-group">
          <div>*</div>
          <span>签字确认</span>
          <el-button type="primary" class="el-icon-plus" @click="openSignature">签名</el-button>
        </div>
        <img
          v-if="imgSrc"
          style="width: 200px; height: 100px; margin-left: 25px"
          :src="imgSrc"
          fit="fit"
        />
      </div>
    </div>
    <div slot="footer" class="dialog-footer select-member-footer">
      <el-button @click="handleClose">取 消</el-button>
      <el-button type="primary" @click="handleConfirm" :disabled="!imgSrc">
        确认发送
      </el-button>
    </div>
    <SignatureCanvas
      :visible="signatureDialogVisible"
      @confirm="handleSignatureConfirm"
      @close="signatureDialogVisible = false"
    />
  </el-dialog>
</template>
<script>
import SignatureCanvas from "@/components/SignatureCanvas.vue";
export default {
  name: "ReceiveConfirmDialog",
  components: {
    SignatureCanvas,
  },
  props: {
    visible: {
      type: Boolean,
      default: false,
    },
    sampleCount: {
      type: Number,
      default: 1,
    },
  },
  data() {
    return {
      signatureDialogVisible: false,
      imgSrc: "",
    };
  },
  methods: {
    handleClose() {
      this.$emit("update:visible", false);
      this.imgSrc = "";
    },
    openSignature() {
      this.signatureDialogVisible = true;
    },
    handleSignatureConfirm(imageData) {
      this.signatureDialogVisible = false;
      this.imgSrc = imageData;
    },
    handleConfirm() {
      if (!this.imgSrc) {
        this.$message.warning("请先完成签名确认");
        return;
      }
      this.$emit("confirm", this.imgSrc);
      this.handleClose();
    },
  },
};
</script>
<style lang="less" scoped>
::v-deep .el-dialog__header {
  border-bottom: 1px solid #e4e7ed;
}
.receive-dialog {
  padding: 20px;
  .receive-tip {
    font-size: 14px;
    color: #333;
    margin-bottom: 20px;
    .sample-count {
      color: #409EFF;
      font-weight: bold;
    }
  }
}
.approval-dialog-approve {
  margin-top: 26px;
  img {
    border: 2px dashed #049c9a;
  }
}
.add-group {
  padding-left: 25px;
  margin-top: 14px;
  display: flex;
  align-items: center;
  margin-bottom: 19px;
  div {
    color: #f56c6c;
  }
  span {
    font-weight: 500;
    font-size: 14px;
    color: #222222;
    line-height: 21px;
    margin: 0 32px 0 8px;
  }
}
.dialog-footer {
  align-items: center;
  display: flex;
  justify-content: center;
  button {
    width: 150px;
  }
}
</style>
src/views/dataManagement/sampleSubmissionList/list.vue
New file
@@ -0,0 +1,199 @@
<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-input v-model="form.planCode" placeholder="请输入"></el-input>
          </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">
              取样操作记录列表
            </div>
          </div>
        </div>
      </template>
      <template #table>
        <el-table-column
          prop="planName"
          label="所属项目课题方案"
        ></el-table-column>
        <el-table-column
          prop="experimentNo"
          label="实验编号"
        ></el-table-column>
        <el-table-column
          prop="experimentName"
          label="实验名称"
        ></el-table-column>
        <el-table-column
          prop="sampleNo"
          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="sampleStatus"
          label="样品总数/待送达/待接收/已接收"
        ></el-table-column>
        <el-table-column label="操作" width="100">
          <template slot-scope="scope">
            <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 {
      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",
        },
      ],
    };
  },
  created() {
    this.getTableData();
  },
  methods: {
    resetForm() {
      this.form = {
        planName: "",
        planCode: "",
        creator: "",
        createTime: [],
        approver: "",
        status: "",
      };
    },
    handleSearch() {
      // 实现查询逻辑
      console.log("查询条件:", this.form);
      this.getTableData();
    },
    handleDetail(row) {
      this.$router.push({
        path: "/sampleManage/submission",
        query: {
          id: row.id,
          type: 'view'
        }
      });
    },
    getTableData() {
      // 获取列表数据
      this.tableData = this.mockListData;
      this.total = this.mockListData.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: #ffffff;
    border-radius: 8px 8px 0px 0px;
    border: 1px solid #049c9a;
    padding: 16px 29px;
    font-weight: bold;
    font-size: 18px;
    color: #049c9a;
    width: unset;
  }
}
</style>
src/views/dataManagement/sampleSubmissionList/submission.vue
New file
@@ -0,0 +1,279 @@
<template>
  <Card>
    <div class="header-title-left">
      <img src="@/assets/public/headercard.png" />
      <div>所属实验调度</div>
    </div>
    <Table :data="schedulingData" :total="schedulingTotal" :height="null">
      <template>
        <el-table-column prop="planCode" label="所属项目课题方案" width="150"></el-table-column>
        <el-table-column prop="experimentCode" label="实验编号" width="150"></el-table-column>
        <el-table-column prop="experimentName" label="实验名称" width="150"></el-table-column>
        <el-table-column prop="notifyTime" label="通知时间" width="150"></el-table-column>
        <el-table-column prop="startTime" label="实验开始时间" width="150"></el-table-column>
        <el-table-column prop="endTime" label="实验结束时间" width="150"></el-table-column>
        <el-table-column prop="participants" label="参加人员" width="150"></el-table-column>
        <el-table-column prop="status" label="状态" width="100"></el-table-column>
      </template>
    </Table>
    <!-- 送样-收样记录部分 -->
    <div class="sample-record-section">
      <div class="header-title-left" style="margin-top: 38px">
        <img src="@/assets/public/headercard.png" />
        <span>送样-收样记录</span>
        <el-button
          type="primary"
          class="el-icon-plus"
          @click="showAdditives = !showAdditives"
        >
          {{ showAdditives ? "收起辅料详情" : "展开辅料详情" }}
        </el-button>
      </div>
      <div class="sample-code">取样单编号:{{ form.sampleCode || 'DD-25010BS01' }}</div>
      <div class="sample-table">
        <div class="table-header">
          <el-button type="primary" class="batch-send" @click="handleBatchSend">批量发送</el-button>
        </div>
        <Table :data="sampleList" :total="sampleTotal" :height="null" @selection-change="handleSelectionChange">
          <template>
            <el-table-column type="selection" width="55"></el-table-column>
            <el-table-column prop="index" label="序号" width="80"></el-table-column>
            <el-table-column prop="processTime" label="工艺时间间/h" width="120"></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="100"></el-table-column>
            <el-table-column prop="ph" label="PH" width="100"></el-table-column>
            <el-table-column prop="waterAmount" label="加水量" width="100"></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="100"></el-table-column>
            <el-table-column prop="sampleTime" label="取样时间" width="150"></el-table-column>
            <el-table-column prop="photo" label="拍照" width="100">
              <template slot-scope="scope">
                <img v-if="scope.row.photo" :src="scope.row.photo" class="sample-photo" />
              </template>
            </el-table-column>
            <el-table-column prop="operator" label="操作人" width="100"></el-table-column>
            <el-table-column prop="status" label="状态" width="100"></el-table-column>
            <el-table-column label="操作" width="100" fixed="right">
              <template slot-scope="scope">
                <el-button type="text" @click="handleSend(scope.row)" v-if="scope.row.status === '待发送'">发送</el-button>
              </template>
            </el-table-column>
          </template>
        </Table>
      </div>
    </div>
    <receive-confirm-dialog
      :visible.sync="showReceiveConfirm"
      :sample-count="selectedSamples.length"
      @confirm="confirmSend"
    />
  </Card>
</template>
<script>
import ReceiveConfirmDialog from './components/receiveConfirmDialog.vue'
export default {
  name: "SampleSubmission",
  components: {
    ReceiveConfirmDialog
  },
  data() {
    return {
      showReceiveConfirm: false,
      showAdditives: true,
      schedulingData: [{
        planCode: 'DD-EX001',
        experimentCode: 'DD-EX001',
        experimentName: '名称名称名称',
        notifyTime: '2025-1-2 14:50:19',
        startTime: '2025-02-27',
        endTime: '2025-06-06',
        participants: '张弘, 李天浩, 陈三, 李四',
        status: '已确认'
      }],
      schedulingTotal: 1,
      form: {
        sampleCode: "DD-25010BS01",
      },
      sampleList: [
        {
          index: 1,
          processTime: "2",
          sampleName: "名称名称名称",
          sampleCode: "314234321",
          temperature: "34",
          ph: "34",
          waterAmount: "34",
          additive1: "10g",
          additive2: "5g",
          additive3: "3g",
          additive4: "",
          additive5: "",
          additive6: "",
          additive7: "",
          additive8: "",
          additive9: "",
          additive10: "",
          sampleAmount: "34",
          sampleTime: "2025-1-9 15:26:08",
          photo: "@/assets/sample-photos/photo1.jpg",
          operator: "张三",
          status: "待发送"
        },
        {
          index: 2,
          processTime: "4",
          sampleName: "名称名称名称",
          sampleCode: "314234321",
          temperature: "23",
          ph: "23",
          waterAmount: "23",
          additive1: "8g",
          additive2: "4g",
          additive3: "",
          additive4: "",
          additive5: "",
          additive6: "",
          additive7: "",
          additive8: "",
          additive9: "",
          additive10: "",
          sampleAmount: "23",
          sampleTime: "2025-1-9 15:26:08",
          photo: "@/assets/sample-photos/photo2.jpg",
          operator: "李四",
          status: "待接收"
        },
        {
          index: 3,
          processTime: "8",
          sampleName: "名称名称名称",
          sampleCode: "314234321",
          temperature: "23",
          ph: "23",
          waterAmount: "23",
          additive1: "",
          additive2: "",
          additive3: "",
          additive4: "",
          additive5: "",
          additive6: "",
          additive7: "",
          additive8: "",
          additive9: "",
          additive10: "",
          sampleAmount: "23",
          sampleTime: "2025-1-9 15:26:08",
          photo: "@/assets/sample-photos/photo3.jpg",
          operator: "王五",
          status: "已接收"
        }
      ],
      sampleTotal: 3,
      selectedSamples: []
    };
  },
  methods: {
    handleSelectionChange(selection) {
      this.selectedSamples = selection;
    },
    handleSend(row) {
      this.selectedSamples = [row];
      this.showReceiveConfirm = true;
    },
    handleBatchSend() {
      if (this.selectedSamples.length === 0) {
        this.$message.warning('请先选择要发送的样品');
        return;
      }
      this.showReceiveConfirm = true;
    },
    confirmSend(signature) {
      // 处理发送确认逻辑
      const sampleIds = this.selectedSamples.map(item => item.id);
      console.log('发送样品', { sampleIds, signature });
      // 更新状态
      this.selectedSamples.forEach(sample => {
        const index = this.sampleList.findIndex(item => item.id === sample.id);
        if (index > -1) {
          this.sampleList[index].status = '待接收';
        }
      });
      this.$message.success(`成功发送 ${this.selectedSamples.length} 个样品`);
      this.selectedSamples = [];
    }
  }
};
</script>
<style lang="less" scoped>
.header-title-left {
  display: flex;
  align-items: center;
  gap: 13px;
  margin-bottom: 20px;
  img {
    width: 12px;
    height: 19px;
  }
  div, span {
    flex-shrink: 0;
    font-weight: bold;
    font-size: 18px;
    color: #222222;
    line-height: 27px;
    font-family: "Source Han Sans CN Bold Bold";
  }
  div:before {
    content: "*";
    color: #f56c6c;
    margin-right: 4px;
  }
  .el-button {
    margin-left: 16px;
  }
}
.sample-record-section {
  .sample-code {
    font-family: PingFangSC, PingFang SC;
    font-weight: 400;
    font-size: 14px;
    color: rgba(0,0,0,0.88);
    margin: 20px 0;
  }
  .table-header {
    margin-bottom: 16px;
    text-align: left;
  }
  .sample-photo {
    width: 60px;
    height: 60px;
    object-fit: cover;
  }
}
</style>
src/views/dataManagement/testResultReport/components/checkout-result.vue
New file
@@ -0,0 +1,108 @@
<template>
    <el-dialog
      title="检验数据结果汇总"
      :visible.sync="visible"
      width="80%"
      :close-on-click-modal="false"
      @close="handleClose"
    >
      <div class="approval-dialog">
        <div class="approval-content">
          <Card class="approval-content-card">
            <template style="position: relative">
              <div class="header-title">
                <div class="header-title-left">
                  <img src="@/assets/public/headercard.png" />
                  <span>本次检验结果总表</span>
                </div>
              </div>
              <div class="header-title">
                <div class="header-title-left">
                  <img src="@/assets/public/headercard.png" />
                  <span>本次检验说明</span>
                </div>
              </div>
            </template>
          </Card>
        </div>
      </div>
    </el-dialog>
  </template>
  <script>
  export default {
    name: "CheckoutResult",
    props: {
      visible: {
        type: Boolean,
        default: false,
      },
      content: {
        type: Object,
        default: () => ({}),
      },
    },
    methods: {
      handleClose() {
        this.$emit("update:visible", false);
      },
    },
  };
  </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;
      border-radius: 10px;
    }
  }
  .approval-content-card {
    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;
      }
      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:first-child {
    .header-title-left {
      margin-top: 0;
    }
  }
  </style>
src/views/dataManagement/testResultReport/components/evaluation-dialog.vue
New file
@@ -0,0 +1,84 @@
<template>
  <el-dialog
    :visible.sync="dialogVisible"
    :title="dialogTitle"
    width="79.17%"
    @close="handleClose"
  >
    <EvaluateTable ref="evaluateTable" />
    <div slot="footer" class="dialog-footer">
      <el-button type="primary" @click="handleSubmit" class="submit-btn">确认评定结果</el-button>
    </div>
  </el-dialog>
</template>
<script>
const EVALUATION_TYPES = ['labTechnician', 'experimenter'];
export default {
  name: "EvaluationDialog",
  props: {
    modelValue: {
      type: Boolean,
      default: false,
    },
    type: {
      type: String,
      default: 'labTechnician'
    },
  },
  data() {
    return {
      dialogVisible: false,
    };
  },
  computed: {
    dialogTitle() {
      return this.type === 'labTechnician' ? '化验师工作评定' : '实验员工作评定';
    }
  },
  watch: {
    modelValue: {
      handler(val) {
        this.dialogVisible = val;
      },
      immediate: true,
    },
    dialogVisible(val) {
      if (!val) {
        this.$emit('update:modelValue', false);
      }
    }
  },
  methods: {
    handleClose() {
      this.$emit('update:modelValue', false);
    },
    handleSubmit() {
      const evaluateTable = this.$refs.evaluateTable;
      if (!evaluateTable) {
        this.$message.warning('评价表格未加载完成');
        return;
      }
      const evaluateData = {
        type: this.type,
        activeIndex: evaluateTable.activeIndex
      };
      this.$emit('submit', evaluateData);
    },
  },
};
</script>
<style lang="less" scoped>
.dialog-footer {
  padding: 20px;
  border-top: 1px solid #eee;
  .submit-btn {
    min-width: 80px;
  }
}
</style>
src/views/dataManagement/testResultReport/components/experimental-scheduling.vue
New file
@@ -0,0 +1,75 @@
<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="select-member-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>
</style>
src/views/dataManagement/testResultReport/detail.vue
New file
@@ -0,0 +1,783 @@
<template>
  <Card>
    <el-form
      ref="form"
      :model="form"
      :rules="rules"
      inline
      label-position="top"
    >
      <div class="header-title">
        <div class="header-title-left">
          <img src="@/assets/public/headercard.png" />
          <div>一 、所属实验调度</div>
        </div>
        <el-button
          type="primary"
          class="el-icon-plus"
          @click="handleSelectScheduling"
          >选择实验调度</el-button
        >
      </div>
      <Table
        :data="[selectedScheduling].filter(Boolean)"
        :total="0"
        :height="null"
        class="rwuTable"
      >
        <el-table-column
          prop="planName"
          label="所属项目课题方案"
          min-width="180"
        ></el-table-column>
        <el-table-column
          prop="planCode"
          label="实验编号"
          min-width="120"
        ></el-table-column>
        <el-table-column
          prop="experimentName"
          label="实验名称"
          min-width="120"
        ></el-table-column>
        <el-table-column
          prop="noticeTime"
          label="通知时间"
          min-width="150"
        ></el-table-column>
        <el-table-column
          prop="startTime"
          label="实验开始时间"
          min-width="150"
        ></el-table-column>
        <el-table-column
          prop="endTime"
          label="实验结束时间"
          min-width="150"
        ></el-table-column>
        <el-table-column
          prop="participants"
          label="参加人员"
          min-width="180"
        ></el-table-column>
        <el-table-column
          prop="status"
          label="状态"
          min-width="100"
        ></el-table-column>
      </Table>
      <div class="header-title">
        <div class="header-title-left">
          <img src="@/assets/public/headercard.png" />
          <div>一 、实验结果</div>
        </div>
      </div>
      <Table
        :data="experimentResultData"
        :total="0"
        :height="null"
        class="rwuTable"
      >
        <el-table-column
          prop="planName"
          label="所属项目课题方案"
          min-width="180"
        ></el-table-column>
        <el-table-column
          prop="experimentCode"
          label="实验编号"
          min-width="120"
        ></el-table-column>
        <el-table-column
          prop="experimentName"
          label="实验名称"
          min-width="120"
        ></el-table-column>
        <el-table-column
          prop="experimentDate"
          label="实验日期"
          min-width="120"
        ></el-table-column>
        <el-table-column
          prop="experimenter"
          label="实验员"
          min-width="100"
        ></el-table-column>
        <el-table-column
          prop="createTime"
          label="创建日期"
          min-width="150"
        ></el-table-column>
        <el-table-column
          prop="creator"
          label="创建人"
          min-width="100"
        ></el-table-column>
        <el-table-column
          prop="status"
          label="当前状态"
          min-width="120"
        ></el-table-column>
        <el-table-column label="操作" min-width="100" fixed="right">
          <template slot-scope="scope">
            <el-button type="text" @click="handleViewDetail(scope.row)"
              >详情</el-button
            >
          </template>
        </el-table-column>
      </Table>
      <div class="header-title">
        <div class="header-title-left">
          <img src="@/assets/public/headercard.png" />
          <div>二 、检验结果</div>
        </div>
      </div>
      <Table
        :data="inspectionResultData"
        :total="0"
        :height="null"
        class="rwuTable"
      >
        <el-table-column
          prop="planName"
          label="所属项目课题方案"
          min-width="180"
        ></el-table-column>
        <el-table-column
          prop="experimentCode"
          label="所属实验编号"
          min-width="120"
        ></el-table-column>
        <el-table-column
          prop="createTime"
          label="创建时间"
          min-width="150"
        ></el-table-column>
        <el-table-column
          prop="creator"
          label="创建人"
          min-width="100"
        ></el-table-column>
        <el-table-column
          prop="status"
          label="状态"
          min-width="100"
        ></el-table-column>
        <el-table-column label="操作" min-width="100" fixed="right">
          <template slot-scope="scope">
            <el-button
              type="text"
              @click="handleViewInspectionDetail(scope.row)"
              >详情</el-button
            >
          </template>
        </el-table-column>
      </Table>
      <div class="header-title">
        <div class="header-title-left">
          <img src="@/assets/public/headercard.png" />
          <div>三 、实验数据分析及结论</div>
        </div>
      </div>
      <div class="header-title">
        <div class="header-title-left">
          <img src="@/assets/public/headercard.png" />
          <div>四 、化验师工作评定</div>
        </div>
      </div>
      <div class="evaluate-card">
        <div class="evaluate-card-people">
          <div
            v-for="technician in labTechnicians"
            :key="technician.id"
            class="evaluate-card-people-item"
            :class="{ 'active-people': selectedLabTechnician?.id === technician.id }"
            @click="handleSelectLabTechnician(technician)"
          >
            {{ technician.name }}
          </div>
        </div>
        <div class="evaluate-card-cotent">
          <template v-if="selectedLabTechnician">
            <template v-if="selectedLabTechnician.data">
              <EvaluateTable
                class="evaluate-table"
                :isReadonly="true"
                :type="1"
                :viewJson="JSON.stringify(selectedLabTechnician.data)"
              />
            </template>
            <template v-else>
              <div class="no-data">暂未评定</div>
              <div class="to-evaluate" @click="handleEvaluate('labTechnician')">去评价</div>
            </template>
          </template>
        </div>
      </div>
      <div class="header-title">
        <div class="header-title-left">
          <img src="@/assets/public/headercard.png" />
          <div>五 、实验员工作评定</div>
        </div>
      </div>
      <div class="evaluate-card">
        <div class="evaluate-card-people">
          <div
            v-for="experimenter in experimenters"
            :key="experimenter.id"
            class="evaluate-card-people-item"
            :class="{ 'active-people': selectedExperimenter?.id === experimenter.id }"
            @click="handleSelectExperimenter(experimenter)"
          >
            {{ experimenter.name }}
          </div>
        </div>
        <div class="evaluate-card-cotent">
          <template v-if="selectedExperimenter">
            <template v-if="selectedExperimenter.data">
              <EvaluateTable
                ref="experimenterEvaluateTable"
                class="evaluate-table"
                :isReadonly="true"
                :viewJson="JSON.stringify(selectedExperimenter.data)"
              />
            </template>
            <template v-else>
              <div class="no-data">暂未评定</div>
              <div class="to-evaluate" @click="handleEvaluate('experimenter')">去评价</div>
            </template>
          </template>
        </div>
      </div>
      <div class="add-project-footer">
        <el-button type="primary" class="save-btn">发送</el-button>
        <el-button>存草稿</el-button>
      </div>
    </el-form>
    <!-- 选择实验调度弹窗 -->
    <experimental-scheduling
      :show.sync="schedulingDialogVisible"
      @select="handleSchedulingSelect"
    />
    <!-- 评价弹窗 -->
    <evaluation-dialog
      :modelValue="evaluationDialogVisible"
      :type="currentEvaluationType"
      @update:modelValue="updateEvaluationDialogVisible"
      @submit="handleEvaluationSubmit"
    />
    <!-- 检验结果详情弹窗 -->
    <checkout-result
      :visible.sync="checkoutResultVisible"
      :data="currentCheckoutResult"
    />
    <!-- 新增组件弹窗 -->
    <add-component-dialog
      v-if="showNewDialog"
      :visible.sync="showNewDialog"
      @confirm="handleAddComponent"
    />
  </Card>
</template>
<script>
import ExperimentalScheduling from "./components/experimental-scheduling.vue";
import moment from "moment";
import EvaluationDialog from "./components/evaluation-dialog.vue";
import CheckoutResult from "./components/checkout-result.vue";
import AddComponentDialog from "@/components/AddComponentDialog";
export default {
  name: "AddProject",
  components: {
    ExperimentalScheduling,
    EvaluationDialog,
    CheckoutResult,
    AddComponentDialog
  },
  data() {
    return {
      showNewDialog: true,
      dialogVisible: false,
      schedulingDialogVisible: false,
      isEdit: false,
      currentEditIndex: -1,
      selectedScheduling: null,
      form: {
        recordNo: "",
        testItemName: "",
        testItemNo: "",
        testMethodName: "",
        testMethodNo: "",
      },
      rules: {
        recordNo: [
          {
            required: true,
            message: "请输入原始检验记录编号",
            trigger: "blur",
          },
        ],
        testItemName: [
          { required: true, message: "请输入检测项名字", trigger: "blur" },
        ],
        testItemNo: [
          { required: true, message: "请输入检测项编号", trigger: "blur" },
        ],
        testMethodName: [
          { required: true, message: "请输入检测方法名字", trigger: "blur" },
        ],
        testMethodNo: [
          { required: true, message: "请输入检测方法编号", trigger: "blur" },
        ],
      },
      taskTableData: [],
      experimentResultData: [
        {
          planName: "名称名称名称",
          experimentCode: "3814763",
          experimentName: "名称名称22",
          experimentDate: "2024-05-28",
          experimenter: "刘大大",
          createTime: "2025-1-2 16:27:17",
          creator: "周乐心",
          status: "实验员已提交",
        },
      ],
      inspectionResultData: [
        {
          planName: "名称名称名称",
          experimentCode: "31423764",
          createTime: "",
          creator: "",
          status: "已提交",
        },
        {
          planName: "名称名称名称",
          experimentCode: "31423764",
          createTime: "",
          creator: "",
          status: "已提交",
        },
      ],
      // 化验师数据
      labTechnicians: [
        {
          id: 1,
          name: '张三',
          role: '化验师',
          data: null // 评定数据,null 表示未评定
        },
        {
          id: 2,
          name: '李四',
          role: '化验师',
          data: null
        }
      ],
      selectedLabTechnician: null,
      // 实验员数据
      experimenters: [
        {
          id: 1,
          name: '王五',
          role: '实验员',
          data: null
        },
        {
          id: 2,
          name: '赵六',
          role: '实验员',
          data: null
        }
      ],
      selectedExperimenter: null,
      // 弹窗控制
      evaluationDialogVisible: false,
      currentEvaluationType: '',
      checkoutResultVisible: false,
      currentCheckoutResult: null
    };
  },
  watch: {
    selectedLabTechnician: {
      handler(newVal) {
        console.log("selectedLabTechnician", newVal);
      },
    },
  },
  created() {
    // 默认选中第一个人员
    this.selectedLabTechnician = this.labTechnicians[0];
    this.selectedExperimenter = this.experimenters[0];
  },
  methods: {
    handleSelectScheduling() {
      this.schedulingDialogVisible = true;
    },
    handleSchedulingSelect(selectedData) {
      if (selectedData && selectedData.length > 0) {
        this.selectedScheduling = selectedData[0];
        this.$message.success("选择成功");
      }
    },
    handleAddTask() {
      this.isEdit = false;
      this.currentEditIndex = -1;
      this.dialogVisible = true;
    },
    handleEditTask(row) {
      this.isEdit = true;
      this.currentEditIndex = this.taskTableData.findIndex(
        (item) => item === row
      );
      const editData = {
        sampleCode: row.sampleCode,
        testData: row.testData,
        testTypes: [],
        photos: [],
        spectrums: [],
      };
      if (row.photos && row.photos.length > 0) {
        editData.testTypes.push("photo");
        editData.photos = row.photos.map((photo) => ({
          name: photo.name,
          url: photo.url,
          raw: null,
        }));
      }
      if (row.spectrums && row.spectrums.length > 0) {
        editData.testTypes.push("spectrum");
        editData.spectrums = row.spectrums.map((spectrum) => ({
          name: spectrum.name,
          url: spectrum.url,
          raw: null,
        }));
      }
      this.$nextTick(() => {
        this.dialogVisible = true;
        this.$refs.addDialog.setFormData(editData);
      });
    },
    handleDeleteTask(row) {
      this.$confirm("确认删除该检测数据吗?", "提示", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning",
      })
        .then(() => {
          const index = this.taskTableData.findIndex((item) => item === row);
          if (index > -1) {
            const item = this.taskTableData[index];
            if (item.photos) {
              item.photos.forEach((photo) => {
                if (photo.url.startsWith("blob:")) {
                  URL.revokeObjectURL(photo.url);
                }
              });
            }
            if (item.spectrums) {
              item.spectrums.forEach((spectrum) => {
                if (spectrum.url.startsWith("blob:")) {
                  URL.revokeObjectURL(spectrum.url);
                }
              });
            }
            this.taskTableData.splice(index, 1);
            this.$message.success("删除成功");
          }
        })
        .catch(() => {});
    },
    handleTaskSubmit(formData) {
      const photos = formData.photos.map((file) => ({
        name: file.name,
        url: file.url || URL.createObjectURL(file.raw),
      }));
      const spectrums = formData.spectrums.map((file) => ({
        name: file.name,
        url: file.url || URL.createObjectURL(file.raw),
      }));
      const newData = {
        sampleCode: formData.sampleCode,
        testData: formData.testData,
        testResult: "",
        photos,
        spectrums,
        createTime: this.isEdit
          ? this.taskTableData[this.currentEditIndex].createTime
          : moment().format("YYYY-MM-DD HH:mm:ss"),
      };
      if (this.isEdit && this.currentEditIndex > -1) {
        const oldData = this.taskTableData[this.currentEditIndex];
        if (oldData.photos) {
          oldData.photos.forEach((photo) => {
            if (
              photo.url.startsWith("blob:") &&
              !photos.find((p) => p.url === photo.url)
            ) {
              URL.revokeObjectURL(photo.url);
            }
          });
        }
        if (oldData.spectrums) {
          oldData.spectrums.forEach((spectrum) => {
            if (
              spectrum.url.startsWith("blob:") &&
              !spectrums.find((s) => s.url === spectrum.url)
            ) {
              URL.revokeObjectURL(spectrum.url);
            }
          });
        }
        this.taskTableData.splice(this.currentEditIndex, 1, newData);
        this.$message.success("更新成功");
      } else {
        this.taskTableData.push(newData);
        this.$message.success("添加成功");
      }
      this.dialogVisible = false;
      this.isEdit = false;
      this.currentEditIndex = -1;
    },
    getPhotoUrls(photos) {
      return photos.map((photo) => photo.url);
    },
    handleViewDetail(row) {
      console.log("查看详情", row);
    },
    handleViewInspectionDetail(row) {
      this.currentCheckoutResult = row;
      this.checkoutResultVisible = true;
    },
    // 选择化验师
    handleSelectLabTechnician(technician) {
      this.selectedLabTechnician = technician;
    },
    // 选择实验员
    handleSelectExperimenter(experimenter) {
      this.selectedExperimenter = experimenter;
    },
    // 去评价
    handleEvaluate(type) {
      this.currentEvaluationType = type;
      this.evaluationDialogVisible = true;
    },
    // 处理评价提交
    handleEvaluationSubmit(evaluationData) {
      const { type, activeIndex } = evaluationData;
      if (type === 'labTechnician') {
        // 更新化验师列表中的数据
        const labTechnician = this.labTechnicians.find(item => item.id === this.selectedLabTechnician.id);
        if (labTechnician) {
          labTechnician.data = activeIndex;
          // 更新当前选中的化验师数据
          this.selectedLabTechnician = { ...labTechnician };
        }
      } else if (type === 'experimenter') {
        // 更新实验员列表中的数据
        const experimenter = this.experimenters.find(item => item.id === this.selectedExperimenter.id);
        if (experimenter) {
          experimenter.data = activeIndex;
          // 更新当前选中的实验员数据
          this.selectedExperimenter = { ...experimenter };
        }
      }
      // 这里可以添加数据持久化的逻辑,比如调用API保存到后端
      // this.saveEvaluationData(type, activeIndex);
      this.$message.success('评价提交成功');
      this.evaluationDialogVisible = false;
    },
    // 更新弹窗显示状态
    updateEvaluationDialogVisible(value) {
      this.evaluationDialogVisible = value;
    }
  },
};
</script>
<style scoped lang="less">
.el-form--inline .el-form-item {
  margin-right: 83px;
}
.header-title {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 13px;
  margin-top: 38px;
  margin-bottom: 38px;
  .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";
    }
    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;
      }
    }
  }
}
.rwuTable {
  width: 85%;
  padding-left: 40px;
}
.add-project-footer {
  margin-top: 43px;
  button {
    width: 220px;
  }
  .save-btn {
    margin-right: 20px;
  }
}
.image-preview {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  .preview-image {
    width: 50px;
    height: 50px;
    border-radius: 4px;
    cursor: pointer;
  }
  .image-slot {
    display: flex;
    justify-content: center;
    align-items: center;
    width: 100%;
    height: 100%;
    background: #f5f7fa;
    color: #909399;
  }
}
.spectrum-link {
  display: block;
  margin-bottom: 5px;
  &:last-child {
    margin-bottom: 0;
  }
}
.evaluate-card {
  width: calc(95% - 40px);
  background: #eff8fa;
  padding: 30px;
  margin-left: 29px;
  .evaluate-card-people {
    display: flex;
    align-items: center;
    gap: 16px;
    .evaluate-card-people-item {
      width: 125px;
      background: #ffffff;
      border-radius: 6px;
      padding: 10px 5px;
      font-family: PingFangSC, PingFang SC;
      font-weight: 400;
      font-size: 16px;
      color: #333333;
      line-height: 22px;
      text-align: center;
    }
    .active-people {
      background: #ebffff;
      border-radius: 6px;
      border: 1px solid #049c9a;
      color: rgba(4, 156, 154, 1);
    }
  }
  .evaluate-card-cotent {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    .no-data {
      font-family: PingFangSC, PingFang SC;
      font-weight: 500;
      font-size: 20px;
      color: #909399;
      line-height: 28px;
      margin-top: 58px;
      margin-bottom: 30px;
    }
    .to-evaluate {
      width: 65px;
      background: #049c9a;
      border-radius: 4px;
      font-family: SourceHanSansCN, SourceHanSansCN;
      font-weight: 400;
      font-size: 14px;
      color: #ffffff;
      line-height: 32px;
      text-align: center;
      margin-bottom: 12px;
    }
    .evaluate-table{
      width: 100%;
      margin-top: 29px;
    }
  }
}
</style>
src/views/dataManagement/testResultReport/list.vue
New file
@@ -0,0 +1,344 @@
<template>
  <div class="list">
    <TableCustom :queryForm="form" :tableData="tableData" :total="total" :height="null">
      <template #search>
        <el-form :model="form" labelWidth="auto" inline>
          <el-form-item label="所属项目课题方案:">
            <el-input v-model="form.projectPlan" placeholder="请输入"></el-input>
          </el-form-item>
          <el-form-item label="实验编号:">
            <el-input v-model="form.experimentNo" 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"
            ></el-date-picker>
          </el-form-item>
          <el-form-item label="状态:">
            <el-select v-model="form.status" placeholder="请选择">
              <el-option label="待提交" value="pending"></el-option>
              <el-option label="已提交" value="submitted"></el-option>
            </el-select>
          </el-form-item>
          <el-form-item label="">
            <el-button type="default" @click="resetForm">重置</el-button>
            <el-button type="primary" style="margin-left: 10px;" @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="handleAddResult" class="el-icon-plus" type="primary">
            新增实验结果
          </el-button>
        </div>
      </template>
      <template #table>
        <el-table-column prop="projectPlan" label="所属项目课题方案"></el-table-column>
        <el-table-column prop="experimentNo" label="实验编号"></el-table-column>
        <el-table-column prop="experimentName" label="实验名称"></el-table-column>
        <el-table-column prop="engineer" label="工艺工程师"></el-table-column>
        <el-table-column prop="chemist" label="化验师"></el-table-column>
        <el-table-column prop="engineerApproved" label="是否评定">
          <template slot-scope="scope">
            <el-tag :type="scope.row.engineerApproved === '是' ? 'success' : 'danger'">
              {{ scope.row.engineerApproved }}
            </el-tag>
          </template>
        </el-table-column>
        <el-table-column prop="experimenter" label="实验员"></el-table-column>
        <el-table-column prop="experimenterApproved" label="是否评定">
          <template slot-scope="scope">
            <el-tag :type="scope.row.experimenterApproved === '是' ? 'success' : 'danger'">
              {{ scope.row.experimenterApproved }}
            </el-tag>
          </template>
        </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="scope.row.status === '已提交' ? 'success' : 'warning'">
              {{ scope.row.status }}
            </el-tag>
          </template>
        </el-table-column>
        <el-table-column label="操作" width="280">
          <template slot-scope="scope">
            <template v-if="currentType === 'list'">
              <!-- 待提交状态 -->
              <template v-if="scope.row.status === '待提交'">
                <el-button type="text" @click="handleSubmit(scope.row)">提交</el-button>
                <el-button type="text" @click="handleEdit(scope.row)">编辑</el-button>
                <el-button type="text" @click="handleDetail(scope.row.id)">详情</el-button>
                <el-button type="text" @click="handleDelete(scope.row)">删除</el-button>
              </template>
              <!-- 待评定状态 -->
              <template v-else-if="scope.row.status === '待评定'">
                <el-button type="text" @click="handleEvaluate(scope.row)">评定</el-button>
                <el-button type="text" @click="handleDetail(scope.row.id)">详情</el-button>
              </template>
              <!-- 已提交状态 -->
              <template v-else-if="scope.row.status === '已提交'">
                <el-button type="text" @click="handleDetail(scope.row.id)">详情</el-button>
              </template>
            </template>
            <!-- 草稿箱 -->
            <template v-else>
              <el-button type="text" @click="handleEdit(scope.row)">编辑</el-button>
              <el-button type="text" @click="handleDetail(scope.row.id)">详情</el-button>
              <el-button type="text" @click="handleDelete(scope.row)">删除</el-button>
            </template>
          </template>
        </el-table-column>
      </template>
    </TableCustom>
  </div>
</template>
<script>
export default {
  name: "TestResultReport",
  data() {
    return {
      currentType: "list", // 当前显示类型:list-列表,draft-草稿箱
      form: {
        projectPlan: "",
        experimentNo: "",
        experimentName: "",
        createTime: [],
        status: ""
      },
      tableData: [],
      total: 0,
      // 正式列表数据
      mockListData: [
        {
          id: '1',
          projectPlan: '名称名称名称',
          experimentNo: '31423764',
          experimentName: '名称名称名称',
          engineer: '李治悦',
          chemist: '刘大大',
          engineerApproved: '否',
          experimenter: '周乐心; 张瑞瑞',
          experimenterApproved: '否',
          createTime: '2025-1-2 16:27:17',
          status: '待提交'
        },
        {
          id: '2',
          projectPlan: '高性能材料研究',
          experimentNo: '31423765',
          experimentName: '材料强度测试',
          engineer: '赵雨',
          chemist: '王泽',
          engineerApproved: '是',
          experimenter: '李明; 张华',
          experimenterApproved: '是',
          createTime: '2025-1-2 16:30:17',
          status: '已提交'
        },
        {
          id: '3',
          projectPlan: '新型合金开发',
          experimentNo: '31423766',
          experimentName: '合金性能分析',
          engineer: '杨静',
          chemist: '陈亮',
          engineerApproved: '是',
          experimenter: '王刚',
          experimenterApproved: '否',
          createTime: '2025-1-2 16:35:17',
          status: '待评定'
        }
      ],
      // 草稿箱数据
      mockDraftData: [
        {
          id: '4',
          projectPlan: '环保材料研究',
          experimentNo: '31423767',
          experimentName: '材料降解测试',
          engineer: '张伟',
          chemist: '李强',
          engineerApproved: '否',
          experimenter: '王芳',
          experimenterApproved: '否',
          createTime: '2025-1-2 16:40:17',
          status: '草稿'
        },
        {
          id: '5',
          projectPlan: '纳米材料开发',
          experimentNo: '31423768',
          experimentName: '纳米结构分析',
          engineer: '刘洋',
          chemist: '周杰',
          engineerApproved: '否',
          experimenter: '孙明',
          experimenterApproved: '否',
          createTime: '2025-1-2 16:45:17',
          status: '草稿'
        }
      ]
    };
  },
  created() {
    this.getTableData();
  },
  methods: {
    resetForm() {
      this.form = {
        projectPlan: "",
        experimentNo: "",
        experimentName: "",
        createTime: [],
        status: ""
      };
    },
    handleSearch() {
      this.getTableData();
    },
    handleAddResult() {
      this.$router.push({
        path: "/dataManagement/testResultReport/detail",
        query: {
          id: 'new',
          type: 'add'
        }
      });
    },
    handleEdit(row) {
      this.$router.push({
        path: "/dataManagement/testResultReport/detail",
        query: {
          id: row.id,
          type: 'edit'
        }
      });
    },
    handleDetail(id) {
      this.$router.push({
        path: "/dataManagement/testResultReport/detail",
        query: {
          id: id,
          type: 'view'
        }
      });
    },
    handleSubmit(row) {
      this.$confirm('确认提交该实验结果?', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(() => {
        // TODO: 调用提交接口
        this.$message.success('提交成功');
        this.getTableData();
      }).catch(() => {});
    },
    handleDelete(row) {
      this.$confirm('确认删除该实验结果?', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(() => {
        // TODO: 调用删除接口
        this.$message.success('删除成功');
        this.getTableData();
      }).catch(() => {});
    },
    handleEvaluate(row) {
      this.$router.push({
        path: "/dataManagement/testResultReport/detail",
        query: {
          id: row.id,
          type: 'evaluate'
        }
      });
    },
    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>
src/views/deliveryAssessment/assayTaskList/components/AssessmentDialog.vue
New file
@@ -0,0 +1,208 @@
<template>
    <el-dialog :visible.sync="dialogVisible" title="课题评定详情" width="70%" @close="handleClose">
        <el-form :model="form" inline label-position="top" :rules="rules" ref="formRef">
            <el-row :gutter="20">
                <el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
                    <el-form-item label="报告编号" prop="reportNo" required>
                        <el-input v-model="form.reportNo" disabled placeholder="自动生成" />
                    </el-form-item>
                </el-col>
                <el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
                    <el-form-item label="报告名称" prop="reportName">
                        <el-input v-model="form.reportName" placeholder="请输入" />
                    </el-form-item>
                </el-col>
            </el-row>
        </el-form>
        <div class="content-box">
            <el-row :gutter="16">
                <el-col style="margin-top: 5px;" :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
                    <Table :total="0" :height="null" :data="criteriaList" show-summary :summary-method="getSummaries"
                        :span-method="arraySpanMethod">
                        <el-table-column type="index" label="序号" width="80" />
                        <el-table-column prop="criteria" label="规程型课题评定标准" />
                        <el-table-column prop="fullScore" label="满分值" width="100" />
                        <el-table-column label="评定分值" prop="score" width="200">
                            <template #default="{ row }">
                                <el-input-number v-model="row.score" :min="0" :max="row.fullScore" :step="1" />
                            </template>
                        </el-table-column>
                        <el-table-column prop="rule" label="创新型课题报告评分规则">
                            <template>
                                <div>
                                    <div>1、规程型课题评定总分的满分为5分。</div>
                                    <div>2、某分项工作完成,但出现以下三种错误中的1种,则减1分:</div>
                                    <div>①有缺项、漏项;</div>
                                    <div>②或不完整清晰;</div>
                                    <div>③或工作效率人为拖延。</div>
                                    <div>3、不能完成某分项的全部工作,或课题不涉及该分项内容,则该分项评0分。</div>
                                </div>
                            </template>
                        </el-table-column>
                    </Table>
                </el-col>
            </el-row>
        </div>
        <div class="assessed">
            <div>评定时间:2025-2-20 11:08:00</div>
            <div>评定人:张三</div>
        </div>
    </el-dialog>
</template>
<script>
export default {
    name: 'AssessmentDialog',
    props: {
        modelValue: {
            type: Boolean,
            default: false
        },
        reportData: {
            type: Object,
            default: () => { }
        }
    },
    data() {
        return {
            dialogVisible: false,
            form: {
                reportNo: '',
                reportName: '',
            },
            rules: {
                reportName: [
                    { required: true, message: '请输入报告名称', trigger: 'blur' }
                ],
            },
            criteriaList: [
                {
                    criteria: '文献资料调查:全面性,系统性 编辑逻辑清晰,表达规范',
                    fullScore: 1,
                    score: 0,
                },
                {
                    criteria: '专业/技术路线与方法:合理性、可行性;实验设计科学、能实现研究目标,方法先进、完整、可靠;',
                    fullScore: 2,
                    score: 0,
                },
                {
                    criteria: '课题报告完成度高,预期研究结果果是否具有技术价值和应用价值。风险识别:识别了潜在的技术风险、市场风险和管理风险。',
                    fullScore: 3,
                    score: 0,
                },
            ]
        }
    },
    watch: {
        modelValue: {
            handler(val) {
                this.dialogVisible = val;
            },
            immediate: true
        },
        reportData: {
            handler(val) {
                if (val) {
                    this.form.reportNo = val.reportNo || '';
                    this.form.reportName = val.reportName || '';
                }
            },
            immediate: true
        }
    },
    methods: {
        handleClose() {
            this.$emit('update:modelValue', false);
        },
        async handleSubmit() {
            try {
                await this.$refs.formRef.validate();
                const totalScore = this.criteriaList.reduce((sum, item) => sum + (item.score || 0), 0);
                const assessmentData = {
                    ...this.form,
                    totalScore,
                    criteriaScores: this.criteriaList.map(item => ({
                        criteria: item.criteria,
                        score: item.score || 0
                    }))
                };
                this.$emit('submit', assessmentData);
                this.handleClose();
            } catch (error) {
                // 表单验证失败
            }
        },
        getSummaries(param) {
            const { columns, data } = param;
            const sums = [];
            columns.forEach((column, index) => {
                if (index === 0) {
                    sums[index] = '合计';
                    return;
                }
                const values = data.map(item => Number(item[column.property]));
                if (!values.every(value => isNaN(value))) {
                    sums[index] = values.reduce((prev, curr) => {
                        const value = Number(curr);
                        if (!isNaN(value)) {
                            return prev + curr;
                        } else {
                            return prev;
                        }
                    }, 0);
                    sums[index] += ' 分';
                } else {
                    sums[index] = '';
                }
            });
            return sums;
        },
        arraySpanMethod({ row, column, rowIndex, columnIndex }) {
            if (columnIndex === 4 && rowIndex == 0) {
                return {
                    rowspan: 6,
                    colspan: 1
                }
            } else if (rowIndex !== 0 && columnIndex === 4) {
                return [0, 0]
            }
        }
    }
}
</script>
<style lang="less" scoped>
.content-box {
    &-left {
        margin-top: 5px;
        display: flex;
        flex-direction: column;
        border: 1px solid #EBEEF5;
        border-radius: 8px 8px 0px 0px;
        font-size: 12px;
        &-th {
            line-height: 40px;
            background: #FAFAFA !important;
            color: #909399;
            padding: 0 10px;
            font-weight: bold;
        }
        &-body {
            padding: 0 10px;
            line-height: 23px;
        }
    }
}
.assessed {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 25px;
    margin-top: 20px;
}
</style>
src/views/deliveryAssessment/assayTaskList/components/CraftDialog.vue
New file
@@ -0,0 +1,231 @@
<template>
    <el-dialog :visible.sync="dialogVisible" title="课题评定详情" width="70%" @close="handleClose">
        <el-form :model="form" inline label-position="top" :rules="rules" ref="formRef">
            <el-row :gutter="20">
                <el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
                    <el-form-item label="报告编号" prop="reportNo" required>
                        <el-input v-model="form.reportNo" disabled placeholder="自动生成" />
                    </el-form-item>
                </el-col>
                <el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
                    <el-form-item label="报告名称" prop="reportName">
                        <el-input v-model="form.reportName" placeholder="请输入" />
                    </el-form-item>
                </el-col>
            </el-row>
        </el-form>
        <div class="content-box">
            <el-row :gutter="16">
                <el-col style="margin-top: 5px;" :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
                    <Table :total="0" :height="null" :data="criteriaList" show-summary :summary-method="getSummaries"
                        :span-method="arraySpanMethod">
                        <el-table-column type="index" label="序号" width="80" />
                        <el-table-column prop="criteria" label="创新型课题标准" />
                        <el-table-column prop="fullScore" label="满分值" width="100" />
                        <el-table-column label="评定分值" prop="score" width="200">
                            <template #default="{ row }">
                                <el-input-number v-model="row.score" :min="0" :max="row.fullScore" :step="1" />
                            </template>
                        </el-table-column>
                        <el-table-column prop="rule" label="创新型课题报告评分规则">
                            <template>
                                <div>
                                    <div>1、各分项评满分,应满足以下四项要求:</div>
                                    <div>①分项内容:清晰、系统、完整,结构逻辑清晰,无缺项;</div>
                                    <div>②团队工作运行顺畅、计划时间高效。</div>
                                    <div>③工作结果完成度高。</div>
                                    <div>④课题文档报告完成度高。</div>
                                    <div>2、某分项工作完成,但出现以下三种错误中的1种,则减1分:</div>
                                    <div>①有缺项、漏项;</div>
                                    <div>②或不完整清晰;</div>
                                    <div>③或工作效率人为拖延。</div>
                                    <div>3、某分项工作基本完成,但出现三种错误中的2-3种,则减2分:</div>
                                    <div> ①有缺项、漏项;</div>
                                    <div>②或不完整清晰;</div>
                                    <div>③或工作效率人为拖延。</div>
                                    <div>4、不能完成某分项的全部工作,或课题不涉及该分项内容,则该分项评0分。</div>
                                </div>
                            </template>
                        </el-table-column>
                    </Table>
                </el-col>
            </el-row>
        </div>
        <div class="assessed">
            <div>评定时间:2025-2-20 11:08:00</div>
            <div>评定人:张三</div>
        </div>
    </el-dialog>
</template>
<script>
export default {
    name: 'CraftDialog',
    props: {
        modelValue: {
            type: Boolean,
            default: false
        },
        reportData: {
            type: Object,
            default: () => { }
        }
    },
    data() {
        return {
            dialogVisible: false,
            form: {
                reportNo: '',
                reportName: '',
            },
            rules: {
                reportName: [
                    { required: true, message: '请输入报告名称', trigger: 'blur' }
                ],
            },
            criteriaList: [
                {
                    criteria: '课题名称依准确性、报告内容完整性:研究目标是否完整、是否研究工作内容合理性、是否围绕目标展开开、是否具有逻辑性和条理性',
                    fullScore: 3,
                    score: 0,
                },
                {
                    criteria: '文献资料调查:全面性、系统性编辑逻辑顺畅,表达规范',
                    fullScore: 2,
                    score: 0,
                },
                {
                    criteria: '专业/技术路线与方法:合理性、可行性;实验设计科学、能完成研究目标、方法完整、完整、可靠;',
                    fullScore: 3,
                    score: 0,
                },
                {
                    criteria: '实验验证工作可行性强,团队工作调度支持实验设备、材料等条件的准备良好,时间计划合理,能效按时完成',
                    fullScore: 2,
                    score: 0,
                },
                {
                    criteria: '实验设计合理性:①对照设置,重复次数,样本量足够。②变量控制:自变量控制,因变量测量,干扰因素分析识别;确保结果的准确性③验证结果解释分析:符合科学原理、逻辑严密、不存在漏洞。具有实际应用价值,解决了实际问题或实现了研究目的。',
                    fullScore: 3,
                    score: 0,
                },
                {
                    criteria: '课题报告完成度高,预期研究结果果是否具有技术价值和应用价值。风险识别:识别了潜在的技术风险、市场风险和管理风险。',
                    fullScore: 2,
                    score: 0,
                }
            ]
        }
    },
    watch: {
        modelValue: {
            handler(val) {
                this.dialogVisible = val;
            },
            immediate: true
        },
        reportData: {
            handler(val) {
                if (val) {
                    this.form.reportNo = val.reportNo || '';
                    this.form.reportName = val.reportName || '';
                }
            },
            immediate: true
        }
    },
    methods: {
        handleClose() {
            this.$emit('update:modelValue', false);
        },
        async handleSubmit() {
            try {
                await this.$refs.formRef.validate();
                const totalScore = this.criteriaList.reduce((sum, item) => sum + (item.score || 0), 0);
                const assessmentData = {
                    ...this.form,
                    totalScore,
                    criteriaScores: this.criteriaList.map(item => ({
                        criteria: item.criteria,
                        score: item.score || 0
                    }))
                };
                this.$emit('submit', assessmentData);
                this.handleClose();
            } catch (error) {
                // 表单验证失败
            }
        },
        getSummaries(param) {
            const { columns, data } = param;
            const sums = [];
            columns.forEach((column, index) => {
                if (index === 0) {
                    sums[index] = '合计';
                    return;
                }
                const values = data.map(item => Number(item[column.property]));
                if (!values.every(value => isNaN(value))) {
                    sums[index] = values.reduce((prev, curr) => {
                        const value = Number(curr);
                        if (!isNaN(value)) {
                            return prev + curr;
                        } else {
                            return prev;
                        }
                    }, 0);
                    sums[index] += ' 分';
                } else {
                    sums[index] = '';
                }
            });
            return sums;
        },
        arraySpanMethod({ row, column, rowIndex, columnIndex }) {
            if (columnIndex === 4 && rowIndex == 0) {
                return {
                    rowspan: 6,
                    colspan: 1
                }
            } else if (rowIndex !== 0 && columnIndex === 4) {
                return [0, 0]
            }
        }
    }
}
</script>
<style lang="less" scoped>
.content-box {
    &-left {
        margin-top: 5px;
        display: flex;
        flex-direction: column;
        border: 1px solid #EBEEF5;
        border-radius: 8px 8px 0px 0px;
        font-size: 12px;
        &-th {
            line-height: 40px;
            background: #FAFAFA !important;
            color: #909399;
            padding: 0 10px;
            font-weight: bold;
        }
        &-body {
            padding: 0 10px;
            line-height: 23px;
        }
    }
}
.assessed {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 25px;
    margin-top: 20px;
}
</style>
src/views/deliveryAssessment/assayTaskList/index.vue
New file
@@ -0,0 +1,154 @@
<template>
    <div class="list">
        <TableCustom :queryForm="queryForm" :tableData="tableData" :total="total" @currentChange="handleCurrentChange"
            @sizeChange="handleSizeChange">
            <template #search>
                <el-form :model="form" label-width="140px" inline>
                    <el-form-item label="项目组名称:">
                        <el-input v-model="form.name" placeholder="请输入" />
                    </el-form-item>
                    <el-form-item label="课题类型:">
                        <el-input v-model="form.name" placeholder="请输入" />
                    </el-form-item>
                    <el-form-item label="课题名称:">
                        <el-input v-model="form.name" placeholder="请输入" />
                    </el-form-item>
                    <el-form-item label="提交人:">
                        <el-input v-model="form.name" placeholder="请输入" />
                    </el-form-item>
                    <el-form-item label="状态:">
                        <el-select v-model="form.name" placeholder="请选择" />
                    </el-form-item>
                    <el-form-item class="search-btn-box">
                        <el-button>重置</el-button>
                        <el-button type="primary">查询</el-button>
                    </el-form-item>
                </el-form>
            </template>
            <template #setting>
                <div class="top-box-integral">
                    <div style="background-color:rgba(232, 250, 246, 1)" v-for="item in 3" :key="item"
                        class="top-box-integral-card">
                        <div class="top-box-integral-card-title">{{ ['课题合计数量', '待评定', '已评定'][item - 1] }}</div>
                        <div style="color:rgba(4, 156, 154, 1)" class="top-box-integral-card-num">99.9</div>
                    </div>
                </div>
            </template>
            <template #table>
                <el-table-column prop="name" label="所属项目组" />
                <el-table-column prop="age" label="课题类型" />
                <el-table-column prop="age" label="报告编号" />
                <el-table-column prop="age" label="报告名称" />
                <el-table-column prop="age" label="提交人" />
                <el-table-column prop="age" label="评定结果" />
                <el-table-column prop="age" label="累积分值" />
                <el-table-column prop="age" label="评定时间" />
                <el-table-column prop="age" label="状态">
                    <template #default="{ row }">
                        <el-tag v-if="row.status == 1" type="info" color="#fff">已评定</el-tag>
                        <el-tag v-else type="success">待评定</el-tag>
                    </template>
                </el-table-column>
                <el-table-column prop="age" label="操作">
                    <template #default="{ row }">
                        <el-button type="text">详情</el-button>
                    </template>
                </el-table-column>
            </template>
        </TableCustom>
        <!-- 化验师 审批人 -->
        <AssessmentDialog :modelValue="assessmentVisible" :reportData="currentReport" />
        <!-- 工艺工程师 -->
        <CraftDialog :modelValue="craftVisible" :reportData="currentReport" />
    </div>
</template>
<script>
import AssessmentDialog from './components/AssessmentDialog.vue'
import CraftDialog from './components/CraftDialog.vue'
export default {
    name: 'AssayTaskList',
    components: {
        AssessmentDialog,
        CraftDialog
    },
    data() {
        return {
            form: {
            },
            tableData: [],
            queryForm: {
                pageSize: 10,
                pageNum: 1
            },
            total: 0,
            assessmentVisible: false,
            currentReport: {},
            craftVisible: false,
        }
    },
    methods: {
        handleCurrentChange(page) {
            this.queryForm.pageNum = page
            this.getList()
        },
        handleSizeChange(size) {
            this.queryForm.pageSize = size
            this.getList()
        },
        getList() {
        },
        handleDetail(row) {
            // 处理详情
        },
        handleAssessment(row) {
            this.currentReport = row;
            this.assessmentVisible = true;
        },
        handleAssessmentSubmit(data) {
            console.log('评定提交数据:', data);
            // 处理评定提交
        }
    }
}
</script>
<style scoped lang="less">
.list {
    height: 100%;
}
.top-box-integral {
    display: flex;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 28px;
    &-card {
        flex: 1;
        background: #E8FAF6;
        box-shadow: 0px 10px 10px 0px rgba(0, 0, 0, 0.06);
        border-radius: 10px;
        padding: 21px 20px;
        &-title {
            font-family: 'SourceHanSansCN-Medium';
            font-size: 14px;
            color: rgba(0, 0, 0, 0.8);
        }
        &-num {
            font-family: 'SF Compact Display Black';
            text-align: center;
            font-weight: 900;
            font-size: 50px;
            color: #049C9A;
            line-height: 60px;
        }
    }
}
</style>
src/views/deliveryAssessment/clinicalTrial/components/detail.vue
New file
@@ -0,0 +1,115 @@
<template>
    <el-dialog :visible.sync="dialogVisible" title="新增临床试验" width="50%" @close="handleClose">
        <div class="content-box">
            <div class="header-title-left">
                <img src="@/assets/public/headercard.png" />
                <div>所属项目组</div>
            </div>
            <Table :data="criteriaList" :height="null">
                <el-table-column prop="name" label="项目组名称" />
                <el-table-column prop="name" label="项目负责人" />
                <el-table-column prop="name" label="项目组成员" />
                <el-table-column prop="name" label="创建时间" />
            </Table>
            <div class="header-title-left">
                <img src="@/assets/public/headercard.png" />
                <div>临床试验内容</div>
            </div>
            <el-input type="textarea" :rows="4" placeholder="请输入" v-model="form.textarea" />
            <div class="header-title-left">
                <img src="@/assets/public/headercard.png" />
                <div>临床试验时间</div>
            </div>
            <el-date-picker v-model="form.value1" type="datetime" placeholder="请选择" />
            <div class="header-title-left">
                <img src="@/assets/public/headercard.png" />
                <div>评定积分</div>
            </div>
            <el-input-number placeholder="请输入" v-model="form.score" :min="0" :max="form.fullScore" :precision="1"
                :step="1" />
        </div>
        <template #footer>
            <span class="dialog-footer select-member-footer">
                <el-button type="primary">确认</el-button>
            </span>
        </template>
    </el-dialog>
</template>
<script>
export default {
    name: 'Detail',
    props: {
        modelValue: {
            type: Boolean,
            default: false
        },
        reportData: {
            type: Object,
            default: () => { }
        }
    },
    data() {
        return {
            dialogVisible: false,
            form: {
                textarea: '',
                value1: '',
                score: null,
                fullScore: 3
            },
            rules: {
                reportName: [
                    { required: true, message: '请输入报告名称', trigger: 'blur' }
                ],
            },
            criteriaList: []
        }
    },
    watch: {
        modelValue: {
            handler(val) {
                this.dialogVisible = val;
            },
            immediate: true
        },
        reportData: {
            handler(val) {
                if (val) {
                    this.form = val || {};
                }
            },
            immediate: true
        }
    },
    methods: {
        handleClose() {
        },
    }
}
</script>
<style lang="less" scoped>
.header-title-left {
    display: flex;
    align-items: center;
    gap: 13px;
    margin: 10px 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';
    }
}
</style>
src/views/deliveryAssessment/clinicalTrial/index.vue
New file
@@ -0,0 +1,127 @@
<template>
    <div class="list">
        <TableCustom :queryForm="queryForm" :tableData="tableData" :total="total" @currentChange="handleCurrentChange"
            @sizeChange="handleSizeChange">
            <template #search>
                <el-form :model="form" label-width="140px" inline>
                    <el-form-item label="项目组名称:">
                        <el-input v-model="form.name" placeholder="请输入" />
                    </el-form-item>
                    <el-form-item label="任务内容:">
                        <el-input v-model="form.name" placeholder="请输入" />
                    </el-form-item>
                    <el-form-item class="search-btn-box">
                        <el-button>重置</el-button>
                        <el-button type="primary">查询</el-button>
                    </el-form-item>
                </el-form>
            </template>
            <template #setting>
                <el-button @click="assessmentVisible = true, currentReport = {}" class="el-icon-plus" type="primary">
                    新增临床试验</el-button>
            </template>
            <template #table>
                <el-table-column prop="name" label="所属项目组" />
                <el-table-column prop="age" label="临床试验内容" />
                <el-table-column prop="age" label="    临床试验时间" />
                <el-table-column prop="age" label="添加人" />
                <el-table-column prop="age" label="评定积分" />
                <el-table-column prop="age" label="累计分数" />
                <el-table-column prop="age" label="评定时间" />
                <el-table-column prop="age" label="操作">
                    <template #default="{ row }">
                        <el-button type="text">详情</el-button>
                    </template>
                </el-table-column>
            </template>
        </TableCustom>
        <Detail :modelValue="assessmentVisible" :reportData="currentReport" />
    </div>
</template>
<script>
import Detail from './components/detail.vue'
export default {
    name: 'ClinicalTrial',
    components: {
        Detail
    },
    data() {
        return {
            form: {
            },
            tableData: [],
            queryForm: {
                pageSize: 10,
                pageNum: 1
            },
            total: 0,
            assessmentVisible: false,
            currentReport: {}
        }
    },
    methods: {
        handleCurrentChange(page) {
            this.queryForm.pageNum = page
            this.getList()
        },
        handleSizeChange(size) {
            this.queryForm.pageSize = size
            this.getList()
        },
        getList() {
        },
        handleDetail(row) {
            // 处理详情
        },
        handleAssessment(row) {
            this.currentReport = row;
            this.assessmentVisible = true;
        },
    }
}
</script>
<style scoped lang="less">
.list {
    height: 100%;
}
.top-box-integral {
    display: flex;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 28px;
    &-card {
        flex: 1;
        background: #E8FAF6;
        box-shadow: 0px 10px 10px 0px rgba(0, 0, 0, 0.06);
        border-radius: 10px;
        padding: 21px 20px;
        &-title {
            font-family: 'SourceHanSansCN-Medium';
            font-size: 14px;
            color: rgba(0, 0, 0, 0.8);
        }
        &-num {
            font-family: 'SF Compact Display Black';
            text-align: center;
            font-weight: 900;
            font-size: 50px;
            color: #049C9A;
            line-height: 60px;
        }
    }
}
.tip-warring {
    margin-top: 20px;
    color: rgba(255, 73, 85, 1);
}
</style>
src/views/deliveryAssessment/experimentResults/components/detail.vue
New file
@@ -0,0 +1,54 @@
<template>
    <el-dialog :visible.sync="dialogVisible" title="化验师工作评定" width="79.17%" @close="handleClose">
        <EvaluateTable ref="evaluateTable" />
        <div class="assessed">
            <div>评定时间:2025-2-20 11:08:00</div>
            <div>评定人:张三</div>
        </div>
    </el-dialog>
</template>
<script>
export default {
    name: 'Detail',
    props: {
        modelValue: {
            type: Boolean,
            default: false
        },
        reportData: {
            type: Object,
            default: () => { }
        }
    },
    data() {
        return {
            dialogVisible: false,
        }
    },
    watch: {
        modelValue: {
            handler(val) {
                this.dialogVisible = val;
            },
            immediate: true
        },
    },
    methods: {
        handleClose() {
            console.log(this.$refs.evaluateTable.total);
            console.log(this.$refs.evaluateTable.activeIndex);
        },
    }
}
</script>
<style lang="less" scoped>
.assessed {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 25px;
    margin-top: 20px;
}
</style>
src/views/deliveryAssessment/experimentResults/index.vue
New file
@@ -0,0 +1,143 @@
<template>
    <div class="list">
        <TableCustom :queryForm="queryForm" :tableData="tableData" :total="total" @currentChange="handleCurrentChange"
            @sizeChange="handleSizeChange">
            <template #search>
                <el-form :model="form" label-width="140px" inline>
                    <el-form-item label="项目课题方案名称:">
                        <el-input v-model="form.name" placeholder="请输入" />
                    </el-form-item>
                    <el-form-item label="所属实验编号:">
                        <el-input v-model="form.name" placeholder="请输入" />
                    </el-form-item>
                    <el-form-item label="实验名称:">
                        <el-input v-model="form.name" placeholder="请输入" />
                    </el-form-item>
                    <el-form-item label="创建时间:">
                        <el-date-picker v-model="value1" type="daterange" range-separator="至" start-placeholder="开始日期"
                            end-placeholder="结束日期">
                        </el-date-picker>
                    </el-form-item>
                    <el-form-item label="项目阶段:">
                        <el-select v-model="form.status" placeholder="请选择">
                            <el-option label="是" value="1"></el-option>
                            <el-option label="否" value="0"></el-option>
                        </el-select>
                    </el-form-item>
                    <el-form-item class="search-btn-box">
                        <el-button>重置</el-button>
                        <el-button type="primary">查询</el-button>
                    </el-form-item>
                </el-form>
            </template>
            <template #table>
                <el-table-column prop="name" label="所属项目课题方案" />
                <el-table-column prop="age" label="项目阶段" />
                <el-table-column prop="age" label="实验编号" />
                <el-table-column prop="age" label="实验名称" />
                <el-table-column prop="age" label="工艺工程师" />
                <el-table-column prop="age" label="实验员" />
                <el-table-column prop="age" label="是否评定">
                    <template #default="{ row }">
                        <span :style="{ color: ['green', 'red'][row.status - 1] }">{{ ['是', '否'][row.status - 1]
                        }}</span>
                    </template>
                </el-table-column>
                <el-table-column prop="age" label="创建日期" />
                <el-table-column prop="age" label="操作">
                    <template #default="{ row }">
                        <el-button type="text">评定详情</el-button>
                    </template>
                </el-table-column>
            </template>
        </TableCustom>
        <Detail :modelValue="assessmentVisible" :reportData="currentReport" />
    </div>
</template>
<script>
import Detail from './components/detail.vue'
export default {
    name: 'ExperimentResults',
    components: {
        Detail
    },
    data() {
        return {
            form: {
            },
            tableData: [],
            queryForm: {
                pageSize: 10,
                pageNum: 1
            },
            total: 0,
            assessmentVisible: false,
            currentReport: {}
        }
    },
    methods: {
        handleCurrentChange(page) {
            this.queryForm.pageNum = page
            this.getList()
        },
        handleSizeChange(size) {
            this.queryForm.pageSize = size
            this.getList()
        },
        getList() {
        },
        handleDetail(row) {
            // 处理详情
        },
        handleAssessment(row) {
            this.currentReport = row;
            this.assessmentVisible = true;
        },
    }
}
</script>
<style scoped lang="less">
.list {
    height: 100%;
}
.top-box-integral {
    display: flex;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 28px;
    &-card {
        flex: 1;
        background: #E8FAF6;
        box-shadow: 0px 10px 10px 0px rgba(0, 0, 0, 0.06);
        border-radius: 10px;
        padding: 21px 20px;
        &-title {
            font-family: 'SourceHanSansCN-Medium';
            font-size: 14px;
            color: rgba(0, 0, 0, 0.8);
        }
        &-num {
            font-family: 'SF Compact Display Black';
            text-align: center;
            font-weight: 900;
            font-size: 50px;
            color: #049C9A;
            line-height: 60px;
        }
    }
}
.tip-warring {
    margin-top: 20px;
    color: rgba(255, 73, 85, 1);
}
</style>
src/views/deliveryAssessment/processEngineerEvaluate/index.vue
New file
@@ -0,0 +1,157 @@
<template>
    <div class="list">
        <TableCustom :queryForm="queryForm" :tableData="tableData" :total="total" @currentChange="handleCurrentChange"
            @sizeChange="handleSizeChange">
            <template #search>
                <el-form :model="form" label-width="140px" inline>
                    <el-form-item label="所属项目课题方案:">
                        <el-input v-model="form.name" placeholder="请输入" />
                    </el-form-item>
                    <el-form-item label="实验编号:">
                        <el-input v-model="form.name" placeholder="请输入" />
                    </el-form-item>
                    <el-form-item label="创建日期:">
                        <el-input v-model="form.name" placeholder="请输入" />
                    </el-form-item>
                    <el-form-item label="状态:">
                        <el-select v-model="form.name" placeholder="请选择" />
                    </el-form-item>
                    <el-form-item class="search-btn-box">
                        <el-button>重置</el-button>
                        <el-button type="primary">查询</el-button>
                    </el-form-item>
                </el-form>
            </template>
            <template #setting>
                <div class="table-title">
                    实验结果汇报列表
                </div>
            </template>
            <template #table>
                <el-table-column prop="name" label="所属项目课题方案" />
                <el-table-column prop="age" label="实验编号" />
                <el-table-column prop="age" label="工艺工程师" />
                <el-table-column prop="age" label="化验师" />
                <el-table-column prop="age" label="实验员" />
                <el-table-column prop="age" label="创建日期" />
                <el-table-column prop="age" label="状态">
                    <template #default="{ row }">
                        <el-tag v-if="row.status == 1" type="info" color="#fff">已评定</el-tag>
                        <el-tag v-else type="success">待评定</el-tag>
                    </template>
                </el-table-column>
                <el-table-column prop="age" label="操作">
                    <template #default="{ row }">
                        <el-button type="text" @click="assessmentVisible = true">详情</el-button>
                    </template>
                </el-table-column>
            </template>
        </TableCustom>
    </div>
</template>
<script>
export default {
    name: 'ProcessEngineerEvaluate',
    data() {
        return {
            form: {
            },
            tableData: [],
            queryForm: {
                pageSize: 10,
                pageNum: 1
            },
            total: 0,
        }
    },
    methods: {
        handleCurrentChange(page) {
            this.queryForm.pageNum = page
            this.getList()
        },
        handleSizeChange(size) {
            this.queryForm.pageSize = size
            this.getList()
        },
        getList() {
        },
        handleDetail(row) {
            // 处理详情
        },
    }
}
</script>
<style scoped lang="less">
.list {
    height: 100%;
}
.top-box-integral {
    display: flex;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 28px;
    &-card {
        flex: 1;
        background: #E8FAF6;
        box-shadow: 0px 10px 10px 0px rgba(0, 0, 0, 0.06);
        border-radius: 10px;
        padding: 21px 20px;
        &-title {
            font-family: 'SourceHanSansCN-Medium';
            font-size: 14px;
            color: rgba(0, 0, 0, 0.8);
        }
        &-num {
            font-family: 'SF Compact Display Black';
            text-align: center;
            font-weight: 900;
            font-size: 50px;
            color: #049C9A;
            line-height: 60px;
        }
    }
}
.tip-warring {
    margin-top: 20px;
    color: rgba(255, 73, 85, 1);
}
.table-title {
    width: 220px;
    height: 50px;
    background: #FFFFFF;
    border-radius: 8px 8px 0px 0px;
    border: 1px solid #049C9A;
    display: flex;
    align-items: center;
    justify-content: center;
    font-family: SourceHanSansCN, SourceHanSansCN;
    font-weight: bold;
    font-size: 18px;
    color: #049C9A;
    line-height: 27px;
}
.expand-box {
    padding: 20px;
    background: linear-gradient(180deg, #049C9A 0%, #0ACBCA 100%);
    border-radius: 20px;
    &-title {
        font-weight: 500;
        font-size: 16px;
        color: #FFFFFF;
        line-height: 24px;
        margin-bottom: 20px;
    }
}
</style>
src/views/deliveryAssessment/restsTask/components/detail.vue
New file
@@ -0,0 +1,104 @@
<template>
    <el-dialog :visible.sync="dialogVisible" title="其他任务详情" width="70%" @close="handleClose">
        <div class="content-box">
            <div class="header-title-left">
                <img src="@/assets/public/headercard.png" />
                <div>所属项目组</div>
            </div>
            <Table :data="criteriaList" :height="null">
                <el-table-column prop="name" label="项目组名称" />
                <el-table-column prop="name" label="项目负责人" />
                <el-table-column prop="name" label="项目组成员" />
                <el-table-column prop="name" label="创建时间" />
            </Table>
            <div class="header-title-left">
                <img src="@/assets/public/headercard.png" />
                <div>任务内容</div>
            </div>
            <div>清理</div>
            <div class="header-title-left">
                <img src="@/assets/public/headercard.png" />
                <div>任务时间</div>
            </div>
            <div>2020-12-31 12:00:00</div>
            <div class="header-title-left">
                <img src="@/assets/public/headercard.png" />
                <div>评定积分</div>
            </div>
            <div>2</div>
        </div>
    </el-dialog>
</template>
<script>
export default {
    name: 'Detail',
    props: {
        modelValue: {
            type: Boolean,
            default: false
        },
        reportData: {
            type: Object,
            default: () => { }
        }
    },
    data() {
        return {
            dialogVisible: false,
            form: {},
            rules: {
                reportName: [
                    { required: true, message: '请输入报告名称', trigger: 'blur' }
                ],
            },
            criteriaList: []
        }
    },
    watch: {
        modelValue: {
            handler(val) {
                this.dialogVisible = val;
            },
            immediate: true
        },
        reportData: {
            handler(val) {
                if (val) {
                    this.form = val || {};
                }
            },
            immediate: true
        }
    },
    methods: {
        handleClose() {
        },
    }
}
</script>
<style lang="less" scoped>
.header-title-left {
    display: flex;
    align-items: center;
    gap: 13px;
    margin: 10px 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';
    }
}
</style>
src/views/deliveryAssessment/restsTask/index.vue
New file
@@ -0,0 +1,128 @@
<template>
    <div class="list">
        <TableCustom :queryForm="queryForm" :tableData="tableData" :total="total" @currentChange="handleCurrentChange"
            @sizeChange="handleSizeChange">
            <template #search>
                <el-form :model="form" label-width="140px" inline>
                    <el-form-item label="项目组名称:">
                        <el-input v-model="form.name" placeholder="请输入" />
                    </el-form-item>
                    <el-form-item label="任务内容:">
                        <el-input v-model="form.name" placeholder="请输入" />
                    </el-form-item>
                    <el-form-item class="search-btn-box">
                        <el-button>重置</el-button>
                        <el-button type="primary">查询</el-button>
                    </el-form-item>
                </el-form>
            </template>
            <!-- 工艺工程师 -->
            <template #setting>
                <el-button @click="assessmentVisible = true, currentReport = {}" class="el-icon-plus" type="primary">
                    新增其他任务</el-button>
            </template>
            <template #table>
                <el-table-column prop="name" label="所属项目组" />
                <el-table-column prop="age" label="任务内容" />
                <el-table-column prop="age" label="任务时间" />
                <el-table-column prop="age" label="添加人" />
                <el-table-column prop="age" label="评定积分" />
                <el-table-column prop="age" label="累计分数" />
                <el-table-column prop="age" label="评定时间" />
                <el-table-column prop="age" label="操作">
                    <template #default="{ row }">
                        <el-button type="text">详情</el-button>
                    </template>
                </el-table-column>
            </template>
        </TableCustom>
        <Detail :modelValue="assessmentVisible" :reportData="currentReport" />
    </div>
</template>
<script>
import Detail from './components/detail.vue'
export default {
    name: 'RestsTask',
    components: {
        Detail
    },
    data() {
        return {
            form: {
            },
            tableData: [],
            queryForm: {
                pageSize: 10,
                pageNum: 1
            },
            total: 0,
            assessmentVisible: false,
            currentReport: {}
        }
    },
    methods: {
        handleCurrentChange(page) {
            this.queryForm.pageNum = page
            this.getList()
        },
        handleSizeChange(size) {
            this.queryForm.pageSize = size
            this.getList()
        },
        getList() {
        },
        handleDetail(row) {
            // 处理详情
        },
        handleAssessment(row) {
            this.currentReport = row;
            this.assessmentVisible = true;
        },
    }
}
</script>
<style scoped lang="less">
.list {
    height: 100%;
}
.top-box-integral {
    display: flex;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 28px;
    &-card {
        flex: 1;
        background: #E8FAF6;
        box-shadow: 0px 10px 10px 0px rgba(0, 0, 0, 0.06);
        border-radius: 10px;
        padding: 21px 20px;
        &-title {
            font-family: 'SourceHanSansCN-Medium';
            font-size: 14px;
            color: rgba(0, 0, 0, 0.8);
        }
        &-num {
            font-family: 'SF Compact Display Black';
            text-align: center;
            font-weight: 900;
            font-size: 50px;
            color: #049C9A;
            line-height: 60px;
        }
    }
}
.tip-warring {
    margin-top: 20px;
    color: rgba(255, 73, 85, 1);
}
</style>
src/views/deliveryAssessment/taskList/components/AssessmentDialog.vue
New file
@@ -0,0 +1,244 @@
<template>
    <el-dialog :visible.sync="dialogVisible" title="课题评定详情" width="70%" @close="handleClose">
        <el-form :model="form" inline label-position="top" :rules="rules" ref="formRef">
            <el-row :gutter="20">
                <el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
                    <el-form-item label="报告编号" prop="reportNo" required>
                        <el-input v-model="form.reportNo" disabled placeholder="自动生成" />
                    </el-form-item>
                </el-col>
                <el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
                    <el-form-item label="报告名称" prop="reportName">
                        <el-input v-model="form.reportName" placeholder="请输入" />
                    </el-form-item>
                </el-col>
            </el-row>
        </el-form>
        <div class="content-box">
            <el-row :gutter="16">
                <el-col :xs="24" :sm="24" :md="4" :lg="4" :xl="4">
                    <div class="content-box-left">
                        <div class="content-box-left-th">设立课题规则</div>
                        <div class="content-box-left-body" :style="{ height: `calc(${$baseTableHeight()}px - 40px)` }">
                            1、根据可研报告、产品构思设计的工艺研究路线,一条工艺路线设立一个课题。如果一个课题中有多个化合物需要开发研究,则每个化合物作为一个分题;分题归集到该课题中,最终形成课题报告。不同课题报告中的分题不能重复使用。
                            2、在可行研究阶段,工艺开发升级,重新规划工艺研究路线,则以新规划的工艺路线方案来设定课题</div>
                    </div>
                </el-col>
                <el-col style="margin-top: 5px;" :xs="24" :sm="24" :md="20" :lg="20" :xl="20">
                    <Table :total="0" :height="null" :data="criteriaList" show-summary :summary-method="getSummaries"
                        :span-method="arraySpanMethod">
                        <el-table-column type="index" label="序号" width="80" />
                        <el-table-column prop="criteria" label="创新型课题标准" />
                        <el-table-column prop="fullScore" label="满分值" width="100" />
                        <el-table-column label="评定分值" prop="score" width="200">
                            <template #default="{ row }">
                                <el-input-number v-model="row.score" :min="0" :max="row.fullScore" :step="1" />
                            </template>
                        </el-table-column>
                        <el-table-column prop="rule" label="创新型课题报告评分规则">
                            <template>
                                <div>
                                    <div>1、各分项评满分,应满足以下四项要求:</div>
                                    <div>①分项内容:清晰、系统、完整,结构逻辑清晰,无缺项;</div>
                                    <div>②团队工作运行顺畅、计划时间高效。</div>
                                    <div>③工作结果完成度高。</div>
                                    <div>④课题文档报告完成度高。</div>
                                    <div>2、某分项工作完成,但出现以下三种错误中的1种,则减1分:</div>
                                    <div>①有缺项、漏项;</div>
                                    <div>②或不完整清晰;</div>
                                    <div>③或工作效率人为拖延。</div>
                                    <div>3、某分项工作基本完成,但出现三种错误中的2-3种,则减2分:</div>
                                    <div> ①有缺项、漏项;</div>
                                    <div>②或不完整清晰;</div>
                                    <div>③或工作效率人为拖延。</div>
                                    <div>4、不能完成某分项的全部工作,或课题不涉及该分项内容,则该分项评0分。</div>
                                </div>
                            </template>
                        </el-table-column>
                    </Table>
                </el-col>
            </el-row>
        </div>
        <div class="assessed">
            <div>评定时间:2025-2-20 11:08:00</div>
            <div>评定人:张三</div>
        </div>
        <template #footer>
            <span class="dialog-footer select-member-footer">
                <el-button type="primary" @click="handleSubmit">提交评定结果</el-button>
            </span>
        </template>
    </el-dialog>
</template>
<script>
export default {
    name: 'AssessmentDialog',
    props: {
        modelValue: {
            type: Boolean,
            default: false
        },
        reportData: {
            type: Object,
            default: () => { }
        }
    },
    data() {
        return {
            dialogVisible: false,
            form: {
                reportNo: '',
                reportName: '',
            },
            rules: {
                reportName: [
                    { required: true, message: '请输入报告名称', trigger: 'blur' }
                ],
            },
            criteriaList: [
                {
                    criteria: '课题名称依准确性、报告内容完整性:研究目标是否完整、是否研究工作内容合理性、是否围绕目标展开开、是否具有逻辑性和条理性',
                    fullScore: 3,
                    score: 0,
                },
                {
                    criteria: '文献资料调查:全面性、系统性编辑逻辑顺畅,表达规范',
                    fullScore: 2,
                    score: 0,
                },
                {
                    criteria: '专业/技术路线与方法:合理性、可行性;实验设计科学、能完成研究目标、方法完整、完整、可靠;',
                    fullScore: 3,
                    score: 0,
                },
                {
                    criteria: '实验验证工作可行性强,团队工作调度支持实验设备、材料等条件的准备良好,时间计划合理,能效按时完成',
                    fullScore: 2,
                    score: 0,
                },
                {
                    criteria: '实验设计合理性:①对照设置,重复次数,样本量足够。②变量控制:自变量控制,因变量测量,干扰因素分析识别;确保结果的准确性③验证结果解释分析:符合科学原理、逻辑严密、不存在漏洞。具有实际应用价值,解决了实际问题或实现了研究目的。',
                    fullScore: 3,
                    score: 0,
                },
                {
                    criteria: '课题报告完成度高,预期研究结果果是否具有技术价值和应用价值。风险识别:识别了潜在的技术风险、市场风险和管理风险。',
                    fullScore: 2,
                    score: 0,
                }
            ]
        }
    },
    watch: {
        modelValue: {
            handler(val) {
                this.dialogVisible = val;
            },
            immediate: true
        },
        reportData: {
            handler(val) {
                if (val) {
                    this.form.reportNo = val.reportNo || '';
                    this.form.reportName = val.reportName || '';
                }
            },
            immediate: true
        }
    },
    methods: {
        handleClose() {
            this.$emit('update:modelValue', false);
        },
        async handleSubmit() {
            try {
                await this.$refs.formRef.validate();
                const totalScore = this.criteriaList.reduce((sum, item) => sum + (item.score || 0), 0);
                const assessmentData = {
                    ...this.form,
                    totalScore,
                    criteriaScores: this.criteriaList.map(item => ({
                        criteria: item.criteria,
                        score: item.score || 0
                    }))
                };
                this.$emit('submit', assessmentData);
                this.handleClose();
            } catch (error) {
                // 表单验证失败
            }
        },
        getSummaries(param) {
            const { columns, data } = param;
            const sums = [];
            columns.forEach((column, index) => {
                if (index === 0) {
                    sums[index] = '合计';
                    return;
                }
                const values = data.map(item => Number(item[column.property]));
                if (!values.every(value => isNaN(value))) {
                    sums[index] = values.reduce((prev, curr) => {
                        const value = Number(curr);
                        if (!isNaN(value)) {
                            return prev + curr;
                        } else {
                            return prev;
                        }
                    }, 0);
                    sums[index] += ' 分';
                } else {
                    sums[index] = '';
                }
            });
            return sums;
        },
        arraySpanMethod({ row, column, rowIndex, columnIndex }) {
            if (columnIndex === 4 && rowIndex == 0) {
                return {
                    rowspan: 6,
                    colspan: 1
                }
            } else if (rowIndex !== 0 && columnIndex === 4) {
                return [0, 0]
            }
        }
    }
}
</script>
<style lang="less" scoped>
.content-box {
    &-left {
        margin-top: 5px;
        display: flex;
        flex-direction: column;
        border: 1px solid #EBEEF5;
        border-radius: 8px 8px 0px 0px;
        font-size: 12px;
        &-th {
            line-height: 40px;
            background: #FAFAFA !important;
            color: #909399;
            padding: 0 10px;
            font-weight: bold;
        }
        &-body {
            padding: 0 10px;
            line-height: 23px;
        }
    }
}
.assessed {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 25px;
    margin-top: 20px;
}
</style>
src/views/deliveryAssessment/taskList/index.vue
New file
@@ -0,0 +1,171 @@
<template>
    <div class="list">
        <TableCustom :queryForm="queryForm" :tableData="tableData" :total="total" @currentChange="handleCurrentChange"
            @sizeChange="handleSizeChange">
            <template #search>
                <el-form :model="form" label-width="140px" inline>
                    <el-form-item label="项目组名称:">
                        <el-input v-model="form.name" placeholder="请输入" />
                    </el-form-item>
                    <el-form-item label="课题类型:">
                        <el-input v-model="form.name" placeholder="请输入" />
                    </el-form-item>
                    <el-form-item label="课题名称:">
                        <el-input v-model="form.name" placeholder="请输入" />
                    </el-form-item>
                    <el-form-item label="提交人:">
                        <el-input v-model="form.name" placeholder="请输入" />
                    </el-form-item>
                    <el-form-item label="状态:">
                        <el-select v-model="form.name" placeholder="请选择" />
                    </el-form-item>
                    <el-form-item class="search-btn-box">
                        <el-button>重置</el-button>
                        <el-button type="primary">查询</el-button>
                    </el-form-item>
                </el-form>
            </template>
            <template #setting>
                <div class="top-box-integral">
                    <div style="background-color:rgba(232, 250, 246, 1)" v-for="item in 3" :key="item"
                        class="top-box-integral-card">
                        <div class="top-box-integral-card-title">{{ ['课题合计数量', '待评定', '已评定'][item - 1] }}</div>
                        <div style="color:rgba(4, 156, 154, 1)" class="top-box-integral-card-num">99.9</div>
                    </div>
                </div>
                <div class="tip-warring">
                    <div>
                        【注意】每个课题交付评分标准:
                    </div>
                    <div>
                        1、课题由工程师/化验师设定,并清晰完整完成课题目标,评优秀3分;
                    </div>
                    <div>
                        2、课题由工程师/化验师设定,并清晰完整完成课题目标,评优秀3分;
                    </div>
                    <div>
                        3、课题由上级设定,工程师/化验师清晰完整完成课题目标,评正常2分;
                    </div>
                    <div>
                        4、课题由上级设定,工程师/化验师只能阐述课题内容,无法延伸工作思路,无开发思维的运行能力、创新能力,无法实现专题目标,评失误0分;
                    </div>
                </div>
            </template>
            <template #table>
                <el-table-column prop="name" label="所属项目组" />
                <el-table-column prop="age" label="课题类型" />
                <el-table-column prop="age" label="报告编号" />
                <el-table-column prop="age" label="报告名称" />
                <el-table-column prop="age" label="提交人" />
                <el-table-column prop="age" label="评定结果" />
                <el-table-column prop="age" label="累积分值" />
                <el-table-column prop="age" label="评定时间" />
                <el-table-column prop="age" label="状态">
                    <template #default="{ row }">
                        <el-tag v-if="row.status == 1" type="info" color="#fff">已评定</el-tag>
                        <el-tag v-else type="success">待评定</el-tag>
                    </template>
                </el-table-column>
                <el-table-column prop="age" label="操作">
                    <template #default="{ row }">
                        <el-button type="text">详情</el-button>
                        <el-button type="text">评定</el-button>
                    </template>
                </el-table-column>
            </template>
        </TableCustom>
        <AssessmentDialog :modelValue="assessmentVisible" :reportData="currentReport"
            @submit="handleAssessmentSubmit" />
    </div>
</template>
<script>
import AssessmentDialog from './components/AssessmentDialog.vue'
export default {
    name: 'TaskList',
    components: {
        AssessmentDialog
    },
    data() {
        return {
            form: {
            },
            tableData: [],
            queryForm: {
                pageSize: 10,
                pageNum: 1
            },
            total: 0,
            assessmentVisible: false,
            currentReport: {}
        }
    },
    methods: {
        handleCurrentChange(page) {
            this.queryForm.pageNum = page
            this.getList()
        },
        handleSizeChange(size) {
            this.queryForm.pageSize = size
            this.getList()
        },
        getList() {
        },
        handleDetail(row) {
            // 处理详情
        },
        handleAssessment(row) {
            this.currentReport = row;
            this.assessmentVisible = true;
        },
        handleAssessmentSubmit(data) {
            console.log('评定提交数据:', data);
            // 处理评定提交
        }
    }
}
</script>
<style scoped lang="less">
.list {
    height: 100%;
}
.top-box-integral {
    display: flex;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 28px;
    &-card {
        flex: 1;
        background: #E8FAF6;
        box-shadow: 0px 10px 10px 0px rgba(0, 0, 0, 0.06);
        border-radius: 10px;
        padding: 21px 20px;
        &-title {
            font-family: 'SourceHanSansCN-Medium';
            font-size: 14px;
            color: rgba(0, 0, 0, 0.8);
        }
        &-num {
            font-family: 'SF Compact Display Black';
            text-align: center;
            font-weight: 900;
            font-size: 50px;
            color: #049C9A;
            line-height: 60px;
        }
    }
}
.tip-warring {
    margin-top: 20px;
    color: rgba(255, 73, 85, 1);
}
</style>
src/views/deliveryAssessment/testerWorkerEvaluate/add.vue
New file
@@ -0,0 +1,200 @@
<template>
    <Card>
        <template>
            <div class="header-title" style="margin-bottom: 18px;">
                <div class="header-title-left">
                    <img src="@/assets/public/headercard.png" />
                    <div>所属实验调度</div>
                </div>
                <el-button class="el-icon-plus" type="primary" @click="showScheduling = true"> 选择实验调度</el-button>
            </div>
            <Table :tableData="tableData" :total="total" :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" style="margin-top: 60px;">
                <div class="header-title-left">
                    <img src="@/assets/public/headercard.png" />
                    <div>被评定实验员</div>
                </div>
                <el-button class="el-icon-plus" type="primary" @click="addMember"> 选择实验员</el-button>
            </div>
            <div class="member-list">
                <div class="member-list-card">
                    <div class="member-item">
                        <div class="member-title">实验员</div>
                        <div class="member-name-box-2">
                            <div v-for="i in [1, 2, 3, 4, 5, 6, 7, 8]" :key="i" class="member-name">张三</div>
                        </div>
                        <div class="member-edit" @click="addMember">修改</div>
                    </div>
                </div>
            </div>
            <div class="header-title" style="margin-bottom: 18px;margin-top: 60px;">
                <div class="header-title-left">
                    <img src="@/assets/public/headercard.png" />
                    <div>工作标准评定</div>
                </div>
            </div>
            <EvaluateTable :type="2" />
            <div class="add-project-footer">
                <el-button type="primary">保存</el-button>
                <el-button>存草稿</el-button>
            </div>
        </template>
        <SelectMember ref="selectMember" />
        <ExperimentalScheduling :show="showScheduling" />
    </Card>
</template>
<script>
import ExperimentalScheduling from "@/views/dataManagement/confirmation-sheet/components/experimental-scheduling";
export default {
    name: 'AddTesterWorkerEvaluate',
    components: {
        ExperimentalScheduling,
    },
    data() {
        return {
            showScheduling: false,
        }
    },
    methods: {
        submitForm() {
        },
        addMember() {
            this.$refs.selectMember.open()
        },
    }
}
</script>
<style scoped lang="less">
.el-form--inline .el-form-item {
    margin-right: 83px;
}
.header-title {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 13px;
    .header-title-left {
        display: flex;
        align-items: center;
        gap: 13px;
        img {
            width: 12px;
            height: 19px;
        }
        div {
            flex-shrink: 0;
            font-weight: bold;
            font-size: 18px;
            color: #222222;
            line-height: 27px;
            font-family: 'Source Han Sans CN Bold Bold';
            &:before {
                content: '*';
                color: #F56C6C;
                margin-right: 4px;
            }
        }
    }
}
.member-list {
    margin-top: 18px;
    display: flex;
    flex-wrap: wrap;
    gap: 28px;
    .member-list-card {
        position: relative;
        width: 340px;
        height: 400px;
        border-radius: 8px;
        border: 1px solid #DCDFE6;
        background: linear-gradient(to bottom, rgba(255, 77, 79, 0.20) 0%, rgba(255, 242, 194, 0) 70%);
        .member-item {
            height: 100%;
            display: flex;
            flex-direction: column;
            .member-title {
                margin-top: 20px;
                width: 100%;
                font-family: 'Source Han Sans CN Bold Bold';
                font-weight: bold;
                font-size: 16px;
                color: rgba(0, 0, 0, 0.8);
                line-height: 16px;
                text-align: center;
            }
            .member-name-box-2 {
                padding: 0 20px;
                padding-top: 40px;
                display: grid;
                grid-template-columns: repeat(4, 1fr);
                align-items: flex-start;
                flex-wrap: wrap;
                gap: 20px;
                justify-content: center;
            }
            .member-name {
                width: 60px;
                height: 60px;
                background: #7D8B79;
                border-radius: 50%;
                text-align: center;
                line-height: 60px;
                font-weight: 500;
                font-size: 16px;
                color: #FFFFFF;
            }
            .member-edit {
                cursor: pointer;
                position: absolute;
                bottom: 10px;
                left: 50%;
                transform: translateX(-50%);
                font-weight: 400;
                font-size: 12px;
                color: #FF4D4F;
                line-height: 22px;
                width: 40px;
                background: #FFF1F0;
                border-radius: 4px;
                border: 1px solid #FFCCC7;
                text-align: center;
            }
        }
    }
}
.add-project-footer {
    display: flex;
    gap: 20px;
    margin-top: 43px;
    button {
        width: 220px;
    }
}
</style>
src/views/deliveryAssessment/testerWorkerEvaluate/index.vue
New file
@@ -0,0 +1,217 @@
<template>
    <div class="list">
        <TableCustom :queryForm="queryForm" :tableData="tableData" :total="total" @currentChange="handleCurrentChange"
            @sizeChange="handleSizeChange">
            <template #search>
                <el-form :model="form" label-width="140px" inline>
                    <el-form-item label="所属项目课题方案:">
                        <el-input v-model="form.name" placeholder="请输入" />
                    </el-form-item>
                    <el-form-item label="实验编号:">
                        <el-input v-model="form.name" placeholder="请输入" />
                    </el-form-item>
                    <el-form-item label="实验名称:">
                        <el-input v-model="form.name" placeholder="请输入" />
                    </el-form-item>
                    <el-form-item label="评定时间:">
                        <el-date-picker v-model="value1" type="daterange" range-separator="至" start-placeholder="开始日期"
                            end-placeholder="结束日期">
                        </el-date-picker>
                    </el-form-item>
                    <el-form-item class="search-btn-box">
                        <el-button>重置</el-button>
                        <el-button type="primary">查询</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="handleAdd" class="el-icon-plus" type="primary">
                        新增实验员工作评定</el-button>
                </div>
            </template>
            <template #table>
                <el-table-column prop="name" label="所属项目组" />
                <el-table-column prop="age" label="检测项名称" />
                <el-table-column prop="age" label="检测项编号" />
                <el-table-column prop="age" label="备注" />
                <el-table-column prop="age" label="创建人" />
                <el-table-column prop="age" label="创建时间" />
                <el-table-column prop="age" label="状态">
                    <template #default="{ row }">
                        <el-tag v-if="row.status == 1" type="info" color="#fff">已评定</el-tag>
                        <el-tag v-else type="success">待评定</el-tag>
                    </template>
                </el-table-column>
                <el-table-column prop="age" label="操作">
                    <template #default="{ row }">
                        <el-button type="text" @click="assessmentVisible = true">详情</el-button>
                    </template>
                </el-table-column>
            </template>
        </TableCustom>
    </div>
</template>
<script>
export default {
    name: 'TesterWorkerEvaluate',
    data() {
        return {
            currentType: 'list', // 当前显示类型:list-列表,draft-草稿箱
            form: {
            },
            tableData: [],
            queryForm: {
                pageSize: 10,
                pageNum: 1
            },
            total: 0,
        }
    },
    methods: {
        handleAdd() {
            this.$router.push({
                path: '/deliveryAssessment/addTesterWorkerEvaluate'
            })
        },
        handleCurrentChange(page) {
            this.queryForm.pageNum = page
            this.getList()
        },
        handleSizeChange(size) {
            this.queryForm.pageSize = size
            this.getList()
        },
        getList() {
        },
        handleTypeChange(type) {
            this.currentType = type;
            this.getList();
        },
    }
}
</script>
<style scoped lang="less">
.list {
    height: 100%;
}
.top-box-integral {
    display: flex;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 28px;
    &-card {
        flex: 1;
        background: #E8FAF6;
        box-shadow: 0px 10px 10px 0px rgba(0, 0, 0, 0.06);
        border-radius: 10px;
        padding: 21px 20px;
        &-title {
            font-family: 'SourceHanSansCN-Medium';
            font-size: 14px;
            color: rgba(0, 0, 0, 0.8);
        }
        &-num {
            font-family: 'SF Compact Display Black';
            text-align: center;
            font-weight: 900;
            font-size: 50px;
            color: #049C9A;
            line-height: 60px;
        }
    }
}
.tip-warring {
    margin-top: 20px;
    color: rgba(255, 73, 85, 1);
}
.table-title {
    width: 220px;
    height: 50px;
    background: #FFFFFF;
    border-radius: 8px 8px 0px 0px;
    border: 1px solid #049C9A;
    display: flex;
    align-items: center;
    justify-content: center;
    font-family: SourceHanSansCN, SourceHanSansCN;
    font-weight: bold;
    font-size: 18px;
    color: #049C9A;
    line-height: 27px;
}
.expand-box {
    padding: 20px;
    background: linear-gradient(180deg, #049C9A 0%, #0ACBCA 100%);
    border-radius: 20px;
    &-title {
        font-weight: 500;
        font-size: 16px;
        color: #FFFFFF;
        line-height: 24px;
        margin-bottom: 20px;
    }
}
.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>
src/views/deliveryAssessment/testingAndEvaluation/components/AssessmentDialog.vue
New file
@@ -0,0 +1,208 @@
<template>
    <el-dialog :visible.sync="dialogVisible" title="检测项评定详情" width="70%" @close="handleClose">
        <el-form :model="form" inline label-position="top" :rules="rules" ref="formRef">
            <el-row :gutter="20">
                <el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
                    <el-form-item label="检测项编号" prop="reportNo" required>
                        <el-input v-model="form.reportNo" disabled placeholder="自动生成" />
                    </el-form-item>
                </el-col>
                <el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
                    <el-form-item label="检测项名称" prop="reportName">
                        <el-input v-model="form.reportName" placeholder="请输入" />
                    </el-form-item>
                </el-col>
            </el-row>
        </el-form>
        <div class="content-box">
            <el-row :gutter="16">
                <el-col style="margin-top: 5px;" :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
                    <Table :total="0" :height="null" :data="criteriaList" show-summary :summary-method="getSummaries"
                        :span-method="arraySpanMethod">
                        <el-table-column type="index" label="序号" width="80" />
                        <el-table-column prop="criteria" label="规程型课题评定标准" />
                        <el-table-column prop="fullScore" label="满分值" width="100" />
                        <el-table-column label="评定分值" prop="score" width="200">
                            <template #default="{ row }">
                                <el-input-number v-model="row.score" :min="0" :max="row.fullScore" :step="1" />
                            </template>
                        </el-table-column>
                        <el-table-column prop="rule" label="创新型课题报告评分规则">
                            <template>
                                <div>
                                    <div>1、规程型课题评定总分的满分为5分。</div>
                                    <div>2、某分项工作完成,但出现以下三种错误中的1种,则减1分:</div>
                                    <div>①有缺项、漏项;</div>
                                    <div>②或不完整清晰;</div>
                                    <div>③或工作效率人为拖延。</div>
                                    <div>3、不能完成某分项的全部工作,或课题不涉及该分项内容,则该分项评0分。</div>
                                </div>
                            </template>
                        </el-table-column>
                    </Table>
                </el-col>
            </el-row>
        </div>
        <div class="assessed">
            <div>评定时间:2025-2-20 11:08:00</div>
            <div>评定人:张三</div>
        </div>
    </el-dialog>
</template>
<script>
export default {
    name: 'AssessmentDialog',
    props: {
        modelValue: {
            type: Boolean,
            default: false
        },
        reportData: {
            type: Object,
            default: () => { }
        }
    },
    data() {
        return {
            dialogVisible: false,
            form: {
                reportNo: '',
                reportName: '',
            },
            rules: {
                reportName: [
                    { required: true, message: '请输入报告名称', trigger: 'blur' }
                ],
            },
            criteriaList: [
                {
                    criteria: '文献资料调查:全面性,系统性 编辑逻辑清晰,表达规范',
                    fullScore: 1,
                    score: 0,
                },
                {
                    criteria: '专业/技术路线与方法:合理性、可行性;实验设计科学、能实现研究目标,方法先进、完整、可靠;',
                    fullScore: 2,
                    score: 0,
                },
                {
                    criteria: '课题报告完成度高,预期研究结果果是否具有技术价值和应用价值。风险识别:识别了潜在的技术风险、市场风险和管理风险。',
                    fullScore: 3,
                    score: 0,
                },
            ]
        }
    },
    watch: {
        modelValue: {
            handler(val) {
                this.dialogVisible = val;
            },
            immediate: true
        },
        reportData: {
            handler(val) {
                if (val) {
                    this.form.reportNo = val.reportNo || '';
                    this.form.reportName = val.reportName || '';
                }
            },
            immediate: true
        }
    },
    methods: {
        handleClose() {
            this.$emit('update:modelValue', false);
        },
        async handleSubmit() {
            try {
                await this.$refs.formRef.validate();
                const totalScore = this.criteriaList.reduce((sum, item) => sum + (item.score || 0), 0);
                const assessmentData = {
                    ...this.form,
                    totalScore,
                    criteriaScores: this.criteriaList.map(item => ({
                        criteria: item.criteria,
                        score: item.score || 0
                    }))
                };
                this.$emit('submit', assessmentData);
                this.handleClose();
            } catch (error) {
                // 表单验证失败
            }
        },
        getSummaries(param) {
            const { columns, data } = param;
            const sums = [];
            columns.forEach((column, index) => {
                if (index === 0) {
                    sums[index] = '合计';
                    return;
                }
                const values = data.map(item => Number(item[column.property]));
                if (!values.every(value => isNaN(value))) {
                    sums[index] = values.reduce((prev, curr) => {
                        const value = Number(curr);
                        if (!isNaN(value)) {
                            return prev + curr;
                        } else {
                            return prev;
                        }
                    }, 0);
                    sums[index] += ' 分';
                } else {
                    sums[index] = '';
                }
            });
            return sums;
        },
        arraySpanMethod({ row, column, rowIndex, columnIndex }) {
            if (columnIndex === 4 && rowIndex == 0) {
                return {
                    rowspan: 6,
                    colspan: 1
                }
            } else if (rowIndex !== 0 && columnIndex === 4) {
                return [0, 0]
            }
        }
    }
}
</script>
<style lang="less" scoped>
.content-box {
    &-left {
        margin-top: 5px;
        display: flex;
        flex-direction: column;
        border: 1px solid #EBEEF5;
        border-radius: 8px 8px 0px 0px;
        font-size: 12px;
        &-th {
            line-height: 40px;
            background: #FAFAFA !important;
            color: #909399;
            padding: 0 10px;
            font-weight: bold;
        }
        &-body {
            padding: 0 10px;
            line-height: 23px;
        }
    }
}
.assessed {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 25px;
    margin-top: 20px;
}
</style>
src/views/deliveryAssessment/testingAndEvaluation/index.vue
New file
@@ -0,0 +1,212 @@
<template>
    <div class="list">
        <TableCustom :queryForm="queryForm" :tableData="tableData" :total="total" @currentChange="handleCurrentChange"
            @sizeChange="handleSizeChange">
            <template #search>
                <el-form :model="form" label-width="140px" inline>
                    <el-form-item label="所属项目组:">
                        <el-input v-model="form.name" placeholder="请输入" />
                    </el-form-item>
                    <el-form-item label="检测项名称:">
                        <el-input v-model="form.name" placeholder="请输入" />
                    </el-form-item>
                    <el-form-item label="检测项编号:">
                        <el-input v-model="form.name" placeholder="请输入" />
                    </el-form-item>
                    <el-form-item label="状态:">
                        <el-select v-model="form.name" placeholder="请选择" />
                    </el-form-item>
                    <el-form-item class="search-btn-box">
                        <el-button>重置</el-button>
                        <el-button type="primary">查询</el-button>
                    </el-form-item>
                </el-form>
            </template>
            <template #setting>
                <div class="table-title">
                    项目检查项、检验包
                </div>
            </template>
            <template #tableCustom>
                <Table :data="tableData" :total="0" @row-click="handleRowClick" row-key="id"
                    :expand-row-keys="expandRowKeys">
                    <el-table-column type="expand" width="1">
                        <template #default="{ row }">
                            <div class="expand-box">
                                <div class="expand-box-title">报告列表</div>
                                <Table :total="0" :height="null">
                                    <el-table-column prop="name" label="报告内容" />
                                    <el-table-column prop="name" label="制订人" />
                                    <el-table-column prop="name" label="制订日期" />
                                    <el-table-column prop="name" label="检测项编号" />
                                    <el-table-column prop="name" label="审批人" />
                                    <el-table-column prop="name" label="审批时间" />
                                    <el-table-column prop="age" label="状态">
                                        <template #default="{ row }">
                                            <el-tag v-if="row.status == 1" type="info" color="#fff">已通过</el-tag>
                                            <el-tag v-else type="success">未通过</el-tag>
                                        </template>
                                    </el-table-column>
                                    <el-table-column prop="age" label="操作">
                                        <template #default="{ row }">
                                            <el-button type="text">详情</el-button>
                                        </template>
                                    </el-table-column>
                                </Table>
                            </div>
                        </template>
                    </el-table-column>
                    <el-table-column prop="name" label="所属项目组" />
                    <el-table-column prop="age" label="检测项名称" />
                    <el-table-column prop="age" label="检测项编号" />
                    <el-table-column prop="age" label="备注" />
                    <el-table-column prop="age" label="创建人" />
                    <el-table-column prop="age" label="创建时间" />
                    <el-table-column prop="age" label="状态">
                        <template #default="{ row }">
                            <el-tag v-if="row.status == 1" type="info" color="#fff">已评定</el-tag>
                            <el-tag v-else type="success">待评定</el-tag>
                        </template>
                    </el-table-column>
                    <el-table-column prop="age" label="操作">
                        <template #default="{ row }">
                            <el-button type="text" @click="assessmentVisible = true">详情</el-button>
                        </template>
                    </el-table-column>
                </Table>
            </template>
        </TableCustom>
        <AssessmentDialog :modelValue="assessmentVisible" :reportData="currentReport"
            @submit="handleAssessmentSubmit" />
    </div>
</template>
<script>
import AssessmentDialog from './components/AssessmentDialog.vue'
export default {
    name: 'TestingAndEvaluation',
    components: {
        AssessmentDialog
    },
    data() {
        return {
            form: {
            },
            tableData: [{ id: 1 }, { id: 2 }],
            queryForm: {
                pageSize: 10,
                pageNum: 1
            },
            total: 0,
            assessmentVisible: false,
            currentReport: {},
            expandRowKeys: []
        }
    },
    methods: {
        handleRowClick(row, column, event) {
            if (column.label === '操作') return
            if (this.expandRowKeys.includes(row.id)) {
                this.expandRowKeys = this.expandRowKeys.filter(key => key !== row.id);
            } else {
                this.expandRowKeys.push(row.id);
            }
        },
        handleCurrentChange(page) {
            this.queryForm.pageNum = page
            this.getList()
        },
        handleSizeChange(size) {
            this.queryForm.pageSize = size
            this.getList()
        },
        getList() {
        },
        handleDetail(row) {
            // 处理详情
        },
        handleAssessment(row) {
            this.currentReport = row;
            this.assessmentVisible = true;
        },
        handleAssessmentSubmit(data) {
            console.log('评定提交数据:', data);
            // 处理评定提交
        }
    }
}
</script>
<style scoped lang="less">
.list {
    height: 100%;
}
.top-box-integral {
    display: flex;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 28px;
    &-card {
        flex: 1;
        background: #E8FAF6;
        box-shadow: 0px 10px 10px 0px rgba(0, 0, 0, 0.06);
        border-radius: 10px;
        padding: 21px 20px;
        &-title {
            font-family: 'SourceHanSansCN-Medium';
            font-size: 14px;
            color: rgba(0, 0, 0, 0.8);
        }
        &-num {
            font-family: 'SF Compact Display Black';
            text-align: center;
            font-weight: 900;
            font-size: 50px;
            color: #049C9A;
            line-height: 60px;
        }
    }
}
.tip-warring {
    margin-top: 20px;
    color: rgba(255, 73, 85, 1);
}
.table-title {
    width: 220px;
    height: 50px;
    background: #FFFFFF;
    border-radius: 8px 8px 0px 0px;
    border: 1px solid #049C9A;
    display: flex;
    align-items: center;
    justify-content: center;
    font-family: SourceHanSansCN, SourceHanSansCN;
    font-weight: bold;
    font-size: 18px;
    color: #049C9A;
    line-height: 27px;
}
.expand-box {
    padding: 20px;
    background: linear-gradient(180deg, #049C9A 0%, #0ACBCA 100%);
    border-radius: 20px;
    &-title {
        font-weight: 500;
        font-size: 16px;
        color: #FFFFFF;
        line-height: 24px;
        margin-bottom: 20px;
    }
}
</style>
src/views/projectList/addProject.vue
@@ -111,8 +111,6 @@
                margin-right: 4px;
            }
        }
    }
}