feat: 添加登录功能与文章编辑功能
refactor: 重构API服务与全局状态管理 style: 优化UI样式与布局 fix: 修复文章列表与详情页的显示问题 docs: 更新类型定义与注释 chore: 更新依赖包与配置文件
This commit is contained in:
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user