From 9cb1c3c39136c89974bc4049fca5e450e757b1be Mon Sep 17 00:00:00 2001 From: 13404089107 <puwei@sinata.cn> Date: 星期五, 15 八月 2025 17:51:03 +0800 Subject: [PATCH] fix --- laboratory/src/components/DynamicComponent/index.vue | 183 +++++++++++++++++++++++++++++---------------- 1 files changed, 119 insertions(+), 64 deletions(-) diff --git a/laboratory/src/components/DynamicComponent/index.vue b/laboratory/src/components/DynamicComponent/index.vue index 8faa19a..218e9c9 100644 --- a/laboratory/src/components/DynamicComponent/index.vue +++ b/laboratory/src/components/DynamicComponent/index.vue @@ -16,16 +16,17 @@ <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" @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"> + <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"> @@ -33,12 +34,25 @@ <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="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> @@ -47,9 +61,9 @@ </template> </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"> + <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" @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> @@ -57,29 +71,36 @@ </div> <!-- 文件上传 --> <div v-else-if="item.type == 'fileUpload'"> - <el-upload v-if="editable" action="#" :file-list="item.data.fileList" - :on-change="(file, fileList) => handleFileChange(idx, fileList)" list-type="text"> + <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"> <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"> - {{ file.name }} + <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="#" :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" - :http-request="() => { }" :before-upload="beforeImageUpload" list-type="picture-card" - class="image-uploader"> + :before-upload="beforeImageUpload" + list-type="picture-card" + :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"> - <img v-for="image in item.data.imageList" :key="image.uid" :src="image.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> @@ -90,11 +111,15 @@ </div> - <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"> + <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> + + <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> @@ -104,6 +129,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' export default { name: "DynamicComponent", @@ -138,6 +166,11 @@ }, data() { return { + apiConfig: apiConfig, + uploadUrl: apiConfig.imgUrl, + uploadHeaders: { + Authorization: sessionStorage.getItem('token') || '' + }, showAddDialog: false, components: [], tableHeaderDialog: { @@ -155,6 +188,8 @@ }, headerList: [], defaultImageUrl: 'https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg', // 默认图片地址 + imagePreviewVisible: false, + imagePreviewUrl: '', }; }, watch: { @@ -176,9 +211,15 @@ break; case 'fileUpload': componentData = { fileList: component.data }; + console.log('component.data component.data',component.data) break; case 'imageUpload': - componentData = { imageList: component.data }; + componentData = { imageList: component.data.map(item=>{ + return { + ...item, + url: getFullUrl(item.url), + } + }) }; break; } return { @@ -195,20 +236,22 @@ } }, methods: { + getFullUrl, + downloadFileByUrl, getUserDisplayText(fieldName, rowData) { // 检查是否有对应的userInfo数据 const userInfoKey = `${fieldName}_userInfo`; - + // 如果没有rowData或fieldName,直接返回空字符串 if (!rowData || !fieldName) { return ''; } - + // 情况1: 有_userInfo数据 if (rowData[userInfoKey] && Array.isArray(rowData[userInfoKey])) { return rowData[userInfoKey].map(user => user.label || '').join(', '); } - + // 情况2: 只有用户ID数组 if (Array.isArray(rowData[fieldName])) { // 使用participants查找用户信息 @@ -217,12 +260,12 @@ return user ? (user.nickName || user.userName || userId) : userId; }).join(', '); } - + // 情况3: 已经是字符串(可能是之前格式化过的) if (typeof rowData[fieldName] === 'string') { return rowData[fieldName]; } - + // 默认返回空字符串 return ''; }, @@ -230,7 +273,7 @@ if (!userArray || !Array.isArray(userArray) || userArray.length === 0) { return ''; } - + // 查找参与者列表中的用户,获取昵称并用逗号拼接 const userNames = userArray.map(userId => { const user = this.participants.find(p => p.userId === userId); @@ -263,6 +306,7 @@ submit() { const data = this.components.map(component => { let componentData = null; + const prefix = apiConfig.showImgUrl; switch (component.type) { case 'richText': @@ -278,10 +322,21 @@ }; break; case 'fileUpload': - componentData = component.data.fileList; + 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; + componentData = component.data.imageList.map(image => { + if (image.url && image.url.startsWith(prefix)) { + return { ...image, url: image.url.substring(prefix.length) }; + } + return image; + }); break; } @@ -297,18 +352,16 @@ // if (!this.editable) return; const { idx, rowIndex, isEdit } = this.rowDialog; - + // 处理formData中的数据,保证用户信息的完整性 const processedData = { ...formData }; - + // 调试输出 - console.log('添加/编辑行数据:', processedData,'isEdit',isEdit); - if (isEdit) { // Vue无法检测到对象或数组深层属性的变化,使用Vue.set来确保响应式 this.$set( - this.components[idx].data.rows, - rowIndex, + this.components[idx].data.rows, + rowIndex, { ...processedData, updateTime: new Date().toLocaleString() @@ -321,15 +374,14 @@ updateTime: new Date().toLocaleString() }); } - console.log('this.components',this.components); - + // 手动触发组件更新 this.$forceUpdate(); // 延迟发送事件,确保数据已更新 this.$nextTick(() => { this.emitUpdate(); }); - + this.rowDialog.visible = false; this.rowDialog.form = {}; }, @@ -425,49 +477,50 @@ }, handleFileChange(idx, fileList) { if (!this.editable) return; - - fileList = fileList.map(file => { - if (!file.url) { - file.url = 'https://picsum.photos/200/200'; - } - if (!file.name) { - file.name = '默认文件.txt'; - } - return file; - }); + // 只做 fileList 同步 + this.components[idx].data.fileList = fileList; + this.emitUpdate(); + }, + handleFileSuccess(res, file, fileList, idx) { + // 上传成功后设置真实 url + file.url = this.getFullUrl(res.msg); this.components[idx].data.fileList = fileList; this.emitUpdate(); }, handleImageChange(idx, fileList) { if (!this.editable) return; - - fileList = fileList.map(file => { - if (!file.url) { - file.url = 'https://picsum.photos/200/200'; - } - return file; - }); + // 只做 imageList 同步 this.components[idx].data.imageList = fileList; this.emitUpdate(); }, handleImageSuccess(res, file, fileList, idx) { - file.url = 'https://picsum.photos/200/200'; + // 上传成功后设置真实 url + file.url = this.getFullUrl(res.msg); this.components[idx].data.imageList = fileList; + this.emitUpdate(); }, beforeImageUpload(file) { const isJPG = file.type === 'image/jpeg'; const isPNG = file.type === 'image/png'; - const isLt2M = file.size / 1024 / 1024 < 2; + // const isLt2M = file.size / 1024 / 1024 < 2; if (!isJPG && !isPNG) { 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; + }, + handlePreview(file, idx) { + // 使用el-image的preview-src-list实现预览 + // 这里直接用Element的图片预览能力,实际上el-upload会自动处理 + // 但如果你想自定义弹窗,可以用如下代码: + this.imagePreviewUrl = this.getFullUrl(file.url); + this.imagePreviewVisible = true; }, emitUpdate() { // 先创建新对象,这有助于触发更新 @@ -475,6 +528,8 @@ this.$emit('update:dataSource', updatedComponents); }, }, + computed: { + }, }; </script> -- Gitblit v1.7.1