2025-09-29 16:51:36 +08:00
|
|
|
|
<script setup>
|
2026-03-12 14:43:05 +08:00
|
|
|
|
import { ref, onMounted, watch, onUnmounted, getCurrentInstance } from 'vue'
|
2025-09-29 16:51:36 +08:00
|
|
|
|
|
|
|
|
|
|
const props = defineProps({
|
|
|
|
|
|
src: {
|
|
|
|
|
|
type: String,
|
|
|
|
|
|
required: true
|
|
|
|
|
|
},
|
|
|
|
|
|
alt: {
|
|
|
|
|
|
type: String,
|
|
|
|
|
|
default: '图片'
|
|
|
|
|
|
},
|
|
|
|
|
|
maxHeight: {
|
|
|
|
|
|
type: Number,
|
|
|
|
|
|
default: 500
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
const isExpanded = ref(false)
|
|
|
|
|
|
const showExpand = ref(false)
|
2026-02-06 11:17:10 +08:00
|
|
|
|
const isImageLoaded = ref(false)
|
2026-03-12 14:43:05 +08:00
|
|
|
|
const imgRef = ref(null)
|
|
|
|
|
|
const resizeObserver = ref(null)
|
|
|
|
|
|
const instance = getCurrentInstance()
|
|
|
|
|
|
|
|
|
|
|
|
// 全局通知图片加载/尺寸变化
|
|
|
|
|
|
const notifyImageLoaded = () => {
|
|
|
|
|
|
if (instance?.appContext.config.globalProperties.$notifyImageLoaded) {
|
|
|
|
|
|
instance.appContext.config.globalProperties.$notifyImageLoaded();
|
|
|
|
|
|
}
|
|
|
|
|
|
document.dispatchEvent(new CustomEvent('longPicImageLoaded'));
|
|
|
|
|
|
};
|
2026-02-06 11:17:10 +08:00
|
|
|
|
|
2026-03-12 14:43:05 +08:00
|
|
|
|
// 检查图片高度并通知外部
|
2026-02-06 11:17:10 +08:00
|
|
|
|
const checkImageHeight = (img) => {
|
2026-03-12 14:43:05 +08:00
|
|
|
|
if (typeof window === 'undefined') return
|
2026-02-06 11:17:10 +08:00
|
|
|
|
const naturalHeight = img.naturalHeight || img.offsetHeight
|
|
|
|
|
|
console.log(props.alt, naturalHeight, '图片实际高度')
|
2026-03-12 14:43:05 +08:00
|
|
|
|
// 仅初始化时判断是否显示展开按钮
|
2026-02-06 11:17:10 +08:00
|
|
|
|
showExpand.value = naturalHeight > props.maxHeight
|
|
|
|
|
|
isImageLoaded.value = true
|
2026-03-12 14:43:05 +08:00
|
|
|
|
notifyImageLoaded()
|
2026-02-06 11:17:10 +08:00
|
|
|
|
}
|
2025-09-29 16:51:36 +08:00
|
|
|
|
|
2026-03-12 14:43:05 +08:00
|
|
|
|
// 图片加载完成处理
|
2025-09-29 16:51:36 +08:00
|
|
|
|
const handleImageLoad = (event) => {
|
2026-02-06 11:17:10 +08:00
|
|
|
|
if (isImageLoaded.value) return
|
|
|
|
|
|
checkImageHeight(event.target)
|
2025-09-29 16:51:36 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-12 14:43:05 +08:00
|
|
|
|
// 监听元素尺寸变化
|
|
|
|
|
|
const observeResize = () => {
|
|
|
|
|
|
if (typeof window === 'undefined' || !window.ResizeObserver) return
|
|
|
|
|
|
resizeObserver.value = new ResizeObserver((entries) => {
|
|
|
|
|
|
notifyImageLoaded()
|
|
|
|
|
|
})
|
|
|
|
|
|
if (imgRef.value) {
|
|
|
|
|
|
resizeObserver.value.observe(imgRef.value)
|
2026-02-06 11:17:10 +08:00
|
|
|
|
}
|
2026-03-12 14:43:05 +08:00
|
|
|
|
}
|
2026-02-06 11:17:10 +08:00
|
|
|
|
|
|
|
|
|
|
// 展开/收起切换
|
2025-09-29 16:51:36 +08:00
|
|
|
|
const toggleExpand = () => {
|
|
|
|
|
|
isExpanded.value = !isExpanded.value
|
2026-03-12 14:43:05 +08:00
|
|
|
|
setTimeout(() => notifyImageLoaded(), 100)
|
2025-09-29 16:51:36 +08:00
|
|
|
|
}
|
2026-02-06 11:17:10 +08:00
|
|
|
|
|
2026-03-12 14:43:05 +08:00
|
|
|
|
// 生命周期
|
|
|
|
|
|
onMounted(() => {
|
|
|
|
|
|
if (typeof window === 'undefined') return
|
|
|
|
|
|
const img = imgRef.value
|
|
|
|
|
|
if (img && img.complete) {
|
|
|
|
|
|
checkImageHeight(img)
|
|
|
|
|
|
}
|
|
|
|
|
|
observeResize()
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
onUnmounted(() => {
|
|
|
|
|
|
if (resizeObserver.value) {
|
|
|
|
|
|
resizeObserver.value.disconnect()
|
|
|
|
|
|
}
|
2026-02-06 11:17:10 +08:00
|
|
|
|
})
|
2025-09-29 16:51:36 +08:00
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<template>
|
|
|
|
|
|
<div class="image-container">
|
|
|
|
|
|
<div
|
2026-02-06 11:17:10 +08:00
|
|
|
|
class="image-wrapper"
|
|
|
|
|
|
:class="{ 'expanded': isExpanded }"
|
2026-03-12 14:43:05 +08:00
|
|
|
|
:style="{
|
|
|
|
|
|
maxHeight: !isExpanded ? `${maxHeight}px` : 'none',
|
|
|
|
|
|
aspectRatio: !isExpanded ? '16/9' : 'unset' // 展开后取消宽高比限制
|
|
|
|
|
|
}"
|
2025-09-29 16:51:36 +08:00
|
|
|
|
>
|
|
|
|
|
|
<img
|
2026-03-12 14:43:05 +08:00
|
|
|
|
ref="imgRef"
|
2026-02-06 11:17:10 +08:00
|
|
|
|
:src="src"
|
|
|
|
|
|
:alt="alt"
|
|
|
|
|
|
@load="handleImageLoad"
|
|
|
|
|
|
class="responsive-image"
|
2026-03-12 14:43:05 +08:00
|
|
|
|
loading="eager"
|
|
|
|
|
|
decoding="async"
|
2025-09-29 16:51:36 +08:00
|
|
|
|
>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
2026-03-12 14:43:05 +08:00
|
|
|
|
<!-- 按钮组容器 -->
|
|
|
|
|
|
<div v-if="showExpand" class="hint-wrapper">
|
|
|
|
|
|
<div class="expand-hint" @click="toggleExpand" v-show="!isExpanded">
|
|
|
|
|
|
<span>点击查看完整图片</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="collapse-hint" @click="toggleExpand" v-show="isExpanded">
|
|
|
|
|
|
<span>收起图片</span>
|
|
|
|
|
|
</div>
|
2025-09-29 16:51:36 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<style scoped>
|
|
|
|
|
|
.image-container {
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
width: 100%;
|
2026-02-06 11:17:10 +08:00
|
|
|
|
margin: 0 auto;
|
2026-03-12 14:43:05 +08:00
|
|
|
|
min-height: 100px; /* 兜底高度,防止加载前塌陷 */
|
2025-09-29 16:51:36 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.image-wrapper {
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
overflow: hidden;
|
2026-02-06 11:17:10 +08:00
|
|
|
|
transition: max-height 0.3s ease-in-out;
|
2025-09-29 16:51:36 +08:00
|
|
|
|
position: relative;
|
2026-03-12 14:43:05 +08:00
|
|
|
|
/* 移除固定aspect-ratio,改为动态绑定 */
|
2025-09-29 16:51:36 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-12 14:43:05 +08:00
|
|
|
|
/* 未展开时添加渐变遮罩 */
|
2026-02-06 11:17:10 +08:00
|
|
|
|
.image-wrapper:not(.expanded)::after {
|
2025-09-29 16:51:36 +08:00
|
|
|
|
content: '';
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
bottom: 0;
|
|
|
|
|
|
left: 0;
|
|
|
|
|
|
right: 0;
|
2026-02-06 11:17:10 +08:00
|
|
|
|
height: 80px;
|
|
|
|
|
|
background: linear-gradient(transparent, rgba(255,255,255,0.95));
|
2025-09-29 16:51:36 +08:00
|
|
|
|
pointer-events: none;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-12 14:43:05 +08:00
|
|
|
|
/* 展开后隐藏遮罩 */
|
2025-09-29 16:51:36 +08:00
|
|
|
|
.image-wrapper.expanded::after {
|
|
|
|
|
|
display: none;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.responsive-image {
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
height: auto;
|
|
|
|
|
|
display: block;
|
2026-03-12 14:43:05 +08:00
|
|
|
|
object-fit: contain; /* 确保图片完整显示,不拉伸 */
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* 按钮容器 */
|
|
|
|
|
|
.hint-wrapper {
|
|
|
|
|
|
width: 100%;
|
2025-09-29 16:51:36 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-06 11:17:10 +08:00
|
|
|
|
.expand-hint, .collapse-hint {
|
2025-09-29 16:51:36 +08:00
|
|
|
|
text-align: center;
|
|
|
|
|
|
padding: 8px;
|
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
|
font-size: 14px;
|
2026-02-06 11:17:10 +08:00
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
margin-top: 8px;
|
2025-09-29 16:51:36 +08:00
|
|
|
|
transition: background-color 0.3s;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-06 11:17:10 +08:00
|
|
|
|
.expand-hint {
|
|
|
|
|
|
background: rgba(64, 158, 255, 0.1);
|
|
|
|
|
|
color: #409eff;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-29 16:51:36 +08:00
|
|
|
|
.expand-hint:hover {
|
|
|
|
|
|
background: rgba(64, 158, 255, 0.2);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.collapse-hint {
|
|
|
|
|
|
background: rgba(103, 194, 58, 0.1);
|
|
|
|
|
|
color: #67c23a;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.collapse-hint:hover {
|
|
|
|
|
|
background: rgba(103, 194, 58, 0.2);
|
|
|
|
|
|
}
|
|
|
|
|
|
</style>
|