From c3ccabfc59431aea657379c72c2e09c8e1b27d18 Mon Sep 17 00:00:00 2001
From: 董国庆 <364620639@qq.com>
Date: 星期五, 12 九月 2025 15:24:42 +0800
Subject: [PATCH] 图片回显

---
 laboratory/src/components/DynamicComponent/index.vue |  370 ++++++++++++++++++++++++++++++++++------------------
 1 files changed, 242 insertions(+), 128 deletions(-)

diff --git a/laboratory/src/components/DynamicComponent/index.vue b/laboratory/src/components/DynamicComponent/index.vue
index a093c13..54bfc79 100644
--- a/laboratory/src/components/DynamicComponent/index.vue
+++ b/laboratory/src/components/DynamicComponent/index.vue
@@ -4,40 +4,91 @@
       <div class="add-group">
         <div v-if="title">*</div>
         <span v-if="title">{{ title }}</span>
-        <el-button v-if="editable" @click="showAddDialog = true" class="el-icon-plus" type="primary">
-          添加组件</el-button>
+        <el-button
+          v-if="editable"
+          @click="showAddDialog = true"
+          class="el-icon-plus"
+          type="primary"
+        >
+          添加组件</el-button
+        >
       </div>
 
       <!-- 选择组件弹窗 -->
-      <AddComponentDialog v-if="editable" :visible="showAddDialog" @confirm="addComponent"
-        @close="showAddDialog = false" />
+      <AddComponentDialog
+        v-if="editable"
+        :visible="showAddDialog"
+        @confirm="addComponent"
+        @close="showAddDialog = false"
+      />
 
       <!-- 动态渲染组件 -->
-      <div v-for="(item, idx) in components" :key="item.id" class="dynamic-component">
+      <div
+        v-for="(item, idx) in components"
+        :key="item.id"
+        class="dynamic-component"
+      >
         <!-- 富文本 -->
         <div v-if="item.type == 'richText'">
-          <AiEditor :ref="`editor_${item.id}`" :value="item.data.content" height="200px" :readOnly="!editable"
-            placeholder="请输入内容..." :disabled="!editable" />
+          <AiEditor
+            :ref="`editor_${item.id}`"
+            :value="item.data.content"
+            height="400px"
+            :readOnly="!editable"
+            placeholder="请输入内容..."
+            :disabled="!editable"
+          />
         </div>
         <!-- 自定义表格 -->
         <div v-else-if="item.type == 'customTable'" style="flex: 1">
           <div v-if="editable" class="table-actions">
-            <el-button size="mini" @click="showTableHeaderDialog(idx)">添加表头</el-button>
-            <el-button size="mini" type="primary" @click="showAddRowDialog(idx, item.data.headers)">添加数据</el-button>
+            <el-button size="mini" @click="showTableHeaderDialog(idx)"
+              >添加表头</el-button
+            >
+            <el-button
+              size="mini"
+              type="primary"
+              @click="showAddRowDialog(idx, item.data.headers)"
+              >添加数据</el-button
+            >
           </div>
-          <Table :data="item.data.rows" :total="null" :height="null" class="groupTable"
-            :key="item.id + '_' + JSON.stringify(item.data.rows).length">
-            <el-table-column v-for="(header, hidx) in item.data.headers" :key="hidx" :label="header.name"
-              :prop="header.name">
+          <Table
+            :data="item.data.rows"
+            :total="null"
+            :height="null"
+            class="groupTable"
+            :key="item.id + '_' + JSON.stringify(item.data.rows).length"
+          >
+            <el-table-column
+              v-for="(header, hidx) in item.data.headers"
+              :key="hidx"
+              :label="header.name"
+              :prop="header.name"
+            >
               <template slot-scope="scope">
                 <!-- 用户类型显示 -->
                 <template v-if="header.type === 'user'">
                   {{ getUserDisplayText(header.name, scope.row) }}
                 </template>
-                <!-- 图片类型显示 -->
+                <!-- 图片类型显示,兼容数组和字符串 -->
                 <template v-else-if="header.type === 'image'">
