董国庆
2025-06-23 6f81691ab09d586470426ee0bfa99cec83797f7b
实验运行模块 图片换真实上传
1个文件已添加
20个文件已修改
477 ■■■■■ 已修改文件
laboratory/package.json 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
laboratory/src/components/DynamicComponent/addTableData.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
laboratory/src/components/DynamicComponent/index.vue 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
laboratory/src/components/SignatureCanvas.vue 71 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
laboratory/src/components/service.js 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
laboratory/src/main.js 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
laboratory/src/router/index.js 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
laboratory/src/views/dataManagement/confirmation-sheet/components/add.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
laboratory/src/views/dataManagement/confirmation-sheet/components/confirm-dialog.vue 19 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
laboratory/src/views/dataManagement/confirmation-sheet/components/review-dialog.vue 32 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
laboratory/src/views/dataManagement/dispatching/editDispatch.vue 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
laboratory/src/views/dataManagement/sampleManage/addSample.vue 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
laboratory/src/views/dataManagement/sampleManage/components/receiveConfirmDialog.vue 19 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
laboratory/src/views/dataManagement/sampleRecordList/changeRecord.vue 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
laboratory/src/views/dataManagement/sampleRecordList/components/confirmDialog.vue 22 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
laboratory/src/views/dataManagement/sampleRecordList/components/sampleDialog.vue 98 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
laboratory/src/views/dataManagement/sampleSubmissionList/components/receiveConfirmDialog.vue 22 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
laboratory/src/views/dataManagement/schemeManagement/addPlan.vue 49 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
laboratory/src/views/dataManagement/schemeManagement/list.vue 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
laboratory/src/views/dataManagement/schemeManagement/service.js 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
laboratory/src/views/dataManagement/schemeManagement/stop-experiment.vue 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
laboratory/package.json
@@ -17,7 +17,6 @@
    "core-js": "^3.41.0",
    "element-ui": "^2.15.6",
    "moment": "^2.30.1",
    "dayjs": "^1.11.0",
    "sm-crypto": "^0.3.13",
    "vue": "^2.7.16",
    "vue-router": "^3.6.5",
@@ -26,7 +25,8 @@
  "devDependencies": {
    "@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6",
    "@babel/preset-env": "^7.26.9",
    "@vue/cli-plugin-babel": "~4.5.0",7
    "@vue/cli-plugin-babel": "~4.5.0",
    "@vue/cli-plugin-eslint": "~4.5.0",
    "@vue/cli-plugin-router": "^4.5.15",
    "@vue/cli-service": "~4.5.0",
    "autoprefixer": "^9.8.8",
laboratory/src/components/DynamicComponent/addTableData.vue
@@ -1,6 +1,6 @@
<template>
  <el-dialog
    title="添加表数据"
    :title="isEdit? '编辑表数据' : '添加表数据'"
    :visible.sync="dialogVisible"
    width="60%"
    :close-on-click-modal="false"
laboratory/src/components/DynamicComponent/index.vue
@@ -197,7 +197,12 @@
                componentData = { fileList: component.data };
                break;
              case 'imageUpload':
                componentData = { imageList: component.data };
                componentData = { imageList: component.data.map(item=>{
                  return {
                    ...item,
                    url: getFullUrl(item.url),
                  }
                }) };
                break;
            }
            return {
laboratory/src/components/SignatureCanvas.vue
@@ -32,6 +32,8 @@
</template>
<script>
import { customUploadRequest, getFullUrl } from '@/utils/utils'
import {editSignPicture} from './service'
export default {
  name: 'SignatureCanvas',
  props: {
@@ -163,30 +165,61 @@
      this.drawDashedBorder()
    },
    // 新增:base64转blob
    dataURLtoBlob(dataurl) {
      const arr = dataurl.split(',');
      const mime = arr[0].match(/:(.*?);/)[1];
      const bstr = atob(arr[1]);
      let n = bstr.length;
      const u8arr = new Uint8Array(n);
      while (n--) {
        u8arr[n] = bstr.charCodeAt(n);
      }
      return new Blob([u8arr], { type: mime });
    },
    // 修改:确认签名时上传图片
    confirmSignature() {
      const canvas = this.$refs.signatureCanvas
      const ctx = this.context
      const canvas = this.$refs.signatureCanvas;
      const ctx = this.context;
      // 保存当前画布内容
      const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height)
      const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
      // 先填充背景色
      ctx.fillStyle = 'rgba(239, 248, 250, 1)'
      ctx.fillRect(0, 0, canvas.width, canvas.height)
      ctx.fillStyle = 'rgba(239, 248, 250, 1)';
      ctx.fillRect(0, 0, canvas.width, canvas.height);
      // 创建一个临时画布来保存签名内容
      const tempCanvas = document.createElement('canvas')
      tempCanvas.width = canvas.width
      tempCanvas.height = canvas.height
      const tempCtx = tempCanvas.getContext('2d')
      tempCtx.putImageData(imageData, 0, 0)
      const tempCanvas = document.createElement('canvas');
      tempCanvas.width = canvas.width;
      tempCanvas.height = canvas.height;
      const tempCtx = tempCanvas.getContext('2d');
      tempCtx.putImageData(imageData, 0, 0);
      // 将签名内容绘制到主画布上
      ctx.drawImage(tempCanvas, 0, 0)
      // 导出图片
      const signatureImage = canvas.toDataURL('image/png')
      this.$emit('confirm', signatureImage)
      ctx.drawImage(tempCanvas, 0, 0);
      // 导出图片为base64
      const base64Data = canvas.toDataURL('image/png');
      // base64转blob
      const blob = this.dataURLtoBlob(base64Data);
      // 新增:生成带时间戳的文件名
      const timestamp = Date.now();
      const fileName = `signature_${timestamp}.png`;
      const file = new File([blob], fileName, { type: blob.type });
      // 上传
      customUploadRequest({
        file,
        onSuccess: (res) => {
          // 假设返回的图片路径为 res.url
          const url = res.msg || res.data || res
          editSignPicture({signPicture:res.msg}).then(res=>{
            if(res.code==200){
              this.$message.success('签名成功');
              this.$emit('confirm', url);
            }
          })
        },
        onError: (err) => {
          this.$message && this.$message.error ? this.$message.error('上传签名失败') : alert('上传签名失败');
        }
      });
    }
  },
  beforeDestroy() {
laboratory/src/components/service.js
New file
@@ -0,0 +1,9 @@
import axios from '@/utils/request';
// 列表
export const editSignPicture = (data) => {
  return axios.post('/api/system/user/editSignPicture', { ...data })
}
export const queryDetail = (data) => {
  return axios.get('/system/user/queryDetail',)
}
laboratory/src/main.js
@@ -21,10 +21,6 @@
import './assets/tailwind.css'
import './styles/element-variables.less'
import dayjs from "../node_modules/dayjs/dayjs.min.js";
import "dayjs/locale/zh-cn";
dayjs.locale("zh-cn");
laboratory/src/router/index.js
@@ -22,7 +22,6 @@
      keepAlive: true,  ------是否缓存
    }
 */
