From 527efb36f35b471710e445972673abff45bacdac Mon Sep 17 00:00:00 2001
From: 董国庆 <364620639@qq.com>
Date: 星期五, 12 九月 2025 17:36:09 +0800
Subject: [PATCH] 401跳转登录
---
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