-                  <img v-if="scope.row[header.name]" :src="getFullUrl(scope.row[header.name])" alt="头像"
-                    class="table-image" />
+                  <template v-if="Array.isArray(scope.row[header.name])">
+                    <el-image
+                      v-for="(img, i) in scope.row[header.name]"
+                      :key="i"
+                      :src="getFullUrl(img)"
+                      :preview-src-list="scope.row[header.name].map(getFullUrl)"
+                      class="table-image"
+                    />
+                  </template>
+                  <template v-else>
+                    <el-image
+                      v-if="scope.row[header.name]"
+                      :src="getFullUrl(scope.row[header.name])"
+                      :preview-src-list="[getFullUrl(scope.row[header.name])]"
+                      class="table-image"
+                    />
+                  </template>
                 </template>
                 <!-- 其他类型 -->
                 <template v-else>
@@ -45,65 +96,128 @@
                 </template>
               </template>
             </el-table-column>
-            <el-table-column label="更新时间" prop="updateTime" min-width="180"></el-table-column>
+            <el-table-column
+              label="更新时间"
+              prop="updateTime"
+              min-width="180"
+            ></el-table-column>
             <el-table-column label="操作" min-width="200" v-if="dialogCanEdit">
               <template slot-scope="scope">
-                <el-button type="text" @click="handleEditRow(idx, scope.$index)">编辑</el-button>
-                <el-button type="text" v-if="editable" @click="handleDeleteRow(idx, scope.$index)">删除</el-button>
+                <el-button type="text" @click="handleEditRow(idx, scope.$index)"
+                  >编辑</el-button
+                >
+                <el-button
+                  type="text"
+                  v-if="editable"
+                  @click="handleDeleteRow(idx, scope.$index)"
+                  >删除</el-button
+                >
               </template>
             </el-table-column>
           </Table>
         </div>
         <!-- 文件上传 -->
         <div v-else-if="item.type == 'fileUpload'">
-          <el-upload v-if="editable" :action="uploadUrl" :headers="uploadHeaders" :file-list="item.data.fileList"
+          <el-upload
+            v-if="editable"
+            :action="uploadUrl"
+            :headers="uploadHeaders"
+            :file-list="item.data.fileList"
             :on-change="(file, fileList) => handleFileChange(idx, fileList)"
-            :on-success="(res, file, fileList) => handleFileSuccess(res, file, fileList, idx)"
-            list-type="text">
+            :on-success="
+              (res, file, fileList) =>
+                handleFileSuccess(res, file, fileList, idx)
+            "
+            list-type="text"
+          >
             <el-button size="small" icon="el-icon-upload2">点击上传</el-button>
           </el-upload>
           <div v-else>
-            <div v-for="file in item.data.fileList" :key="file.uid" class="file-list-item" >
-              <span style="color: #409EFF; cursor: pointer;" @click="downloadFileByUrl(file.url, file.name)">{{ file.name }}</span>
+            <div
+              v-for="file in item.data.fileList"
+              :key="file.uid"
+              class="file-list-item"
+            >
+              <span
+                style="color: #409eff; cursor: pointer"
+                @click="downloadFileByUrl(file.url, file.name)"
+                >{{ file.name }}</span
+              >
             </div>
           </div>
         </div>
         <!-- 图片上传 -->
         <div v-else-if="item.type == 'imageUpload'">
           <div class="image-upload-container">
-            <el-upload v-if="editable" 
-            :action="uploadUrl" 
-            :headers="uploadHeaders"
-             :file-list="item.data.imageList"
+            <el-upload
+              v-if="editable"
+              :action="uploadUrl"
+              :headers="uploadHeaders"
+              :file-list="item.data.imageList"
               :on-change="(file, fileList) => handleImageChange(idx, fileList)"
-              :on-success="(res, file, fileList) => handleImageSuccess(res, file, fileList, idx)" :auto-upload="true"
-              :before-upload="beforeImageUpload" 
+              :on-success="
+                (res, file, fileList) =>
+                  handleImageSuccess(res, file, fileList, idx)
+              "
+              :auto-upload="true"
+              :before-upload="beforeImageUpload"
               list-type="picture-card"
-              :on-preview="(file) => handlePreview(file, idx)" class="image-uploader">
+              :on-preview="(file) => handlePreview(file, idx)"
+              class="image-uploader"
+            >
               <i class="el-icon-plus"></i>
               <div class="upload-text">上传图片</div>
             </el-upload>
             <div v-else class="image-preview">
