puzhibing
6 天以前 0db8c9b94a9ac655549d5eba5c2a6c92ac386c06
culture/src/views/strain-library/breeding-record/SlantRecordDialog.vue
@@ -1,15 +1,15 @@
<template>
  <el-dialog :visible.sync="visible" :title="editData ? '编辑培养皿观察记录' : '新增培养皿观察记录'" width="80%" @close="handleClose">
  <el-dialog :visible.sync="visible" :title="readOnly ? '培养皿观察记录详情' : editData ? '编辑培养皿观察记录' : '新增培养皿观察记录'" width="80%" @close="handleClose">
    <el-form :model="form" :rules="rules" ref="formRef" label-width="120px" label-position="top">
      <el-row :gutter="24">
        <el-col :span="12">
          <el-form-item label="分离菌落编号" prop="separateColonyCode">
            <el-input :disabled="roleType!=4" v-model="form.separateColonyCode" placeholder="请输入分离菌落编号" />
            <el-input :disabled="!editable" v-model="form.separateColonyCode" placeholder="请输入分离菌落编号" />
          </el-form-item>
        </el-col>
        <el-col :span="12">
          <el-form-item label="形状强壮度排名" prop="strength">
            <el-input :disabled="roleType!=4" v-model="form.strength" placeholder="请输入形状强壮度排名" />
            <el-input :disabled="!editable" v-model="form.strength" placeholder="请输入形状强壮度排名" />
          </el-form-item>
        </el-col>
      </el-row>
@@ -22,17 +22,49 @@
      </el-table-column>
      <el-table-column prop="desc" label="形态记录">
        <template #default="{ row }">
          <el-input class="el-input-full" style="width: 100%;" :disabled="roleType!=4" v-model="row.desc" placeholder="请输入形态记录"
          <el-input class="el-input-full" style="width: 100%;" :disabled="!editable" v-model="row.desc" placeholder="请输入形态记录"
            @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" 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="beforeUpload"
            action="#" :limit="5" class="mini-upload">
            <i class="el-icon-plus"></i>
          <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="!editable || 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="getFullUrl(img.url)"
                  :preview-src-list="row.images.map(i => getFullUrl(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="editable" @click.stop="rowHandleRemove(row, img, row.images.filter(f => f !== img))"></i>
              </div>
              <div
                v-if="editable"
                class="upload-thumb upload-btn"
                @click.stop="triggerUpload(row.index)"
              >
                <i class="el-icon-plus"></i>
              </div>
            </div>
          </el-upload>
        </template>
      </el-table-column>
@@ -42,18 +74,23 @@
        </template>
      </el-table-column>
    </el-table>
    <div style="text-align: center;" v-if="roleType==4">
      <el-button type="primary" @click="handleOk">保存</el-button>
    <div style="text-align: center;" v-if="editable">
      <el-button type="primary" @click="handleOk" :disabled="hasUploading">保存</el-button>
    </div>
  </el-dialog>
</template>
<script>
import moment from 'moment'
import { customUploadRequest, getFullUrl, getUploadedFilePath, normalizeFileList } from '@/utils/utils'
export default {
  name: 'SlantRecordDialog',
  props: {
    visible: Boolean,
    readOnly: {
      type: Boolean,
      default: false
    },
    editData: {
      type: Object,
      default: null
@@ -81,7 +118,8 @@
        index: i + 1,
        desc: '',
        time: '',
        images: []
        images: [],
        uploading: false
      }))
    }
  },
@@ -98,14 +136,16 @@
                index: index + 1,
                desc: record.desc || '',
                time: record.time || this.getNowTime(),
                images: record.images || []
                images: this.normalizeImages(record.images),
                uploading: false
              }))
              while (this.tableData.length < 10) {
                this.tableData.push({
                  index: this.tableData.length + 1,
                  desc: '',
                  time: '',
                  images: []
                  images: [],
                  uploading: false
                })
              }
            } catch (e) {
@@ -114,7 +154,8 @@
                index: i + 1,
                desc: '',
                time: '',
                images: []
                images: [],
                uploading: false
              }))
            }
          } else {
@@ -122,7 +163,8 @@
              index: i + 1,
              desc: '',
              time: '',
              images: []
              images: [],
              uploading: false
            }))
          }
        } else {
@@ -131,7 +173,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()
      }
    }
@@ -155,6 +208,7 @@
      }
    },
    handleOk() {
      if (!this.editable) return
      this.$refs.formRef.validate(valid => {
        if (!valid) return
@@ -177,7 +231,8 @@
          index: i + 1,
          desc: '',
          time: '',
          images: []
          images: [],
          uploading: false
        }))
        this.handleClose()
      })
@@ -185,33 +240,81 @@
    handleClose() {
      this.$emit('update:visible', false)
    },
    beforeUpload(file) {
      // 这里只做本地预览
      return new Promise(resolve => {
        const reader = new FileReader()
        reader.onload = e => {
          resolve()
    rowBeforeUpload(file, row) {
      if (!file.type || !file.type.startsWith('image/')) {
        this.$message.error('只能上传图片文件')
        return false
      }
      if (file.size > 10 * 1024 * 1024) {
        this.$message.error('图片大小不能超过 10MB')
        return false
      }
      return true
    },
    rowHandleUpload(options, row) {
      row.uploading = true;
      const { file, onSuccess, onError } = options;
      customUploadRequest({
        file,
        onSuccess: (res) => {
          const uploadedPath = getUploadedFilePath(res)
          if (uploadedPath) {
            const fileObj = {
              name: file.name,
              url: uploadedPath,
              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 && res.msg ? res.msg : '上传接口未返回文件地址');
            onError(new Error('missing uploaded file path'));
          }
          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) }))
    },
    handleRemove(row, file, fileList) {
      row.images = fileList
    rowHandleRemove(row, file, fileList) {
      row.images = fileList;
      row.uploading = false;
    },
    handlePreview(row, file) {
      this.previewImg = file.url
      this.previewVisible = true
    },
    handleDescBlur(row) {
      console.log(row)
      if (row.desc) {
      if (this.editable && row.desc) {
        row.time = this.getNowTime()
        this.$forceUpdate()
      }
    },
    triggerUpload(index) {
      if (!this.editable) return
      const uploadRef = this.$refs['uploadRef' + index];
      if (uploadRef && uploadRef.$el) {
        const input = uploadRef.$el.querySelector('input[type=file]');
        if (input) input.click();
      }
    },
    getFullUrl,
    normalizeImages(images) {
      return normalizeFileList(images)
    }
  },
  computed: {
    editable() {
      return [1, 4].includes(Number(this.roleType)) && !this.readOnly
    },
    hasUploading() {
      return this.tableData.some(row => row.uploading)
    }
  }
}
@@ -222,52 +325,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>