预览图
- 组件代码
<template>
<div class="pic-wrap" :style="{ gap: gap + 'px' }">
<div
class="img-box"
:class="{ 'disable-hover': shouldShowMask(index) }"
v-for="(it, index) in showList"
:key="index"
>
<div class="img-box-item">
<el-image
:style="imageStyle"
class="elImage"
:src="it"
:fit="imageFit"
:preview-src-list="list"
:initial-index="index"
@error="handleError"
@show="handlePreviewShow"
>
<template #error>
<div class="image-error">
<i class="el-icon-picture-outline"></i>
</div>
</template>
</el-image>
<div
class="num-box"
v-if="shouldShowMask(index)"
:style="{ background: maskBackground }"
>
+{{ list.length - showList.length }}
</div>
</div>
<div class="el-icon-view" v-if="shouldShowEye(index)"></div>
</div>
</div>
</template>
<script>
export default {
name: "CommonImgPreview",
props: {
list: {
type: Array,
default: () => [],
},
needShowIndex: {
type: Boolean,
default: false,
},
width: {
type: [String, Number],
default: 32,
},
height: {
type: [String, Number],
default: 32,
},
maxCount: {
type: Number,
default: 5,
},
gap: {
type: Number,
default: 4,
},
imageFit: {
type: String,
default: "cover",
},
showMoreCount: {
type: Boolean,
default: true,
},
maskBackground: {
type: String,
default: "rgba(0, 0, 0, 0.3)",
},
borderRadius: {
type: [String, Number],
default: 4,
},
counterSeparator: {
type: String,
default: " / ",
},
counterCurrentClass: {
type: String,
default: "current-index",
},
counterTotalClass: {
type: String,
default: "total-count",
},
counterStyle: {
type: Object,
default: () => ({
current: {
fontSize: "16px",
fontWeight: "bold",
color: "#fff",
},
separator: {
color: "#ffffff",
},
total: {
fontSize: "14px",
color: "#909399",
},
}),
},
},
computed: {
showList() {
return this.list.slice(0, this.maxCount);
},
imageStyle() {
return {
width: `${this.width}px`,
height: `${this.height}px`,
borderRadius: `${this.borderRadius}px`,
};
},
},
data() {
return {
previewIndex: 0,
currentIndex: 0,
};
},
mounted() {
if (!this.needShowIndex) return;
// 监听点击事件
this.$el.addEventListener("click", this.handleImageClick);
},
beforeDestroy() {
this.$el.removeEventListener("click", this.handleImageClick);
},
methods: {
handleError(e) {
this.$emit("error", e);
},
shouldShowMask(index) {
return (
this.showMoreCount &&
index === this.showList.length - 1 &&
this.list.length > this.maxCount
);
},
shouldShowEye(index) {
if (this.list.length > this.maxCount && index === this.maxCount - 1) {
return false;
}
return true;
},
handlePreviewShow() {
this.$nextTick(() => {
const viewer = document.querySelector(".el-image-viewer__mask");
if (!viewer) return;
console.log(viewer, "fs");
});
},
handleImageClick(e) {
if (e.target.tagName !== "IMG") return;
setTimeout(() => {
const viewer = document.querySelector(".el-image-viewer__wrapper");
if (!viewer) return;
// 获取当前点击的图片索引
const imgElements = this.$el.querySelectorAll("img");
const index = Array.from(imgElements).indexOf(e.target);
// 修改:创建计数器容器
const counterContainer = document.createElement("div");
counterContainer.className = "image-counter";
counterContainer.style.position = "fixed";
counterContainer.style.top = "40px";
counterContainer.style.left = "50%";
counterContainer.style.transform = "translateX(-50%)";
counterContainer.style.background = "rgba(0, 0, 0, 0.5)";
counterContainer.style.padding = "8px 12px";
counterContainer.style.borderRadius = "4px";
counterContainer.style.zIndex = "2010";
const currentIndexEl = document.createElement("span");
currentIndexEl.className = this.counterCurrentClass;
Object.assign(currentIndexEl.style, this.counterStyle.current);
const separator = document.createElement("span");
separator.className = "separator";
separator.textContent = this.counterSeparator;
Object.assign(separator.style, this.counterStyle.separator);
const totalCountEl = document.createElement("span");
totalCountEl.className = this.counterTotalClass;
Object.assign(totalCountEl.style, this.counterStyle.total);
// 修改:更新计数的方式
const updateCount = (newIndex) => {
currentIndexEl.textContent = newIndex;
totalCountEl.textContent = this.list.length;
// 清空并重新添加元素
counterContainer.innerHTML = "";
counterContainer.appendChild(currentIndexEl);
counterContainer.appendChild(separator);
counterContainer.appendChild(totalCountEl);
};
// 初始化计数器显示
updateCount(index + 1);
viewer.appendChild(counterContainer);
viewer.setAttribute("data-current", index + 1);
const prevBtn = viewer.querySelector(".el-image-viewer__prev");
const nextBtn = viewer.querySelector(".el-image-viewer__next");
const handlePrev = () => {
const currentNum = parseInt(viewer.getAttribute("data-current"));
const newIndex = currentNum > 1 ? currentNum - 1 : this.list.length;
viewer.setAttribute("data-current", newIndex);
updateCount(newIndex);
};
const handleNext = () => {
const currentNum = parseInt(viewer.getAttribute("data-current"));
const newIndex = currentNum < this.list.length ? currentNum + 1 : 1;
viewer.setAttribute("data-current", newIndex);
updateCount(newIndex);
};
if (prevBtn) {
prevBtn.addEventListener("click", handlePrev);
}
if (nextBtn) {
nextBtn.addEventListener("click", handleNext);
}
const handleKeydown = (event) => {
if (event.key === "ArrowLeft") handlePrev();
if (event.key === "ArrowRight") handleNext();
};
document.addEventListener("keydown", handleKeydown);
const cleanup = () => {
document.removeEventListener("keydown", handleKeydown);
if (prevBtn) {
prevBtn.removeEventListener("click", handlePrev);
}
if (nextBtn) {
nextBtn.removeEventListener("click", handleNext);
}
if (counterContainer.parentNode) {
counterContainer.parentNode.removeChild(counterContainer);
}
};
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
mutation.removedNodes.forEach((node) => {
if (node === viewer) {
cleanup();
observer.disconnect();
}
});
});
});
observer.observe(document.body, { childList: true, subtree: true });
}, 0);
},
},
};
</script>
<style lang="scss" scoped>
.pic-wrap {
gap: 4px;
pointer-events: auto;
position: relative;
cursor: pointer;
width: fit-content;
display: flex;
flex-wrap: wrap;
.mask {
pointer-events: none;
display: flex;
align-items: center;
justify-content: flex-start;
position: absolute;
transition: 0.3s;
opacity: 0;
top: 0;
left: 50%;
width: 32px;
height: 32px;
border-radius: 4px;
transform: translateX(-50%);
background-color: rgba(0, 0, 0, 0.5);
}
.mask.active {
opacity: 1;
pointer-events: none;
}
.view-icon {
width: 16px;
height: 16px;
background-size: contain;
}
}
.img-box {
display: flex;
justify-content: flex-start;
align-items: center;
gap: 4px;
flex-wrap: wrap;
position: relative;
transition: all 0.6s;
.el-icon-view {
opacity: 0;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
transition: opacity 0.3s ease;
}
&:hover:not(.disable-hover) {
transition: all 0.3s;
border-radius: 4px;
.elImage {
background: #000;
}
/deep/ img {
opacity: 0.6;
border-radius: 4px;
}
/deep/ .el-image-viewer__canvas > img {
opacity: 1;
border-radius: 4px;
}
.el-icon-view {
pointer-events: none;
opacity: 1;
color: #fff;
}
}
}
.img-box-item {
width: 100%;
height: 100%;
display: flex;
.elImage {
position: relative;
}
.num-box {
pointer-events: none;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 32px;
color: #fff;
background: rgba(0, 0, 0, 0.3);
border-radius: 4px;
display: flex;
align-items: center;
justify-content: center;
}
}
.image-error {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
background: #f5f7fa;
color: #909399;
}
</style>
2.调用组件示例
<CommonImgPreview
:list="row.checkImg"
needShowIndex>
</CommonImgPreview>