-              <el-image v-for="image in item.data.imageList" :key="image.uid" :src="getFullUrl(image.url)"
-                :preview-src-list="item.data.imageList.map(img => getFullUrl(img.url))" class="preview-image" />
+              <el-image
+                v-for="image in item.data.imageList"
+                :key="image.uid"
+                :src="getFullUrl(image.url)"
+                :preview-src-list="
+                  item.data.imageList.map((img) => getFullUrl(img.url))
+                "
+                class="preview-image"
+              />
             </div>
             <div class="uploaf-notice">支持.jpg .png格式</div>
           </div>
         </div>
-        <img v-if="editable" src="@/assets/public/delete.png" @click="removeComponent(idx)" alt="删除"
-          class="delete-icon" />
+        <img
+          v-if="editable"
+          src="@/assets/public/delete.png"
+          @click="removeComponent(idx)"
+          alt="删除"
+          class="delete-icon"
+        />
       </div>
     </div>
 
-
-    <addTableHeader :visible.sync="tableHeaderDialog.visible" :participants="participants" @confirm="confirmAddHeader">
+    <addTableHeader
+      :visible.sync="tableHeaderDialog.visible"
+      :participants="participants"
+      @confirm="confirmAddHeader"
+    >
     </addTableHeader>
-    <addTableData :visible.sync="rowDialog.visible" :headerList="rowDialog.headers" :editData="rowDialog.form"
-      :isEdit="rowDialog.isEdit" @success="confirmAddRow">
+    <addTableData
+      :visible.sync="rowDialog.visible"
+      :headerList="rowDialog.headers"
+      :editData="rowDialog.form"
+      :isEdit="rowDialog.isEdit"
+      @success="confirmAddRow"
+    >
     </addTableData>
 
-    <el-dialog :visible.sync="imagePreviewVisible" width="auto" top="10vh" :show-close="true" v-if="imagePreviewUrl">
-      <img :src="imagePreviewUrl" style="max-width:80vw;max-height:70vh;display:block;margin:auto;" />
+    <el-dialog
+      :visible.sync="imagePreviewVisible"
+      width="auto"
+      top="10vh"
+      :show-close="true"
+      v-if="imagePreviewUrl"
+    >
+      <img
+        :src="imagePreviewUrl"
+        style="max-width: 80vw; max-height: 70vh; display: block; margin: auto"
+      />
     </el-dialog>
   </div>
 </template>
@@ -114,9 +228,9 @@
 import Table from "../Table/index.vue";
 import addTableHeader from "./addTableHeader.vue";
 import addTableData from "./addTableData.vue";
