From 0db8c9b94a9ac655549d5eba5c2a6c92ac386c06 Mon Sep 17 00:00:00 2001
From: puzhibing <393733352@qq.com>
Date: 星期四, 08 十月 2026 11:20:56 +0800
Subject: [PATCH] 提交最新代码

---
 culture/src/views/strain-library/breeding-record/SlantRecordDialog.vue |  223 +++++++++++++++++++++++++++++++++++++------------------
 1 files changed, 150 insertions(+), 73 deletions(-)

diff --git a/culture/src/views/strain-library/breeding-record/SlantRecordDialog.vue b/culture/src/views/strain-library/breeding-record/SlantRecordDialog.vue
index f16bb5e..a2194f6 100644
--- a/culture/src/views/strain-library/breeding-record/SlantRecordDialog.vue
+++ b/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,24 +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 || 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="!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>
@@ -49,7 +74,7 @@
         </template>
       </el-table-column>
     </el-table>
-    <div style="text-align: center;" v-if="roleType==4">
+    <div style="text-align: center;" v-if="editable">
       <el-button type="primary" @click="handleOk" :disabled="hasUploading">保存</el-button>
     </div>
   </el-dialog>
@@ -57,10 +82,15 @@
 
 <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
@@ -106,7 +136,8 @@
                 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({
@@ -142,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()
       }
     }
@@ -166,6 +208,7 @@
       }
     },
     handleOk() {
+      if (!this.editable) return
       this.$refs.formRef.validate(valid => {
         if (!valid) return
 
@@ -197,37 +240,79 @@
     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) {
+      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) }))
-      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
       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)
     }
@@ -240,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>

--
Gitblit v1.7.1