组件抽离:el-upload支持图片粘贴上传并添加预览 根据不同的证明材料,如:粘贴/上传图片,输入链接
【这个网站有相似功能: pqina.nl/filepond/#m...
效果图:

粘贴上传方法:
js
<!-- 图片粘贴上传 -->
<div class="info-item" style="align-items: start">
<span class="label">Proof:</span>
<div class="two-col-container">
<div
class="item"
v-for="(
proofType, index
) in approvalInformationObj.requiredProofTypes"
:key="proofType.id"
>
<div class="proof-item">
<div class="label" style="text-align: left; display: flex">
<span v-if="proofType.required" style="color: red">*</span>
<span class="proof-label-text" :title="proofType.value"
>{{ proofType.value }}:</span
>
</div>
<div class="upload-container">
<!-- 图片上传类型 (type: 0) -->
<template v-if="proofType.type === 0">
<div
:class="`upload-area ${
currentFocusArea === index ? 'active-upload' : ''
}`"
@click="setActiveUploadArea(index)"
@mouseenter="showUploadHint(index, $event)"
@mouseleave="hideUploadHint()"
>
<el-upload
:class="`image-uploader proof-uploader-${index}`"
:action="uploadAction"
:show-file-list="false"
:on-success="(response: any, file: any) => handleProofSuccess(response, file, index)"
:before-upload="beforeUpload"
:http-request="(options: any) => customUploadProof(options, index)"
:auto-upload="true"
list-type="picture-card"
>
<el-icon title="Supports uploading files"
><Plus
/></el-icon>
<!-- <img
v-if="proofData[index]?.displayUrl"
:src="proofData[index].displayUrl"
class="upload-image"
/>
<div v-else class="upload-placeholder">
<i class="el-icon-plus"></i>
<div>Upload pictures</div>
</div> -->
</el-upload>
<!-- 当前活跃上传区域指示器 -->
<!-- <div
v-if="currentFocusArea === index"
class="active-indicator"
>
<span>当前选中</span>
</div> -->
</div>
<!-- 显示已上传的多个图片 -->
<div
v-if="proofData[index]?.images?.length > 0"
class="uploaded-images"
>
<div
v-for="(image, imageIndex) in proofData[index].images"
:key="imageIndex"
class="image-item"
:class="{
active: image.url === proofData[index].displayUrl,
}"
>
<img
:src="image.url"
class="thumbnail"
@click="changeDisplayImage(index, image.url)"
/>
<div class="image-actions">
<el-button
type="primary"
:icon="Search"
circle
@click.stop="previewImage(image.url)"
title="preview"
/>
<el-button
type="danger"
:icon="Delete"
circle
@click.stop="removeImage(index, imageIndex)"
title="delete"
/>
</div>
</div>
</div>
</template>
<!-- 链接输入类型 (type: 1) -->
<template v-else-if="proofType.type === 1">
<el-input
v-model="proofData[index].url"
type="textarea"
:rows="3"
:placeholder="`Please input ${proofType.value} URL or text content`"
style="margin-top: 10px"
/>
</template>
</div>
<!-- 描述信息 -->
<!-- <div
v-if="proofType.description"
class="proof-description"
>
{{ proofType.description }}
</div> -->
</div>
</div>
</div>
</div>
上传的图片进行预览操作:

js
<!-- 图片预览弹窗 -->
<el-dialog
v-model="imagePreviewVisible"
title="Preview Picture"
width="90%"
:style="{ maxWidth: '1400px', minWidth: '800px' }"
append-to-body
>
<div class="image-preview-container" ref="previewContainer">
<div class="image-controls">
<el-button-group>
<el-button @click="zoomIn" :icon="ZoomIn" size="small" title="Zoom In">Zoom In</el-button>
<el-button @click="zoomOut" size="small" :icon="ZoomOut" title="Zoom Out">Zoom Out</el-button>
<el-button @click="resetZoom" size="small" :icon="Refresh" title="Reset">Reset</el-button>
<el-button @click="toggleFullscreen" size="small" :icon="FullScreen" title="Fullscreen">{{ isFullscreen ? 'Exit Fullscreen' : 'Fullscreen' }}</el-button>
</el-button-group>
<span class="zoom-indicator">{{ Math.round(imageScale * 100) }}%</span>
</div>
<div
class="image-wrapper"
@wheel="handleWheel"
ref="imageWrapper"
>
<img
:src="previewImageUrl"
alt="预览图片"
class="preview-image"
:style="imageStyle"
@error="handleImageError"
@load="handleImageLoad"
draggable="false"
/>
</div>
</div>
<template #footer>
<el-button @click="closePreview">Closed</el-button>
</template>
</el-dialog>
js方法:
js
import { ref, computed, onMounted,onUnmounted, nextTick, reactive } from "vue";
import { Delete, Search, WarnTriangleFilled, FullScreen, ZoomIn, ZoomOut, Refresh, Plus} from "@element-plus/icons-vue";
import { uploadFileApiNew } from "@/api/featuredPapers/index";
// 初始化加载
onMounted(async () => {
document.addEventListener("paste", handlePaste);
// 初始化事件监听器
setTimeout(updateEventListeners, 500);
// 添加拖拽区域监听
window.addEventListener("dragover", (e) => {
// 阻止默认行为
e.preventDefault();
});
window.addEventListener("drop", handleDrop);
document.addEventListener('fullscreenchange', () => {
isFullscreen.value = !!document.fullscreenElement;
});
});
onUnmounted(() => {
document.removeEventListener("paste", handlePaste);
window.removeEventListener("dragover", (e) => e.preventDefault());
window.removeEventListener("drop", handleDrop);
document.removeEventListener('fullscreenchange', () => {
isFullscreen.value = !!document.fullscreenElement;
});
});
let approvalInformationObj = reactive<any>({
performanceSettingId: "",
commissionTypeId: "",
jmRole: "",
jmPaidConfirmed: "",
jmPassRate: "",
jmProportion: "",
calcalationFormula: "",
paperId: "",
sectionId: "",
remark: "",
amount: "",
siTitle: "",
siPublished: null,
siType: "",
articleType: "",
paymentAmount: "",
requiredProofTypes: [
{
description:
"邀请证明(备注:若截图部分内容较多,请提交时圈出invite,invitation, agree等类似关键词,及邮件收发时间,便于审核人员审查,提高通过率)",
id: 3,
type: 0,
value:
"Proof of Invitation(Note: If the screenshot contains extensive content, please highlight keywords like 'invite', 'invitation', or 'agree', and include the email timestamps to facilitate reviewer verification and improve approval rates.)",
required: true,
},
{
description: "学者scopus链接",
id: 4,
type: 1,
value: "Scholar's Scopus Profile Link",
required: true,
},
{
description: "学者单位截图(备注:国家需体现,且与姓名在同一页展现)",
id: 5,
type: 0,
value: "Screenshot of Scholar's Institutional Affiliation",
required: true,
},
],
deTotalCommission: null,
});
// 图片上传相关
const uploadAction = ref(""); // 实际使用时替换为后端上传接口
// 新的证明材料数据结构 初始化 证明材料数据
const proofData = ref<{
[key: number]: {
displayUrl?: string;
url?: string;
note?: string;
fileId?: number;
images?: { url: string; name: string; fileId?: number }[];
};
}>({
"0": { images: [], url: "", displayUrl: "", note: "" },
"1": { images: [], url: "", displayUrl: "", note: "" },
"2": { images: [], url: "", displayUrl: "", note: "" },
});
// 当前焦点区域
const currentFocusArea = ref<any>(0);
// 是否显示焦点提示
const showFocusHint = ref(false);
// 焦点提示的位置
const focusHintPosition = ref({ top: 0, left: 0 });
// 设置活跃上传区域
const setActiveUploadArea = (index: number) => {
currentFocusArea.value = index;
// 显示简短提示
showFocusHint.value = true;
setTimeout(() => {
showFocusHint.value = false;
}, 1500);
};
// 显示上传提示
const showUploadHint = (index: number, event: MouseEvent) => {
const rect = (event.currentTarget as HTMLElement).getBoundingClientRect();
focusHintPosition.value = {
top: rect.top - 30,
left: rect.left + rect.width / 2 - 100,
};
// 临时更新焦点区域用于显示提示文本
const tempFocus = currentFocusArea.value;
currentFocusArea.value = index;
showFocusHint.value = true;
// 恢复原来的焦点区域
setTimeout(() => {
if (currentFocusArea.value === index && !showFocusHint.value) {
currentFocusArea.value = tempFocus;
}
}, 100);
};
// 隐藏上传提示
const hideUploadHint = () => {
showFocusHint.value = false;
};
// 上传成功回调
const handleProofSuccess = async (response: any, file: any, index: number) => {
// 初始化该索引的数据对象
if (!proofData.value[index]) {
proofData.value[index] = { images: [] };
}
if (!proofData.value[index].images) {
proofData.value[index].images = [];
}
try {
// 调用真实上传接口
const uploadResponse = await uploadFileApiNew(file.raw);
if (uploadResponse.code === 2000 && uploadResponse.data) {
const fileId = uploadResponse.data.id;
const url = URL.createObjectURL(file.raw);
proofData.value[index].images!.push({
url,
name: file.name,
fileId: fileId,
});
proofData.value[index].displayUrl = url;
proofData.value[index].fileId = fileId; // 记录当前显示图片的文件ID
ElMessage.success("Image uploaded successfully.");
} else {
ElMessage.error("Image upload failed.");
}
} catch (error) {
console.error("Upload error:", error);
ElMessage.error("Image upload failed.");
}
};
// 上传前检查
const beforeUpload = (file: File) => {
const isImage = file.type.startsWith("image/");
const isLt5M = file.size / 1024 / 1024 < 5;
if (!isImage) {
ElMessage.error("Only image files can be uploaded!");
return false;
}
if (!isLt5M) {
ElMessage.error("The image size must not exceed 5MB.");
return false;
}
return true;
};
// 自定义上传函数
const customUploadProof = (options: any, index: number) => {
handleImageFile(options.file, index);
};
// 处理图片文件
const handleImageFile = async (file: File, index: number) => {
if (!beforeUpload(file)) return;
// 初始化该索引的数据对象
if (!proofData.value[index]) {
proofData.value[index] = { images: [] };
}
if (!proofData.value[index].images) {
proofData.value[index].images = [];
}
try {
// 调用真实上传接口
const uploadResponse = await uploadFileApiNew(file);
if (uploadResponse.code === 2000 && uploadResponse.data) {
const fileId = uploadResponse.data.uploadFileData.id || "";
const imgUrl = URL.createObjectURL(file);
const imageData = {
url: imgUrl,
name: file.name,
fileId: fileId,
};
proofData.value[index].images!.push(imageData);
proofData.value[index].displayUrl = imgUrl;
proofData.value[index].fileId = fileId; // 记录当前显示图片的文件ID
ElMessage.success("Image uploaded successfully.");
} else {
ElMessage.error("Image upload failed.");
}
} catch (error) {
console.error("Upload error:", error);
ElMessage.error("Image upload failed.");
}
};
// 切换展示图片
const changeDisplayImage = (proofIndex: number, url: string) => {
if (proofData.value[proofIndex]) {
proofData.value[proofIndex].displayUrl = url;
// 找到对应图片的文件ID并更新
const selectedImage = proofData.value[proofIndex].images?.find(
(img) => img.url === url
);
if (selectedImage?.fileId) {
proofData.value[proofIndex].fileId = selectedImage.fileId;
}
}
};
// 移除图片
const removeImage = (proofIndex: number, imageIndex: number) => {
if (proofData.value[proofIndex]?.images) {
proofData.value[proofIndex].images!.splice(imageIndex, 1);
if (proofData.value[proofIndex].images!.length === 0) {
proofData.value[proofIndex].displayUrl = "";
} else {
proofData.value[proofIndex].displayUrl =
proofData.value[proofIndex].images![0].url;
}
}
};
// 显示焦点提示
const showFocusIndicator = (e: MouseEvent, index: number) => {
// 更新当前焦点区域
currentFocusArea.value = index;
// 计算提示位置
const rect = (e.currentTarget as HTMLElement).getBoundingClientRect();
focusHintPosition.value = {
top: rect.top - 25,
left: rect.left + rect.width / 2 - 75,
};
// 显示提示
showFocusHint.value = true;
// 3秒后自动隐藏
setTimeout(() => {
showFocusHint.value = false;
}, 2000);
};
// 更新事件监听器
const updateEventListeners = () => {
// 移除旧的事件监听器,避免重复绑定
document.querySelectorAll(".upload-container").forEach((container) => {
container.removeEventListener("click", () => {});
});
// 添加新的事件监听器
const invitationUploader = document.querySelector(".invitation-uploader");
const scopusUploader = document.querySelector(".scopus-uploader");
const referencesUploader = document.querySelector(".references-uploader");
const identityUploader = document.querySelector(".identity-uploader");
if (invitationUploader) {
invitationUploader.addEventListener("click", (e) => {
currentFocusArea.value = "invitation";
showFocusIndicator(e as MouseEvent, "invitation");
});
}
if (scopusUploader) {
scopusUploader.addEventListener("click", (e) => {
currentFocusArea.value = "scopus";
showFocusIndicator(e as MouseEvent, "scopus");
});
}
if (referencesUploader) {
referencesUploader.addEventListener("click", (e) => {
currentFocusArea.value = "references";
showFocusIndicator(e as MouseEvent, "references");
});
}
if (identityUploader) {
identityUploader.addEventListener("click", (e) => {
currentFocusArea.value = "identity";
showFocusIndicator(e as MouseEvent, "identity");
});
}
// 添加上传区域的容器监听
document.querySelectorAll(".upload-container").forEach((container) => {
// 判断这是哪个上传区域
if (container.querySelector(".invitation-uploader")) {
container.addEventListener("click", (e) => {
if (e.target === container) {
currentFocusArea.value = "invitation";
showFocusIndicator(e as MouseEvent, "invitation");
// 点击容器时,模拟点击上传组件
const uploader = container.querySelector(".invitation-uploader");
if (uploader && uploader instanceof HTMLElement) {
uploader.click();
}
}
});
} else if (container.querySelector(".scopus-uploader")) {
container.addEventListener("click", (e) => {
if (e.target === container) {
currentFocusArea.value = "scopus";
showFocusIndicator(e as MouseEvent, "scopus");
// 点击容器时,模拟点击上传组件
const uploader = container.querySelector(".scopus-uploader");
if (uploader && uploader instanceof HTMLElement) {
uploader.click();
}
}
});
} else if (container.querySelector(".references-uploader")) {
container.addEventListener("click", (e) => {
if (e.target === container) {
currentFocusArea.value = "references";
showFocusIndicator(e as MouseEvent, "references");
// 点击容器时,模拟点击上传组件
const uploader = container.querySelector(".references-uploader");
if (uploader && uploader instanceof HTMLElement) {
uploader.click();
}
}
});
} else if (container.querySelector(".identity-uploader")) {
container.addEventListener("click", (e) => {
if (e.target === container) {
currentFocusArea.value = "identity";
showFocusIndicator(e as MouseEvent, "identity");
// 点击容器时,模拟点击上传组件
const uploader = container.querySelector(".identity-uploader");
if (uploader && uploader instanceof HTMLElement) {
uploader.click();
}
}
});
}
});
};
// 处理粘贴事件
const handlePaste = (event: ClipboardEvent) => {
const items = event.clipboardData?.items;
if (!items) return;
// 检查当前焦点元素,判断应该上传到哪个区域
const activeElement = document.activeElement;
let targetIndex = currentFocusArea.value;
let foundArea = false;
// 根据当前激活的元素判断上传区域
if (activeElement) {
// 尝试从当前元素向上查找上传容器
let element = activeElement as HTMLElement;
while (element && !foundArea) {
if (element.classList?.contains("upload-container")) {
// 查找proof-uploader的索引
const uploader = element.querySelector("[class*='proof-uploader-']");
if (uploader) {
const classList = Array.from(uploader.classList);
const uploaderClass = classList.find((cls) =>
cls.startsWith("proof-uploader-")
);
if (uploaderClass) {
targetIndex = parseInt(uploaderClass.split("-")[2]);
foundArea = true;
}
}
break;
}
// 直接检查当前元素是否是上传器
if (element.classList.value.includes("proof-uploader-")) {
const classList = Array.from(element.classList);
const uploaderClass = classList.find((cls) =>
cls.startsWith("proof-uploader-")
);
if (uploaderClass) {
targetIndex = parseInt(uploaderClass.split("-")[2]);
foundArea = true;
}
break;
}
element = element.parentElement as HTMLElement;
}
}
let imageFound = false;
// 处理所有粘贴的图片
for (let i = 0; i < items.length; i++) {
if (items[i].type.indexOf("image") !== -1) {
const file = items[i].getAsFile();
if (!file) continue;
imageFound = true;
// 使用确定的目标区域上传图片
handleImageFile(file, targetIndex);
}
}
// 只有找到图片时才阻止默认粘贴行为
if (imageFound) {
event.preventDefault();
// 更新当前焦点区域,以便下次粘贴时使用
currentFocusArea.value = targetIndex;
}
};
// 处理拖拽事件
const handleDrop = (e: DragEvent) => {
e.preventDefault();
// 检查拖拽的是否为文件
if (e.dataTransfer?.files.length) {
// 根据拖拽位置判断目标区域
const target = document.elementFromPoint(e.clientX, e.clientY);
if (!target) return;
// 查找最近的上传区域
let proofIndex = -1;
let container = null;
// 尝试从点击元素向上查找上传容器
let element = target as HTMLElement;
while (element && proofIndex === -1) {
if (element.classList?.contains("upload-container")) {
container = element;
// 查找proof-uploader的索引
const uploader = element.querySelector("[class*='proof-uploader-']");
if (uploader) {
const classList = Array.from(uploader.classList);
const uploaderClass = classList.find((cls) =>
cls.startsWith("proof-uploader-")
);
if (uploaderClass) {
proofIndex = parseInt(uploaderClass.split("-")[2]);
}
}
break;
}
// 直接检查当前元素是否是上传器
if (element.classList.value.includes("proof-uploader-")) {
const classList = Array.from(element.classList);
const uploaderClass = classList.find((cls) =>
cls.startsWith("proof-uploader-")
);
if (uploaderClass) {
proofIndex = parseInt(uploaderClass.split("-")[2]);
}
break;
}
element = element.parentElement as HTMLElement;
}
if (proofIndex !== -1) {
// 更新当前焦点区域
currentFocusArea.value = proofIndex;
// 处理所有拖拽的文件
Array.from(e.dataTransfer.files).forEach((file) => {
if (file.type.startsWith("image/")) {
handleImageFile(file, proofIndex);
}
});
// 如果找到了容器,显示焦点提示
if (container) {
showFocusIndicator(e as unknown as MouseEvent, proofIndex);
}
}
}
};
// 图片预览相关
const imagePreviewVisible = ref(false);
const previewImageUrl = ref("");
const imageScale = ref(1);
const isFullscreen = ref(false);
const previewContainer = ref();
const imageWrapper = ref();
// 图片预览控制方法
const imageStyle = computed(() => ({
transform: `scale(${imageScale.value})`,
transition: 'transform 0.3s ease',
}));
// 预览图片
const previewImage = (url: string) => {
// 使用 el-dialog 预览图片
previewImageUrl.value = url;
imagePreviewVisible.value = true;
};
// 放大图片
const zoomIn = () => {
imageScale.value = Math.min(imageScale.value * 1.2, 5);
// 缩放后保持顶部可见
nextTick(() => {
if (imageWrapper.value) {
imageWrapper.value.scrollTop = 0;
}
});
};
// 缩小图片
const zoomOut = () => {
imageScale.value = Math.max(imageScale.value / 1.2, 0.1);
// 缩放后保持顶部可见
nextTick(() => {
if (imageWrapper.value) {
imageWrapper.value.scrollTop = 0;
}
});
};
// 重置缩放
const resetZoom = () => {
imageScale.value = 1;
// 重置后滚动到顶部
nextTick(() => {
if (imageWrapper.value) {
imageWrapper.value.scrollTop = 0;
imageWrapper.value.scrollLeft = 0;
}
});
};
// 处理鼠标滚轮缩放
const handleWheel = (event: WheelEvent) => {
event.preventDefault();
const wrapper = imageWrapper.value;
if (!wrapper) return;
// 记录当前滚动位置和鼠标相对位置
const rect = wrapper.getBoundingClientRect();
const mouseX = event.clientX - rect.left;
const mouseY = event.clientY - rect.top;
const scrollLeft = wrapper.scrollLeft;
const scrollTop = wrapper.scrollTop;
// 计算鼠标在图片中的相对位置
const relativeX = (scrollLeft + mouseX) / imageScale.value;
const relativeY = (scrollTop + mouseY) / imageScale.value;
const oldScale = imageScale.value;
const delta = event.deltaY > 0 ? 0.9 : 1.1;
imageScale.value = Math.max(0.1, Math.min(5, imageScale.value * delta));
// 缩放完成后调整滚动位置,保持鼠标位置不变
nextTick(() => {
if (wrapper) {
const newScrollLeft = relativeX * imageScale.value - mouseX;
const newScrollTop = relativeY * imageScale.value - mouseY;
wrapper.scrollLeft = Math.max(0, newScrollLeft);
wrapper.scrollTop = Math.max(0, newScrollTop);
}
});
};
// 图片加载完成时重置状态
const handleImageLoad = () => {
resetZoom();
};
// 图片加载错误处理
const handleImageError = () => {
ElMessage.error("图片加载失败");
};
// 全屏切换
const toggleFullscreen = () => {
if (!document.fullscreenElement) {
previewContainer.value?.requestFullscreen?.();
isFullscreen.value = true;
} else {
document.exitFullscreen?.();
isFullscreen.value = false;
}
};
// 关闭预览
const closePreview = () => {
if (isFullscreen.value) {
document.exitFullscreen?.();
isFullscreen.value = false;
}
imagePreviewVisible.value = false;
resetZoom();
};
相关样式:
js
.info-item {
margin-bottom: 15px;
display: flex;
align-items: center;
}
.label {
min-width: 155px;
// font-weight: bold;
color: #606266;
text-align: right;
}
.two-col-container {
display: flex;
flex-wrap: wrap;
gap: 16px; /* 子项之间的间距 */
// width: 100%;
width: 60%;
margin-left: 10px;
}
.item {
width: calc(50% - 8px); /* 50% 减去一半 gap 实现两列等宽 */
border: 1px dashed #ccc;
box-sizing: border-box;
padding: 12px;
// text-align: center;
border-radius: 6px;
background-color: #fff;
}
.proof-item {
margin-bottom: 20px;
}
.proof-label-text {
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
overflow: hidden;
text-overflow: ellipsis;
line-height: 1.4;
max-height: 2.8em; /* 1.4 * 2 = 2.8em for 2 lines */
word-break: break-word;
}
.upload-container {
margin-top: 10px;
cursor: pointer; /* 添加指针样式,提示用户可点击 */
padding: 5px; /* 添加内边距使点击区域更大 */
border-radius: 6px;
transition: background-color 0.2s;
}
.upload-container:hover {
background-color: rgba(64, 158, 255, 0.1); /* 添加悬停效果 */
}
/* 上传区域样式优化 */
.upload-area {
position: relative;
border: 2px dashed transparent;
border-radius: 8px;
padding: 4px;
transition: all 0.3s ease;
cursor: pointer;
// display: flex;
// justify-content: center;
}
.upload-area:hover {
border-color: #409eff;
background-color: rgba(64, 158, 255, 0.05);
}
.upload-area.active-upload {
border-color: #409eff;
background-color: rgba(64, 158, 255, 0.1);
box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.2);
}
.image-uploader {
border: 1px dashed #d9d9d9;
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
width: 148px;
height: 148px;
display: inline-block;
}
.image-uploader:hover {
border-color: #409eff;
}
.uploaded-images {
display: flex;
flex-wrap: wrap;
margin-top: 10px;
gap: 8px;
justify-content: flex-start;
align-items: flex-start;
}
.image-item {
position: relative;
width: 80px;
height: 80px;
border: 2px solid #e4e7ed;
border-radius: 8px;
overflow: hidden;
cursor: pointer;
transition: all 0.3s ease;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
&.active {
border: 2px solid #409eff;
transform: scale(1.05);
box-shadow: 0 4px 12px rgba(64, 158, 255, 0.3);
}
&:hover {
border-color: #409eff;
transform: scale(1.05);
box-shadow: 0 4px 12px rgba(64, 158, 255, 0.3);
}
&:hover .image-actions {
opacity: 1;
}
}
.thumbnail {
width: 100%;
height: 100%;
object-fit: cover;
}
.image-actions {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
opacity: 0;
transition: opacity 0.3s;
gap: 5px;
}
.image-actions .el-button {
color: white;
font-size: 16px;
padding: 8px;
&:hover {
background-color: rgba(255, 255, 255, 0.2);
border-radius: 50%;
}
}
/* 图片预览相关样式 */
.image-preview-container {
display: flex;
flex-direction: column;
height: 70vh;
user-select: none;
}
.image-controls {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px 0;
border-bottom: 1px solid #ebeef5;
margin-bottom: 10px;
flex-shrink: 0;
}
.zoom-indicator {
font-size: 14px;
color: #606266;
font-weight: 500;
}
.image-wrapper {
flex: 1;
overflow: auto;
position: relative;
background-color: #f5f7fa;
border-radius: 8px;
/* 优化滚动性能 */
scroll-behavior: smooth;
}
.preview-image {
display: block;
margin: 0 auto;
border-radius: 8px;
transform-origin: top center;
user-select: none;
/* 移除最大尺寸限制,确保图片可以正常缩放 */
max-width: none;
max-height: none;
width: auto;
height: auto;
/* 当图片小于容器时居中显示,当图片大于容器时从顶部开始显示 */
vertical-align: top;
}
/* 全屏状态下的样式调整 */
.image-preview-container:fullscreen {
height: 100vh;
background-color: #000;
padding: 20px;
}
.image-preview-container:fullscreen .image-wrapper {
background-color: #000;
}
.image-preview-container:fullscreen .image-controls {
background-color: rgba(0, 0, 0, 0.8);
color: white;
border-radius: 8px;
padding: 15px;
border-bottom: 1px solid #666;
}
.image-preview-container:fullscreen .zoom-indicator {
color: white;
}