2025-09-29 16:51:36 +08:00
|
|
|
|
<script setup>
|
2026-02-06 11:17:10 +08:00
|
|
|
|
import { ref, onMounted, watch } 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
|
|
|
|
// 标记图片是否已加载,防止@load重复触发
|
|
|
|
|
|
const isImageLoaded = ref(false)
|
|
|
|
|
|
|
|
|
|
|
|
// 核心:VuePress SSR适配,仅在浏览器端执行图片高度判断
|
|
|
|
|
|
const checkImageHeight = (img) => {
|
|
|
|
|
|
if (typeof window === 'undefined') return // 服务端直接返回
|
|
|
|
|
|
const naturalHeight = img.naturalHeight || img.offsetHeight
|
|
|
|
|
|
console.log(props.alt, naturalHeight, '图片实际高度')
|
|
|
|
|
|
showExpand.value = naturalHeight > props.maxHeight
|
|
|
|
|
|
isImageLoaded.value = true
|
|
|
|
|
|
}
|
2025-09-29 16:51:36 +08:00
|
|
|
|
|
2026-02-06 11:17:10 +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-02-06 11:17:10 +08:00
|
|
|
|
// 浏览器端挂载后,主动检查图片(解决@load未触发的边界情况)
|
|
|
|
|
|
onMounted(() => {
|
|
|
|
|
|
if (typeof window === 'undefined') return
|
|
|
|
|
|
const img = document.querySelector(`.responsive-image[src="${props.src}"]`)
|
|
|
|
|
|
if (img && img.complete) { // 图片已缓存完成,主动触发高度判断
|
|
|
|
|
|
checkImageHeight(img)
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
// 展开/收起切换
|
2025-09-29 16:51:36 +08:00
|
|
|
|
const toggleExpand = () => {
|
|
|
|
|
|
isExpanded.value = !isExpanded.value
|
|
|
|
|
|
}
|
2026-02-06 11:17:10 +08:00
|
|
|
|
|
|
|
|
|
|
// 监听展开状态,同步更新提示显示(兜底)
|
|
|
|
|
|
watch(isExpanded, (val) => {
|
|
|
|
|
|
if (!showExpand.value) return
|
|
|
|
|
|
showExpand.value = !val
|
|
|
|
|
|
})
|
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 }"
|
|
|
|
|
|
:style="{ maxHeight: !isExpanded ? `${maxHeight}px` : 'none' }"
|
2025-09-29 16:51:36 +08:00
|
|
|
|
>
|
|
|
|
|
|
<img
|
2026-02-06 11:17:10 +08:00
|
|
|
|
:src="src"
|
|
|
|
|
|
:alt="alt"
|
|
|
|
|
|
@load="handleImageLoad"
|
|
|
|
|
|
class="responsive-image"
|
|
|
|
|
|
loading="lazy"
|
2025-09-29 16:51:36 +08:00
|
|
|
|
>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
2026-02-06 11:17:10 +08:00
|
|
|
|
<!-- 展开提示:仅图片高度超限时显示 -->
|
|
|
|
|
|
<div v-if="showExpand" class="expand-hint" @click="toggleExpand" v-show="!isExpanded">
|
2025-09-29 16:51:36 +08:00
|
|
|
|
<span>点击查看完整图片</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
2026-02-06 11:17:10 +08:00
|
|
|
|
<!-- 收起提示:仅展开后显示 -->
|
|
|
|
|
|
<div class="collapse-hint" @click="toggleExpand" v-show="isExpanded">
|
2025-09-29 16:51:36 +08:00
|
|
|
|
<span>收起图片</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<style scoped>
|
|
|
|
|
|
.image-container {
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
width: 100%;
|
2026-02-06 11:17:10 +08:00
|
|
|
|
margin: 0 auto;
|
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-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;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.image-wrapper.expanded::after {
|
|
|
|
|
|
display: none;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.responsive-image {
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
height: auto;
|
|
|
|
|
|
display: block;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
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>
|