feat: 添加登录功能与文章编辑功能

refactor: 重构API服务与全局状态管理

style: 优化UI样式与布局

fix: 修复文章列表与详情页的显示问题

docs: 更新类型定义与注释

chore: 更新依赖包与配置文件
This commit is contained in:
qingfeng1121
2025-10-30 19:00:59 +08:00
parent 85bf3214cc
commit 6d90b5842f
27 changed files with 2400 additions and 304 deletions

View File

@@ -2,13 +2,13 @@
<div id="article-detail-page">
<!-- 加载状态 -->
<div v-if="loading" class="loading-state-container">
<el-skeleton :count="1" />
<el-skeleton :count="3" />
<el-skeleton :count="1" />
<el-skeleton :count="3" />
</div>
<!-- 错误状态 -->
<div v-else-if="error" class="error-state-container">
<el-alert :title="error" type="error" show-icon />
<el-alert :title="error" type="error" show-icon />
<el-button type="primary" @click="fetchArticleDetail">重新加载</el-button>
</div>
@@ -21,7 +21,7 @@
<div class="article-meta-info">
<span class="meta-item">
<i class="el-icon-date"></i>
{{ formatDate(article.createTime) }}
{{ formatDate(article.createdAt) }}
</span>
<span class="meta-item">
<i class="el-icon-folder"></i>
@@ -36,15 +36,15 @@
<!-- 文章内容区域 -->
<div class="article-content-area">
<div v-html="article.content"></div>
<markdownViewer :markdownContent="article.markdownscontent" />
</div>
<!-- 文章底部信息 -->
<div class="article-footer-section">
<div class="article-tag-list">
<span v-for="tag in article.tags || []" :key="tag" class="el-tag el-tag--primary">
<!-- <span v-for="tag in article.tags || []" :key="tag" class="el-tag el-tag--primary">
{{ tag }}
</span>
</span> -->
</div>
<!-- 文章操作按钮 -->
@@ -54,13 +54,13 @@
</el-button>
</div>
</div>
<!-- 相关文章推荐 -->
<div class="related-articles-section" v-if="relatedArticles.length > 0">
<h3>相关文章</h3>
<div class="related-articles-list">
<div v-for="item in relatedArticles" :key="item.id" class="related-article-card"
@click="handleRelatedArticleClick(item.id)">
<div v-for="item in relatedArticles" :key="item.articleid" class="related-article-card"
@click="handleRelatedArticleClick(item.articleid)">
<i class="el-icon-document"></i>
<span>{{ item.title }}</span>
</div>
@@ -75,7 +75,7 @@
<!-- 评论区组件 -->
<div>
<messageboard class="comment-section" v-if="article && Object.keys(article).length > 0" />
<messageboard class="comment-section" />
</div>
</div>
@@ -86,18 +86,18 @@
import { useRoute, useRouter } from 'vue-router'
import { ref, onMounted } from 'vue'
import { articleService } from '@/services'
import { messageService } from '@/services'
import { categoryAttributeService } from '@/services'
import { useGlobalStore } from '@/store/globalStore'
import { ElMessage } from 'element-plus'
import type { Article } from '@/types'
import { formatDate } from '@/utils/dateUtils'
import messageboard from './messageboard.vue'
import markdownViewer from './markdown.vue'
// 路由相关
const route = useRoute()
const router = useRouter()
// 响应式状态管理
const globalStore = useGlobalStore()
const article = ref<Article | null>(null) // 使用 null 作为初始值,避免类型不匹配问题
const loading = ref(false)
const error = ref('')
@@ -112,26 +112,22 @@ const fetchArticleDetail = async () => {
error.value = ''
// 获取路由参数
const articleId = route.query.url as string
console.log('获取文章ID:', articleId)
const articleId = globalStore.getValue('articleInfo')?.articleid || null
if (!articleId) {
throw new Error('文章ID不存在')
throw new Error('文章不存在')
}
// 获取文章详情
const response = await articleService.getArticleById(Number(articleId))
if (response.data) {
article.value = response.data
const response = await globalStore.getValue('articleInfo')
// const markdowndata = await
if (response) {
article.value = response
// 获取并设置分类名称
const categoryResponse = await categoryAttributeService.getAttributeById(article.value.attributeid)
const categoryResponse = await categoryAttributeService.getAttributeById(Number(article.value.attributeid))
article.value.categoryName = categoryResponse.data.attributename || '未分类'
// 增加文章浏览量
try {
await articleService.incrementArticleViews(Number(articleId))
console.log('文章浏览量增加成功')
// 更新前端显示的浏览量
if (article.value.viewCount) {
article.value.viewCount++
@@ -142,32 +138,15 @@ const fetchArticleDetail = async () => {
console.error('增加文章浏览量失败:', err)
// 不阻止主流程
}
// 获取相关文章(同属性下的其他文章)
if (article.value.attributeid) {
try {
const relatedResponse = await articleService.getArticlesByCategory(article.value.attributeid)
// 过滤掉当前文章并取前5篇作为相关文章
relatedArticles.value = relatedResponse.data
? relatedResponse.data.filter((item: Article) => item.id !== article.value?.id).slice(0, 5)
: []
} catch (err) {
console.error('获取相关文章失败:', err)
// 不阻止主流程
}
}
} else {
throw new Error('文章不存在或已被删除')
}
console.log('获取文章详情成功:', article.value)
} catch (err) {
error.value = err instanceof Error ? err.message : '获取文章详情失败,请稍后重试'
console.error('获取文章详情失败:', err)
ElMessage.error(error.value)
} finally {
loading.value = false
console.log('文章详情加载完成')
}
}
@@ -198,13 +177,6 @@ onMounted(() => {
</script>
<style scoped>
/* 页面主容器 */
#article-detail-page {
min-height: 100vh;
background-color: #f5f7fa;
padding: 40px 0;
}
/* 文章详情容器 */
.article-detail-wrapper {
max-width: 900px;