|
|
- <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>
|