251 lines
5.1 KiB
Vue
251 lines
5.1 KiB
Vue
<script setup>
|
|
import { ref, watch } from 'vue'
|
|
|
|
// 组件属性
|
|
const props = defineProps({
|
|
// 是否显示
|
|
visible: {
|
|
type: Boolean,
|
|
default: false
|
|
},
|
|
// 图片数组
|
|
images: {
|
|
type: Array,
|
|
default: () => []
|
|
},
|
|
// 当前索引
|
|
currentIndex: {
|
|
type: Number,
|
|
default: 0
|
|
},
|
|
// 查看模式:'with-title' 或 'without-title'
|
|
mode: {
|
|
type: String,
|
|
default: 'with-title'
|
|
}
|
|
})
|
|
|
|
// 组件事件
|
|
const emit = defineEmits(['close', 'update:currentIndex'])
|
|
|
|
// 当前显示的图片索引
|
|
const currentImageIndex = ref(props.currentIndex)
|
|
|
|
// 监听props变化
|
|
watch(() => props.currentIndex, (newVal) => {
|
|
currentImageIndex.value = newVal
|
|
})
|
|
|
|
watch(() => props.visible, (newVal) => {
|
|
if (newVal) {
|
|
currentImageIndex.value = props.currentIndex
|
|
}
|
|
})
|
|
|
|
// 切换图片
|
|
const prevImage = () => {
|
|
currentImageIndex.value = (currentImageIndex.value - 1 + props.images.length) % props.images.length
|
|
}
|
|
|
|
const nextImage = () => {
|
|
currentImageIndex.value = (currentImageIndex.value + 1) % props.images.length
|
|
}
|
|
|
|
// 关闭弹窗
|
|
const closeModal = () => {
|
|
emit('close')
|
|
}
|
|
|
|
// 点击遮罩关闭
|
|
const handleOverlayClick = () => {
|
|
closeModal()
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<div class="gallery-modal" v-if="visible">
|
|
<!-- 遮罩层 -->
|
|
<div class="modal-overlay" @click="handleOverlayClick"></div>
|
|
|
|
<!-- 弹窗内容 -->
|
|
<div class="modal-content" :class="{ 'without-title': mode === 'without-title' }">
|
|
<!-- 图片区域 -->
|
|
<div class="gallery-image-wrapper">
|
|
<!-- 无标题模式下的导航按钮 -->
|
|
<div v-if="mode === 'without-title'" class="nav-btn prev-btn side-btn" @click="prevImage">
|
|
<img src="/static/images/btn_prev.png" alt="上一张" class="nav-icon" />
|
|
</div>
|
|
<img :src="images[currentImageIndex]?.src" :alt="images[currentImageIndex]?.title" class="gallery-image" />
|
|
<!-- 无标题模式下的导航按钮 -->
|
|
<div v-if="mode === 'without-title'" class="nav-btn next-btn side-btn" @click="nextImage">
|
|
<img src="/static/images/btn_next.png" alt="下一张" class="nav-icon" />
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 有标题模式下的控制区域 -->
|
|
<div v-if="mode === 'with-title'" class="gallery-controls">
|
|
<div class="nav-btn prev-btn" @click="prevImage">
|
|
<img src="/static/images/btn_prev.png" alt="上一张" class="nav-icon" />
|
|
</div>
|
|
<div class="gallery-title">{{ images[currentImageIndex]?.title }}</div>
|
|
<div class="nav-btn next-btn" @click="nextImage">
|
|
<img src="/static/images/btn_next.png" alt="下一张" class="nav-icon" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 关闭按钮(在对话框外边下方) -->
|
|
<div class="close-btn" @click="closeModal">
|
|
<img src="/static/images/btn_close.png" alt="关闭" class="close-icon" />
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped>
|
|
/* 弹窗容器 */
|
|
.gallery-modal {
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100vw;
|
|
height: 100vh;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
z-index: 1000;
|
|
}
|
|
|
|
/* 遮罩层 */
|
|
.modal-overlay {
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
background-color: rgba(0, 0, 0, 0.7);
|
|
}
|
|
|
|
/* 弹窗内容 */
|
|
.modal-content {
|
|
position: relative;
|
|
width: 700rpx;
|
|
background-color: #d72717;
|
|
border-radius: 20rpx;
|
|
padding: 30rpx;
|
|
box-sizing: border-box;
|
|
z-index: 1001;
|
|
animation: modalIn 0.3s ease;
|
|
}
|
|
|
|
/* 无标题模式下的弹窗内容 */
|
|
.modal-content.without-title {
|
|
padding-bottom: 30rpx;
|
|
}
|
|
|
|
/* 图片区域 */
|
|
.gallery-image-wrapper {
|
|
position: relative;
|
|
width: 100%;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
|
|
/* 无标题模式下的侧边导航按钮 */
|
|
.side-btn {
|
|
position: absolute;
|
|
top: 50%;
|
|
transform: translateY(-50%);
|
|
z-index: 10;
|
|
}
|
|
|
|
.side-btn.prev-btn {
|
|
left: -40rpx;
|
|
}
|
|
|
|
.side-btn.next-btn {
|
|
right: -40rpx;
|
|
}
|
|
|
|
@keyframes modalIn {
|
|
from {
|
|
opacity: 0;
|
|
transform: scale(0.9);
|
|
}
|
|
to {
|
|
opacity: 1;
|
|
transform: scale(1);
|
|
}
|
|
}
|
|
|
|
/* 关闭按钮(在对话框外边下方) */
|
|
.close-btn {
|
|
width: 80rpx;
|
|
height: 80rpx;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
cursor: pointer;
|
|
z-index: 1001;
|
|
margin-top: 30rpx;
|
|
}
|
|
|
|
.close-icon {
|
|
width: 100%;
|
|
height: 100%;
|
|
object-fit: contain;
|
|
}
|
|
|
|
/* 上方图片区域 */
|
|
.gallery-image-wrapper {
|
|
width: 100%;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
margin-bottom: 20rpx;
|
|
}
|
|
|
|
.gallery-image {
|
|
width: 100%;
|
|
height: auto;
|
|
max-height: 600rpx;
|
|
object-fit: contain;
|
|
border-radius: 20rpx;
|
|
display: block;
|
|
}
|
|
|
|
/* 下方控制区域 */
|
|
.gallery-controls {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
height: 80rpx;
|
|
}
|
|
|
|
.nav-btn {
|
|
width: 70rpx;
|
|
height: 70rpx;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
cursor: pointer;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.nav-icon {
|
|
width: 100%;
|
|
height: 100%;
|
|
object-fit: contain;
|
|
}
|
|
|
|
.gallery-title {
|
|
flex: 1;
|
|
text-align: center;
|
|
color: #fff;
|
|
font-size: 40rpx;
|
|
font-weight: 500;
|
|
letter-spacing: 6rpx;
|
|
padding: 0 20rpx;
|
|
}
|
|
</style> |