-import apiConfig from '../../utils/baseurl'
-import { getFullUrl } from '@/utils/utils'
-import { downloadFileByUrl } from '@/utils/utils'
+import apiConfig from "../../utils/baseurl";
+import { getFullUrl } from "@/utils/utils";
+import { downloadFileByUrl,getAllocateIp } from "@/utils/utils";
 
 export default {
   name: "DynamicComponent",
@@ -134,27 +248,27 @@
     },
     participants: {
       type: Array,
-      default: () => []
+      default: () => [],
     },
     dataSource: {
       type: Array,
-      default: () => []
+      default: () => [],
     },
     editable: {
       type: Boolean,
-      default: true
+      default: true,
     },
     dialogCanEdit: {
       type: Boolean,
-      default: true
-    }
+      default: true,
+    },
   },
   data() {
     return {
       apiConfig: apiConfig,
       uploadUrl: apiConfig.imgUrl,
       uploadHeaders: {
-        Authorization: sessionStorage.getItem('token') || ''
+        Authorization: sessionStorage.getItem("token") || "",
       },
       showAddDialog: false,
       components: [],
@@ -172,53 +286,56 @@
         form: {},
       },
       headerList: [],
-      defaultImageUrl: 'https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg', // 默认图片地址
+      defaultImageUrl:
+        "https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg", // 默认图片地址
       imagePreviewVisible: false,
-      imagePreviewUrl: '',
+      imagePreviewUrl: "",
     };
   },
   watch: {
     dataSource: {
       handler(newVal) {
         if (newVal) {
-          newVal = newVal.map(component => {
+          newVal = newVal.map((component) => {
             let componentData = null;
 
             switch (component.type) {
-              case 'richText':
-                componentData = { content: component.data }
+              case "richText":
+                componentData = { content: component.data };
                 break;
-              case 'customTable':
+              case "customTable":
                 componentData = {
                   headers: component.data.headers,
-                  rows: component.data.rows
+                  rows: component.data.rows,
                 };
                 break;
-              case 'fileUpload':
+              case "fileUpload":
                 componentData = { fileList: component.data };
-                console.log('component.data component.data',component.data)
+                console.log("component.data component.data", component.data);
                 break;
-              case 'imageUpload':
-                componentData = { imageList: component.data.map(item=>{
-                  return {
-                    ...item,
-                    url: getFullUrl(item.url),
-                  }
-                }) };
+              case "imageUpload":
+                componentData = {
+                  imageList: component.data.map((item) => {
+                    return {
+                      ...item,
+                      url: getFullUrl(item.url),
+                    };
+                  }),
+                };
                 break;
             }
             return {
               type: component.type,
               id: component.id || Math.random().toString(36).substr(2, 9),
-              data: componentData
-            }
-          })
+              data: componentData,
+            };
+          });
         }
         this.components = newVal ? [...newVal] : [];
       },
       immediate: true,
-      deep: true
-    }
+      deep: true,
+    },
   },
   methods: {
     getFullUrl,
@@ -229,49 +346,51 @@
 
       // 如果没有rowData或fieldName,直接返回空字符串
       if (!rowData || !fieldName) {
-        return '';
+        return "";
       }
 
       // 情况1: 有_userInfo数据
       if (rowData[userInfoKey] && Array.isArray(rowData[userInfoKey])) {
-        return rowData[userInfoKey].map(user => user.label || '').join(', ');
+        return rowData[userInfoKey].map((user) => user.label || "").join(", ");
       }
 
       // 情况2: 只有用户ID数组
       if (Array.isArray(rowData[fieldName])) {
         // 使用participants查找用户信息
-        return rowData[fieldName].map(userId => {
-          const user = this.participants.find(p => p.userId === userId);
-          return user ? (user.nickName || user.userName || userId) : userId;
-        }).join(', ');
+        return rowData[fieldName]
+          .map((userId) => {
+            const user = this.participants.find((p) => p.userId === userId);
+            return user ? user.nickName || user.userName || userId : userId;
+          })
+          .join(", ");
       }
 
       // 情况3: 已经是字符串(可能是之前格式化过的)
-      if (typeof rowData[fieldName] === 'string') {
+      if (typeof rowData[fieldName] === "string") {
         return rowData[fieldName];
       }
 
       // 默认返回空字符串
-      return '';
+      return "";
     },
     formatUserNames(userArray) {
       if (!userArray || !Array.isArray(userArray) || userArray.length === 0) {
-        return '';
+        return "";
       }
 
       // 查找参与者列表中的用户,获取昵称并用逗号拼接
-      const userNames = userArray.map(userId => {
-        const user = this.participants.find(p => p.userId === userId);
+      const userNames = userArray.map((userId) => {
+        const user = this.participants.find((p) => p.userId === userId);
         return user ? user.nickName : userId;
       });
-      return userNames.join(', ');
+      return userNames.join(", ");
     },
     addComponent(type) {
       if (!this.editable) return;
 
       if (type === "customTable") {
         if (!this.participants || this.participants.length === 0) {
-          this.$message.warning('请先选择实验调度');
+          this.$message.warning("请先选择实验调度");
           this.showAddDialog = false;
           return;
         }
@@ -289,34 +408,34 @@
       this.emitUpdate();
     },
     submit() {
-      const data = this.components.map(component => {
+      const data = this.components.map((component) => {
         let componentData = null;
-        const prefix = apiConfig.showImgUrl;
-
+        // const prefix = apiConfig.showImgUrl;
+        const prefix = getAllocateIp();
         switch (component.type) {
-          case 'richText':
+          case "richText":
             const editorRef = this.$refs[`editor_${component.id}`];
             const editor = Array.isArray(editorRef) ? editorRef[0] : editorRef;
-            const content = editor ? editor.getContent() : '';
-            componentData = content && content !== '<p></p>' ? content : '';
+            const content = editor ? editor.getContent() : "";
+            componentData = content && content !== "<p></p>" ? content : "";
             break;
-          case 'customTable':
+          case "customTable":
             componentData = {
               headers: component.data.headers,
-              rows: component.data.rows
+              rows: component.data.rows,
             };
             break;
-          case 'fileUpload':
-            componentData = component.data.fileList.map(file => {
-              console.log('fileUpload fileUpload fileUpload',file)
+          case "fileUpload":
+            componentData = component.data.fileList.map((file) => {
+              console.log("fileUpload fileUpload fileUpload", file);
               if (file.url && file.url.startsWith(prefix)) {
                 return { ...file, url: file.url.substring(prefix.length) };
               }
               return file;
             });
             break;
-          case 'imageUpload':
-            componentData = component.data.imageList.map(image => {
+          case "imageUpload":
+            componentData = component.data.imageList.map((image) => {
               if (image.url && image.url.startsWith(prefix)) {
                 return { ...image, url: image.url.substring(prefix.length) };
               }
@@ -327,11 +446,11 @@
 
         return {
           type: component.type,
-          data: componentData
+          data: componentData,
         };
       });
 
-      this.$emit('submit', data);
+      this.$emit("submit", data);
     },
     confirmAddRow(formData) {
       // if (!this.editable) return;
@@ -344,19 +463,15 @@
       // 调试输出
       if (isEdit) {
         // Vue无法检测到对象或数组深层属性的变化,使用Vue.set来确保响应式
-        this.$set(
-          this.components[idx].data.rows,
-          rowIndex,
-          {
-            ...processedData,
-            updateTime: new Date().toLocaleString()
-          }
-        );
+        this.$set(this.components[idx].data.rows, rowIndex, {
+          ...processedData,
+          updateTime: new Date().toLocaleString(),
+        });
       } else {
         // 使用数组方法push会被Vue检测到
         this.components[idx].data.rows.push({
           ...processedData,
-          updateTime: new Date().toLocaleString()
+          updateTime: new Date().toLocaleString(),
         });
       }
 
@@ -387,24 +502,24 @@
       const { idx } = this.tableHeaderDialog;
       this.components[idx].data.headers.push({ ...data });
 
-      this.components[idx].data.rows.forEach(row => {
-        if (typeof row === 'object' && row !== null) {
-          if (data.type === 'user') {
+      this.components[idx].data.rows.forEach((row) => {
+        if (typeof row === "object" && row !== null) {
+          if (data.type === "user") {
             this.$set(row, data.name, []);
           } else {
-            this.$set(row, data.name, '');
+            this.$set(row, data.name, "");
           }
         } else {
           const newRow = {};
-          this.components[idx].data.headers.forEach(header => {
+          this.components[idx].data.headers.forEach((header) => {
             if (header.name === data.name) {
-              if (header.type === 'user') {
+              if (header.type === "user") {
                 newRow[header.name] = [];
               } else {
-                newRow[header.name] = '';
+                newRow[header.name] = "";
               }
             } else {
-              newRow[header.name] = row[header.name] || '';
+              newRow[header.name] = row[header.name] || "";
             }
           });
           const rowIndex = this.components[idx].data.rows.indexOf(row);
@@ -458,7 +573,7 @@
           this.$message.success("删除成功");
           this.emitUpdate();
         })
-        .catch(() => { });
+        .catch(() => {});
     },
     handleFileChange(idx, fileList) {
       if (!this.editable) return;
@@ -485,18 +600,18 @@
       this.emitUpdate();
     },
     beforeImageUpload(file) {
-      const isJPG = file.type === 'image/jpeg';
-      const isPNG = file.type === 'image/png';
-      const isLt2M = file.size / 1024 / 1024 < 2;
+      const isJPG = file.type === "image/jpeg";
+      const isPNG = file.type === "image/png";
+      // const isLt2M = file.size / 1024 / 1024 < 2;
 
       if (!isJPG && !isPNG) {
-        this.$message.error('上传图片只能是 JPG 或 PNG 格式!');
+        this.$message.error("上传图片只能是 JPG 或 PNG 格式!");
         return false;
       }
-      if (!isLt2M) {
-        this.$message.error('上传图片大小不能超过 2MB!');
-        return false;
-      }
+      // if (!isLt2M) {
+      //   this.$message.error('上传图片大小不能超过 2MB!');
+      //   return false;
+      // }
       this.imagePreviewVisible = true;
       return true;
     },
@@ -510,11 +625,10 @@
     emitUpdate() {
       // 先创建新对象,这有助于触发更新
       const updatedComponents = JSON.parse(JSON.stringify(this.components));
-      this.$emit('update:dataSource', updatedComponents);
+      this.$emit("update:dataSource", updatedComponents);
     },
   },
-  computed: {
-  },
+  computed: {},
 };
 </script>
 

--
Gitblit v1.7.1