| | |
| | | <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="200px" :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"> |
| | |
| | | </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" /> |
| | | <img v-if="scope.row[header.name]" :src="getFullUrl(scope.row[header.name])" alt="头像" |
| | | class="table-image" /> |
| | | </template> |
| | | <!-- 其他类型 --> |
| | | <template v-else> |
| | |
| | | </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> |
| | |
| | | </div> |
| | | <!-- 文件上传 --> |
| | | <div v-else-if="item.type == 'fileUpload'"> |
| | | <el-upload v-if="editable" :http-request="customUploadRequest" :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"> |
| | |
| | | <!-- 图片上传 --> |
| | | <div v-else-if="item.type == 'imageUpload'"> |
| | | <div class="image-upload-container"> |
| | | <el-upload v-if="editable" :http-request="customUploadRequest" :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> |
| | | |
| | | |
| | | <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"> |
| | |
| | | import addTableHeader from "./addTableHeader.vue"; |
| | | import addTableData from "./addTableData.vue"; |
| | | import apiConfig from '../../utils/baseurl' |
| | | import { customUploadRequest, getFullUrl } from '@/utils/utils' |
| | | import { getFullUrl } from '@/utils/utils' |
| | | |
| | | export default { |
| | | name: "DynamicComponent", |
| | |
| | | }, |
| | | data() { |
| | | return { |
| | | apiConfig:apiConfig, |
| | | apiConfig: apiConfig, |
| | | uploadUrl: apiConfig.imgUrl, |
| | | uploadHeaders: { |
| | | Authorization: sessionStorage.getItem('token') || '' |
| | | }, |
| | | showAddDialog: false, |
| | | components: [], |
| | | tableHeaderDialog: { |
| | |
| | | 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查找用户信息 |
| | |
| | | return user ? (user.nickName || user.userName || userId) : userId; |
| | | }).join(', '); |
| | | } |
| | | |
| | | |
| | | // 情况3: 已经是字符串(可能是之前格式化过的) |
| | | if (typeof rowData[fieldName] === 'string') { |
| | | return rowData[fieldName]; |
| | | } |
| | | |
| | | |
| | | // 默认返回空字符串 |
| | | return ''; |
| | | }, |
| | |
| | | if (!userArray || !Array.isArray(userArray) || userArray.length === 0) { |
| | | return ''; |
| | | } |
| | | |
| | | |
| | | // 查找参与者列表中的用户,获取昵称并用逗号拼接 |
| | | const userNames = userArray.map(userId => { |
| | | const user = this.participants.find(p => p.userId === userId); |
| | |
| | | submit() { |
| | | const data = this.components.map(component => { |
| | | let componentData = null; |
| | | const prefix = apiConfig.showImgUrl; |
| | | |
| | | switch (component.type) { |
| | | case 'richText': |
| | |
| | | }; |
| | | break; |
| | | case 'fileUpload': |
| | | componentData = component.data.fileList; |
| | | componentData = component.data.fileList.map(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; |
| | | } |
| | | |
| | |
| | | // if (!this.editable) return; |
| | | |
| | | const { idx, rowIndex, isEdit } = this.rowDialog; |
| | | |
| | | |
| | | // 处理formData中的数据,保证用户信息的完整性 |
| | | const processedData = { ...formData }; |
| | | |
| | | |
| | | // 调试输出 |
| | | console.log('添加/编辑行数据:', processedData,'isEdit',isEdit); |
| | | |
| | | 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() |
| | |
| | | updateTime: new Date().toLocaleString() |
| | | }); |
| | | } |
| | | console.log('this.components',this.components); |
| | | |
| | | console.log('this.components', this.components); |
| | | |
| | | // 手动触发组件更新 |
| | | this.$forceUpdate(); |
| | | // 延迟发送事件,确保数据已更新 |
| | | this.$nextTick(() => { |
| | | this.emitUpdate(); |
| | | }); |
| | | |
| | | |
| | | this.rowDialog.visible = false; |
| | | this.rowDialog.form = {}; |
| | | }, |
| | |
| | | }, |
| | | handleFileSuccess(res, file, fileList, idx) { |
| | | // 上传成功后设置真实 url |
| | | file.url = res.data.url; |
| | | file.url = this.getFullUrl(res.msg); |
| | | this.components[idx].data.fileList = fileList; |
| | | this.emitUpdate(); |
| | | }, |
| | |
| | | }, |
| | | handleImageSuccess(res, file, fileList, idx) { |
| | | // 上传成功后设置真实 url |
| | | file.url = res.data.url; |
| | | file.url = this.getFullUrl(res.msg); |
| | | this.components[idx].data.imageList = fileList; |
| | | this.emitUpdate(); |
| | | }, |
| | |
| | | this.$message.error('上传图片大小不能超过 2MB!'); |
| | | return false; |
| | | } |
| | | this.imagePreviewVisible = true; |
| | | return true; |
| | | }, |
| | | handlePreview(file, idx) { |
| | |
| | | const updatedComponents = JSON.parse(JSON.stringify(this.components)); |
| | | this.$emit('update:dataSource', updatedComponents); |
| | | }, |
| | | getFullUrl, |
| | | }, |
| | | computed: { |
| | | }, |