2025-03-08 21:24:14 +08:00
|
|
|
<script setup>
|
2025-03-10 12:37:25 +08:00
|
|
|
import {ref, onMounted, computed} from 'vue'
|
|
|
|
import {useRoute} from 'vue-router'
|
|
|
|
import api from '../../utils/axios.js'
|
|
|
|
import {blogImage, userProfile} from '../../utils/imageResource'
|
|
|
|
import router from "../../router/index.js";
|
2025-03-08 21:24:14 +08:00
|
|
|
|
2025-03-10 12:37:25 +08:00
|
|
|
// 获取路由参数
|
|
|
|
const route = useRoute()
|
|
|
|
const blog = ref(null)
|
|
|
|
const posterInfo = ref(null)
|
|
|
|
const id = route.params.id
|
2025-03-08 21:24:14 +08:00
|
|
|
|
2025-03-10 12:37:25 +08:00
|
|
|
// 在组件挂载时获取数据
|
|
|
|
onMounted(async () => {
|
|
|
|
// 获取博客数据
|
|
|
|
const blogResponse = await api.get(`/blogs/${id}`)
|
|
|
|
if (blogResponse.code === 1) {
|
|
|
|
await router.push('/404')
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (blogResponse.code === 0) {
|
|
|
|
blog.value = blogResponse.blog
|
|
|
|
// 获取发布者信息
|
|
|
|
const posterResponse = await api.get(`/userinfo?UID=${blog.value.poster}`)
|
|
|
|
if (posterResponse.code === 0) {
|
|
|
|
posterInfo.value = posterResponse.info
|
|
|
|
} else {
|
|
|
|
console.error('Failed to fetch poster info:', posterResponse)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
2025-03-08 21:24:14 +08:00
|
|
|
|
2025-03-10 12:37:25 +08:00
|
|
|
// 处理博客内容中的 <holder> 占位符,替换为 <img> 标签
|
|
|
|
const processedContent = computed(() => {
|
|
|
|
if (!blog.value || !blog.value.content) return ''
|
|
|
|
let content = blog.value.content
|
|
|
|
const regex = /<holder image ([\w.]+\.\w+) width=([\d.]+) height=([\d.]+)>/g
|
|
|
|
return content.replace(regex, (match, filename, width, height) => {
|
|
|
|
const url = blogImage(filename) // filename 是完整的 "1a2b3c4d.png"
|
|
|
|
return `<img src="${url}" width="${width}" height="${height}" alt="Blog Image" />`
|
|
|
|
})
|
2025-03-08 21:24:14 +08:00
|
|
|
})
|
|
|
|
</script>
|
|
|
|
|
|
|
|
<template>
|
2025-03-10 12:37:25 +08:00
|
|
|
<div class="blog-container">
|
|
|
|
<!-- 使用 Transition 包裹博客内容 -->
|
|
|
|
<Transition name="fade">
|
|
|
|
<div v-if="blog" class="blog-wrapper">
|
|
|
|
<h1>{{ blog.title }}</h1>
|
|
|
|
<div class="blog-meta">
|
|
|
|
<div class="avatar">
|
|
|
|
<img
|
|
|
|
v-if="posterInfo"
|
|
|
|
:src="userProfile(posterInfo.profile)"
|
|
|
|
alt="Poster Avatar"
|
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
<span>{{ posterInfo?.username || '' }}</span>
|
|
|
|
<span>{{ blog.post_date }}</span>
|
|
|
|
</div>
|
|
|
|
<div class="blog-content" v-html="processedContent"></div>
|
|
|
|
<div class="comments-section">
|
|
|
|
<Transition name="fade">
|
|
|
|
<p v-if="blog.allow_comments === 1">评论区域待实现</p>
|
|
|
|
<p v-else>不允许评论</p>
|
|
|
|
</Transition>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<!-- 当 blog 未加载时显示加载提示 -->
|
|
|
|
<div v-else class="loading">加载中...</div>
|
|
|
|
</Transition>
|
2025-03-08 21:24:14 +08:00
|
|
|
</div>
|
|
|
|
</template>
|
|
|
|
|
|
|
|
<style scoped>
|
2025-03-10 12:37:25 +08:00
|
|
|
.loading {
|
|
|
|
height: 0;
|
|
|
|
text-align: center;
|
|
|
|
font-size: 1.2em;
|
|
|
|
}
|
|
|
|
|
|
|
|
.blog-wrapper {
|
|
|
|
width: 100%;
|
2025-03-08 21:24:14 +08:00
|
|
|
display: flex;
|
|
|
|
flex-direction: column;
|
|
|
|
align-items: flex-start;
|
2025-03-10 12:37:25 +08:00
|
|
|
height: calc(100vh - 130px);
|
|
|
|
}
|
|
|
|
|
|
|
|
.blog-container {
|
|
|
|
display: flex;
|
|
|
|
flex-direction: column;
|
|
|
|
align-items: center;
|
|
|
|
justify-content: center;
|
|
|
|
width: 100%;
|
|
|
|
|
|
|
|
overflow: auto;
|
|
|
|
max-width: 1200px;
|
|
|
|
margin: 0 auto;
|
|
|
|
padding: 20px 30px 50px;
|
|
|
|
background-color: #333; /* 默认深色背景 */
|
|
|
|
color: #fff; /* 默认深色文字 */
|
|
|
|
}
|
|
|
|
|
|
|
|
.blog-meta {
|
|
|
|
display: flex;
|
|
|
|
align-items: center;
|
|
|
|
gap: 10px;
|
|
|
|
width: 100%;
|
|
|
|
margin-bottom: 20px;
|
|
|
|
}
|
|
|
|
|
|
|
|
.avatar, .avatar img {
|
|
|
|
width: 40px;
|
|
|
|
height: 40px;
|
|
|
|
border-radius: 50%;
|
|
|
|
overflow: hidden;
|
|
|
|
}
|
|
|
|
|
|
|
|
.blog-content {
|
|
|
|
margin-bottom: 20px;
|
2025-03-08 21:24:14 +08:00
|
|
|
width: 100%;
|
2025-03-10 12:37:25 +08:00
|
|
|
display: flex;
|
|
|
|
flex-direction: column;
|
|
|
|
align-items: center;
|
|
|
|
}
|
|
|
|
|
|
|
|
.comments-section {
|
|
|
|
margin-top: 20px;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 浅色模式 */
|
|
|
|
.theme-light .blog-container {
|
|
|
|
background-color: #ffffff;
|
|
|
|
color: #333;
|
|
|
|
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
|
|
|
}
|
|
|
|
|
|
|
|
.fade-enter-active,
|
|
|
|
.fade-leave-active {
|
|
|
|
transition: opacity 0.5s ease, transform 0.5s ease;
|
|
|
|
}
|
|
|
|
|
|
|
|
.fade-enter-from,
|
|
|
|
.fade-leave-to {
|
|
|
|
opacity: 0;
|
|
|
|
transform: translateY(20px); /* 从下方 20px 进入 */
|
|
|
|
}
|
|
|
|
|
|
|
|
.fade-enter-to,
|
|
|
|
.fade-leave-from {
|
|
|
|
opacity: 1;
|
|
|
|
transform: translateY(0);
|
2025-03-08 21:24:14 +08:00
|
|
|
}
|
|
|
|
</style>
|