console.log('222222222222222222222',JSON.parse(sessionStorage.getItem('userInfo')).roleType=='5')
const routes = [{
        path: "/",
@@ -258,7 +257,7 @@
                            title: "样品管理",
                            keepAlive: true,
                            privilege:'sampleManage',
                            hide:JSON.parse(sessionStorage.getItem('userInfo')).roleType=='5'?true:false,
                            hide:JSON.parse(sessionStorage.getItem('userInfo'))&&JSON.parse(sessionStorage.getItem('userInfo')).roleType=='5'?true:false,
                            //   privilege:'sampleManage_manage'
                        },
                        component: () => import("../views/dataManagement/sampleManage/list.vue"),
laboratory/src/views/dataManagement/confirmation-sheet/components/add.vue
@@ -356,7 +356,7 @@
        const submitData = {
          dispatchId: this.selectedScheduling.id, // 实验调度ID
          auditStatus: 1, // 待确认状态
          confirmSign: 'https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg' || signatureImage, // 签字图片
          confirmSign: signatureImage, // 签字图片
          signTime: new Date().toISOString(), // 签字时间
          testMethodConfirmSheetTerms: this.testItems.map(item => ({
            id: item.id, // 保留原有ID(编辑时使用)
laboratory/src/views/dataManagement/confirmation-sheet/components/confirm-dialog.vue
@@ -5,6 +5,7 @@
    width="80%"
    :close-on-click-modal="false"
    @close="handleClose"
    @open='open'
    class="submit-confirm-dialog"
  >
    <div class="approval-content">
@@ -46,7 +47,7 @@
          <el-button type="primary" @click="openSignature">签名</el-button>
        </div>
        <div v-if="imgSrc" class="signature-preview">
          <img :src="imgSrc" alt="签名" />
          <img :src="getFullUrl(imgSrc)" alt="签名" />
        </div>
      </div>
    </div>
@@ -66,6 +67,9 @@
<script>
import SignatureCanvas from "@/components/SignatureCanvas.vue";
import {queryDetail} from '@/components/service.js'
import {getFullUrl} from '@/utils/utils.js'
export default {
  name: "ConfirmDialog",
@@ -99,6 +103,13 @@
    };
  },
  methods: {
    open(){
      queryDetail().then(res=>{
        if(res){
          this.imgSrc=res.signPicture
        }
      })
    },
    handleClose() {
      this.$emit("update:visible", false);
      this.imgSrc = "";
@@ -111,7 +122,10 @@
    },
    handleSignatureConfirm(imageData) {
      this.signatureDialogVisible = false;
      this.imgSrc = imageData;
      this.imgSrc = ""; // 先清空
      this.$nextTick(() => {
        this.imgSrc = imageData;
      });
    },
    handleConfirm() {
      if (!this.imgSrc) {
@@ -125,6 +139,7 @@
      this.$emit("confirm", this.imgSrc);
      this.handleClose();
    },
    getFullUrl
  },
};
</script>
laboratory/src/views/dataManagement/confirmation-sheet/components/review-dialog.vue
@@ -1,5 +1,5 @@
<template>
  <el-dialog title="审核检测方法确认单" :visible="dialogVisible" width="80%" :close-on-click-modal="false" @close="handleClose"
  <el-dialog title="审核检测方法确认单" :visible="dialogVisible" width="80%" @open='open' :close-on-click-modal="false" @close="handleClose"
    v-loading="loading">
    <div class="approval-dialog">
      <div class="approval-content">
@@ -69,7 +69,7 @@
        <span>签字确认</span>
        <el-button type="primary" class="el-icon-plus" @click="openSignature">签名</el-button>
      </div>
      <img v-if="imgSrc" :src="imgSrc" alt="签名" class="signature-preview" />
      <img v-if="imgSrc" :src="getFullUrl(imgSrc)" alt="签名" class="signature-preview" />
    </div>
    <div slot="footer" class="dialog-footer">
@@ -77,7 +77,7 @@
      <el-button type="primary" @click="handleConfirm" :disabled="!imgSrc" v-if="type === 'review'">确 认</el-button>
    </div>
    <SignatureCanvas :visible="signatureDialogVisible" @confirm="handleSignatureConfirm" />
    <SignatureCanvas :visible="signatureDialogVisible" @close="signatureDialogVisible=false" @confirm="handleSignatureConfirm" />
  </el-dialog>
</template>
@@ -85,6 +85,8 @@
import SignatureCanvas from "@/components/SignatureCanvas.vue";
import ApprovalProcess from "@/components/approvalProcess";
import { getDetail, sign } from '../service';
import {getFullUrl} from '@/utils/utils.js'
import {queryDetail} from '@/components/service.js'
export default {
  name: "ReviewDialog",
@@ -136,14 +138,24 @@
      }
    }
  },
  methods: {
  methods: {getFullUrl,
    open(){
      queryDetail().then(res=>{
        if(res){
          this.imgSrc=res.signPicture
        }
      })
    },
    async getDetailData() {
      try {
        this.loading = true;
        const res = await getDetail({ id: this.id });
        console.log('res', res)
        if (res) {
          this.formData = res
          this.formData = {
            ...res,
           confirmSign: getFullUrl(res.confirmSign)
          }
          // 组装流程数据
          let processData = [];
          // 提交节点
@@ -152,7 +164,7 @@
            mode: "list",
            fields: [
              { label: "提交人:", value: res.createBy || "" },
              { label: ' ', value: res.confirmSign || "", type: 'img' },
              { label: ' ', value: getFullUrl(res.confirmSign) || "", type: 'img' },
              { label: "提交时间:", value: res.createTime || "" },
            ],
          });
@@ -162,7 +174,7 @@
              mode: "list",
              fields: [
                { label: "审核人:", value: res.auditPersonName || "" },
                { label: ' ', value: res.auditSign || "", type: 'img' },
                { label: ' ', value: getFullUrl(res.auditSign) || "", type: 'img' },
                { label: "审核时间:", value: res.auditTime || "" },
              ],
            });
@@ -188,6 +200,7 @@
    },
    handleClose() {
      this.dialogVisible = false;
      this.signatureDialogVisible=false
      this.$emit("close", false);
      this.imgSrc = "";
      this.formData = {
@@ -210,7 +223,10 @@
    },
    handleSignatureConfirm(imageData) {
      this.signatureDialogVisible = false;
      this.imgSrc = 'https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg';
      this.imgSrc = ""; // 先清空
      this.$nextTick(() => {
        this.imgSrc = imageData;
      });
    },
    handleConfirm() {
      if (!this.imgSrc) {
laboratory/src/views/dataManagement/dispatching/editDispatch.vue
@@ -4,6 +4,7 @@
      :title="dialogTitle"
      :visible.sync="visible"
      width="80%"
      @open='open'
      :close-on-click-modal="false"
      @close="handleClose"
    >
@@ -236,7 +237,7 @@
        <img
          v-if="imgSrc"
          style="width: 200px; height: 100px; margin-left: 25px"
          :src="imgSrc"
          :src="getFullUrl(imgSrc)"
          fit="fit"
        />
      </div>
@@ -263,6 +264,8 @@
import SignatureCanvas from "@/components/SignatureCanvas.vue";
import AIEditor from "@/components/AiEditor";
import { getDetailById, sign } from "./service";
import {queryDetail} from '@/components/service.js'
import {getFullUrl} from '@/utils/utils.js'
export default {
  name: "ApprovalDialog",
@@ -357,6 +360,15 @@
    },
  },
  methods: {
    getFullUrl,
    open(){
      queryDetail().then(res=>{
        if(res){
          this.imgSrc=res.signPicture
          console.log('1111111111',getFullUrl(this.imgSrc),res.signPicture)
        }
      })
    },
    // 获取详情
    getDetail(id) {
      getDetailById({ id })
laboratory/src/views/dataManagement/sampleManage/addSample.vue
@@ -141,8 +141,8 @@
                  <el-image
                    v-for="(url, index) in scope.row.pictures.split(',')" 
                    :key="index"
                    src="https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg"
                    :preview-src-list="[url]"
                    :src="getFullUrl(url)"
                    :preview-src-list="[getFullUrl(url)]"
                    style="width: 50px; height: 50px;"
                  />
                </template>
@@ -253,6 +253,7 @@
import addTime from "./components/addTime.vue";
import ReceiveConfirmDialog from "./components/receiveConfirmDialog.vue";
import { add, getDetail, update, batchCollectSamples } from "./service";
import {getFullUrl} from '@/utils/utils.js'
export default {
  name: "AddSample",
@@ -327,14 +328,13 @@
    },
  },
  methods: {
    getFullUrl,
    // 获取详情数据
    async getDetailData(id) {
      try {
        const res = await getDetail({ id });
        if (res) {
          const detail = res;
          console.log('获取到的详情数据:', detail);
          // 设置表单数据
          this.form = {
            ...this.form,
@@ -386,14 +386,14 @@
          }
          // 如果是详情模式,禁用所有输入
          if (this.pageType == 'detail') {
            this.$nextTick(() => {
              const inputs = document.querySelectorAll('input, textarea, select');
              inputs.forEach(input => {
                input.disabled = true;
              });
            });
          }
          // if (this.pageType == 'detail') {
          //   this.$nextTick(() => {
          //     const inputs = document.querySelectorAll('input, textarea, select');
          //     inputs.forEach(input => {
          //       input.disabled = true;
          //     });
          //   });
          // }
        }
      } catch (error) {
        console.error('获取详情失败:', error);
@@ -507,8 +507,6 @@
        };
        // 打印提交数据
        console.log('草稿提交数据:', submitData);
        let res;
        if (this.pageType == 'edit') {
          // 编辑模式调用update接口
@@ -530,7 +528,6 @@
      }
    },
    handlePendingSelectionChange(selection) {
      console.log("pending selection change:", selection);
      console.log("pending samples data:", this.pendingSamples);
      this.selectedSamples = selection;
      // 强制更新视图
@@ -554,7 +551,7 @@
    },
    confirmReceive(signature) {
      // 获取选中样品的ID并拼接
      const recordOperationIds = this.selectedSamples.map(item => item.id).join(',');
      const recordOperationIds = this.selectedSamples.map(item => item.id);
      
      // 调用批量收样接口
      batchCollectSamples({
@@ -591,10 +588,7 @@
      
    },
    isSelectable(row) {
      console.log('isSelectable row:', row);
      console.log('row status:', row.status, typeof row.status);
      const result = String(row.status) == '2';
      console.log('isSelectable result:', result);
      return result;
    },
  },
laboratory/src/views/dataManagement/sampleManage/components/receiveConfirmDialog.vue
@@ -3,6 +3,7 @@
    title="接收签字确认"
    :visible.sync="visible"
    width="500px"
    @open="open"
    :close-on-click-modal="false"
    @close="handleClose"
  >
@@ -20,7 +21,7 @@
        <img
          v-if="imgSrc"
          style="width: 200px; height: 100px; margin-left: 25px"
          :src="imgSrc"
          :src="getFullUrl(imgSrc)"
          fit="fit"
        />
      </div>
@@ -43,6 +44,8 @@
<script>
import SignatureCanvas from "@/components/SignatureCanvas.vue";
import {getFullUrl} from '@/utils/utils.js'
import {queryDetail} from '@/components/service.js'
export default {
  name: "ReceiveConfirmDialog",
@@ -66,6 +69,14 @@
    };
  },
  methods: {
    getFullUrl,
    open(){
      queryDetail().then(res=>{
        if(res){
          this.imgSrc=res.signPicture
        }
      })
    },
    handleClose() {
      this.$emit("update:visible", false);
      this.imgSrc = "";
@@ -75,8 +86,10 @@
    },
    handleSignatureConfirm(imageData) {
      this.signatureDialogVisible = false;
      // this.imgSrc = imageData;
      this.imgSrc = 'https://img.yzcdn.cn/vant/ipad.png';
      this.imgSrc = ""; // 先清空
      this.$nextTick(() => {
        this.imgSrc = imageData;
      });
    },
    handleConfirm() {
      if (!this.imgSrc) {
laboratory/src/views/dataManagement/sampleRecordList/changeRecord.vue
@@ -67,8 +67,8 @@
              <el-image
                v-for="(url, index) in scope.row.pictures.split(',')" 
                :key="index"
                :src="url"
                :preview-src-list="[url]"
                :src="getFullUrl(url)"
                :preview-src-list="[getFullUrl(url)]"
                style="width: 50px; height: 50px;"
              />
            </template>
@@ -126,6 +126,7 @@
import ConfirmDialog from './components/confirmDialog.vue';
import { getDetail, add, update, commitRecord } from './service';
import moment from 'moment';
import { getFullUrl } from '@/utils/utils'
export default {
  name: "ChangeRecord",
@@ -171,6 +172,7 @@
    }
  },
  methods: {
    getFullUrl,
    async getDetailData() {
      try {
        const res = await getDetail({ id: this.id });
laboratory/src/views/dataManagement/sampleRecordList/components/confirmDialog.vue
@@ -3,6 +3,7 @@
    title="提交确认"
    :visible.sync="visible"
    width="80%"
    @open='open'
    :close-on-click-modal="false"
    @close="handleClose"
  >
@@ -177,7 +178,7 @@
      <img
        v-if="imgSrc"
        style="width: 200px; height: 100px; margin-left: 25px"
        :src="imgSrc"
        :src="getFullUrl(imgSrc)"
        fit="fit"
      />
    </div>
@@ -197,6 +198,8 @@
<script>
import SignatureCanvas from "@/components/SignatureCanvas.vue";
import moment from 'moment';
import {getFullUrl} from '@/utils/utils.js'
import {queryDetail} from '@/components/service.js'
export default {
  name: "ConfirmDialog",
@@ -224,21 +227,32 @@
  data() {
    return {
      signatureDialogVisible: false,
      imgSrc: "https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg",
      imgSrc: "",
      showAdditives: true,
    };
  },
  methods: {
    getFullUrl,
    open(){
      queryDetail().then(res=>{
        if(res){
          this.imgSrc=res.signPicture
        }
      })
    },
    handleClose() {
      this.$emit("update:visible", false);
      this.imgSrc = "https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg";
      this.imgSrc = "";
    },
    openSignature() {
      this.signatureDialogVisible = true;
    },
    handleSignatureConfirm(imageData) {
      this.signatureDialogVisible = false;
      this.imgSrc = imageData;
      this.imgSrc = ""; // 先清空
      this.$nextTick(() => {
        this.imgSrc = imageData;
      });
    },
    handleConfirm() {
      if (!this.imgSrc) {
laboratory/src/views/dataManagement/sampleRecordList/components/sampleDialog.vue
@@ -117,13 +117,15 @@
                >
                  <el-upload
                    class="upload-demo"
                    action="#"
                    :action="uploadUrl"
                    :headers="uploadHeaders"
                    :file-list="fileList"
                    :auto-upload="false"
                    :auto-upload="true"
                    list-type="picture-card"
                    :on-change="handleChange"
                    :on-remove="handleRemove"
                    :before-upload="beforeUpload"
                    :on-change="handleImageChange"
                    :on-remove="handleImageRemove"
                    :on-success="handleImageSuccess"
                    :on-preview="handlePreview"
                    multiple
                  >
                    <i class="el-icon-plus"></i>
@@ -144,6 +146,8 @@
<script>
import { updateRecordOperation } from '../service'
import { getFullUrl } from '@/utils/utils';
import apiConfig from '@/utils/baseurl';
export default {
  name: "SampleDialog",
@@ -194,9 +198,12 @@
          { required: true, message: "请输入加水量", trigger: "blur" },
        ],
      },
      uploadUrl: apiConfig.imgUrl,
      uploadHeaders: {
        Authorization: sessionStorage.getItem('token') || ''
      },
      fileList: [],
      defaultImage: 'https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg',
    };
     };
  },
  watch: {
    data: {
@@ -208,8 +215,11 @@
            const imageUrls = val.pictures.split(',');
            this.fileList = imageUrls.map((url, index) => ({
              name: `sample-image-${index + 1}.png`,
              url: url
              url: getFullUrl(url),
              status: 'success',
            }));
          } else {
            this.fileList = [];
          }
        }
      },
@@ -222,55 +232,57 @@
      this.form = this.$options.data().form;
      this.fileList = [];
    },
    // 上传前校验
    beforeUpload(file) {
      const isImage = file.type.startsWith('image/');
      const isLt2M = file.size / 1024 / 1024 < 2;
      if (!isImage) {
        this.$message.error('只能上传图片文件!');
        return false;
      }
      if (!isLt2M) {
        this.$message.error('图片大小不能超过 2MB!');
        return false;
      }
      return true;
    },
    // 文件状态改变时的钩子
    handleChange(file, fileList) {
    handleImageChange(file, fileList) {
      this.fileList = fileList;
      // 更新pictures字段
      if (fileList.length === 0) {
        this.form.pictures = this.defaultImage;
      } else {
        const imageUrls = fileList.map(file => file.url || this.defaultImage);
        this.form.pictures = imageUrls.join(',');
      // 只在移除时处理form.pictures,上传成功时在handleImageSuccess处理
      if (file.status === 'removed') {
        const urls = fileList.map(f => f.url).filter(Boolean);
        this.form.pictures = urls.join(',');
      }
    },
    // 移除文件时的钩子
    handleRemove(file, fileList) {
    handleImageSuccess(res, file, fileList) {
      // res.msg为图片url
      const url = res.msg;
      file.url = getFullUrl(url);
      // 更新fileList中对应file的url
      this.fileList = fileList.map(f => {
        if (f.uid === file.uid) {
          return file;
        }
        return f;
      });
      // 更新form.pictures
      const urls = this.fileList.map(f => f.url).filter(Boolean);
      this.form.pictures = urls.join(',');
    },
    handleImageRemove(file, fileList) {
      this.fileList = fileList;
      // 更新pictures字段
      if (fileList.length === 0) {
        this.form.pictures = this.defaultImage;
      } else {
        const imageUrls = fileList.map(file => file.url || this.defaultImage);
        this.form.pictures = imageUrls.join(',');
      }
      const urls = fileList.map(f => f.url).filter(Boolean);
      this.form.pictures = urls.join(',');
    },
    handlePreview(file) {
      this.$alert(`<img src='${file.url}' style='width:100%' />`, '图片预览', {
        dangerouslyUseHTMLString: true
      });
    },
    async handleSubmit() {
      this.$refs.form.validate(async (valid) => {
        if (valid) {
          // 处理图片路径为相对路径
          let pictures = this.form.pictures;
          if (pictures) {
            const prefix = apiConfig.showImgUrl;
            pictures = pictures.split(',').map(url => {
              return url.startsWith(prefix) ? url.substring(prefix.length) : url;
            }).join(',');
          }
          const submitData = {
            ...this.form,
            pictures,
            handlePersonId: JSON.parse(sessionStorage.getItem('userInfo') || '{}').userId || "",
            handlePersonName: JSON.parse(sessionStorage.getItem('userInfo') || '{}').nickName || "",
            fileList: this.fileList,
          };
          try {
            // 先调用updateRecordOperation接口保存数据
            const res = await updateRecordOperation(submitData);
laboratory/src/views/dataManagement/sampleSubmissionList/components/receiveConfirmDialog.vue
@@ -4,6 +4,7 @@
    :visible.sync="visible"
    width="500px"
    :close-on-click-modal="false"
    @open='open'
    @close="handleClose"
  >
    <div class="receive-dialog">
@@ -20,7 +21,7 @@
        <img
          v-if="imgSrc"
          style="width: 200px; height: 100px; margin-left: 25px"
          :src="imgSrc"
          :src="getFullUrl(imgSrc)"
          fit="fit"
        />
      </div>
@@ -43,6 +44,8 @@
<script>
import SignatureCanvas from "@/components/SignatureCanvas.vue";
import {getFullUrl} from '@/utils/utils.js'
import {queryDetail} from '@/components/service.js'
export default {
  name: "ReceiveConfirmDialog",
@@ -66,6 +69,14 @@
    };
  },
  methods: {
    getFullUrl,
    open(){
      queryDetail().then(res=>{
        if(res){
          this.imgSrc=res.signPicture
        }
      })
    },
    handleClose() {
      this.$emit("update:visible", false);
      this.imgSrc = "";
@@ -75,16 +86,17 @@
    },
    handleSignatureConfirm(imageData) {
      this.signatureDialogVisible = false;
      // this.imgSrc = imageData;
      this.imgSrc = 'https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg';
      this.imgSrc = ""; // 先清空
      this.$nextTick(() => {
        this.imgSrc = imageData;
      });
    },
    handleConfirm() {
      if (!this.imgSrc) {
        this.$message.warning("请先完成签名确认");
        return;
      }
      const defaultSignature = 'https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg';
      this.$emit("confirm", this.imgSrc || defaultSignature);
      this.$emit("confirm", this.imgSrc);
      this.handleClose();
    },
  },
laboratory/src/views/dataManagement/schemeManagement/addPlan.vue
@@ -198,7 +198,7 @@
import AiEditor from "@/components/AiEditor";
import { getGroupByDispatchId, getParticipantsByDispatchId, getDetail } from "./service";
import moment from 'moment';
import { add,update } from "./service";
import { add,update,updateTester } from "./service";
export default {
  name: "AddProject",
@@ -271,7 +271,7 @@
    this.userRole=userInfo.roleType || '';
    // 检查是否为编辑模式
    if (this.$route.query.type === 'edit' && this.$route.query.id) {
      this.isEdit = true;
      this.isEdit = this.userRole==3?false:true;
      this.editId = this.$route.query.id;
      await this.loadEditData();
    }
@@ -396,19 +396,17 @@
          formData.experimentMaterial = JSON.stringify(this.form.experimentMaterial);
          
          // 编辑模式下添加id参数
          if (this.isEdit && this.editId) {
          if (this.editId) {
            formData.id = this.editId;
          }
          
          // 根据是否为编辑模式调用不同接口
          const apiCall = this.isEdit ? update(formData) : add(formData);
          const apiCall = this.editId ? update(formData) : this.isEdit? updateTester(formData): add(formData);
          
          apiCall.then(res => {
            if (res.code === 200) {
              this.$message.success(status === 1 ? '保存成功' : '草稿保存成功');
              if (status === 1) {
                this.$router.go(-1);
              }
              this.$router.go(-1);
            } else {
              this.$message.error(res.msg || (status === 1 ? '保存失败' : '草稿保存失败'));
            }
@@ -549,6 +547,7 @@
        // 填充实验调度信息
        if (data.experimentDispatch?.id) {
          this.form.dispatchId = data.experimentDispatch.id;
          console.log('experimentStepRecord experimentStepRecord',JSON.parse(data.experimentStepRecord))
          this.groupTableData = [{ ...data.experimentDispatch }];
          
          // 获取组别信息
@@ -614,25 +613,25 @@
          }
          // 设置动态组件的初始数据
          if (!this.isEdit) {
            if (this.$refs.materialComponent && this.form.experimentMaterial) {
              this.$refs.materialComponent.setInitialData(this.form.experimentMaterial);
            }
            if (this.$refs.equipmentComponent && this.form.experimentDevice) {
              this.$refs.equipmentComponent.setInitialData(this.form.experimentDevice);
            }
          // if (!this.isEdit) {
          //   // if (this.$refs.materialComponent && this.form.experimentMaterial) {
          //   //   this.$refs.materialComponent.setInitialData(this.form.experimentMaterial);
          //   // }
          //   // if (this.$refs.equipmentComponent && this.form.experimentDevice) {
          //   //   this.$refs.equipmentComponent.setInitialData(this.form.experimentDevice);
          //   // }
            // 设置步骤内容的初始数据
            this.stepList.forEach((step, index) => {
              const stepContentRef = this.$refs['stepContent' + index];
              if (stepContentRef && step.content) {
                const editor = Array.isArray(stepContentRef) ? stepContentRef[0] : stepContentRef;
                if (editor?.setInitialData) {
                  editor.setInitialData(step.content);
                }
              }
            });
          }
          //   // 设置步骤内容的初始数据
          //   this.stepList.forEach((step, index) => {
          //     const stepContentRef = this.$refs['stepContent' + index];
          //     if (stepContentRef && step.content) {
          //       const editor = Array.isArray(stepContentRef) ? stepContentRef[0] : stepContentRef;
          //       if (editor?.setInitialData) {
          //         editor.setInitialData(step.content);
          //       }
          //     }
          //   });
          // }
        });
      } catch (error) {
laboratory/src/views/dataManagement/schemeManagement/list.vue
@@ -59,21 +59,26 @@
        </el-table-column>
        <el-table-column label="操作" width="250">
          <template slot-scope="scope">
            <!-- 超级管理员(1)和审批人(2) -->
            <template v-if="userRole == '1' || userRole == '2'">
            <!-- 草稿箱模式下,只显示编辑和详情 -->
            <template v-if="currentType === 'draft'">
              <el-button type="text" @click="handleEdit(scope.row)">编辑</el-button>
              <el-button type="text" @click="handleDetail(scope.row)">详情</el-button>
            </template>
            <!-- 工艺工程师(3) -->
            <template v-if="userRole == '3'">
              <!-- <el-button type="text" @click="handleEdit(scope.row)" >编辑</el-button> -->
              <el-button type="text" @click="handleDetail(scope.row)">详情</el-button>
            </template>
            <!-- 实验员(5) -->
            <template v-if="userRole == '5'">
              <el-button type="text" @click="handleEdit(scope.row)" v-if="scope.row.status == 1">编辑</el-button>
              <el-button type="text" @click="handleDetail(scope.row)" v-if="scope.row.status == 6">详情</el-button>
            <!-- 非草稿箱,按原有角色逻辑 -->
            <template v-else>
              <!-- 超级管理员(1)和审批人(2) -->
              <template v-if="userRole == '1' || userRole == '2'">
                <el-button type="text" @click="handleDetail(scope.row)">详情</el-button>
              </template>
              <!-- 工艺工程师(3) -->
              <template v-if="userRole == '3'">
                <el-button type="text" @click="handleDetail(scope.row)">详情</el-button>
              </template>
              <!-- 实验员(5) -->
              <template v-if="userRole == '5'">
                <el-button type="text" @click="handleEdit(scope.row)" v-if="scope.row.status == 1">编辑</el-button>
                <el-button type="text" @click="handleDetail(scope.row)" v-if="scope.row.status == 6">详情</el-button>
              </template>
            </template>
          </template>
        </el-table-column>
laboratory/src/views/dataManagement/schemeManagement/service.js
@@ -13,9 +13,13 @@
  return axios.post('/api/t-experiment-scheme/add', { ...data })
}
//修改
export const update = (data) => {
export const updateTester = (data) => {
  return axios.post('/api/t-experiment-scheme/updateTester', { ...data })
}
//修改
export const update = (data) => {
  return axios.post('/api/t-experiment-scheme/update', { ...data })
}
//删除
export const deleteById = (data) => {
  return axios.delete('/open/t-experiment-scheme/deleteById', { params:data })
laboratory/src/views/dataManagement/schemeManagement/stop-experiment.vue
@@ -87,7 +87,7 @@
        </div>
        <img
          v-if="imgSrc"
          :src="imgSrc"
          :src="getFullUrl(imgSrc)"
          alt="签名"
          class="signature-preview"
        />
@@ -101,6 +101,7 @@
    <SignatureCanvas
      :visible="signatureCanvasVisible"
        @close="signatureCanvasVisible=false"
      @confirm="handleSignatureConfirm"
    />
  </Card>
@@ -110,6 +111,8 @@
import AiEditor from '@/components/AiEditor'
import SignatureCanvas from "@/components/SignatureCanvas.vue"
import { getDetail, applicationTermination } from './service'
import {queryDetail} from '@/components/service.js'
import {getFullUrl} from '@/utils/utils.js'
export default {
  name: 'StopExperiment',
@@ -134,11 +137,20 @@
    this.id = this.$route.query.id
    if (this.id) {
      this.getExperimentDetail()
      this.open()
    } else {
      this.$message.error('参数错误,缺少实验ID')
    }
  },
  methods: {
    getFullUrl,
    open(){
      queryDetail().then(res=>{
        if(res){
          this.imgSrc=res.signPicture
        }
      })
    },
    // 获取实验详情
    async getExperimentDetail() {
      try {
@@ -151,7 +163,6 @@
          this.$message.warning('未获取到实验详情')
        }
      } catch (error) {
        console.error('获取实验详情失败:', error)
        this.$message.error('获取实验详情失败')
      } finally {
        this.loading = false
@@ -197,8 +208,10 @@
    },
    handleSignatureConfirm(imageData) {
      this.signatureCanvasVisible = false
      // this.imgSrc = imageData
      this.imgSrc = 'https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg'
      this.imgSrc = ""; // 先清空
      this.$nextTick(() => {
        this.imgSrc = imageData;
      });
    },
    async handleConfirm() {
      if (!this.imgSrc) {
@@ -228,16 +241,12 @@
            stopFile: filePaths,             // 中止文件路径,多个文件路径通过逗号拼接
            stopFileName: fileNames          // 中止文件名称,多个文件名称通过逗号拼接
          }
          console.log('提交的数据:', formData)
          await applicationTermination(formData)
          this.$message.success('提交成功')
          this.handleDialogClose()
          // 提交成功后返回列表页
          this.$router.go(-1)
        } catch (error) {
          console.error('提交失败:', error)
          this.$message.error('提交失败')
        } finally {
          this.loading = false