董国庆
2025-06-28 42feb0af0ae1d486d0474c76711fdb67c778bcf3
Merge branch 'main' of http://120.76.84.145:10101/gitblit/r/H5/leshan-laboratory
1个文件已添加
59个文件已修改
2286 ■■■■■ 已修改文件
culture/src/components/AiEditor/index.vue 115 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/components/SignatureCanvas.vue 106 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/components/confirm-storage-dialog/index.vue 57 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/components/service.js 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/utils/baseurl.js 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/utils/utils.js 75 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/views/deliveryAssessment/projectTeamIntegral/detail.vue 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/views/middleground/index.vue 107 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/views/middleground/service.js 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/views/pedigree-chart/add.vue 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/views/pedigree-chart/addProgenitor.vue 11 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/views/pedigree-chart/components/AddSublevelForm.vue 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/views/pedigree-chart/components/AddSublevelPlan.vue 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/views/pedigree-chart/components/ParentForm.vue 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/views/pedigree-chart/components/PlanForm.vue 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/views/pedigree-chart/index.vue 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/views/pedigree-chart/progenitorComponents/AddAncestor.vue 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/views/pedigree-chart/progenitorComponents/AddSublevelForm.vue 17 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/views/pedigree-chart/service.js 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/views/strain-library/breeding-record/SlantRecordDialog.vue 186 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/views/strain-library/breeding-record/add.vue 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/views/strain-library/breeding-record/confirm-preserve-dialog.vue 58 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/views/strain-library/breeding-record/confirm-storage-dialog.vue 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/views/strain-library/breeding-record/index.vue 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/views/strain-library/breeding-record/inoculation-slope-record-dialog.vue 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/views/strain-library/breeding-record/preserve-strain-record-dialog.vue 46 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/views/strain-library/breeding-record/separation-record-dialog.vue 399 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/views/strain-library/breeding-record/service.js 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/views/strain-library/main-cell-library/index.vue 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/views/strain-library/main-cell-library/record.vue 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/views/strain-library/production-cell-library/record.vue 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/views/strain-library/strain-library-manage/add.vue 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/views/strain-library/strain-library-manage/components/AddRecordDialog.vue 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/views/strain-library/strain-library-manage/components/RecordDetailDialog.vue 19 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/views/strain-library/strain-library-manage/components/RecordTimeline.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/views/strain-library/strain-library-manage/components/StrainDetail.vue 71 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/views/strain-library/strain-library-manage/record.vue 88 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/views/strain-library/strain-library-manage/service.js 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/views/strain-library/validation/chief-cell/DetailConditionDialog.vue 11 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/views/strain-library/validation/chief-cell/add.vue 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/views/strain-library/validation/chief-cell/confirm-detail.vue 9 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/views/strain-library/validation/chief-cell/index.vue 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/views/strain-library/validation/chief-cell/primitive-cell-detail-dialog.vue 9 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/views/strain-library/validation/primitive-cell/DetailConditionDialog.vue 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/views/strain-library/validation/primitive-cell/add.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/views/strain-library/validation/primitive-cell/confirm-detail.vue 9 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/views/strain-library/validation/primitive-cell/index.vue 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/views/strain-library/validation/primitive-cell/primitive-cell-detail-dialog.vue 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/views/strainReportLibrary/reportLibraryOne/add.vue 82 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/views/strainReportLibrary/reportLibraryOne/components/approval/index.vue 31 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/views/strainReportLibrary/reportLibraryOne/components/evaluate/index.vue 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/views/strainReportLibrary/reportLibraryOneFour/add.vue 96 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/views/strainReportLibrary/reportLibraryOneFour/components/approval/index.vue 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/views/strainReportLibrary/reportLibraryOneFour/components/evaluate/index.vue 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/views/strainReportLibrary/reportLibraryOneTWO/add.vue 98 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/views/strainReportLibrary/reportLibraryOneTWO/components/approval/index.vue 28 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/views/strainReportLibrary/reportLibraryOneTWO/components/evaluate/index.vue 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/views/strainReportLibrary/reportLibraryOneThree/add.vue 93 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/views/strainReportLibrary/reportLibraryOneThree/components/approval/index.vue 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/views/strainReportLibrary/reportLibraryOneThree/components/evaluate/index.vue 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
culture/src/components/AiEditor/index.vue
@@ -7,6 +7,8 @@
<script>
import { AiEditor } from "aieditor";
import 'aieditor/dist/style.css'
// import { customUploadRequest, getFullUrl } from '@/utils/utils'
import apiConfig from '@/utils/baseurl'
export default {
  name: 'AiEditor',
@@ -60,10 +62,13 @@
      },
      immediate: true
    },
    readOnly(newVal) {
      if (this.editor) {
        this.editor.setReadOnly(newVal)
      }
    readOnly: {
      handler(newVal) {
        if (this.editor) {
          this.editor.setReadOnly(newVal)
        }
      },
      immediate: true
    }
  },
  mounted() {
@@ -97,7 +102,107 @@
        // 禁用调整大小功能
        resize: false,
        // 添加唯一标识
        id: `editor-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`
        id: `editor-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
        // 自定义图片上传功能
        image: {
          uploadUrl: apiConfig.imgUrl,
          uploadHeaders: {
            Authorization: sessionStorage.getItem('token') || '',
            // ...headers
          },
          uploader: (file, uploadUrl, headers, formName = 'file') => {
            const formData = new FormData();
            formData.append('file', file);
            return new Promise((resolve, reject) => {
              fetch(apiConfig.imgUrl, {
                method: "post",
                headers: { 'Accept': 'application/json', ...headers },
                body: formData,
              }).then((resp) => resp.json())
                .then(json => {
                  let data = {
                    "errorCode": 0,
                    "data": {
                      "src": apiConfig.showImgUrl + json.msg,
                      "alt": "图片 alt"
                    }
                  }
                  resolve(data);
                }).catch((error) => {
                  // reject(error);
                })
            });
          },
        },
        video: {
          uploadUrl: apiConfig.imgUrl,
          uploadFormName: "video", //上传时的文件表单名称
          uploadHeaders: {
            Authorization: sessionStorage.getItem('token') || '',
          },
          uploader: (file, uploadUrl, headers, formName = 'file') => {
            const formData = new FormData();
            formData.append('file', file);
            return new Promise((resolve, reject) => {
              fetch(apiConfig.imgUrl, {
                method: "post",
                headers: { 'Accept': 'application/json', ...headers },
                body: formData,
              }).then((resp) => resp.json())
                .then(json => {
                  let data = {
                    "errorCode": 0,
                    "data": {
                      "src": apiConfig.showImgUrl + json.msg,
                      "poster": "http://your-domain.com/poster.jpg"
                    }
                  }
                  resolve(data);
                }).catch((error) => {
                  // reject(error);
                })
            });
          },
        },
        attachment: {
          uploadUrl: apiConfig.imgUrl,
          uploadFormName: "attachment", //上传时的文件表单名称
          // uploadHeaders: {
          //     "jwt": "xxxxx",
          //     "other": "xxxx",
          // },
          uploadHeaders: () => {
            return {
              Authorization: sessionStorage.getItem('token') || '',
            }
          },
          uploader: (file, uploadUrl, headers, formName = 'file') => {
            const formData = new FormData();
            formData.append('file', file);
            return new Promise((resolve, reject) => {
              fetch(apiConfig.imgUrl, {
                method: "post",
                headers: { 'Accept': 'application/json', ...headers },
                body: formData,
              }).then((resp) => resp.json())
                .then(json => {
                  let data = {
                    "errorCode": 0,
                    "data": {
                      "href": apiConfig.showImgUrl + json.msg,
                      "fileName": file.name
                    }
                  }
                  resolve(data);
                }).catch((error) => {
                  // reject(error);
                })
            });
          },
        },
      }
      try {
culture/src/components/SignatureCanvas.vue
@@ -1,27 +1,11 @@
<template>
  <el-dialog
    title="确认签字"
    :visible.sync="visible"
    :width="dialogWidth"
    :close-on-click-modal="false"
    custom-class="signature-dialog"
    append-to-body
    @close="$emit('update:visible', false)"
  >
  <el-dialog title="确认签字" :visible.sync="visible" :width="dialogWidth" :close-on-click-modal="false"
    custom-class="signature-dialog" append-to-body @close="$emit('update:visible', false)">
    <div class="signature-container">
      <div class="signature-content">
        <canvas
          ref="signatureCanvas"
          :width="canvasWidth"
          :height="canvasHeight"
          @mousedown="startDrawing"
          @mousemove="draw"
          @mouseup="stopDrawing"
          @mouseleave="stopDrawing"
          @touchstart="handleTouchStart"
          @touchmove="handleTouchMove"
          @touchend="stopDrawing"
        ></canvas>
        <canvas ref="signatureCanvas" :width="canvasWidth" :height="canvasHeight" @mousedown="startDrawing"
          @mousemove="draw" @mouseup="stopDrawing" @mouseleave="stopDrawing" @touchstart="handleTouchStart"
          @touchmove="handleTouchMove" @touchend="stopDrawing"></canvas>
      </div>
      <div class="signature-footer">
        <el-button type="default" @click="clearCanvas">重置</el-button>
@@ -32,6 +16,8 @@
</template>
<script>
import { customUploadRequest, getFullUrl } from '@/utils/utils'
import {editSignPicture} from './service'
export default {
  name: 'SignatureCanvas',
  props: {
@@ -91,11 +77,11 @@
      this.context.lineWidth = 2
      this.context.lineCap = 'round'
      this.context.lineJoin = 'round'
      // 清空画布并绘制虚线边框
      this.clearCanvas()
    },
    drawDashedBorder() {
      const ctx = this.context
      ctx.setLineDash([5, 5])
@@ -114,16 +100,16 @@
    draw(event) {
      if (!this.isDrawing) return
      const rect = this.$refs.signatureCanvas.getBoundingClientRect()
      const x = event.clientX - rect.left
      const y = event.clientY - rect.top
      this.context.beginPath()
      this.context.moveTo(this.lastX, this.lastY)
      this.context.lineTo(x, y)
      this.context.stroke()
      this.lastX = x
      this.lastY = y
      this.hasDrawn = true
@@ -141,17 +127,17 @@
    handleTouchMove(event) {
      event.preventDefault()
      if (!this.isDrawing) return
      const touch = event.touches[0]
      const rect = this.$refs.signatureCanvas.getBoundingClientRect()
      const x = touch.clientX - rect.left
      const y = touch.clientY - rect.top
      this.context.beginPath()
      this.context.moveTo(this.lastX, this.lastY)
      this.context.lineTo(x, y)
      this.context.stroke()
      this.lastX = x
      this.lastY = y
      this.hasDrawn = true
@@ -170,7 +156,7 @@
    confirmSignature() {
      const canvas = this.$refs.signatureCanvas
      const ctx = this.context
      // 校验是否签名
      if (this.isCanvasBlank()) {
        this.$message && this.$message.warning('请先确认签名')
@@ -178,24 +164,58 @@
      }
      // 保存当前画布内容
      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)
      // 创建一个临时画布来保存签名内容
      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', 'https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg')
      // 导出图片为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('上传签名失败');
        }
      });
    },
        // 新增: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 });
    },
    // 新增方法:判断画布是否为空白
@@ -215,6 +235,7 @@
  :deep(.el-dialog__body) {
    padding: 0;
  }
  :deep(.el-dialog) {
    margin-top: 10vh !important;
  }
@@ -223,12 +244,12 @@
.signature-container {
  background: #fff;
  border-radius: 4px;
  .signature-content {
    padding: 20px;
    display: flex;
    justify-content: center;
    canvas {
      border-radius: 4px;
      background: rgba(239, 248, 250, 1);
@@ -236,16 +257,17 @@
      height: 100%;
    }
  }
  .signature-footer {
    padding: 20px;
    border-top: 1px solid #dcdfe6;
    display: flex;
    justify-content: flex-end;
    gap: 12px;
    button{
    button {
      width: 150px;
    }
  }
}
</style>
</style>
culture/src/components/confirm-storage-dialog/index.vue
@@ -1,13 +1,6 @@
<template>
  <el-dialog
    :visible.sync="visible"
    title="签字确认"
    width="520px"
    @open="handleOpen"
    :close-on-click-modal="false"
    custom-class="record-detail-dialog"
    @close="handleClose"
  >
  <el-dialog :visible.sync="visible" title="签字确认" width="520px" @open="handleOpen" :close-on-click-modal="false"
    custom-class="record-detail-dialog" @close="handleClose">
    <div class="dialog-content">
      <div class="confirm-tip">
        {{ text }}
@@ -17,16 +10,11 @@
        <el-form-item required>
          <template #label>
            <span>{{ name }}</span>
            <el-button
              type="primary"
              class="sign-btn"
              @click="showSignature = true"
              >签名</el-button
            >
            <el-button type="primary" class="sign-btn" @click="showSignature = true">签名</el-button>
          </template>
          <div class="signature-area" :class="{ waiting: !form.signature }">
            <template v-if="form.signature">
              <img :src="form.signature" :alt="name" />
              <el-image :src="getFullUrl(form.signature)"  :preview-src-list="[getFullUrl(form.signature)]"  :alt="name" />
            </template>
            <template v-else>
              <span class="waiting-text">等待确认</span>
@@ -36,20 +24,17 @@
      </el-form>
    </div>
    <div class="footer-btns">
      <el-button @click="handleClose" style="margin-right: 16px"
        >取消</el-button
      >
      <el-button @click="handleClose" style="margin-right: 16px">取消</el-button>
      <el-button type="primary" @click="handleConfirm" :disabled="!form.signature">确认</el-button>
    </div>
    <signature-canvas
      :visible.sync="showSignature"
      @confirm="handleSignatureConfirm"
    />
    <signature-canvas :visible.sync="showSignature" @confirm="handleSignatureConfirm" />
  </el-dialog>
</template>
<script>
import SignatureCanvas from "@/components/SignatureCanvas.vue";
import { getFullUrl } from '@/utils/utils'
import { queryDetail } from '../service'
export default {
  name: "ConfirmStorageDialog",
  components: { SignatureCanvas },
@@ -68,7 +53,7 @@
    },
    handleSignature: {
      type: String,
      default:"",
      default: "",
    }
  },
  data() {
@@ -83,8 +68,22 @@
    };
  },
  methods: {
    getFullUrl(url) {
      if (url) {
        return getFullUrl(url);
      }
    },
    handleOpen() {
       this.form.signature = this.handleSignature;
      if (!this.handleSignature) {
        queryDetail().then(res => {
          console.log('///////////////////////');
          this.form.signature = res.signPicture
        })
      } else {
        this.form.signature = this.handleSignature;
      }
    },
    handleClose() {
      this.$emit("update:visible", false);
@@ -109,10 +108,12 @@
  color: #f5222d;
  font-size: 16px;
  margin-bottom: 24px;
  .danger {
    margin-left: 12px;
  }
}
.signature-area {
  height: 120px;
  width: 100%;
@@ -125,20 +126,24 @@
  overflow: hidden;
  padding: 0;
}
.signature-area.waiting {
  border-style: dashed;
  background: #fafafa;
}
.signature-area img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.waiting-text {
  color: #909399;
  font-size: 14px;
}
.sign-btn {
  height: 32px;
  border-radius: 4px;
@@ -147,11 +152,13 @@
  font-weight: 400;
  margin-left: 12px;
}
.footer-btns {
  display: flex;
  justify-content: center;
  padding: 24px;
  padding-top: 0;
  .el-button {
    width: 150px;
  }
culture/src/components/service.js
New file
@@ -0,0 +1,10 @@
import axios from '@/utils/request';
// 列表
export const editSignPicture = (data) => {
  console.log(data)
  return axios.post('/api/system/user/editSignPicture', { ...data })
}
export const queryDetail = (data) => {
  return axios.get('/system/user/queryDetail',)
}
culture/src/utils/baseurl.js
@@ -1,8 +1,9 @@
const apiConfig = {
    // 开发环境
    development: {
        baseURL: "http://192.168.110.34:8081",
        imgUrl: "",
        baseURL: "http://192.168.110.64:8081",
        imgUrl: "http://192.168.110.64:8081/open/file/upload",
        showImgUrl:'http://192.168.110.64:9998/'
    },
    // 生产环境
    production: {
culture/src/utils/utils.js
@@ -1,3 +1,6 @@
import axios from 'axios';
import apiConfig from './baseurl';
export const customRequest = (params) => {
  const { file, onSuccess, onError } = params;
  uploadObs(file)
@@ -7,4 +10,74 @@
    .catch((ret) => {
      onError();
    });
};
};
export const customUploadRequest = ({ file, onSuccess, onError, action, headers }) => {
  // 默认 action 为 apiConfig.imgUrl
  const uploadUrl = action || apiConfig.imgUrl;
  // 默认 headers 带上 Authorization
  const uploadHeaders = {
    Authorization: sessionStorage.getItem('token') || '',
    ...headers
  };
  const formData = new FormData();
  formData.append('file', file);
  axios.post(uploadUrl, formData, { headers: uploadHeaders })
    .then(res => {
      console.log('qweqweqweqw/////',res);
      try {
        onSuccess(res.data);
      } catch (e) {
        console.error('onSuccess error:', e);
      }
    })
    .catch(err => {
      try {
        onError(err, file);
      } catch (e) {
        console.error('onError error:', e);
      }
    });
};
export function getFullUrl(url) {
  if (!url) return '';
  if (/^https?:\/\//.test(url)) return url;
  return apiConfig.showImgUrl + url;
}
/**
 * 通用文件下载方法
 * @param {string} url 文件的下载地址(支持相对和绝对路径)
 * @param {string} name 下载保存的文件名(可选)
 */
export function downloadFileByUrl(url, name) {
  console.log('22222222222222222222',url)
  if (!url) return;
  // 处理相对路径
  const fullUrl = getFullUrl(url);
  console.log('fullUrl fullUrl',fullUrl)
  const a = document.createElement('a');
  a.href = fullUrl;
  if (name) {
    a.download = name;
  } else {
    a.download = '';
  }
  a.style.display = 'none';
  document.body.appendChild(a);
  a.click();
  document.body.removeChild(a);
}
/**
 * 在新标签页打开文件(预览或下载)
 * @param {string} url 文件地址(支持相对和绝对路径)
 */
export function openFileInNewTab(url) {
  if (!url) return;
  const fullUrl = getFullUrl(url);
  window.open(fullUrl, '_blank');
}
culture/src/views/deliveryAssessment/projectTeamIntegral/detail.vue
@@ -24,10 +24,18 @@
        <div class="integral-content">
            <div class="integral-content-box">
                <div class="integral-content-box-left">
                    <div @click="changeActiveItem(item)" class="integral-content-box-left-item"
                        :class="actionsLeftTab == item && 'activeItem'" v-for="item in 2" :key="item">
                        <div>{{ ['菌种工程师', '菌种实验员'][item - 1] }}</div>
                    <div
                        v-if="roleType === 4"
                        @click="changeActiveItem(2)"
                        class="integral-content-box-left-item activeItem"
                    >
                        <div>菌种实验员</div>
                    </div>
                    <template v-else>
                        <div @click="changeActiveItem(item)" class="integral-content-box-left-item" :class="actionsLeftTab == item && 'activeItem'" v-for="item in 2" :key="item">
                            <div>{{ ['菌种工程师', '菌种实验员'][item - 1] }}</div>
                        </div>
                    </template>
                </div>
                <div class="integral-content-box-right">
                    <div v-show="actionsLeftTab != 1" @wheel.prevent="handleWheel"
@@ -50,25 +58,25 @@
                            <div>{{ item.gainer }}</div>
                            <div>
                                <div>{{ item.situationOne }}{{
                                    actionsLeftTab === 2 ?
                                    actionsLeftTab == 2 ?
                                        (selectedExperimenter || {})[item.keys[0]] :
                                        detailData.detailEngineerVO[item.keys[0]]
                                        detailData.detailEngineerVO&&detailData.detailEngineerVO[item.keys[0]]
                                }}</div>
                                <div>{{ item.situationTwo }}{{
                                    actionsLeftTab === 2 ?
                                    actionsLeftTab == 2 ?
                                        (selectedExperimenter || {})[item.keys[1]] :
                                        detailData.detailEngineerVO[item.keys[1]]
                                        detailData.detailEngineerVO&&detailData.detailEngineerVO[item.keys[1]]
                                }}</div>
                            </div>
                            <div>{{
                                actionsLeftTab === 2 ?
                                actionsLeftTab == 2 ?
                                    (selectedExperimenter || {})[item.keys[2]] :
                                    detailData.detailEngineerVO[item.keys[2]]
                                    detailData.detailEngineerVO&&detailData.detailEngineerVO[item.keys[2]]
                            }}</div>
                            <div>{{
                                actionsLeftTab === 2 ?
                                actionsLeftTab == 2 ?
                                    (selectedExperimenter || {})[item.keys[3]] :
                                    detailData.detailEngineerVO[item.keys[3]]
                                    detailData.detailEngineerVO&&detailData.detailEngineerVO[item.keys[3]]
                            }}</div>
                        </div>
                    </div>
@@ -82,8 +90,9 @@
import { getDetailData } from './service'
export default {
    data() {
        const roleType = JSON.parse(sessionStorage.getItem('userInfo')).roleType * 1;
        return {
            actionsLeftTab: 1,
            actionsLeftTab: roleType === 4 ? 2 : 1,
            activeNameTab: '',
            craftList: [
                {
@@ -114,7 +123,8 @@
                },
            ],//菌种实验员
            detailData: {},
            selectedExperimenter: null
            selectedExperimenter: null,
            roleType,
        }
    },
    computed: {
culture/src/views/middleground/index.vue
@@ -29,7 +29,7 @@
        <!-- 日历 -->
        <div class="calendar-section">
          <div class="title-canlender">日历</div>
          <a-calendar @panelChange="onPanelChange"/>
          <a-calendar @panelChange="onPanelChange" />
        </div>
        <!-- 待办事项 -->
@@ -37,20 +37,20 @@
          <div class="title">待办事项</div>
          <!-- 待办事项列表将放置在这里 -->
          <div class="todo-list">
            <div class="todo-item" v-for="(item,index) in list" :key="index" @click.stop="toDetail(item)">
            <div class="todo-item" v-for="(item, index) in list" :key="index" @click.stop="toDetail(item)">
              <div class="todo-details">
                <div class="notice-card">
                  <div class="todo-icon"></div>
                  <div class="red-notice" v-if="item.status==1"></div>
                  <div class="red-notice" v-if="item.status == 1"></div>
                </div>
                <span class="todo-title" :title="item.noticeContent || ''">{{item.noticeContent||''}}</span>
                <span class="todo-title" :title="item.noticeContent || ''">{{ item.noticeContent || '' }}</span>
              </div>
              <div class="todo-meta">
                <div class="me"></div>
                <span class="todo-submitter">提交人: {{item.commitName||''}}</span>
                <span class="todo-submitter">提交人: {{ item.commitName || '' }}</span>
                <div class="time"></div>
                <span class="todo-submitter">{{item.createTime||''}}</span>
                <span class="todo-submitter">{{ item.createTime || '' }}</span>
              </div>
            </div>
          </div>
@@ -64,7 +64,7 @@
<script>
import { loginReq } from './service'
import { Calendar } from "ant-design-vue";
import { getList,read } from "./service";
import { getList, read } from "./service";
import HeaderNav from '../../layouts/components/HeaderNav.vue'
export default {
  name: 'Login',
@@ -84,40 +84,40 @@
      date: new Date(),
      viewWidth: '',
      scale: 1,
      list:[],
      list: [],
      // 审批人
      moduleList2: [
      {
        {
          text: '菌种库',
          icon: require('../../assets/login/img1.png'),
          path: '/strain'
          path: '/strain-library/strain-library-manage'
        },
        {
          text: '菌种报告库',
          icon: require('../../assets/login/img4.png'),
          path: '/deliveryAssessment'
          path: '/strainReportLibrary/reportLibraryOne'
        },
      ],
      // 工艺工程师
      // 菌种报告库
      moduleList3: [
      {
        {
          text: '菌种库',
          icon: require('../../assets/login/img1.png'),
          path: '/strain'
          path: '/strain-library/strain-library-manage'
        },
        {
          text: '菌种报告库',
          icon: require('../../assets/login/img4.png'),
          path: '/deliveryAssessment'
          path: '/strainReportLibrary/reportLibraryOne'
        },
      ],
      // 实验员
      moduleList5: [
      {
        {
          text: '菌种库',
          icon: require('../../assets/login/img1.png'),
          path: '/strain'
          path: '/strain-library/strain-library-manage'
        },
      ],
      // 超级管理员
@@ -149,7 +149,7 @@
  created() {
    // 初始化时检查窗口大小
    this.handleResize();
    getList().then(res=>{
    getList().then(res => {
      this.list = res
    })
@@ -172,41 +172,41 @@
      this.windowWidth = window.innerWidth;
    },
    handleModuleClick(item) {
     if(item.path){
      this.$router.push({
        path: item.path,
      })
     }
      if (item.path) {
        this.$router.push({
          path: item.path,
        })
      }
    },
    toDetail(item){
      read({id:item.id}).then(res=>{
        let urlList=['/strainReportLibrary/reportLibraryOne','/strainReportLibrary/reportLibraryOne','/strain-library/strain-library-manage'
          ,'/strain/pedigree-vhart','/strain/breeding-record','/strain/validation/primitive-cell',
          '/strainReportLibrary/reportLibraryOne','/strainReportLibrary/reportLibraryOne','/deliveryAssessment/projectTeamIntegral',
    toDetail(item) {
      // read({ id: item.id }).then(res => {
      // })
      let urlList = ['/strainReportLibrary/reportLibraryOne', '/strainReportLibrary/reportLibraryOne', '/strain-library/strain-library-manage'
          , '/strain/pedigree-vhart', '/strain/breeding-record', '/strain/validation/primitive-cell',
          '/strainReportLibrary/reportLibraryOne', '/strainReportLibrary/reportLibraryOne', '/deliveryAssessment/projectTeamIntegral',
        ]
        let url=urlList[item.noticeType *1]
        let url = urlList[item.noticeType * 1 - 1]
        this.$router.push({
          path: url,
        });
      })
    }
  },
  computed: {
    currentModuleList() {
      const userInfo = JSON.parse(sessionStorage.getItem('userInfo') || '{}');
      const userType = userInfo.userType;
      const userType = userInfo.roleType * 1;
      console.log('userType', userType)
      switch (userType) {
        case 2: // 审批人
          return this.moduleList2;
        case 3: // 工艺工程师
        case 3: //菌种工程师
          return this.moduleList3;
        // case 4: // 化验师
        //   return this.moduleList4;
        case 5: // 实验员
        case 4: // 实验员
          return this.moduleList5;
        case 6: // 超级管理员
        case 1: // 超级管理员
          return this.moduleList6;
        default:
          return this.moduleList6; // 默认返回化验师的模块列表
@@ -217,11 +217,11 @@
    },
    currentModuleLayout() {
      const userInfo = JSON.parse(sessionStorage.getItem('userInfo') || '{}');
      const userType = userInfo.userType;
      const userType = userInfo.roleType * 1;
      switch (userType) {
        // case 2: // 审批人
        //   return 'layout-4';
        case 4: // 实验员
          return 'layout-1';
        // case 3: // 工艺工程师
        //   return 'layout-4';
        // // case 4: // 化验师
@@ -364,6 +364,19 @@
          grid-template-rows: auto;
        }
      }
      // 1个模块布局(居中显示)
      &.layout-1 {
        display: flex;
        justify-content: center;
        align-items: center;
        min-height: 320px;
        .module-item {
          width: 320px;
          height: 320px;
          margin: 0;
        }
      }
    }
    .module-item {
@@ -469,6 +482,7 @@
        border-radius: 1px;
      }
    }
    // 根据布局调整图标和文字大小
    .layout-5 & {
@@ -547,6 +561,14 @@
        .module-text {
          margin-top: 25px;
        }
      }
    }
    &.layout-1.mobile-layout {
      .module-item {
        width: 100%;
        max-width: 320px;
        height: 320px;
      }
    }
  }
@@ -830,6 +852,7 @@
            &.ant-fullcalendar-today .ant-fullcalendar-date {
              // border-color: #009688;
            }
            &.ant-fullcalendar-last-month-cell {
              .ant-fullcalendar-value {
                // color: #ffffff !important;
@@ -975,7 +998,7 @@
        .todo-meta {
          display: flex;
          align-items: center;
          min-width:260px;
          min-width: 260px;
        }
        .time {
culture/src/views/middleground/service.js
@@ -2,7 +2,7 @@
// 登录
export const getList = (data) => {
    return axios.get('/t-notice/list', { ...data })
    return axios.get('/notice/list', { ...data })
}
export const read = (data) => {
    return axios.get(`/open/t-notice/read?id=${data.id}`, { ...data })
culture/src/views/pedigree-chart/add.vue
@@ -198,7 +198,6 @@
        return;
      }
      const nodeModel = this.selectedNode;
      console.log(nodeModel);
      if (nodeModel.label === "母代") {
        this.$refs.parentForm.openInitData({
@@ -313,7 +312,6 @@
    reloadData() {
      this.getList();
      getDetail({ id: this.$route.query.id }).then((res) => {
        console.log(JSON.stringify(res.pedigreeChartParentAllDetailDTO));
        const nodeMap = new Map();
        const calculateLevel = (node) => {
@@ -413,7 +411,6 @@
      });
    },
    addNodeSign(value, type) {
      console.log('/////8415454545',value);
      this.nodeData = value;
      this.nodeType = type;
      // this.$refs.addAncestor.closeDialog()
@@ -475,7 +472,6 @@
            },
            one: buildTree(this.graphData.nodes, this.graphData.edges),
          };
          console.log("提交的数据:", baseData, this.graphData);
          add(baseData).then((res) => {
            if (res.code == 200) {
              this.$message.success("保存成功");
@@ -487,7 +483,6 @@
    },
    handleDraft() {
      // 实现存草稿逻辑
      console.log("save draft", this.form);
    },
    handleCancel() {
      this.$router.back();
@@ -881,8 +876,6 @@
          },
        })),
      };
      console.log('//////////////',processedData);
      
      // 更新图表数据
@@ -1193,7 +1186,6 @@
    handleAddPlan(value) {
      if (value.formStatus == 'edit') {
        console.log(value, 'params');
        updateChild(value).then((res) => {
          if (res.code === 200) {
            this.$message.success("操作成功");
@@ -1277,7 +1269,6 @@
          (n) => n.nodeId === nodeModel.nodeId
        );
        if (this.graphData.nodes[nodeIndex - 1].label === "子代") {
          console.log(123);
          this.$refs.addSublevelPlan.openInitData({
            strainSourceStart: this.form.strainSourceStart,
@@ -1288,7 +1279,6 @@
            status: "detail",
          });
        } else {
          console.log(123);
          this.$refs.planForm.openInitData({
            strainSourceStart: this.form.strainSourceStart,
culture/src/views/pedigree-chart/addProgenitor.vue
@@ -26,7 +26,7 @@
        </div>
      </div>
      <div class="card" style="margin-top: 30px; display: block" v-if="roleType != 4">
        <div class="title" style="padding: 25px 0">菌种传代生产谱系图</div>
        <div class="title" style="padding: 25px 0">待确认入库菌种列表</div>
        <Table
          :height="null"
          :total="total"
@@ -58,10 +58,10 @@
        <div class="header">
          <div class="title">菌种传代生产谱系图</div>
          <div class="option-btn">
            <el-button type="primary" class="el-icon-plus" @click="addNode">
            <el-button type="primary" class="el-icon-plus" @click="addNode" v-if="$route.query.type != 1" >
              新增</el-button
            >
            <el-button type="primary" @click="setGenerationPlan"
            <el-button type="primary" v-if="$route.query.type != 1" @click="setGenerationPlan"
              >设置传代计划数</el-button
            >
            <el-button type="primary" @click="showDetail">详情</el-button>
@@ -427,7 +427,6 @@
    },
    handleDraft() {
      // 实现存草稿逻辑
      console.log("save draft", this.form);
    },
    handleCancel() {
      this.$router.back();
@@ -789,10 +788,6 @@
        this.$message.warning("该节点已经存在传代计划数节点");
        return;
      }
      console.log({
        ...nodeModel.data,
        label: nodeModel.label,
      });
      this.$refs.planForm.openInitData({
        ...nodeModel.data,
culture/src/views/pedigree-chart/components/AddSublevelForm.vue
@@ -1,11 +1,6 @@
<template>
  <el-dialog
    :title="dialogTitle"
    :visible.sync="dialogVisible"
    width="40%"
    @close="closeDialog"
    :close-on-click-modal="false"
  >
  <el-dialog :title="dialogTitle" :visible.sync="dialogVisible" width="40%" @close="closeDialog"
    :close-on-click-modal="false">
    <el-form :model="form" :rules="rules" ref="form" label-position="top">
      <el-row :gutter="20">
        <el-col :span="10">
@@ -30,20 +25,12 @@
        </el-col>
        <el-col :span="10">
          <el-form-item label="接种菌种编号" prop="strainCode">
            <el-input
              :disabled="dialogTitle.includes('详情')"
              v-model="form.strainCode"
              placeholder="请输入"
            ></el-input>
            <el-input :disabled="dialogTitle.includes('详情')" v-model="form.strainCode" placeholder="请输入"></el-input>
          </el-form-item>
        </el-col>
        <el-col :span="10">
          <el-form-item label="接种菌种名称" prop="strainName">
            <el-input
              :disabled="dialogTitle.includes('详情')"
              v-model="form.strainName"
              placeholder="请输入"
            ></el-input>
            <el-input :disabled="dialogTitle.includes('详情')" v-model="form.strainName" placeholder="请输入"></el-input>
          </el-form-item>
        </el-col>
      </el-row>
@@ -60,17 +47,13 @@
      <el-row :gutter="20">
        <el-col :span="10">
          <el-form-item v-if="form.status === 2" label="废弃原因说明" required>
            <el-input
              :disabled="dialogTitle.includes('详情')"
              v-model="form.discardReason"
              placeholder="请输入"
            ></el-input>
            <el-input :disabled="dialogTitle.includes('详情')" v-model="form.discardReason" placeholder="请输入"></el-input>
          </el-form-item>
        </el-col>
      </el-row>
      <el-row :gutter="20">
        <el-col :span="10">
          <el-form-item label="菌种入库时间" prop="confirmTime" >
          <el-form-item label="菌种入库时间" prop="confirmTime">
            <el-input disabled v-model="form.confirmTime"></el-input>
          </el-form-item>
        </el-col>
@@ -78,12 +61,14 @@
      <el-row v-if="!dialogTitle.includes('新增')" :gutter="20">
        <el-col v-if="form.vaccinateSignature" :span="10">
          <el-form-item label="接种操作人签字">
            <el-image :src="form.vaccinateSignature" />
            <el-image :src="getFullUrl(form.vaccinateSignature)"
              :preview-src-list="[getFullUrl(form.vaccinateSignature)]" />
          </el-form-item>
        </el-col>
        <el-col v-if="form.preserveSignature" :span="10">
          <el-form-item label="菌种保藏人签字">
            <el-image :src="form.preserveSignature" />
            <el-image :src="getFullUrl(form.preserveSignature)"
              :preview-src-list="[getFullUrl(form.preserveSignature)]" />
          </el-form-item>
        </el-col>
      </el-row>
@@ -95,6 +80,7 @@
</template>
<script>
import { getFullUrl } from '@/utils/utils';
import moment from 'moment';
export default {
  data() {
@@ -135,7 +121,14 @@
    };
  },
  methods: {
    getFullUrl(url) {
      if (url) {
        return getFullUrl(url);
      }
    },
    openInitData(value) {
      this.dialogTitle = value.title;
      // 获取用户信息
      const userInfo = JSON.parse(sessionStorage.getItem("userInfo") || "{}");
@@ -148,8 +141,7 @@
      ).padStart(2, "0")}:${String(now.getMinutes()).padStart(2, "0")}:${String(
        now.getSeconds()
      ).padStart(2, "0")}`;
      console.log(value);
      this.form = {
        parentId: value.parentId,
        ...this.form,
@@ -157,15 +149,15 @@
        formStatus: value.formStatus,
        strainCode1: value.strainCode1,
        strainName1: value.strainName1,
        thisName: userInfo.nickName || "",
        thisName: value.createBy ? value.createBy : userInfo.nickName,
        thisTime: value.form.vaccinateTime
          ? value.form.vaccinateTime
          : formatTime,
          confirmTime: value.confirmTime ? moment(value.confirmTime).format('YYYY-MM-DD HH:mm:ss') : moment().format('YYYY-MM-DD HH:mm:ss')
        confirmTime: value.confirmTime ? moment(value.confirmTime).format('YYYY-MM-DD HH:mm:ss') : moment().format('YYYY-MM-DD HH:mm:ss')
      };
      this.dialogVisible = true;
    },
    close(){
    close() {
      this.dialogVisible = false;
    },
    closeDialog() {
culture/src/views/pedigree-chart/components/AddSublevelPlan.vue
@@ -107,7 +107,6 @@
            this.planDialogVisible = true
        },
        closeDialog() {
            console.log('qqweeee/////////////');
            
            this.planDialogVisible = false
            // 重置表单数据
culture/src/views/pedigree-chart/components/ParentForm.vue
@@ -47,14 +47,14 @@
        </el-col>
        <el-col v-if="planForm.vaccinateSignature && planForm.formStatus == 'detail'" :span="10">
            <el-form-item label="接种操作人签字">
              <el-image :src="planForm.vaccinateSignature" />
              <el-image :src="getFullUrl(planForm.vaccinateSignature)" :preview-src-list="[getFullUrl(planForm.vaccinateSignature)]" />
            </el-form-item>
          </el-col>
          <el-col v-if="planForm.preserveSignature && planForm.formStatus == 'detail'" :span="10">
            <el-form-item label="菌种保藏人签字">
              <el-image :src="planForm.preserveSignature" />
              <el-image :src="getFullUrl(planForm.preserveSignature)" :preview-src-list="[getFullUrl(planForm.preserveSignature)]" />
            </el-form-item>
          </el-col>
          </el-col>
      </el-row>
    </el-form>
    <div v-if="planForm.formStatus == 'add'" class="dialog-footer">
@@ -63,6 +63,7 @@
  </el-dialog>
</template>
<script>
import {getFullUrl} from '@/utils/utils'
export default {
  data() {
    return {
@@ -91,7 +92,13 @@
    };
  },
  methods: {
    getFullUrl(url) {
      if (url) {
        return getFullUrl(url);
      }
    },
    openInitData(value) {
      this.planForm = {
        ...this.planForm,
        ...value,
@@ -117,7 +124,6 @@
    handleSubmit() {
      this.$refs.planForm.validate((valid) => {
        if (valid) {
          console.log(this.planForm, "22");
          this.$emit("addNodeSign", this.planForm, 1);
        }
culture/src/views/pedigree-chart/components/PlanForm.vue
@@ -69,7 +69,6 @@
    },
    methods: {
        openInitData(value) {
            console.log(value);
            
            this.planForm = {
                ...this.planForm,
culture/src/views/pedigree-chart/index.vue
@@ -56,8 +56,8 @@
        <el-table-column prop="createBy" label="创建人"></el-table-column>
        <el-table-column label="操作" width="250">
          <template slot-scope="scope">
            <el-button type="text" @click="handleDetail(scope.row)">详情</el-button>
            <el-button type="text" v-if="Number(roleType) == 1" @click="handleDetail(scope.row)">编辑</el-button>
            <el-button style="margin-right: 10px" type="text" @click="handleDetail(scope.row)">详情</el-button>
            <el-button style="margin-right: 10px" type="text" v-if="Number(roleType) == 1" @click="handleDetail(scope.row)">编辑</el-button>
            <!-- 菌种超级管理员 -->
            <el-button type="text" v-if="Number(roleType) == 1" @click="handleDelete(scope.row)">删除</el-button>
          </template>
@@ -162,25 +162,21 @@
    },
    handleApproveSubmit(data) {
      // 处理审批通过
      console.log("审批通过:", data);
      this.approvalDialogVisible = false;
      this.$message.success("审批通过成功");
      this.getTableData();
    },
    handleRejectSubmit(data) {
      // 处理审批驳回
      console.log("审批驳回:", data);
      this.approvalDialogVisible = false;
      this.$message.success("审批驳回成功");
      this.getTableData();
    },
    handleRevokeApprove(row) {
      // 实现撤销审批逻辑
      console.log("撤销审批数据:", row);
    },
    handleEdit(row) {
      // 实现编辑逻辑
      console.log("编辑数据:", row);
    },
    handleDelete(row) {
      // 实现删除逻辑
culture/src/views/pedigree-chart/progenitorComponents/AddAncestor.vue
@@ -127,12 +127,13 @@
      <el-row v-if="planForm.formStatus == 'detail'" :gutter="20">
        <el-col v-if="planForm.vaccinateSignature" :span="10">
          <el-form-item label="接种操作人签字">
            <el-image :src="planForm.vaccinateSignature" />
            <el-image :src="getFullUrl(planForm.vaccinateSignature)" :preview-src-list="[getFullUrl(planForm.vaccinateSignature)]" />
          </el-form-item>
        </el-col>
        <el-col v-if="planForm.preserveSignature" :span="10">
          <el-form-item label="菌种保藏人签字">
            <el-image :src="planForm.preserveSignature" />
            <el-image :src="getFullUrl(planForm.preserveSignature)" :preview-src-list="[getFullUrl(planForm.preserveSignature)]" />
          </el-form-item>
        </el-col>
      </el-row>
@@ -144,6 +145,7 @@
</template>
<script>
import moment from 'moment'
import { getFullUrl } from "@/utils/utils";
export default {
  name: "AddAncestor",
  data() {
@@ -179,8 +181,12 @@
    };
  },
  methods: {
    getFullUrl(url){
      if(url){
        return getFullUrl(url);
      }
    },
    openInitData(value) {
      console.log('45646545645',value)
      // 赋值当前时间
      const now = new Date();
      const pad = (n) => n.toString().padStart(2, "0");
culture/src/views/pedigree-chart/progenitorComponents/AddSublevelForm.vue
@@ -61,12 +61,12 @@
      <el-row v-if="!dialogTitle.includes('新增')" :gutter="20">
        <el-col v-if="form.vaccinateSignature" :span="10">
          <el-form-item label="接种操作人签字">
            <el-image :src="form.vaccinateSignature" />
            <el-image :src="getFullUrl(form.vaccinateSignature)"  :preview-src-list="[getFullUrl(form.vaccinateSignature)]" />
          </el-form-item>
        </el-col>
        <el-col v-if="form.preserveSignature" :span="10">
          <el-form-item label="菌种保藏人签字">
            <el-image :src="form.preserveSignature" />
            <el-image :src="getFullUrl(form.preserveSignature)"  :preview-src-list="[getFullUrl(form.vaccinateSignature)]" />
          </el-form-item>
        </el-col>
      </el-row>
@@ -78,6 +78,7 @@
</template>
<script>
import moment from 'moment'
import { getFullUrl } from "@/utils/utils";
export default {
  data() {
    return {
@@ -114,11 +115,14 @@
    };
  },
  methods: {
    getFullUrl(url) {
      if (url) {
        return getFullUrl(url);
      }
    },
    openInitData(value) {
      console.log(value);
      this.dialogTitle = value.title;
      // 获取用户信息
      console.log('qweqwwqr4qw4rqw854',sessionStorage.getItem("userInfo"));
      const userInfo = JSON.parse(sessionStorage.getItem("userInfo") || "{}");
    
      
@@ -131,25 +135,22 @@
      ).padStart(2, "0")}:${String(now.getMinutes()).padStart(2, "0")}:${String(
        now.getSeconds()
      ).padStart(2, "0")}`;
      console.log('qweqweqwe///////', value)
      this.form = {
        ...this.form,
        ...value.form,
        formStatus: value.formStatus || '',
        thisName: userInfo.nickName || "",
        thisName: value.form.createBy ? value.form.createBy : userInfo.nickName,
        thisTime: value.form && value.form.vaccinateTime
          ? value.form && value.form.vaccinateTime
          : formatTime,
        type: 1,
        confirmTime: value.confirmTime ? moment(value.confirmTime).format('YYYY-MM-DD HH:mm:ss') : moment().format('YYYY-MM-DD HH:mm:ss')
      };
      console.log('485484848', this.form);
      this.$forceUpdate()
      this.dialogVisible = true;
    },
    closeDialog() {
      console.log('78978998789798');
      this.dialogVisible = false;
    },
    handleSubmit() {
culture/src/views/pedigree-chart/service.js
@@ -53,3 +53,9 @@
export const deleteProgenitor = (params) => {
  return axios.delete('/open/t-pedigree-chart/deleteProgenitor', { params })
}
// 获取用户详情
export const queryDetail = (data) => {
  return axios.get('/system/user/queryDetail',)
}
culture/src/views/strain-library/breeding-record/SlantRecordDialog.vue
@@ -26,20 +26,45 @@
            @blur="handleDescBlur(row)" />
        </template>
      </el-table-column>
      <el-table-column prop="images" label="拍照上传" width="180">
      <el-table-column prop="images" label="拍照上传" width="160">
        <template #default="{ row }">
          <el-upload :file-list="row.images" :disabled="roleType!=4 || row.uploading" list-type="picture-card"
            :on-preview="file => handlePreview(row, file)"
            :on-remove="(file, fileList) => handleRemove(row, file, fileList)"
            :on-success="(res, file, fileList) => handleUpload(row, file, fileList)"
            :before-upload="file => beforeUpload(file, row)"
            action="#" class="mini-upload">
            <template v-if="row.uploading">
              <i class="el-icon-loading"></i>
            </template>
            <template v-else>
              <i class="el-icon-plus"></i>
            </template>
          <el-upload
            class="custom-upload-row"
            :ref="'uploadRef' + row.index"
            action="#"
            :file-list="[]"
            :http-request="options => rowHandleUpload(options, row)"
            :before-upload="file => rowBeforeUpload(file, row)"
            :show-file-list="false"
            :disabled="roleType!=4 || row.uploading"
            accept="image/*"
            :auto-upload="true"
          >
            <div class="upload-flex-box">
              <div
                v-for="(img, idx) in row.images"
                :key="img.uid || img.url"
                class="upload-thumb"
                @click.stop="handlePreview(row, img)"
              >
                <el-image
                  :src="img.url"
                  :preview-src-list="row.images.map(i => i.url)"
                  :initial-index="idx"
                  fit="cover"
                  style="width: 100%; height: 100%;"
                  @click.stop.native="handlePreview(row, img)"
                />
                <i class="el-icon-delete" v-if="roleType==4" @click.stop="rowHandleRemove(row, img, row.images.filter(f => f !== img))"></i>
              </div>
              <div
                v-if="roleType==4"
                class="upload-thumb upload-btn"
                @click.stop="triggerUpload(row.index)"
              >
                <i class="el-icon-plus"></i>
              </div>
            </div>
          </el-upload>
        </template>
      </el-table-column>
@@ -57,6 +82,7 @@
<script>
import moment from 'moment'
import { customUploadRequest, getFullUrl } from '@/utils/utils'
export default {
  name: 'SlantRecordDialog',
  props: {
@@ -142,7 +168,18 @@
      }
    },
    visible(val) {
      if (!val) {
      if (val) {
        if (!this.editData) {
          this.reset()
          this.tableData = Array.from({ length: 10 }, (_, i) => ({
            index: i + 1,
            desc: '',
            time: '',
            images: [],
            uploading: false
          }))
        }
      } else {
        this.reset()
      }
    }
@@ -197,23 +234,43 @@
    handleClose() {
      this.$emit('update:visible', false)
    },
    beforeUpload(file, row) {
      row.uploading = true
      return new Promise(resolve => {
        const reader = new FileReader()
        reader.onload = e => {
          resolve()
    rowBeforeUpload(file, row) {
      // 可做校验,暂时直接允许
      return true;
    },
    rowHandleUpload(options, row) {
      row.uploading = true;
      const { file, onSuccess, onError } = options;
      customUploadRequest({
        file,
        onSuccess: (res) => {
          if (res.code === 200) {
            const fileObj = {
              name: file.name,
              url: getFullUrl(res.msg || res.data || ''),
              uid: new Date().getTime()
            };
            if (!Array.isArray(row.images)) this.$set(row, 'images', []);
            row.images.push(fileObj);
            this.$set(row, 'images', [...row.images]);
            this.$message.success('文件上传成功');
            onSuccess(res);
          } else {
            this.$message.error(res.msg || '文件上传失败');
            onError(res);
          }
          row.uploading = false;
        },
        onError: (err) => {
          this.$message.error('文件上传失败');
          row.uploading = false;
          onError(err);
        }
        reader.readAsDataURL(file)
      })
      });
    },
    handleUpload(row, file, fileList) {
      row.images = fileList.map(f => ({ ...f, url: f.url || URL.createObjectURL(f.raw) }))
      row.uploading = false
    },
    handleRemove(row, file, fileList) {
      row.images = fileList
      row.uploading = false
    rowHandleRemove(row, file, fileList) {
      row.images = fileList;
      row.uploading = false;
    },
    handlePreview(row, file) {
      this.previewImg = file.url
@@ -224,6 +281,13 @@
      if (row.desc) {
        row.time = this.getNowTime()
        this.$forceUpdate()
      }
    },
    triggerUpload(index) {
      const uploadRef = this.$refs['uploadRef' + index];
      if (uploadRef && uploadRef.$el) {
        const input = uploadRef.$el.querySelector('input[type=file]');
        if (input) input.click();
      }
    }
  },
@@ -240,52 +304,44 @@
  width: 100% !important;
}
::v-deep(.mini-upload){
::v-deep(.custom-upload-row .upload-flex-box) {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  max-width: 140px;
}
::v-deep(.el-upload--picture-card) {
  width: 40px !important;
  height: 40px !important;
  line-height: 40px !important;
}
::v-deep(.mini-upload .el-upload-list--picture-card .el-upload-list__item) {
  width: 40px !important;
  height: 40px !important;
}
::v-deep(.mini-upload .el-upload-list--picture-card .el-upload-list__item-thumbnail) {
  width: 40px !important;
  height: 40px !important;
  object-fit: cover;
}
::v-deep(.mini-upload .el-upload-list--picture-card .el-upload-list__item-preview),
::v-deep(.mini-upload .el-upload-list--picture-card .el-upload-list__item-delete) {
  width: 18px;
  height: 18px;
  font-size: 14px;
}
::v-deep(.el-upload--picture-card) {
  width: 40px !important;
  height: 40px !important;
  line-height: 40px !important;
.upload-thumb {
  width: 40px;
  height: 40px;
  position: relative;
  border: 1px solid #eee;
  border-radius: 4px;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
}
::v-deep(.mini-upload .el-upload--picture-card i.el-icon-plus) {
  font-size: 18px;
  /* 缩小icon */
.upload-thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.upload-thumb .el-icon-delete {
  position: absolute;
  top: 0px;
  right: 0px;
  font-size: 14px;
  color: #f56c6c;
  background: #fff;
  border-radius: 50%;
  cursor: pointer;
}
.upload-btn {
  cursor: pointer;
  background: #fafafa;
  color: #999;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 100%;
}
</style>
culture/src/views/strain-library/breeding-record/add.vue
@@ -115,8 +115,8 @@
          <el-table-column prop="separateBacterialColoniesCode" label="分离菌落编号" />
          <el-table-column prop="handleSignature" label="接种操作人签字">
            <template slot-scope="scope">
              <el-image :preview-src-list="[scope.row.handleSignature]" style="width: 40px;"
                :src="scope.row.handleSignature" alt="操作人签字" />
              <el-image :preview-src-list="[getFullUrl(scope.row.handleSignature)]" style="width: 120px;height: 48px;"
                :src="getFullUrl(scope.row.handleSignature)" alt="操作人签字" />
            </template>
          </el-table-column>
          <el-table-column prop="createTime" label="操作时间" />
@@ -174,8 +174,8 @@
          <el-table-column prop="handleName" label="接种操作人" />
          <el-table-column prop="handleSignature" label="接种操作人签字">
            <template slot-scope="scope">
              <el-image v-if="scope.row.handleSignature" :preview-src-list="[scope.row.handleSignature]"
                style="width: 40px;" :src="scope.row.handleSignature" alt="操作人签字" />
              <el-image v-if="scope.row.handleSignature"  :preview-src-list="[getFullUrl(scope.row.handleSignature)]"
                style="width: 120px;height: 48px;" :src="getFullUrl(scope.row.handleSignature)" alt="操作人签字" />
            </template>
          </el-table-column>
          <el-table-column prop="handleTime" label="接种操作时间" />
@@ -191,8 +191,8 @@
          </el-table-column>
          <el-table-column prop="preserveSignature" label="菌种保藏人签字">
            <template slot-scope="scope">
              <el-image v-if="scope.row.preserveSignature" :preview-src-list="[scope.row.preserveSignature]"
                style="width: 40px;" :src="scope.row.preserveSignature" alt="操作人签字" />
              <el-image v-if="scope.row.preserveSignature" :preview-src-list="[getFullUrl(scope.row.preserveSignature)]"
                style="width: 120px;height: 48px;" :src="getFullUrl(scope.row.preserveSignature)" alt="操作人签字" />
            </template>
          </el-table-column>
          <el-table-column prop="preserveTime" label="入库保藏/废弃时间" />
@@ -228,15 +228,15 @@
          <el-table-column prop="preserveCode" label="保藏菌种编号" />
          <el-table-column prop="handleSignature" label="菌种操作人签字">
            <template slot-scope="scope">
              <el-image v-if="scope.row.handleSignature" :preview-src-list="[scope.row.handleSignature]"
                style="width: 40px;" :src="scope.row.handleSignature" alt="签字" />
              <el-image v-if="scope.row.handleSignature" :preview-src-list="[getFullUrl(scope.row.handleSignature)]"
                style="width: 120px;height: 48px;" :src="getFullUrl(scope.row.handleSignature)" alt="签字" />
            </template>
          </el-table-column>
          <el-table-column prop="handleTime" label="操作人" />
          <el-table-column prop="preserveSignature" label="菌种保藏人签字">
            <template slot-scope="scope">
              <el-image v-if="scope.row.preserveSignature" :preview-src-list="[scope.row.preserveSignature]"
                style="width: 40px;" :src="scope.row.preserveSignature" alt="签字" />
              <el-image v-if="scope.row.preserveSignature" :preview-src-list="[getFullUrl(scope.row.preserveSignature)]"
                style="width: 120px;height: 48px;" :src="getFullUrl(scope.row.preserveSignature)" alt="签字" />
            </template>
          </el-table-column>
          <el-table-column prop="preserveTime" label="保藏时间" />
@@ -266,9 +266,9 @@
        <ConfirmPreserveDialog :visible.sync="showConfirmPreserveDialog" :editData="editPreserveData"
          @confirm="handleConfirmPreserveSubmit" />
        <div class="end-btn" style="margin-top: 20px;"
          v-if="($route.query.isDetail && [3].includes(roleType)) || !$route.query.isDetail && [1, 3, 4].includes(roleType)">
          <el-button type="primary" v-if="[1, 4].includes(roleType)" @click="handleSubmit(1)">提交</el-button>
          <el-button v-if="!$route.query.isDetail" type="primary" @click="handleSubmit(3)">同步给保藏人</el-button>
          v-if="($route.query.isDetail && [4, 3].includes(roleType)) || !$route.query.isDetail && [1, 3, 4].includes(roleType)">
          <el-button type="primary" v-if="[1,4].includes(roleType)" @click="handleSubmit(1)">提交</el-button>
          <el-button v-if="[1,4].includes(roleType)" type="primary" @click="handleSubmit(3)">同步给保藏人</el-button>
          <el-button v-if="!$route.query.isDetail" type="default" @click="handleSubmit(2)">存草稿</el-button>
        </div>
      </el-form>
@@ -283,7 +283,7 @@
import PreserveStrainRecordDialog from "./preserve-strain-record-dialog.vue";
import ConfirmStorageDialog from "./confirm-storage-dialog.vue";
import ConfirmPreserveDialog from "./confirm-preserve-dialog.vue";
import {getFullUrl} from "@/utils/utils";
import { add, detail, edit, vaccinationSlopesConfirm, culturePreservationsConfirm } from './service'
export default {
  components: {
@@ -413,6 +413,11 @@
    }
  },
  methods: {
    getFullUrl(url){
      if(url){
        return getFullUrl(url)
      }
    },
    ChangeTab(tab) {
      if (!this.$route.query.id) {
        this.activeTab = tab
@@ -519,7 +524,9 @@
              }
            })
          }
          if (this.form.id && this.form.isDraft == 0) {
          console.log(this.form,this.form.isDraft);
          if (this.form.id && (this.form.isDraft == 0 || this.form.isDraft == null)) {
            edit(this.form).then(res => {
              if (res.code === 200) {
                this.$message.success('编辑成功');
@@ -530,8 +537,6 @@
            })
          } else {
            add(this.form).then(res => {
              console.log('//////', res);
              if (res.code === 200) {
                this.$message.success('添加成功');
                this.$router.back();
culture/src/views/strain-library/breeding-record/confirm-preserve-dialog.vue
@@ -6,6 +6,7 @@
      :close-on-click-modal="false"
      custom-class="record-detail-dialog"
      @close="handleClose"
      @opened="handleOpened"
    >
      <div class="dialog-content">
        <div class="confirm-tip">
@@ -20,7 +21,12 @@
            </template>
            <div class="signature-area" :class="{ 'waiting': !form.preserveSignature }">
              <template v-if="form.preserveSignature">
                <img :src="form.preserveSignature" alt="菌种保藏人签字" />
                <el-image
                  :src="getFullUrl(form.preserveSignature)"
                  :preview-src-list="[getFullUrl(form.preserveSignature)]"
                  alt="菌种保藏人签字"
                  style="width: 100%; height: 100%; object-fit: cover; display: block;"
                />
              </template>
              <template v-else>
                <span class="waiting-text">等待确认</span>
@@ -40,42 +46,80 @@
  <script>
  import SignatureCanvas from '@/components/SignatureCanvas.vue';
  import moment from 'moment';
  import { getFullUrl } from '@/utils/utils';
  import { queryDetail } from './service';
  export default {
    name: 'ConfirmStorageDialog',
    name: 'ConfirmPreserveDialog',
    components: { SignatureCanvas },
    props: {
      visible: {
        type: Boolean,
        default: false
      },
      data: {
      editData: {
        type: Object,
        default: () => ({})
        default: () => null
      }
    },
    data() {
      return {
        form: {
          preserveSignature: '',
          preserveTime:''
          preserveTime: ''
        },
        rules: {
          preserveSignature: [
            { required: true, message: '请签名', trigger: 'change' }
          ]
        },
        showSignature: false
        showSignature: false,
        isEdit: false
      }
    },
    watch: {
      visible(val) {
        if (val && this.editData) {
          this.isEdit = true
          this.form = {
            preserveSignature: this.editData.preserveSignature || '',
            preserveTime: this.editData.preserveTime || ''
          }
        } else {
          this.isEdit = false
          this.resetForm()
        }
      }
    },
    methods: {
      handleOpened() {
        if (!this.editData || !this.editData.preserveSignature) {
          queryDetail().then((res) => {
            if (res && res.signPicture) {
              this.form.preserveSignature = res.signPicture
            }
          })
        }
      },
      getFullUrl(url) {
        if (url) {
          return getFullUrl(url)
        }
      },
      resetForm() {
        this.form = {
          preserveSignature: '',
          preserveTime: ''
        }
      },
      handleClose() {
        this.$emit('update:visible', false)
        this.resetForm()
      },
      handleConfirm() {
        this.$refs.form.validate(valid => {
          if (!valid) return
          const confirmData = {
            ...this.data,
            ...this.editData,
            preserveTime: moment().format('YYYY-MM-DD HH:mm:ss'),
            preserveSignature: this.form.preserveSignature
          }
culture/src/views/strain-library/breeding-record/confirm-storage-dialog.vue
@@ -6,6 +6,7 @@
    :close-on-click-modal="false"
    custom-class="record-detail-dialog"
    @close="handleClose"
    @opened="handleOpened"
  >
    <div class="dialog-content">
      <div class="confirm-tip">
@@ -20,7 +21,12 @@
          </template>
          <div class="signature-area" :class="{ 'waiting': !form.preserveSignature }">
            <template v-if="form.preserveSignature">
              <img :src="form.preserveSignature" alt="菌种保藏人签字" />
              <el-image
                :src="getFullUrl(form.preserveSignature)"
                :preview-src-list="[getFullUrl(form.preserveSignature)]"
                alt="菌种保藏人签字"
                style="width: 100%; height: 100%; object-fit: cover; display: block;"
              />
            </template>
            <template v-else>
              <span class="waiting-text">等待确认</span>
@@ -39,6 +45,8 @@
<script>
import SignatureCanvas from '@/components/SignatureCanvas.vue';
import { getFullUrl } from '@/utils/utils';
import { queryDetail } from './service';
export default {
  name: 'ConfirmStorageDialog',
  components: { SignatureCanvas },
@@ -47,9 +55,9 @@
      type: Boolean,
      default: false
    },
    data: {
    editData: {
      type: Object,
      default: () => ({})
      default: () => null
    }
  },
  data() {
@@ -62,18 +70,52 @@
          { required: true, message: '请签名', trigger: 'change' }
        ]
      },
      showSignature: false
      showSignature: false,
      isEdit: false
    }
  },
  watch: {
    visible(val) {
      if (val && this.editData) {
        this.isEdit = true
        this.form = {
          preserveSignature: this.editData.preserveSignature || ''
        }
      } else {
        this.isEdit = false
        this.resetForm()
      }
    }
  },
  methods: {
    handleOpened() {
      if (!this.editData.preserveSignature) {
        queryDetail().then((res) => {
          if (res && res.signPicture) {
            this.form.preserveSignature = res.signPicture
          }
        })
      }
    },
    getFullUrl(url) {
      if (url) {
        return getFullUrl(url)
      }
    },
    resetForm() {
      this.form = {
        preserveSignature: ''
      }
    },
    handleClose() {
      this.$emit('update:visible', false)
      this.resetForm()
    },
    handleConfirm() {
      this.$refs.form.validate(valid => {
        if (!valid) return
        const confirmData = {
          ...this.data,
          ...this.editData,
          preserveSignature: this.form.preserveSignature
        }
        this.$emit('confirm', confirmData)
culture/src/views/strain-library/breeding-record/index.vue
@@ -62,8 +62,8 @@
        <el-table-column prop="createTime" label="创建时间"></el-table-column>
        <el-table-column label="操作" width="250">
          <template slot-scope="scope">
            <el-button type="text" @click="handleDetail(scope.row)">详情</el-button>
            <el-button type="text" @click="handleEdit(scope.row)"
            <el-button  style="margin-right: 10px" type="text" @click="handleDetail(scope.row)">详情</el-button>
            <el-button  style="margin-right: 10px" type="text" @click="handleEdit(scope.row)"
              v-if="([1].includes(roleType) || ([1, 4].includes(roleType) && currentType != 'list'))">编辑</el-button>
            <el-button type="text" @click="handleDelete(scope.row)"
              v-if="[1].includes(roleType) || ([1, 4].includes(roleType) && currentType != 'list')">删除</el-button>
culture/src/views/strain-library/breeding-record/inoculation-slope-record-dialog.vue
@@ -5,6 +5,7 @@
    width="700px"
    :close-on-click-modal="false"
    @close="handleClose"
    @opened="handleOpened"
  >
    <el-form
      :model="form"
@@ -66,7 +67,7 @@
            </template>
            <div class="signature-area" :class="{ 'waiting': !form.handleSignature }">
              <template v-if="form.handleSignature">
                <img :src="form.handleSignature" alt="接种操作人签字" />
                <el-image :src="getFullUrl(form.handleSignature)" :preview-src-list="[getFullUrl(form.handleSignature)]" alt="接种操作人签字" />
              </template>
              <template v-else>
                <span class="waiting-text">等待确认</span>
@@ -91,6 +92,8 @@
<script>
import SignatureCanvas from '@/components/SignatureCanvas.vue';
import { getFullUrl } from '@/utils/utils';
import { queryDetail } from './service';
export default {
  components: { SignatureCanvas },
@@ -126,16 +129,20 @@
        handleSignature: [{ required: true, message: "请签名", trigger: "change" }],
      },
      showSignature: false,
      isEdit: false
    };
  },
  watch: {
    visible(val) {
      if (val && this.editData) {
        // 当对话框显示且有编辑数据时,进行数据回显
        this.isEdit = true;
        this.form = {
          ...this.form,
          ...this.editData
        };
      } else {
        this.isEdit = false;
        this.resetForm();
      }
    }
  },
@@ -154,23 +161,21 @@
        d.getMinutes().toString().padStart(2, "0")
      );
    },
    handleSave() {
      console.log(this.form)
      this.$refs.form.validate((valid) => {
        if(!this.form.handleSignature){
          this.$message.error('请接种操作人签字')
          return
        }
        if (valid) {
          this.$emit("save", { ...this.form });
          this.handleClose();
        }
      });
    getFullUrl(url) {
      if (url) {
        return getFullUrl(url);
      }
    },
    handleClose() {
      this.$emit("update:visible", false);
      // 重置表单数据
    handleOpened() {
      if (!this.editData || !this.editData.handleSignature) {
        queryDetail().then((res) => {
          if (res && res.signPicture) {
            this.form.handleSignature = res.signPicture;
          }
        });
      }
    },
    resetForm() {
      this.form = {
        separateColonyCode: "",
        vaccinationSlopeCode: "",
@@ -181,7 +186,22 @@
        handleName: JSON.parse(sessionStorage.getItem('userInfo'))?.nickName || '',
        handleId: JSON.parse(sessionStorage.getItem('userInfo'))?.userId || '',
      };
      // 重置表单验证
    },
    handleSave() {
      this.$refs.form.validate((valid) => {
        if(!this.form.handleSignature){
          this.$message.error('请接种操作人签字')
          return
        }
        if (valid) {
          this.$emit("save", { ...this.form });
          this.handleClose();
        }
      });
    },
    handleClose() {
      this.$emit("update:visible", false);
      this.resetForm();
      this.$nextTick(() => {
        this.$refs.form && this.$refs.form.clearValidate();
      });
culture/src/views/strain-library/breeding-record/preserve-strain-record-dialog.vue
@@ -1,5 +1,5 @@
<template>
  <el-dialog :visible.sync="visible" title="新增菌种保藏记录" width="900px" :close-on-click-modal="false" @close="handleClose">
  <el-dialog :visible.sync="visible" title="新增菌种保藏记录" width="900px" :close-on-click-modal="false" @close="handleClose" @opened="handleOpened">
    <el-form :model="form" :rules="rules" ref="form" label-width="120px" label-position="top">
      <el-row :gutter="20">
        <el-col :span="12">
@@ -32,7 +32,11 @@
            </template>
            <div class="signature-area" :class="{ 'waiting': !form.handleSignature }">
              <template v-if="form.handleSignature">
                <img :src="form.handleSignature" alt="操作人签名" />
                <el-image
                  :src="getFullUrl(form.handleSignature)"
                  :preview-src-list="[getFullUrl(form.handleSignature)]"
                  alt="操作人签名"
                />
              </template>
              <template v-else>
                <span class="waiting-text">等待确认</span>
@@ -53,6 +57,8 @@
<script>
import SignatureCanvas from '@/components/SignatureCanvas.vue';
import moment from 'moment';
import { getFullUrl } from '@/utils/utils';
import { queryDetail } from './service';
export default {
  components: { SignatureCanvas },
  props: {
@@ -94,18 +100,41 @@
  },
  watch: {
    visible(val) {
      if (val && this.editData) {
      if (val && this.editData && Object.keys(this.editData).length) {
        this.form = {
          ...this.form,
          ...this.editData
        };
      } else if (!val) {
        this.resetForm();
      }
    }
  },
  methods: {
    getFullUrl(url) {
      if (url) return getFullUrl(url);
    },
    handleOpened() {
      if (!this.editData || !this.editData.handleSignature) {
        queryDetail().then(res => {
          if (res && res.signPicture) {
            this.form.handleSignature = res.signPicture;
          }
        });
      }
    },
    resetForm() {
      this.form = {
        forPreserveCode: '',
        verificationConclusion: '',
        preserveMethod: '',
        preserveCode: '',
        handleSignature: '',
        handleTime: '',
      };
    },
    handleSave() {
      this.form.handleTime = moment().format('YYYY-MM-DD HH:mm:ss');
      this.$refs.form.validate((valid) => {
        if (valid) {
          if (!this.form.handleSignature) {
@@ -118,14 +147,7 @@
    },
    handleClose() {
      this.$emit('update:visible', false);
      this.form = {
        forPreserveCode: '',
        verificationConclusion: '',
        preserveMethod: '',
        preserveCode: '',
        handleSignature: '',
        handleTime: '',
      };
      this.resetForm();
    },
    handleSignatureConfirm(dataUrl) {
      this.form.handleSignature = dataUrl;
culture/src/views/strain-library/breeding-record/separation-record-dialog.vue
@@ -1,196 +1,225 @@
<template>
    <el-dialog
      :title="isEdit ? '编辑培养皿分离记录' : '新增培养皿分离记录'"
      :visible.sync="visible"
      width="520px"
      :close-on-click-modal="false"
      custom-class="record-detail-dialog"
      @close="handleClose"
    >
      <div class="dialog-content">
        <el-form :model="formData" label-position="top">
          <el-form-item label="分离菌落编号" required>
            <el-input v-model="formData.separateBacterialColoniesCode" placeholder="请输入" />
          </el-form-item>
          <el-form-item required>
            <template #label>
              <span>操作人签字</span>
              <el-button type="primary" class="sign-btn" @click="showSignature = true">签名</el-button>
            </template>
            <div class="signature-area" :class="{ 'waiting': !formData.handleSignature }">
              <template v-if="formData.handleSignature">
                <img :src="formData.handleSignature" alt="操作人签字" />
              </template>
              <template v-else>
                <span class="waiting-text">等待确认</span>
              </template>
            </div>
          </el-form-item>
        </el-form>
      </div>
      <div class="footer-btns">
        <el-button @click="handleClose">取消</el-button>
        <el-button type="primary" @click="handleConfirm">确认</el-button>
      </div>
      <signature-canvas :visible.sync="showSignature" @confirm="handleSignatureConfirm" />
    </el-dialog>
  </template>
  <script>
  import SignatureCanvas from '@/components/SignatureCanvas.vue'
  import moment from 'moment'
  export default {
    name: 'AddRecordDialog',
    components: { SignatureCanvas },
    props: {
      visible: {
        type: Boolean,
        default: false
      },
      editData: {
        type: Object,
        default: () => null
      }
    },
    data() {
      return {
        formData: {
          separateBacterialColoniesCode: '',
          handleSignature: ''
        },
        showSignature: false,
        isEdit: false
      }
    },
    watch: {
      visible(val) {
        if (val && this.editData) {
          this.isEdit = true
          this.formData = {
            ...this.editData
          }
        } else {
          this.isEdit = false
          this.resetForm()
        }
      }
    },
    methods: {
      resetForm() {
        this.formData = {
          separateBacterialColoniesCode: '',
          handleSignature: ''
        }
      },
      handleClose() {
        this.$emit('update:visible', false)
        this.$emit('close')
        this.resetForm()
      },
      handleConfirm() {
        if (!this.formData.handleSignature) {
          this.$message.warning('请先签名')
          return
        }
        this.formData.createTime = moment().format('YYYY-MM-DD HH:mm:ss')
  <el-dialog :title="isEdit ? '编辑培养皿分离记录' : '新增培养皿分离记录'" :visible.sync="visible" @opened="handleOpened" width="520px"
    :close-on-click-modal="false" custom-class="record-detail-dialog" @close="handleClose">
    <div class="dialog-content">
      <el-form :model="formData" label-position="top">
        <el-form-item label="分离菌落编号" required>
          <el-input v-model="formData.separateBacterialColoniesCode" placeholder="请输入" />
        </el-form-item>
        this.$emit('confirm', this.formData)
        this.formData = {
          separateBacterialColoniesCode: '',
          handleSignature: ''
        }
        this.handleClose()
        <el-form-item required>
          <template #label>
            <span>操作人签字</span>
            <el-button type="primary" class="sign-btn" @click="showSignature = true">签名</el-button>
          </template>
          <div class="signature-area" :class="{ 'waiting': !formData.handleSignature }">
            <template v-if="formData.handleSignature">
              <el-image :src="getFullUrl(formData.handleSignature)"
                :preview-src-list="[getFullUrl(formData.handleSignature)]" alt="操作人签字" />
            </template>
            <template v-else>
              <span class="waiting-text">等待确认</span>
            </template>
          </div>
        </el-form-item>
      </el-form>
    </div>
    <div class="footer-btns">
      <el-button @click="handleClose">取消</el-button>
      <el-button type="primary" @click="handleConfirm">确认</el-button>
    </div>
    <signature-canvas :visible.sync="showSignature" @confirm="handleSignatureConfirm" />
  </el-dialog>
</template>
<script>
import SignatureCanvas from '@/components/SignatureCanvas.vue'
import { getFullUrl } from '@/utils/utils'
import { queryDetail } from './service'
import moment from 'moment'
export default {
  name: 'AddRecordDialog',
  components: { SignatureCanvas },
  props: {
    visible: {
      type: Boolean,
      default: false
    },
    editData: {
      type: Object,
      default: () => null
    }
  },
  data() {
    return {
      formData: {
        separateBacterialColoniesCode: '',
        handleSignature: ''
      },
      handleSignatureConfirm(dataUrl) {
        this.formData.handleSignature = dataUrl
        this.showSignature = false
      showSignature: false,
      isEdit: false
    }
  },
  watch: {
    visible(val) {
      if (val && this.editData) {
        this.isEdit = true
        this.formData = {
          ...this.editData
        }
      } else {
        this.isEdit = false
        this.resetForm()
      }
    }
  },
  methods: {
    handleOpened() {
      if(!this.editData){
        queryDetail().then((res) => {
          this.formData.handleSignature = res.signPicture
        })
      }
      // if (this.editData && !this.editData.handleSignature) {
      //   queryDetail().then((res) => {
      //     this.formData.handleSignature = res.signPicture
      //   })
      // } else {
      // }
    },
    getFullUrl(url) {
      if (url) {
        return getFullUrl(url)
      }
    },
    resetForm() {
      this.formData = {
        separateBacterialColoniesCode: '',
        handleSignature: ''
      }
    },
    handleClose() {
      this.$emit('update:visible', false)
      this.$emit('close')
      this.resetForm()
    },
    handleConfirm() {
      if (!this.formData.handleSignature) {
        this.$message.warning('请先签名')
        return
      }
      this.formData.createTime = moment().format('YYYY-MM-DD HH:mm:ss')
      this.$emit('confirm', this.formData)
      this.formData = {
        separateBacterialColoniesCode: '',
        handleSignature: ''
      }
      this.handleClose()
    },
    handleSignatureConfirm(dataUrl) {
      this.formData.handleSignature = dataUrl
      this.showSignature = false
    }
  }
  </script>
  <style lang="less" scoped>
  .record-detail-dialog {
    :deep(.el-dialog__header) {
      padding: 20px 24px;
      margin: 0;
      border-bottom: 1px solid #DCDFE6;
      .el-dialog__title {
        font-size: 16px;
        font-weight: 600;
        color: #303133;
      }
    }
    :deep(.el-dialog__body) {
      padding: 24px;
}
</script>
<style lang="less" scoped>
.record-detail-dialog {
  :deep(.el-dialog__header) {
    padding: 20px 24px;
    margin: 0;
    border-bottom: 1px solid #DCDFE6;
    .el-dialog__title {
      font-size: 16px;
      font-weight: 600;
      color: #303133;
    }
  }
  .dialog-content {
    :deep(.el-form-item__label) {
      padding-bottom: 8px;
      line-height: 20px;
      font-size: 14px;
      color: #606266;
      &::before {
        content: '*';
        color: #F56C6C;
        margin-right: 4px;
      }
    }
    .type-buttons {
      display: flex;
      gap: 12px;
      .el-button {
        width: 80px;
        border-radius: 4px;
        font-size: 14px;
        font-weight: 400;
        box-sizing: border-box;
      }
    }
    .signature-area {
      height: 120px;
      width: 100%;
      background: #F5F7FA;
      border-radius: 4px;
      display: flex;
      align-items: center;
      justify-content: center;
      border: 1px solid #DCDFE6;
      overflow: hidden;
      padding: 0;
      &.waiting {
        border-style: dashed;
        background: #FAFAFA;
      }
      img {
        width: 100%;
        height: 100%;
        object-fit: cover;
        display: block;
      }
      .waiting-text {
        color: #909399;
        font-size: 14px;
      }
    }
    .sign-btn {
      height: 32px;
      border-radius: 4px;
      font-size: 14px;
      padding: 0 20px;
      font-weight: 400;
      margin-left: 12px;
    }
  }
  .footer-btns {
    display: flex;
    justify-content: space-between;
  :deep(.el-dialog__body) {
    padding: 24px;
    padding-top: 0;
    .el-button {
      width: 150px;
  }
}
.dialog-content {
  :deep(.el-form-item__label) {
    padding-bottom: 8px;
    line-height: 20px;
    font-size: 14px;
    color: #606266;
    &::before {
      content: '*';
      color: #F56C6C;
      margin-right: 4px;
    }
  }
  </style>
  .type-buttons {
    display: flex;
    gap: 12px;
    .el-button {
      width: 80px;
      border-radius: 4px;
      font-size: 14px;
      font-weight: 400;
      box-sizing: border-box;
    }
  }
  .signature-area {
    height: 120px;
    width: 100%;
    background: #F5F7FA;
    border-radius: 4px;
    display: flex;
    align-items: center;
    justify-content: center;
    border: 1px solid #DCDFE6;
    overflow: hidden;
    padding: 0;
    &.waiting {
      border-style: dashed;
      background: #FAFAFA;
    }
    img {
      width: 100%;
      height: 100%;
      object-fit: cover;
      display: block;
    }
    .waiting-text {
      color: #909399;
      font-size: 14px;
    }
  }
  .sign-btn {
    height: 32px;
    border-radius: 4px;
    font-size: 14px;
    padding: 0 20px;
    font-weight: 400;
    margin-left: 12px;
  }
}
.footer-btns {
  display: flex;
  justify-content: space-between;
  padding: 24px;
  padding-top: 0;
  .el-button {
    width: 150px;
  }
}
</style>
culture/src/views/strain-library/breeding-record/service.js
@@ -55,4 +55,9 @@
  return axios.post('/api/t-breeding-and-preservation/culturePreservationsConfirm', {
   ...data
  })
}
// 获取用户详情
export const queryDetail = (data) => {
  return axios.get('/system/user/queryDetail',)
}
culture/src/views/strain-library/main-cell-library/index.vue
@@ -227,7 +227,6 @@
        status: this.form.status,
        type: 2,
      };
      console.log(params);
      getList(params)
        .then((res) => {
culture/src/views/strain-library/main-cell-library/record.vue
@@ -269,7 +269,6 @@
        id: this.currentRecord.id,
        preserveSignature: data.preserveSignature,
      }).then((res) => {
        console.log(res);
        if (res.code == 200) {
          this.$message.success("操作成功");
          this.dialogVisible = false;
culture/src/views/strain-library/production-cell-library/record.vue
@@ -269,7 +269,6 @@
        id: this.currentRecord.id,
        preserveSignature: data.preserveSignature,
      }).then((res) => {
        console.log(res);
        if (res.code == 200) {
          this.$message.success("操作成功");
          this.dialogVisible = false;
culture/src/views/strain-library/strain-library-manage/add.vue
@@ -80,6 +80,7 @@
import SignatureCanvas from '@/components/SignatureCanvas.vue'
import { add, edit, getDetail, addBatch } from './service'
export default {
    name: 'StrainLibraryManageAdd',
    components: {
culture/src/views/strain-library/strain-library-manage/components/AddRecordDialog.vue
@@ -1,24 +1,12 @@
<template>
  <el-dialog
    title="添加出入库记录"
    :visible.sync="visible"
    width="520px"
    :close-on-click-modal="false"
    custom-class="record-detail-dialog"
    @close="handleClose"
  >
  <el-dialog title="添加出入库记录" :visible.sync="visible" @open="handleOpen" width="520px" :close-on-click-modal="false"
    custom-class="record-detail-dialog" @close="handleClose">
    <div class="dialog-content">
      <el-form :model="formData" label-position="top">
        <el-form-item label="出库/入库" required>
          <div class="type-buttons">
            <el-button
              :type="formData.type === '1' ? 'primary' : 'default'"
              @click="formData.type = '1'"
            >出库</el-button>
            <el-button
              :type="formData.type === '2' ? 'primary' : 'default'"
              @click="formData.type = '2'"
            >入库</el-button>
            <el-button :type="formData.type === '1' ? 'primary' : 'default'" @click="formData.type = '1'">出库</el-button>
            <el-button :type="formData.type === '2' ? 'primary' : 'default'" @click="formData.type = '2'">入库</el-button>
          </div>
        </el-form-item>
@@ -29,7 +17,8 @@
          </template>
          <div class="signature-area" :class="{ 'waiting': !formData.handleSignature }">
            <template v-if="formData.handleSignature">
              <img :src="formData.handleSignature" alt="操作人签字" />
              <el-image :src="getFullUrl(formData.handleSignature)"
                :preview-src-list="[getFullUrl(formData.handleSignature)]" alt="操作人签字" />
            </template>
            <template v-else>
              <span class="waiting-text">等待确认</span>
@@ -48,6 +37,8 @@
<script>
import SignatureCanvas from '@/components/SignatureCanvas.vue'
import { getFullUrl } from "@/utils/utils";
import { queryDetail } from '../service'
export default {
  name: 'AddRecordDialog',
  components: { SignatureCanvas },
@@ -67,6 +58,16 @@
    }
  },
  methods: {
    handleOpen() {
      queryDetail().then(res=>{
        this.formData.handleSignature= getFullUrl(res.signPicture)
      })
    },
    getFullUrl(url) {
      if (url) {
        return getFullUrl(url);
      }
    },
    handleClose() {
      this.$emit('update:visible', false)
      this.$emit('close')
@@ -93,31 +94,37 @@
    padding: 20px 24px;
    margin: 0;
    border-bottom: 1px solid #DCDFE6;
    .el-dialog__title {
      font-size: 16px;
      font-weight: 600;
      color: #303133;
    }
  }
  :deep(.el-dialog__body) {
    padding: 24px;
  }
}
.dialog-content {
  :deep(.el-form-item__label) {
    padding-bottom: 8px;
    line-height: 20px;
    font-size: 14px;
    color: #606266;
    &::before {
      content: '*';
      color: #F56C6C;
      margin-right: 4px;
    }
  }
  .type-buttons {
    display: flex;
    gap: 12px;
    .el-button {
      width: 80px;
      border-radius: 4px;
@@ -126,6 +133,7 @@
      box-sizing: border-box;
    }
  }
  .signature-area {
    height: 120px;
    width: 100%;
@@ -137,21 +145,25 @@
    border: 1px solid #DCDFE6;
    overflow: hidden;
    padding: 0;
    &.waiting {
      border-style: dashed;
      background: #FAFAFA;
    }
    img {
      width: 100%;
      height: 100%;
      object-fit: cover;
      display: block;
    }
    .waiting-text {
      color: #909399;
      font-size: 14px;
    }
  }
  .sign-btn {
    height: 32px;
    border-radius: 4px;
@@ -161,13 +173,15 @@
    margin-left: 12px;
  }
}
.footer-btns {
  display: flex;
  justify-content: space-between;
  padding: 24px;
  padding-top: 0;
  .el-button {
    width: 150px;
  }
}
</style>
</style>
culture/src/views/strain-library/strain-library-manage/components/RecordDetailDialog.vue
@@ -14,7 +14,8 @@
                    <el-form-item label="操作人签字" required class="signature-item">
                        <div class="signature-area" :class="{ 'waiting': !formData.handleSignature }">
                            <template v-if="formData.handleSignature">
                                <img :src="formData.handleSignature" alt="操作人签字" />
                                <el-image :src="getFullUrl(formData.handleSignature)"
                                    :preview-src-list="[getFullUrl(formData.handleSignature)]" alt="操作人签字" />
                            </template>
                            <template v-else>
                                <span class="waiting-text">等待确认</span>
@@ -36,7 +37,8 @@
                        </template>
                        <div class="signature-area" :class="{ 'waiting': !formData.preserveSignature }">
                            <template v-if="formData.preserveSignature">
                                <img :src="formData.preserveSignature" alt="保藏人签字" />
                                <el-image :src="getFullUrl(formData.preserveSignature)"
                                    :preview-src-list="[getFullUrl(formData.preserveSignature)]" alt="保藏人签字" />
                            </template>
                            <template v-else>
                                <span class="waiting-text">等待确认</span>
@@ -61,6 +63,8 @@
<script>
import SignatureCanvas from '@/components/SignatureCanvas.vue'
import { getFullUrl } from '@/utils/utils'
import {queryDetail} from '../service'
export default {
    name: 'RecordDetailDialog',
    components: { SignatureCanvas },
@@ -95,8 +99,19 @@
        }
    },
    methods: {
        getFullUrl(url) {
            if (url) {
                return getFullUrl(url);
            }
        },
        opened() {
            this.formData.type = this.recordData.type
            let roleType = JSON.parse(sessionStorage.getItem('userInfo')).roleType * 1
            queryDetail().then(res => {
                if(!this.recordData.preserveSignature && roleType == 3){
                    this.formData.preserveSignature = getFullUrl(res.signPicture)
                }
            })
        },
        handleClose() {
            this.$emit('close')
culture/src/views/strain-library/strain-library-manage/components/RecordTimeline.vue
@@ -36,7 +36,7 @@
    }
  },
  mounted() {
    console.log('123123123',this.list);
    
  }
}
culture/src/views/strain-library/strain-library-manage/components/StrainDetail.vue
@@ -1,14 +1,7 @@
<template>
  <div>
    <el-dialog
      :title="`${title}详情`"
      :visible.sync="visible"
      width="70%"
      :close-on-click-modal="false"
      custom-class="strain-detail-dialog"
      @close="$emit('update:visible', false)"
      @opened="fetchDetail"
    >
    <el-dialog :title="`${title}详情`" :visible.sync="visible" width="70%" :close-on-click-modal="false"
      custom-class="strain-detail-dialog" @close="$emit('update:visible', false)" @opened="fetchDetail">
      <div class="strain-info">
        <!-- 第一行信息 -->
        <div class="info-row">
@@ -63,7 +56,7 @@
      </div>
      <div class="record-table">
        <div class="table-title">{{title}}出/入库记录</div>
        <div class="table-title">{{ title }}出/入库记录</div>
        <el-table :data="detail.records" style="width: 100%">
          <el-table-column label="出库/入库">
            <template #default="{ row }">
@@ -71,14 +64,16 @@
            </template>
          </el-table-column>
          <el-table-column prop="boundTime" label="操作时间" />
          <el-table-column prop="handleName" label="操作人签字" >
            <template #default="{row}">
              <img :src="row.handleSignature" v-if="row.handleSignature" alt="签字图片" style="max-width: 100px; max-height: 50px;">
          <el-table-column prop="handleName" label="操作人签字">
            <template #default="{ row }">
              <el-image :src="getFullUrl(row.handleSignature)" :preview-src-list="[getFullUrl(row.handleSignature)]"  v-if="row.handleSignature" alt="签字图片"
                style="max-width: 100px; max-height: 50px;" />
            </template>
          </el-table-column>
          <el-table-column prop="preserveSignature" label="菌种保藏人签字" >
            <template #default="{row}">
              <img :src="row.preserveSignature" v-if="row.preserveSignature" alt="签字图片" style="max-width: 100px; max-height: 50px;">
          <el-table-column prop="preserveSignature" label="菌种保藏人签字">
            <template #default="{ row }">
              <el-image :src="getFullUrl(row.preserveSignature)" :preview-src-list="[getFullUrl(row.preserveSignature)]" v-if="row.preserveSignature" alt="签字图片"
                style="max-width: 100px; max-height: 50px;" />
            </template>
          </el-table-column>
          <el-table-column label="状态">
@@ -90,39 +85,27 @@
          </el-table-column>
          <el-table-column label="操作" width="100">
            <template #default="{ row }">
              <el-button v-if="!row.confirmTime && roleType == 3" style="margin-right: 10px" type="text" @click="handleConfirm(row)">确认</el-button>
              <el-button
                type="text"
                @click="handleView(row)"
                >详情</el-button
              >
              <el-button v-if="!row.confirmTime && roleType == 3" style="margin-right: 10px" type="text"
                @click="handleConfirm(row)">确认</el-button>
              <el-button type="text" @click="handleView(row)">详情</el-button>
            </template>
          </el-table-column>
        </el-table>
        <div class="pagination">
          <el-pagination
            :current-page.sync="currentPage"
            :page-size="10"
            layout="total, prev, pager, next"
            :total="total"
            @current-change="handlePageChange"
          />
          <el-pagination :current-page.sync="currentPage" :page-size="10" layout="total, prev, pager, next"
            :total="total" @current-change="handlePageChange" />
        </div>
      </div>
    </el-dialog>
    <RecordDetailDialog
      :visible="visibleRecordDetailDialog"
      :recordData="recordData"
      @close="handleDialogClose"
      @confirm="handleOutbound"
      :type="dialogType"
    />
    <RecordDetailDialog :visible="visibleRecordDetailDialog" :recordData="recordData" @close="handleDialogClose"
      @confirm="handleOutbound" :type="dialogType" />
  </div>
</template>
<script>
import { getDetailById,confirmWarehousing } from "../service";
import { getDetailById, confirmWarehousing } from "../service";
import { getFullUrl } from "@/utils/utils";
import RecordDetailDialog from "./RecordDetailDialog.vue";
export default {
  components: { RecordDetailDialog },
@@ -160,16 +143,20 @@
  },
  methods: {
    getFullUrl(url){
      if (url) {
        return getFullUrl(url);
      }
    },
    handleDialogClose() {
      this.recordData = {};
      this.visibleRecordDetailDialog = false;
    },
    fetchDetail() {
    this.roleType = JSON.parse(sessionStorage.getItem("userInfo")).roleType;
      this.roleType = JSON.parse(sessionStorage.getItem("userInfo")).roleType;
      this.query.id = this.detail.id;
      getDetailById(this.query).then((res) => {
        console.log('qweqweqwe',res);
        this.detail.records = res.warehousingList?.records || [];
        this.total = res.warehousingList?.total || 0;
        this.currentPage = res.warehousingList?.current || 1;
@@ -177,7 +164,7 @@
      });
    },
    handleView(row) {
        this.dialogType = "detail";
      this.dialogType = "detail";
      this.recordData = row;
      this.visibleRecordDetailDialog = true;
culture/src/views/strain-library/strain-library-manage/record.vue
@@ -46,38 +46,20 @@
    </el-card>
    <!-- 出入库记录表格 -->
    <TableCustom
      :queryForm="queryForm"
      :tableData="recordList"
      :total="total"
      @currentChange="handlePageChange"
    >
    <TableCustom :queryForm="queryForm" :tableData="recordList" :total="total" @currentChange="handlePageChange">
      <template #setting>
        <div class="tableTitle">
          <div class="flex a-center">
            <div
              class="title"
              :class="{ active: currentType === 'table' }"
              @click="handleTypeChange('table')"
            >
             {{type==1?'原始细胞保藏出/入库登记表':type==2?'主细胞保藏出/入库登记表':'生产细胞保藏出/入库登记表'}}
            <div class="title" :class="{ active: currentType === 'table' }" @click="handleTypeChange('table')">
              {{ type == 1 ? '原始细胞保藏出/入库登记表' : type == 2 ? '主细胞保藏出/入库登记表' : '生产细胞保藏出/入库登记表' }}
            </div>
            <div
              class="drafts"
              :class="{ active: currentType === 'timeline' }"
              @click="handleTypeChange('timeline')"
            >
              {{type==1?'原始细胞保藏出/入库时间轴':type==2?'主细胞保藏出/入库时间轴':'生产细胞保藏出/入库时间轴'}}
            <div class="drafts" :class="{ active: currentType === 'timeline' }" @click="handleTypeChange('timeline')">
              {{ type == 1 ? '原始细胞保藏出/入库时间轴' : type == 2 ? '主细胞保藏出/入库时间轴' : '生产细胞保藏出/入库时间轴' }}
            </div>
          </div>
          <div class="flex a-center">
            <el-button
              v-if="roleType == 4"
              @click="handleAddRecord"
              class="el-icon-plus"
              type="primary"
              >新增出入库记录</el-button
            >
            <el-button v-if="roleType == 4" @click="handleAddRecord" class="el-icon-plus"
              type="primary">新增出入库记录</el-button>
          </div>
        </div>
      </template>
@@ -93,23 +75,15 @@
        <el-table-column prop="boundTime" label="操作时间" />
        <el-table-column prop="signature" label="操作人签字">
          <template #default="{ row }">
            <el-image
              v-if="row.handleSignature"
              style="width: 100px; height: 100px"
              :src="row.handleSignature"
              :preview-src-list="[row.handleSignature]"
            >
            <el-image v-if="row.handleSignature" style="width: 100px; height: 100px" :src="getFullUrl(row.handleSignature)"
              :preview-src-list="[getFullUrl(row.handleSignature)]">
            </el-image>
          </template>
        </el-table-column>
        <el-table-column prop="preserveSignature" label="菌种保藏人签字">
          <template #default="{ row }">
            <el-image
              v-if="row.preserveSignature"
              style="width: 100px; height: 100px"
              :src="row.preserveSignature"
              :preview-src-list="[row.preserveSignature]"
            >
            <el-image v-if="row.preserveSignature" style="width: 100px; height: 100px" :src="getFullUrl(row.preserveSignature)"
              :preview-src-list="[getFullUrl(row.preserveSignature)]">
            </el-image>
          </template>
        </el-table-column>
@@ -122,19 +96,9 @@
        </el-table-column>
        <el-table-column label="操作" width="180">
          <template #default="{ row }">
            <el-button
              v-if="!row.preserveSignature && roleType == 3"
              type="text"
              class="operation-btn"
              @click="handleConfirm(row)"
              >确认</el-button
            >
            <el-button
              type="text"
              class="operation-btn"
              @click="handleView(row)"
              >详情</el-button
            >
            <el-button v-if="!row.preserveSignature && roleType == 3" type="text" class="operation-btn"
              @click="handleConfirm(row)">确认</el-button>
            <el-button type="text" class="operation-btn" @click="handleView(row)">详情</el-button>
            <el-button v-if="roleType == 1" type="text" @click="handleDelete(row)">删除</el-button>
          </template>
        </el-table-column>
@@ -145,18 +109,10 @@
    </TableCustom>
    <!-- 详情弹窗 -->
    <record-detail-dialog
      :visible.sync="dialogVisible"
      :record-data="currentRecord"
      @close="handleDialogClose"
      @confirm="handleOutbound"
      :type="dialogType"
    />
    <record-detail-dialog :visible.sync="dialogVisible" :record-data="currentRecord" @close="handleDialogClose"
      @confirm="handleOutbound" :type="dialogType" />
    <!-- 新增出入库记录弹窗 -->
    <add-record-dialog
      :visible.sync="addDialogVisible"
      @confirm="handleAddRecordConfirm"
    />
    <add-record-dialog :visible.sync="addDialogVisible" @confirm="handleAddRecordConfirm" />
  </div>
</template>
@@ -164,6 +120,7 @@
import RecordDetailDialog from "./components/RecordDetailDialog.vue";
import AddRecordDialog from "./components/AddRecordDialog.vue";
import RecordTimeline from "./components/RecordTimeline.vue";
import { getFullUrl } from "@/utils/utils"
import {
  timeList,
  getDetail,
@@ -198,7 +155,7 @@
      dialogType: "detail",
      roleType: "",
      type: 1,
      };
    };
  },
  created() {
    this.roleType = JSON.parse(sessionStorage.getItem("userInfo")).roleType;
@@ -212,6 +169,11 @@
    }
  },
  methods: {
    getFullUrl(url){
      if (url) {
        return getFullUrl(url);
      }
    },
    handleDelete(row) {
      this.$confirm("确定删除该数据吗?", "提示", {
        confirmButtonText: "确定",
@@ -270,7 +232,7 @@
        id: this.currentRecord.id,
        preserveSignature: data.preserveSignature,
      }).then((res) => {
        console.log(res);
        if (res.code == 200) {
          this.$message.success("操作成功");
          this.dialogVisible = false;
culture/src/views/strain-library/strain-library-manage/service.js
@@ -12,7 +12,7 @@
// 编辑
export const edit = (data) => {
  console.log('qweqweqweqw',data);
  return axios.post('/api/t-train-library/update', { ...data })
}
@@ -55,3 +55,8 @@
export const deleteWarehousing = (params) => {
  return axios.delete('/open/t-train-library/deleteWarehousingById', { params })
}
// 获取用户详情
export const queryDetail = (data) => {
  return axios.get('/system/user/queryDetail',)
}
culture/src/views/strain-library/validation/chief-cell/DetailConditionDialog.vue
@@ -28,14 +28,14 @@
          </div>
          <div class="info-item signature-item">
            <div class="signature-area">
              <img v-if="detail.handleSignature" :src="detail.handleSignature" alt="签字" />
              <el-image style="width: 120px;" v-if="detail.handleSignature" :src="getFullUrl(detail.handleSignature)" :preview-src-list="[getFullUrl(detail.handleSignature)]" alt="签字" />
              <span v-else class="waiting-text">暂无签名</span>
            </div>
          </div>
        </el-col>
        <el-col :span="6" class="info-col">
          <div class="info-item">
            <span class="label">签字时间:</span>{{ detail.preserveTime }}
            <span class="label">签字时间:</span>{{ detail.handleTime }}
          </div>
        </el-col>
      </el-row>
@@ -74,6 +74,7 @@
import DetailConditionDialog from "./DetailConditionDialog.vue";
import { detail } from "./service.js";
import moment from "moment";
import { getFullUrl } from "@/utils/utils.js";
export default {
  name: "DetailConditionDialog",
  components: { DetailConditionDialog },
@@ -139,6 +140,12 @@
    },
  },
  methods: {
    getFullUrl(url){
      if(url){
        return getFullUrl(url)
      }
      return ''
    },
    handleOpen() {
      if (this.value.id) {
        detail({ id: this.value.id }).then(res => {
culture/src/views/strain-library/validation/chief-cell/add.vue
@@ -104,8 +104,8 @@
        handleSignatureConfirm(signatureImage) {
            this.signatureVisible = false;
            const id = this.$route.query.id || this.$route.params.id;
            const submitData = { ...this.form, strainType: 2, isDraft: this.currentAction == 'submit' ? 0 : 1, handleSignature: signatureImage.signature, experimentTime:this.form.experimentTime?moment(this.form.experimentTime).format('YYYY-MM-DD'):''};
            if (this.currentAction === 'submit') {
            const submitData = { ...this.form, strainType: 2, isDraft: this.form.isDraft&&this.currentAction === 'draft' ? this.form.isDraft : this.currentAction == 'submit' ? 0 : 1, handleSignature: signatureImage.signature, experimentTime: this.form.experimentTime ? moment(this.form.experimentTime).format('YYYY-MM-DD') : '' };
            if (this.currentAction === 'submit' || this.form.isDraft == 1) {
                if (id) {
                    // 编辑
                    edit({ ...submitData, id }).then(() => {
culture/src/views/strain-library/validation/chief-cell/confirm-detail.vue
@@ -16,7 +16,7 @@
          <div class="info-item sign-label"><span class="label">菌种实验员签字</span></div>
          <div class="info-item signature-item">
            <div class="signature-area">
              <img v-if="detail.handleSignature" :src="detail.handleSignature" alt="签字" />
              <el-image v-if="detail.handleSignature" :src="getFullUrl(detail.handleSignature)" :preview-src-list="[getFullUrl(detail.handleSignature)]" alt="签字" />
              <span v-else class="waiting-text">暂无签名</span>
            </div>
          </div>
@@ -74,6 +74,7 @@
import EditConditionDialog from './EditConditionDialog.vue'
import DetailConditionDialog from './DetailConditionDialog.vue'
import { detail,confirm } from './service'
import { getFullUrl } from '@/utils/utils'
import moment from 'moment'
export default {
  name: 'ConfirmDetail',
@@ -141,6 +142,12 @@
    this.getDetail()
  },
  methods: {
    getFullUrl(url) {
      if (url) {
        return getFullUrl(url)
      }
      return ''
    },
    getDetail() {
      detail({id: this.$route.query.id}).then(res => {
        this.detail = {
culture/src/views/strain-library/validation/chief-cell/index.vue
@@ -83,7 +83,7 @@
import PrimitiveCellDetailDialog from "./primitive-cell-detail-dialog.vue";
import DetailConditionDialog from "./DetailConditionDialog.vue";
import EditConditionDialog from "./EditConditionDialog.vue";
import moment from "moment";
import { getList, delOne } from "./service";
export default {
  name: "PrimitiveCell",
@@ -123,6 +123,7 @@
  },
  methods: {
    handleDetail(row) {
      row.experimentTime = moment(row.experimentTime).format('YYYY-MM-DD')
      this.currentDetail = row;
      this.detailVisible = true;
    },
culture/src/views/strain-library/validation/chief-cell/primitive-cell-detail-dialog.vue
@@ -1,7 +1,7 @@
<template>
  <el-dialog
    :visible.sync="visible"
    title="原始细胞库资料详情"
    title="主细胞库资料详情"
    width="650px"
    :close-on-click-modal="false"
    @close="handleClose"
@@ -41,7 +41,7 @@
        <el-col :span="12">
          <el-form-item label="菌种实验员签字">
            <div class="signature-area">
              <img v-if="detail.handleSignature" :src="detail.handleSignature" alt="签字" />
              <el-image style="width: 120px;" v-if="detail.handleSignature" :src="getFullUrl(detail.handleSignature)" :preview-src-list="[getFullUrl(detail.handleSignature)]" alt="签字" />
              <span v-else class="waiting-text">暂无签名</span>
            </div>
          </el-form-item>
@@ -57,6 +57,7 @@
</template>
<script>
import { getFullUrl } from '@/utils/utils'
export default {
  name: 'PrimitiveCellDetailDialog',
  props: {
@@ -75,6 +76,10 @@
    }
  },
  methods: {
    getFullUrl(url) {
      if (url) return getFullUrl(url);
      return ''
    },
    handleClose() {
      this.$emit('update:visible', false)
    }
culture/src/views/strain-library/validation/primitive-cell/DetailConditionDialog.vue
@@ -28,7 +28,7 @@
          </div>
          <div class="info-item signature-item">
            <div class="signature-area">
              <img v-if="detail.handleSignature" :src="detail.handleSignature" alt="签字" />
              <el-image style="width: 120px;" v-if="detail.handleSignature" :src="getFullUrl(detail.handleSignature)" :preview-src-list="[getFullUrl(detail.handleSignature)]" alt="签字" />
              <span v-else class="waiting-text">暂无签名</span>
            </div>
          </div>
@@ -73,6 +73,7 @@
<script>
import DetailConditionDialog from "./DetailConditionDialog.vue";
import { detail } from "./service.js";
import { getFullUrl } from "@/utils/utils.js";
import moment from "moment";
export default {
@@ -139,6 +140,9 @@
    },
  },
  methods: {
    getFullUrl(url){
      if (url) return getFullUrl(url);
    },
    handleOpen() {
      if (this.value.id) {
        detail({ id: this.value.id }).then(res => {
culture/src/views/strain-library/validation/primitive-cell/add.vue
@@ -104,7 +104,7 @@
        handleSignatureConfirm(signatureImage) {
            this.signatureVisible = false;
            const id = this.$route.query.id || this.$route.params.id;
            const submitData = { ...this.form, strainType: 1, isDraft:this.form.isDraft?this.form.isDraft:this.currentAction == 'submit' ? 0 : 1, handleSignature: signatureImage.signature, experimentTime:this.form.experimentTime?moment(this.form.experimentTime).format('YYYY-MM-DD'):''};
            const submitData = { ...this.form, strainType: 1, isDraft: this.form.isDraft && this.currentAction === 'draft' ? this.form.isDraft : this.currentAction == 'submit' ? 0 : 1, handleSignature: signatureImage.signature, experimentTime: this.form.experimentTime ? moment(this.form.experimentTime).format('YYYY-MM-DD') : '' };
            if (this.currentAction === 'submit' || this.form.isDraft == 1) {
                if (id) {
                    // 编辑
culture/src/views/strain-library/validation/primitive-cell/confirm-detail.vue
@@ -16,7 +16,7 @@
          <div class="info-item sign-label"><span class="label">菌种实验员签字</span></div>
          <div class="info-item signature-item">
            <div class="signature-area">
              <img v-if="detail.handleSignature" :src="detail.handleSignature" alt="签字" />
              <el-image style="width: 120px;" v-if="detail.handleSignature" :src="getFullUrl(detail.handleSignature)" :preview-src-list="[getFullUrl(detail.handleSignature)]" alt="签字" />
              <span v-else class="waiting-text">暂无签名</span>
            </div>
          </div>
@@ -75,6 +75,7 @@
import DetailConditionDialog from './DetailConditionDialog.vue'
import { detail,confirm } from './service'
import moment from 'moment'
import {getFullUrl} from '@/utils/utils'
export default {
  name: 'ConfirmDetail',
  components: { EditConditionDialog, DetailConditionDialog },
@@ -140,6 +141,12 @@
    this.getDetail()
  },
  methods: {
    getFullUrl(url) {
      if (url) {
        return getFullUrl(url)
      }
      return ''
    },
    getDetail() {
      detail({id: this.$route.query.id}).then(res => {
        this.detail = {
culture/src/views/strain-library/validation/primitive-cell/index.vue
@@ -61,11 +61,11 @@
        </el-table-column>
        <el-table-column label="操作" width="200">
          <template #default="{ row }">
            <el-button type="text" v-if="row.status == 1 && roleType == 3&&currentType != 'draft'"
            <el-button  style="margin-right: 10px" type="text" v-if="row.status == 1 && roleType == 3&&currentType != 'draft'"
              @click="$router.push('/strain/validation/confirm-detail?id=' + row.id)">确认</el-button>
            <el-button type="text" v-if="roleType == 4" @click="handleDetail(row)">详情</el-button>
            <el-button type="text" v-if="roleType != 4" @click="handleDetail2(row)">详情</el-button>
            <el-button type="text" v-if="(roleType == 1) || (roleType == 4 && currentType == 'draft')"
            <el-button  style="margin-right: 10px" type="text" v-if="roleType == 4" @click="handleDetail(row)">详情</el-button>
            <el-button  style="margin-right: 10px" type="text" v-if="roleType != 4" @click="handleDetail2(row)">详情</el-button>
            <el-button  style="margin-right: 10px" type="text" v-if="(roleType == 1) || (roleType == 4 && currentType == 'draft')"
              @click="handleEdit(row)">编辑</el-button>
            <el-button type="text" v-if="(roleType == 1) || (roleType == 4 && currentType == 'draft')"
              @click="handleDelete(row)">删除</el-button>
culture/src/views/strain-library/validation/primitive-cell/primitive-cell-detail-dialog.vue
@@ -41,7 +41,7 @@
        <el-col :span="12">
          <el-form-item label="菌种实验员签字">
            <div class="signature-area">
              <img v-if="detail.handleSignature" :src="detail.handleSignature" alt="签字" />
              <el-image v-if="detail.handleSignature" :src="getFullUrl(detail.handleSignature)" :preview-src-list="[getFullUrl(detail.handleSignature)]" alt="签字" />
              <span v-else class="waiting-text">暂无签名</span>
            </div>
          </el-form-item>
@@ -57,6 +57,7 @@
</template>
<script>
import { getFullUrl } from '@/utils/utils'
export default {
  name: 'PrimitiveCellDetailDialog',
  props: {
@@ -75,6 +76,9 @@
    }
  },
  methods: {
    getFullUrl(url) {
      return getFullUrl(url)
    },
    handleClose() {
      this.$emit('update:visible', false)
    }
culture/src/views/strainReportLibrary/reportLibraryOne/add.vue
@@ -58,8 +58,10 @@
                    </div>
                </div>
                <el-form-item prop="name" style="margin-top: 38px">
                    <el-upload action="https://jsonplaceholder.typicode.com/posts/" :file-list="fileList">
                    <el-upload action="#" :file-list="fileList" :http-request="handleUpload"
                        :before-upload="beforeUpload" :on-preview="handlePreview" :on-remove="handleRemove">
                        <el-button size="small" type="primary">点击上传</el-button>
                        <div slot="tip" class="el-upload__tip">支持任意格式文件上传</div>
                    </el-upload>
                </el-form-item>
                <div class="header-title" style="width: 100%;">
@@ -160,6 +162,7 @@
import { Card } from 'element-ui';
import AiEditor from '@/components/AiEditor'
import ChooseProject from '@/components/chooseProject/index.vue'
import { customUploadRequest, getFullUrl } from "@/utils/utils";
import SelectMember from '@/components/SelectMember'
import { add, getDetail, update } from './service'
@@ -248,7 +251,6 @@
        if (this.$route.query.id) {
            getDetail(this.$route.query.id).then(res => {
                const data = res.data || res;
                console.log('qweqweqweq', data);
                this.form = {
                    ...this.form,
@@ -257,7 +259,7 @@
                };
                let arr = data.evaluate.split(',')
                this.fileList = (data.enclosureUrl ? JSON.parse(data.enclosureUrl) : [])
                this.assessmentTable = this.assessmentTable.map((item, index) => {
                    item.selectedScore = Number(arr[index])
                    return item
@@ -282,6 +284,62 @@
        }
    },
    methods: {
        handlePreview(file) {
            if (file.url) {
                window.open(file.url, '_blank');
            } else {
                this.$message.warning('无可预览地址');
            }
        },
        // 上传前校验
        beforeUpload(file) {
            return true;
        },
        // 自定义上传处理
        handleUpload(options) {
            const { file, onSuccess, onError } = options;
            // 使用封装的customUploadRequest方法
            customUploadRequest({
                file,
                onSuccess: (res) => {
                    if (res.code === 200) {
                        const fileObj = {
                            id: new Date().getTime(),
                            reportId: this.$route.query.id ? this.$route.query.id : "",
                            fileUrl: res.msg || res.data || "",
                            reportType: 2, // 报告类型
                            fileName: file.name,
                            fileSize: file.size,
                        };
                        // 添加到文件列表显示
                        this.fileList.push({
                            name: file.name,
                            url: getFullUrl(fileObj.fileUrl),
                            uid: fileObj.id,
                        });
                        // 添加到表单数据
                        this.form.feasibilityReportFiles.push(fileObj);
                        this.$message.success("文件上传成功");
                        onSuccess(res);
                    }
                },
                onError: (err) => {
                    this.$message.error("文件上传失败");
                    onError(err);
                },
            });
        },
        // 删除文件
        handleRemove(file) {
            const index = this.fileList.findIndex((item) => item.name === file.name);
            if (index !== -1) {
                this.fileList.splice(index, 1);
                this.form.feasibilityReportFiles.splice(index, 1);
            }
        },
        addData() {
            if (this.submitLoading) return;
            this.submitLoading = true;
@@ -309,9 +367,10 @@
                    return;
                }
                const params = {
                    isDraft: this.form.isDraft ? this.form.isDraft : 0,
                    isDraft: 0,
                    projectId: this.projectData[0].id,
                    experimentId: this.selectedMember[0].userId,
                    enclosureUrl: this.fileList.length && JSON.stringify(this.fileList),
                    // reportCode: this.form.reportCode,
                    reportContent: this.$refs.materialEditor.getContent(),
                    reportName: this.form.reportName,
@@ -360,6 +419,7 @@
                    isDraft: 1,
                    projectId: this.projectData[0].id,
                    experimentId: this.selectedMember[0].userId,
                    enclosureUrl: this.fileList.length && JSON.stringify(this.fileList),
                    // reportCode: this.form.reportCode,
                    reportContent: this.$refs.materialEditor.getContent(),
                    reportName: this.form.reportName,
@@ -367,6 +427,20 @@
                    evaluate: str.join(','),
                    reportType: 1
                };
                if (this.$route.query.id) {
                    params.id = this.$route.query.id
                    update(params).then(res => {
                        this.submitLoading = false;
                        if (res.code == 200) {
                            this.$message.success('编辑成功');
                            this.$router.back()
                        } else {
                            this.$message.error(res.msg || '编辑失败');
                            this.submitLoading = false;
                        }
                    }).catch(() => { this.submitLoading = false; })
                    return
                }
                add(params).then(res => {
                    this.submitLoading = false;
                    if (res.code == 200) {
culture/src/views/strainReportLibrary/reportLibraryOne/components/approval/index.vue
@@ -1,5 +1,5 @@
<template>
    <el-dialog  :title="dialogTitle" :visible.sync="visible" width="80%" @open="open" po :close-on-click-modal="false"
    <el-dialog :title="dialogTitle" :visible.sync="visible" width="80%" @open="open" po :close-on-click-modal="false"
        @close="handleClose">
        <div class="approval-dialog" :style="{ height: obj?.isDetail ? '50vh' : '40vh' }">
            <!-- 左侧审批内容 -->
@@ -55,6 +55,19 @@
                            <el-form-item prop="reportContent" style="margin-top: 38px">
                                <ai-editor :readOnly="true" :value="form.reportContent" style="width: 100%;"
                                    placeholder="请输入报告正文" />
                            </el-form-item>
                            <div class="header-title" style="width: 100%;">
                                <div class="header-title-left">
                                    <img src="@/assets/public/headercard.png" />
                                    <span>附件</span>
                                </div>
                            </div>
                            <el-form-item prop="name" style="margin-top: 38px">
                                <el-upload action="#" :file-list="fileList" disabled list-type="text" :on-preview="handlePreview">
                                    <el-button size="small" type="primary">点击上传</el-button>
                                    <div slot="tip" class="el-upload__tip">支持任意格式文件上传</div>
                                </el-upload>
                            </el-form-item>
                            <div class="header-title" style="width: 100%;">
@@ -190,6 +203,7 @@
                auditPersonName: "",
                auditTime: ""
            },
            fileList:[],
            assessmentTable: [
                {
                    category: '1.该分题的菌种专业知识',
@@ -236,7 +250,7 @@
    computed: {
        dialogTitle() {
            return !this.obj.isDetail? "审批" : "详情";
            return !this.obj.isDetail ? "审批" : "详情";
        },
        totalScore() {
            return this.assessmentTable.reduce((sum, item) => sum + (item.selectedScore || 0), 0);
@@ -244,7 +258,7 @@
    },
    methods: {
        open() {
            if (!this.obj.id) {
                this.$message.error('缺少必要参数');
@@ -260,6 +274,8 @@
                };
                let arr = data.evaluate.split(',')
                this.fileList = (data.enclosureUrl ? JSON.parse(data.enclosureUrl) : [])
                this.assessmentTable = this.assessmentTable.map((item, index) => {
                    item.selectedScore = Number(arr[index])
@@ -281,7 +297,7 @@
                    ]
                });
                if (data.status >=2) {
                if (data.status >= 2) {
                    processData.push({
                        type: data.status >= 2 && data.status != 4 ? "primary" : "danger",
                        mode: "list",
@@ -375,6 +391,13 @@
            this.form.pageSize = size
            this.getList()
        },
        handlePreview(file) {
            if (file.url) {
                window.open(file.url, '_blank');
            } else {
                this.$message.warning('无可预览地址');
            }
        },
    },
};
</script>
culture/src/views/strainReportLibrary/reportLibraryOne/components/evaluate/index.vue
@@ -43,7 +43,7 @@
                                            <el-radio :label="1">1分</el-radio>
                                            <el-radio :label="2">2分</el-radio>
                                        </el-radio-group>
                                        <el-radio-group :disabled="obj.viewDetail" v-else
                                        <el-radio-group :disabled="true" v-else
                                            v-model="scope.row.selectedScore">
                                            <el-radio :label="0">否0分</el-radio>
                                            <el-radio :label="-3">是-3分</el-radio>
@@ -65,9 +65,6 @@
                                </el-table-column>
                            </el-table>
                        </div>
                    </el-form>
                </template>
                <!-- <SelectMember ref="selectMember" /> -->
@@ -87,7 +84,7 @@
<script>
import ApprovalProcess from '@/components/approvalProcess'
import AiEditor from '@/components/AiEditor'
import { viewEvaluate } from '../../service'
import { viewEvaluate,getDetail } from '../../service'
@@ -201,6 +198,11 @@
                })
            } else {
                getDetail(this.obj.id).then(res=>{
                   this.assessmentTable[4].selectedScore = res.evaluate.split(',')[4] * 1
                })
                this.form.reportCode = this.obj.reportCode || '';
                this.form.reportName = this.obj.reportName || '';
                this.form.id = this.obj.id || '';
culture/src/views/strainReportLibrary/reportLibraryOneFour/add.vue
@@ -58,8 +58,10 @@
                    </div>
                </div>
                <el-form-item prop="name" style="margin-top: 38px">
                    <el-upload action="https://jsonplaceholder.typicode.com/posts/" :file-list="fileList">
                    <el-upload action="#" :file-list="fileList" :http-request="handleUpload"
                        :before-upload="beforeUpload" :on-preview="handlePreview" :on-remove="handleRemove">
                        <el-button size="small" type="primary">点击上传</el-button>
                        <div slot="tip" class="el-upload__tip">支持任意格式文件上传</div>
                    </el-upload>
                </el-form-item>
                <div class="header-title" style="width: 100%;">
@@ -149,7 +151,8 @@
            </el-form>
        </Card>
        <chooseProject @submit="getProjectData" :show="showChoose" @close="showChoose = false"></chooseProject>
        <SelectMember  :projectId="projectData.length&&projectData[0].id" title="选择菌种实验员" ref="selectMember" @submit="selectUser" :singleSelect="true" />
        <SelectMember :projectId="projectData.length && projectData[0].id" title="选择菌种实验员" ref="selectMember"
            @submit="selectUser" :singleSelect="true" />
    </div>
@@ -158,8 +161,9 @@
import { Card } from 'element-ui';
import AiEditor from '@/components/AiEditor'
import ChooseProject from '@/components/chooseProject/index.vue'
import { customUploadRequest, getFullUrl } from "@/utils/utils";
import SelectMember from '@/components/SelectMember'
import { add,getDetail,update } from './service'
import { add, getDetail, update } from './service'
export default {
    components: { AiEditor, ChooseProject, SelectMember },
@@ -241,8 +245,8 @@
        }
    },
    mounted() {
          // 滚动到页面顶部
          this.$nextTick(() => {
        // 滚动到页面顶部
        this.$nextTick(() => {
            window.scrollTo(0, 0);
        });
        if (this.$route.query.id) {
@@ -255,15 +259,15 @@
                };
                let arr = data.evaluate.split(',')
                this.fileList = (data.enclosureUrl ? JSON.parse(data.enclosureUrl) : [])
                this.assessmentTable = this.assessmentTable.map((item, index) => {
                    item.selectedScore = Number(arr[index])
                    return item
                })
                this.selectedMember = [{
                    nickName:data.experimentName,
                    userId:data.experimentId
                    nickName: data.experimentName,
                    userId: data.experimentId
                }]
                this.projectData = data.projectTeam ?
@@ -277,6 +281,63 @@
        }
    },
    methods: {
        handlePreview(file) {
            if (file.url) {
                window.open(file.url, '_blank');
            } else {
                this.$message.warning('无可预览地址');
            }
        },
        // 上传前校验
        beforeUpload(file) {
            return true;
        },
        // 自定义上传处理
        handleUpload(options) {
            const { file, onSuccess, onError } = options;
            // 使用封装的customUploadRequest方法
            customUploadRequest({
                file,
                onSuccess: (res) => {
                    if (res.code === 200) {
                        const fileObj = {
                            id: new Date().getTime(),
                            reportId: this.$route.query.id ? this.$route.query.id : "",
                            fileUrl: res.msg || res.data || "",
                            reportType: 2, // 报告类型
                            fileName: file.name,
                            fileSize: file.size,
                        };
                        // 添加到文件列表显示
                        this.fileList.push({
                            name: file.name,
                            url: getFullUrl(fileObj.fileUrl),
                            uid: fileObj.id,
                        });
                        // 添加到表单数据
                        this.form.feasibilityReportFiles.push(fileObj);
                        this.$message.success("文件上传成功");
                        onSuccess(res);
                    }
                },
                onError: (err) => {
                    this.$message.error("文件上传失败");
                    onError(err);
                },
            });
        },
        // 删除文件
        handleRemove(file) {
            const index = this.fileList.findIndex((item) => item.name === file.name);
            if (index !== -1) {
                this.fileList.splice(index, 1);
                this.form.feasibilityReportFiles.splice(index, 1);
            }
        },
        async addData() {
            if (this.submitLoading) return;
            this.submitLoading = true;
@@ -304,14 +365,15 @@
                        return;
                    }
                    const params = {
                        isDraft: this.form.isDraft ? this.form.isDraft : 0,
                        isDraft: 0,
                        projectId: this.projectData[0].id,
                        experimentId: this.selectedMember[0].userId,
                        enclosureUrl: this.fileList.length && JSON.stringify(this.fileList),
                        // reportCode: this.form.reportCode,
                        reportContent: this.$refs.materialEditor.getContent(),
                        reportName: this.form.reportName,
                        evaluateTotal: this.totalScore,
                        evaluate: str.join(','),
                        evaluate: str.join(','),
                        reportType: 4
                    };
                    if (this.$route.query.id) {
@@ -356,6 +418,7 @@
                            isDraft: 1,
                            projectId: this.projectData[0].id,
                            experimentId: this.selectedMember[0].userId,
                            enclosureUrl: this.fileList.length && JSON.stringify(this.fileList),
                            // reportCode: this.form.reportCode,
                            reportContent: this.$refs.materialEditor.getContent(),
                            reportName: this.form.reportName,
@@ -363,6 +426,19 @@
                            evaluate: str.join(','),
                            reportType: 4
                        };
                        if (this.$route.query.id) {
                            params.id = this.$route.query.id
                            update(params).then(res => {
                                this.submitLoading = false;
                                if (res.code == 200) {
                                    this.$message.success('编辑成功');
                                    this.$router.back()
                                }
                            }).catch(() => {
                                this.submitLoading = false;
                            })
                            return
                        }
                        add(params).then(res => {
                            this.draftLoading = false;
                            if (res.code == 200) {
culture/src/views/strainReportLibrary/reportLibraryOneFour/components/approval/index.vue
@@ -57,6 +57,13 @@
                                    placeholder="请输入报告正文" />
                            </el-form-item>
                            <el-form-item prop="name" style="margin-top: 38px">
                                <el-upload action="#" :file-list="fileList" disabled list-type="text" :on-preview="handlePreview">
                                    <el-button size="small" type="primary">点击上传</el-button>
                                    <div slot="tip" class="el-upload__tip">支持任意格式文件上传</div>
                                </el-upload>
                            </el-form-item>
                            <div class="header-title" style="width: 100%;">
                                <div class="header-title-left">
                                    <img src="@/assets/public/headercard.png" />
@@ -190,6 +197,7 @@
                auditPersonName: "",
                auditTime: ""
            },
            fileList:[],
            assessmentTable: [
                {
                    category: '1.该分题的菌种专业知识',
@@ -243,6 +251,13 @@
        }
    },
    methods: {
        handlePreview(file) {
            if (file.url) {
                window.open(file.url, '_blank');
            } else {
                this.$message.warning('无可预览地址');
            }
        },
        open() {
            if (!this.obj.id) {
                this.$message.error('缺少必要参数');
@@ -258,7 +273,7 @@
                };
                let arr = data.evaluate.split(',')
                this.fileList = (data.enclosureUrl ? JSON.parse(data.enclosureUrl) : [])
                this.assessmentTable = this.assessmentTable.map((item, index) => {
                    item.selectedScore = Number(arr[index])
                    return item
culture/src/views/strainReportLibrary/reportLibraryOneFour/components/evaluate/index.vue
@@ -195,10 +195,10 @@
    },
    computed: {
        currentAssessmentTable() {
            return this.type == 1 || this.type == '1' ? this.assessmentTable2 : this.assessmentTable1;
            return this.type == 2 || this.type == '2' ? this.assessmentTable1 : this.assessmentTable2;
        },
        fullScore() {
            return this.type == 1 || this.type == '1'
            return this.type == 2 || this.type == '2'
                ? this.assessmentTable1.reduce((sum, item) => sum + (item.fullScore || 0), 0)
                : this.assessmentTable2.reduce((sum, item) => sum + (item.fullScore || 0), 0);
        },
culture/src/views/strainReportLibrary/reportLibraryOneTWO/add.vue
@@ -58,8 +58,10 @@
                    </div>
                </div>
                <el-form-item prop="name" style="margin-top: 38px">
                    <el-upload action="https://jsonplaceholder.typicode.com/posts/" :file-list="fileList">
                    <el-upload action="#" :file-list="fileList" :http-request="handleUpload"
                        :before-upload="beforeUpload" :on-preview="handlePreview" :on-remove="handleRemove">
                        <el-button size="small" type="primary">点击上传</el-button>
                        <div slot="tip" class="el-upload__tip">支持任意格式文件上传</div>
                    </el-upload>
                </el-form-item>
                <div class="header-title" style="width: 100%;">
@@ -149,7 +151,8 @@
            </el-form>
        </Card>
        <chooseProject @submit="getProjectData" :show="showChoose" @close="showChoose = false"></chooseProject>
        <SelectMember :projectId="projectData.length&&projectData[0].id" title="选择菌种实验员" ref="selectMember" @submit="selectUser" :singleSelect="true" />
        <SelectMember :projectId="projectData.length && projectData[0].id" title="选择菌种实验员" ref="selectMember"
            @submit="selectUser" :singleSelect="true" />
    </div>
@@ -158,8 +161,9 @@
import { Card } from 'element-ui';
import AiEditor from '@/components/AiEditor'
import ChooseProject from '@/components/chooseProject/index.vue'
import { customUploadRequest, getFullUrl } from "@/utils/utils";
import SelectMember from '@/components/SelectMember'
import { add, getDetail,update } from './service'
import { add, getDetail, update } from './service'
export default {
    components: { AiEditor, ChooseProject, SelectMember },
@@ -240,8 +244,8 @@
        }
    },
    mounted() {
          // 滚动到页面顶部
          this.$nextTick(() => {
        // 滚动到页面顶部
        this.$nextTick(() => {
            window.scrollTo(0, 0);
        });
        if (this.$route.query.id) {
@@ -255,10 +259,10 @@
                let arr = data.evaluate.split(',')
                this.selectedMember = [{
                    nickName:data.experimentName,
                    userId:data.experimentId
                    nickName: data.experimentName,
                    userId: data.experimentId
                }]
                this.fileList = (data.enclosureUrl ? JSON.parse(data.enclosureUrl) : [])
                this.assessmentTable = this.assessmentTable.map((item, index) => {
                    item.selectedScore = Number(arr[index])
                    return item
@@ -275,6 +279,63 @@
        }
    },
    methods: {
        handlePreview(file) {
            if (file.url) {
                window.open(file.url, '_blank');
            } else {
                this.$message.warning('无可预览地址');
            }
        },
        // 上传前校验
        beforeUpload(file) {
            return true;
        },
        // 自定义上传处理
        handleUpload(options) {
            const { file, onSuccess, onError } = options;
            // 使用封装的customUploadRequest方法
            customUploadRequest({
                file,
                onSuccess: (res) => {
                    if (res.code === 200) {
                        const fileObj = {
                            id: new Date().getTime(),
                            reportId: this.$route.query.id ? this.$route.query.id : "",
                            fileUrl: res.msg || res.data || "",
                            reportType: 2, // 报告类型
                            fileName: file.name,
                            fileSize: file.size,
                        };
                        // 添加到文件列表显示
                        this.fileList.push({
                            name: file.name,
                            url: getFullUrl(fileObj.fileUrl),
                            uid: fileObj.id,
                        });
                        // 添加到表单数据
                        this.form.feasibilityReportFiles.push(fileObj);
                        this.$message.success("文件上传成功");
                        onSuccess(res);
                    }
                },
                onError: (err) => {
                    this.$message.error("文件上传失败");
                    onError(err);
                },
            });
        },
        // 删除文件
        handleRemove(file) {
            const index = this.fileList.findIndex((item) => item.name === file.name);
            if (index !== -1) {
                this.fileList.splice(index, 1);
                this.form.feasibilityReportFiles.splice(index, 1);
            }
        },
        addData() {
            if (this.loading) return;
            this.loading = true;
@@ -304,9 +365,10 @@
                    return;
                }
                const params = {
                    isDraft: this.form.isDraft ? this.form.isDraft : 0,
                    isDraft: 0,
                    projectId: this.projectData[0].id,
                    experimentId: this.selectedMember[0].userId,
                    enclosureUrl: this.fileList.length && JSON.stringify(this.fileList),
                    // reportCode: this.form.reportCode,
                    reportContent: this.$refs.materialEditor.getContent(),
                    reportName: this.form.reportName,
@@ -364,6 +426,7 @@
                        isDraft: 1,
                        projectId: this.projectData[0].id,
                        experimentId: this.selectedMember[0].userId,
                        enclosureUrl: this.fileList.length && JSON.stringify(this.fileList),
                        // reportCode: this.form.reportCode,
                        reportContent: this.$refs.materialEditor.getContent(),
                        reportName: this.form.reportName,
@@ -371,6 +434,23 @@
                        evaluate: str.join(','),
                        reportType: 2
                    };
                    if (this.$route.query.id) {
                        params.id = this.$route.query.id
                        update(params).then(res => {
                            this.loading = false;
                            if (res.code == 200) {
                                this.$message.success('编辑成功');
                                this.$router.back()
                            } else {
                                this.$message.error(res.message || '编辑失败');
                            }
                        }).catch(() => {
                            this.loading = false;
                            this.$message.error('编辑失败');
                        })
                        return
                    }
                    add(params).then(res => {
                        this.loading = false;
                        if (res.code == 200) {
culture/src/views/strainReportLibrary/reportLibraryOneTWO/components/approval/index.vue
@@ -60,6 +60,20 @@
                            <div class="header-title" style="width: 100%;">
                                <div class="header-title-left">
                                    <img src="@/assets/public/headercard.png" />
                                    <span>附件</span>
                                </div>
                            </div>
                            <el-form-item prop="name" style="margin-top: 38px">
                                <el-upload action="#" :file-list="fileList" :http-request="handleUpload"
                                    :before-upload="beforeUpload" :on-preview="handlePreview" :on-remove="handleRemove">
                                    <el-button size="small" type="primary">点击上传</el-button>
                                    <div slot="tip" class="el-upload__tip">支持任意格式文件上传</div>
                                </el-upload>
                            </el-form-item>
                            <div class="header-title" style="width: 100%;">
                                <div class="header-title-left">
                                    <img src="@/assets/public/headercard.png" />
                                    <div>菌种实验员操作评定</div>
                                </div>
                            </div>
@@ -227,6 +241,7 @@
                    rule: ''
                }
            ],
            fileList: [], // 附件列表
            tableData: [],
            rules: {},
            status: "2",
@@ -235,13 +250,20 @@
    },
    computed: {
        dialogTitle() {
            return !this.obj.isDetail? "审批" : "详情";
            return !this.obj.isDetail ? "审批" : "详情";
        },
        totalScore() {
            return this.assessmentTable.reduce((sum, item) => sum + (item.selectedScore || 0), 0);
        }
    },
    methods: {
        handlePreview(file) {
            if (file.url) {
                window.open(file.url, '_blank');
            } else {
                this.$message.warning('无可预览地址');
            }
        },
        open() {
            if (!this.obj.id) {
                this.$message.error('缺少必要参数');
@@ -257,7 +279,7 @@
                };
                let arr = data.evaluate.split(',')
                this.fileList = (data.enclosureUrl ? JSON.parse(data.enclosureUrl) : [])
                this.assessmentTable = this.assessmentTable.map((item, index) => {
                    item.selectedScore = Number(arr[index])
                    return item
@@ -278,7 +300,7 @@
                    ]
                });
                if (data.status >=2) {
                if (data.status >= 2) {
                    processData.push({
                        type: data.status >= 2 && data.status != 4 ? "primary" : "danger",
                        mode: "list",
culture/src/views/strainReportLibrary/reportLibraryOneTWO/components/evaluate/index.vue
@@ -195,10 +195,10 @@
    },
    computed: {
        currentAssessmentTable() {
            return this.type == 1 || this.type == '1' ? this.assessmentTable2 : this.assessmentTable1;
            return this.type == 2 || this.type == '2' ? this.assessmentTable1 : this.assessmentTable2;
        },
        fullScore() {
            return this.type == 1 || this.type == '1'
            return this.type == 2 || this.type == '2'
                ? this.assessmentTable1.reduce((sum, item) => sum + (item.fullScore || 0), 0)
                : this.assessmentTable2.reduce((sum, item) => sum + (item.fullScore || 0), 0);
        },
culture/src/views/strainReportLibrary/reportLibraryOneThree/add.vue
@@ -58,8 +58,10 @@
                    </div>
                </div>
                <el-form-item prop="name" style="margin-top: 38px">
                    <el-upload action="https://jsonplaceholder.typicode.com/posts/" :file-list="fileList">
                    <el-upload action="#" :file-list="fileList" :http-request="handleUpload"
                        :before-upload="beforeUpload" :on-preview="handlePreview" :on-remove="handleRemove">
                        <el-button size="small" type="primary">点击上传</el-button>
                        <div slot="tip" class="el-upload__tip">支持任意格式文件上传</div>
                    </el-upload>
                </el-form-item>
                <div class="header-title" style="width: 100%;">
@@ -149,7 +151,8 @@
            </el-form>
        </Card>
        <chooseProject @submit="getProjectData" :show="showChoose" @close="showChoose = false"></chooseProject>
        <SelectMember  :projectId="projectData.length&&projectData[0].id" title="选择菌种实验员" ref="selectMember" @submit="selectUser" :singleSelect="true" />
        <SelectMember :projectId="projectData.length && projectData[0].id" title="选择菌种实验员" ref="selectMember"
            @submit="selectUser" :singleSelect="true" />
    </div>
@@ -158,8 +161,9 @@
import { Card } from 'element-ui';
import AiEditor from '@/components/AiEditor'
import ChooseProject from '@/components/chooseProject/index.vue'
import { customUploadRequest, getFullUrl } from "@/utils/utils";
import SelectMember from '@/components/SelectMember'
import { add,getDetail,update } from './service'
import { add, getDetail, update } from './service'
export default {
    components: { AiEditor, ChooseProject, SelectMember },
@@ -241,8 +245,8 @@
        }
    },
    mounted() {
          // 滚动到页面顶部
          this.$nextTick(() => {
        // 滚动到页面顶部
        this.$nextTick(() => {
            window.scrollTo(0, 0);
        });
        if (this.$route.query.id) {
@@ -255,14 +259,15 @@
                };
                let arr = data.evaluate.split(',')
                this.fileList = (data.enclosureUrl ? JSON.parse(data.enclosureUrl) : [])
                this.assessmentTable = this.assessmentTable.map((item, index) => {
                    item.selectedScore = Number(arr[index])
                    return item
                })
                this.selectedMember = [{
                    nickName:data.experimentName,
                    userId:data.experimentId
                    nickName: data.experimentName,
                    userId: data.experimentId
                }]
                this.projectData = data.projectTeam ?
                    [{ ...data.projectTeam, staffName: data.projectTeam.staff || '' }] :
@@ -275,6 +280,62 @@
        }
    },
    methods: {
        handlePreview(file) {
            if (file.url) {
                window.open(file.url, '_blank');
            } else {
                this.$message.warning('无可预览地址');
            }
        },
        // 上传前校验
        beforeUpload(file) {
            return true;
        },
        // 自定义上传处理
        handleUpload(options) {
            const { file, onSuccess, onError } = options;
            // 使用封装的customUploadRequest方法
            customUploadRequest({
                file,
                onSuccess: (res) => {
                    if (res.code === 200) {
                        const fileObj = {
                            id: new Date().getTime(),
                            reportId: this.$route.query.id ? this.$route.query.id : "",
                            fileUrl: res.msg || res.data || "",
                            reportType: 2, // 报告类型
                            fileName: file.name,
                            fileSize: file.size,
                        };
                        // 添加到文件列表显示
                        this.fileList.push({
                            name: file.name,
                            url: getFullUrl(fileObj.fileUrl),
                            uid: fileObj.id,
                        });
                        // 添加到表单数据
                        this.form.feasibilityReportFiles.push(fileObj);
                        this.$message.success("文件上传成功");
                        onSuccess(res);
                    }
                },
                onError: (err) => {
                    this.$message.error("文件上传失败");
                    onError(err);
                },
            });
        },
        // 删除文件
        handleRemove(file) {
            const index = this.fileList.findIndex((item) => item.name === file.name);
            if (index !== -1) {
                this.fileList.splice(index, 1);
                this.form.feasibilityReportFiles.splice(index, 1);
            }
        },
        addData() {
            if (this.sendLoading) return;
            this.sendLoading = true;
@@ -303,9 +364,10 @@
                    return;
                }
                const params = {
                    isDraft: this.form.isDraft ? this.form.isDraft : 0,
                    isDraft: 0,
                    projectId: this.projectData[0].id,
                    experimentId: this.selectedMember[0].userId,
                    enclosureUrl: this.fileList.length && JSON.stringify(this.fileList),
                    // reportCode: this.form.reportCode,
                    reportContent: this.$refs.materialEditor.getContent(),
                    reportName: this.form.reportName,
@@ -329,6 +391,19 @@
                    }).finally(() => {
                        this.sendLoading = false;
                    });
                    return
                }
                if (this.$route.query.id) {
                    params.id = this.$route.query.id
                    update(params).then(res => {
                        this.submitLoading = false;
                        if (res.code == 200) {
                            this.$message.success('编辑成功');
                            this.$router.back()
                        }
                    }).catch(() => {
                        this.submitLoading = false;
                    })
                    return
                }
@@ -357,10 +432,12 @@
                })
                try {
                    const params = {
                        isDraft: 1,
                        projectId: this.projectData[0].id,
                        experimentId: this.selectedMember[0].userId,
                        enclosureUrl: this.fileList.length && JSON.stringify(this.fileList),
                        // reportCode: this.form.reportCode,
                        reportContent: this.$refs.materialEditor.getContent(),
                        reportName: this.form.reportName,
culture/src/views/strainReportLibrary/reportLibraryOneThree/components/approval/index.vue
@@ -56,6 +56,20 @@
                                <ai-editor :readOnly="true" :value="form.reportContent" style="width: 100%;"
                                    placeholder="请输入报告正文" />
                            </el-form-item>
                            <div class="header-title" style="width: 100%;">
                                <div class="header-title-left">
                                    <img src="@/assets/public/headercard.png" />
                                    <span>附件</span>
                                </div>
                            </div>
                            <el-form-item prop="name" style="margin-top: 38px">
                                <el-upload action="#" :file-list="fileList" :http-request="handleUpload"
                                    :before-upload="beforeUpload" :on-preview="handlePreview" :on-remove="handleRemove">
                                    <el-button size="small" type="primary">点击上传</el-button>
                                    <div slot="tip" class="el-upload__tip">支持任意格式文件上传</div>
                                </el-upload>
                            </el-form-item>
                            <div class="header-title" style="width: 100%;">
                                <div class="header-title-left">
@@ -190,6 +204,7 @@
                auditPersonName: "",
                auditTime: ""
            },
            fileList: [],
            assessmentTable: [
                {
                    category: '1.该分题的菌种专业知识',
@@ -257,7 +272,7 @@
                };
                let arr = data.evaluate.split(',')
                this.fileList = (data.enclosureUrl ? JSON.parse(data.enclosureUrl) : [])
                this.assessmentTable = this.assessmentTable.map((item, index) => {
                    item.selectedScore = Number(arr[index])
                    return item
culture/src/views/strainReportLibrary/reportLibraryOneThree/components/evaluate/index.vue
@@ -195,10 +195,10 @@
    },
    computed: {
        currentAssessmentTable() {
            return this.type == 1 || this.type == '1' ? this.assessmentTable2 : this.assessmentTable1;
            return this.type == 2 || this.type == '2' ? this.assessmentTable1 : this.assessmentTable2;
        },
        fullScore() {
            return this.type == 1 || this.type == '1'
            return this.type == 2 || this.type == '2'
                ? this.assessmentTable1.reduce((sum, item) => sum + (item.fullScore || 0), 0)
                : this.assessmentTable2.reduce((sum, item) => sum + (item.fullScore || 0), 0);
        },