国外MOSE官网
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

231 lines
5.5 KiB

<script setup lang="ts">
import { ref, watch, onMounted, onBeforeUnmount } from 'vue';
import { Icon } from '@iconify/vue';
// 定义弹窗的属性
const props = defineProps({
// 是否显示弹窗
visible: {
type: Boolean,
default: false
},
// 弹窗标题
title: {
type: String,
default: ''
},
// 弹窗内容
content: {
type: String,
default: ''
},
// 弹窗图片
image: {
type: String,
default: ''
},
// 来源信息(适用于媒体报道)
source: {
type: String,
default: ''
},
// 日期信息
date: {
type: String,
default: ''
},
// 弹窗类型(post 或 media)
type: {
type: String,
default: 'post'
}
});
// 弹窗动画状态
const modalOpen = ref(false);
const modalReady = ref(false);
// 监听visible属性变化
watch(() => props.visible, (newVal) => {
if (newVal) {
document.body.classList.add('overflow-hidden');
modalReady.value = true;
setTimeout(() => {
modalOpen.value = true;
}, 50);
} else {
modalOpen.value = false;
setTimeout(() => {
modalReady.value = false;
document.body.classList.remove('overflow-hidden');
}, 300);
}
}, { immediate: true });
// 定义关闭弹窗的事件
const emit = defineEmits(['close']);
const closeModal = () => {
emit('close');
};
// 点击弹窗外部关闭弹窗
const handleOutsideClick = (e: MouseEvent) => {
const modal = document.querySelector('.modal-content');
if (modal && !modal.contains(e.target as Node)) {
closeModal();
}
};
// 按ESC键关闭弹窗
const handleEscKey = (e: KeyboardEvent) => {
if (e.key === 'Escape') {
closeModal();
}
};
// 添加事件监听器
onMounted(() => {
document.addEventListener('mousedown', handleOutsideClick);
document.addEventListener('keydown', handleEscKey);
});
// 移除事件监听器
onBeforeUnmount(() => {
document.removeEventListener('mousedown', handleOutsideClick);
document.removeEventListener('keydown', handleEscKey);
});
// 添加默认导出
defineExpose({
closeModal
});
</script>
<template>
<div
v-if="modalReady"
class="fixed inset-0 z-50 flex items-center justify-center p-4 md:p-6"
:class="{ 'modal-visible': modalOpen }"
>
<!-- 背景遮罩 -->
<div
class="modal-backdrop absolute inset-0 bg-black transition-opacity duration-300"
:class="{ 'opacity-60': modalOpen, 'opacity-0': !modalOpen }"
></div>
<!-- 弹窗内容 -->
<div
class="modal-content relative bg-background w-full max-w-3xl max-h-[90vh] overflow-y-auto rounded-xl shadow-2xl transform transition-all duration-300"
:class="{ 'opacity-100 scale-100': modalOpen, 'opacity-0 scale-95': !modalOpen }"
>
<!-- 关闭按钮 -->
<button
@click="closeModal"
class="absolute top-4 right-4 z-10 w-8 h-8 flex items-center justify-center rounded-full bg-background-dark/50 hover:bg-background-dark/80 text-text-secondary hover:text-text transition-colors duration-200"
>
<Icon icon="carbon:close" width="20" height="20" />
</button>
<!-- 弹窗头部 - 图片 -->
<div class="relative w-full h-64 md:h-80 overflow-hidden">
<img
:src="image"
:alt="title"
class="w-full h-full object-cover"
/>
<div class="absolute inset-0 bg-gradient-to-t from-background to-transparent"></div>
<!-- 来源和日期信息 -->
<div class="absolute bottom-4 left-4 flex flex-wrap gap-2">
<div v-if="source" class="bg-black/50 backdrop-blur-sm px-3 py-1 rounded-lg text-white text-sm flex items-center">
<Icon icon="carbon:document" class="mr-1.5" width="16" height="16" />
{{ source }}
</div>
<div v-if="date" class="bg-black/50 backdrop-blur-sm px-3 py-1 rounded-lg text-white text-sm flex items-center">
<Icon icon="carbon:calendar" class="mr-1.5" width="16" height="16" />
{{ date }}
</div>
</div>
</div>
<!-- 弹窗内容 -->
<div class="p-6 md:p-8">
<h2 class="text-2xl md:text-3xl font-bold text-text mb-6">{{ title }}</h2>
<div class="prose prose-sm md:prose-base max-w-none text-text-secondary" v-html="content"></div>
<!-- 底部操作区 -->
<div class="mt-8 flex justify-end">
<button
@click="closeModal"
class="px-6 py-2 bg-primary text-white rounded-lg hover:bg-primary-dark transition-colors duration-200"
>
关闭
</button>
</div>
</div>
</div>
</div>
</template>
<style scoped>
.modal-visible {
animation: fadeIn 0.3s ease;
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
/* 自定义滚动条 */
.modal-content {
scrollbar-width: thin;
scrollbar-color: var(--color-primary) transparent;
}
.modal-content::-webkit-scrollbar {
width: 6px;
}
.modal-content::-webkit-scrollbar-track {
background: transparent;
}
.modal-content::-webkit-scrollbar-thumb {
background-color: var(--color-primary-light);
border-radius: 20px;
}
/* 富文本内容样式 */
:deep(.prose) {
color: inherit;
}
:deep(.prose a) {
color: var(--color-primary);
text-decoration: none;
}
:deep(.prose a:hover) {
text-decoration: underline;
}
:deep(.prose img) {
border-radius: 0.5rem;
}
:deep(.prose h2) {
color: var(--color-text);
font-weight: 600;
margin-top: 2em;
margin-bottom: 1em;
}
:deep(.prose h3) {
color: var(--color-text);
font-weight: 600;
margin-top: 1.5em;
margin-bottom: 0.75em;
}
</style>