603 lines
15 KiB
Vue
603 lines
15 KiB
Vue
<template>
|
||
<div class="article-detail-page">
|
||
<div class="mx-auto max-w-screen-xl px-4 py-8">
|
||
<a-row :gutter="[32, 0]">
|
||
<!-- 主内容区 -->
|
||
<a-col :lg="17" :xs="24">
|
||
<!-- 面包屑 -->
|
||
<a-breadcrumb class="mb-6">
|
||
<a-breadcrumb-item><NuxtLink to="/">首页</NuxtLink></a-breadcrumb-item>
|
||
<a-breadcrumb-item v-if="article.categoryPath">
|
||
<NuxtLink :to="article.categoryPath">{{ article.categoryName }}</NuxtLink>
|
||
</a-breadcrumb-item>
|
||
<a-breadcrumb-item>{{ article.title || '文章详情' }}</a-breadcrumb-item>
|
||
</a-breadcrumb>
|
||
|
||
<div v-if="loading" class="article-loading">
|
||
<a-skeleton :paragraph="{ rows: 12 }" active />
|
||
</div>
|
||
|
||
<div v-else-if="!article.id" class="article-empty">
|
||
<a-result status="404" sub-title="您查找的文章不存在或已被删除" title="文章不存在">
|
||
<template #extra>
|
||
<a-button type="primary" @click="$router.back()">返回上一页</a-button>
|
||
</template>
|
||
</a-result>
|
||
</div>
|
||
|
||
<article v-else class="article-container">
|
||
<!-- 封面图 -->
|
||
<div v-if="article.cover" class="article-cover">
|
||
<img :alt="article.title" :src="article.cover" />
|
||
</div>
|
||
|
||
<!-- 标题区 -->
|
||
<div class="article-header">
|
||
<div v-if="article.categoryName" class="article-category-tag">
|
||
{{ article.categoryName }}
|
||
</div>
|
||
<h1 class="article-title">{{ article.title }}</h1>
|
||
<div class="article-meta">
|
||
<span v-if="article.source" class="meta-item">
|
||
<span class="meta-icon">📰</span>来源:{{ article.source }}
|
||
</span>
|
||
<span v-if="article.author" class="meta-item">
|
||
<span class="meta-icon">✍️</span>{{ article.author }}
|
||
</span>
|
||
<span v-if="article.publishTime" class="meta-item">
|
||
<span class="meta-icon">🕐</span>{{ article.publishTime }}
|
||
</span>
|
||
<span v-if="article.views" class="meta-item">
|
||
<span class="meta-icon">👁</span>{{ article.views }} 次阅读
|
||
</span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 摘要 -->
|
||
<div v-if="article.summary" class="article-summary">
|
||
<div class="summary-label">摘要</div>
|
||
<p>{{ article.summary }}</p>
|
||
</div>
|
||
|
||
<!-- 正文 -->
|
||
<div class="article-body" v-html="article.content"></div>
|
||
|
||
<!-- 附件下载 -->
|
||
<div v-if="article.attachments && article.attachments.length" class="article-attachments">
|
||
<div class="attachments-title">📎 相关附件</div>
|
||
<div class="attachments-list">
|
||
<a
|
||
v-for="file in article.attachments"
|
||
:key="file.url"
|
||
:href="file.url"
|
||
class="attachment-item"
|
||
target="_blank"
|
||
>
|
||
<span class="attachment-icon">📄</span>
|
||
<span class="attachment-name">{{ file.name }}</span>
|
||
<span class="attachment-size">{{ file.size }}</span>
|
||
</a>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 标签 -->
|
||
<div v-if="article.tags && article.tags.length" class="article-tags">
|
||
<span class="tags-label">标签:</span>
|
||
<a-tag v-for="tag in article.tags" :key="tag" color="blue">{{ tag }}</a-tag>
|
||
</div>
|
||
|
||
<!-- 声明 -->
|
||
<div class="article-disclaimer">
|
||
<p>声明:本文内容仅代表作者本人观点,不代表本网站立场。如有侵权请联系我们删除。</p>
|
||
</div>
|
||
|
||
<!-- 分享 & 上一篇下一篇 -->
|
||
<div class="article-nav">
|
||
<div v-if="prevArticle" class="nav-prev" @click="goArticle(prevArticle)">
|
||
<span class="nav-dir">« 上一篇</span>
|
||
<span class="nav-title">{{ prevArticle.title }}</span>
|
||
</div>
|
||
<div v-if="nextArticle" class="nav-next" @click="goArticle(nextArticle)">
|
||
<span class="nav-title">{{ nextArticle.title }}</span>
|
||
<span class="nav-dir">下一篇 »</span>
|
||
</div>
|
||
</div>
|
||
</article>
|
||
</a-col>
|
||
|
||
<!-- 侧栏 -->
|
||
<a-col :lg="7" :xs="0" class="hidden lg:block">
|
||
<div class="sidebar">
|
||
<!-- 相关文章 -->
|
||
<div class="sidebar-card">
|
||
<div class="sidebar-title">相关文章</div>
|
||
<div class="related-list">
|
||
<div
|
||
v-for="item in relatedArticles"
|
||
:key="item.id"
|
||
class="related-item"
|
||
@click="goArticle(item)"
|
||
>
|
||
<span class="related-dot">›</span>
|
||
<span class="related-title">{{ item.title }}</span>
|
||
</div>
|
||
<div v-if="!relatedArticles.length" class="related-empty">暂无相关文章</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 热门推荐 -->
|
||
<div class="sidebar-card mt-6">
|
||
<div class="sidebar-title">热门推荐</div>
|
||
<div class="related-list">
|
||
<div
|
||
v-for="item in hotArticles"
|
||
:key="item.id"
|
||
class="related-item hot-item"
|
||
@click="goArticle(item)"
|
||
>
|
||
<span class="hot-rank">{{ item.rank }}</span>
|
||
<span class="related-title">{{ item.title }}</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</a-col>
|
||
</a-row>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<script lang="ts" setup>
|
||
import { message } from 'ant-design-vue'
|
||
|
||
const route = useRoute()
|
||
const router = useRouter()
|
||
const articleId = computed(() => route.params.id as string)
|
||
|
||
const loading = ref(true)
|
||
const article = ref<any>({})
|
||
const prevArticle = ref<any>(null)
|
||
const nextArticle = ref<any>(null)
|
||
const relatedArticles = ref<any[]>([])
|
||
const hotArticles = ref<any[]>([
|
||
{ id: 1, title: '广西数字经济发展报告(2024)', rank: 1 },
|
||
{ id: 2, title: '自治区关于优化营商环境的实施意见', rank: 2 },
|
||
{ id: 3, title: '面向东盟的产业合作政策解读', rank: 3 },
|
||
{ id: 4, title: '广西乡村振兴战略实施进展报告', rank: 4 },
|
||
{ id: 5, title: '北部湾经济区发展最新动态', rank: 5 },
|
||
])
|
||
|
||
useHead({
|
||
title: computed(() => `${article.value?.title || '文章详情'} - 决策咨询网`),
|
||
meta: [
|
||
{ name: 'description', content: computed(() => article.value?.summary || '') },
|
||
]
|
||
})
|
||
|
||
async function loadArticle() {
|
||
loading.value = true
|
||
try {
|
||
// TODO: 接入实际API
|
||
// const res = await getArticleDetail(articleId.value)
|
||
// article.value = res
|
||
// prevArticle.value = res.prev
|
||
// nextArticle.value = res.next
|
||
// relatedArticles.value = res.related || []
|
||
|
||
// 模拟数据
|
||
article.value = {
|
||
id: articleId.value,
|
||
title: '广西自治区党委政府关于加快数字经济发展的实施意见',
|
||
cover: `https://picsum.photos/900/400?random=${articleId.value}`,
|
||
categoryName: '政策要闻',
|
||
categoryPath: '/news',
|
||
source: '广西壮族自治区人民政府',
|
||
author: '政策研究处',
|
||
publishTime: '2024-12-20 09:30:00',
|
||
views: 1286,
|
||
summary: '本意见旨在深入贯彻党中央、国务院关于发展数字经济的战略部署,结合广西实际,加快推进数字产业化和产业数字化,培育壮大数字经济新动能。',
|
||
content: `
|
||
<h2>一、总体要求</h2>
|
||
<p>以习近平新时代中国特色社会主义思想为指导,全面贯彻党的二十大精神,围绕建设数字中国战略部署,立足广西比较优势,坚持创新驱动、数据赋能、融合发展,加快推动数字经济与实体经济深度融合,着力打造面向东盟的数字经济发展高地。</p>
|
||
|
||
<h2>二、主要目标</h2>
|
||
<p>到2026年,数字经济核心产业增加值占GDP比重达到12%,数字经济总量突破1万亿元,数字化转型企业数量超过5000家,建成5G基站15万座。</p>
|
||
|
||
<h2>三、重点任务</h2>
|
||
<h3>(一)加快数字基础设施建设</h3>
|
||
<p>系统推进新型基础设施建设,加快5G、大数据中心、工业互联网等数字基础设施部署,构建高速、泛在、天地一体、云网融合、智能敏捷、绿色低碳的新型数字基础设施体系。</p>
|
||
|
||
<h3>(二)深化数字技术与实体经济融合</h3>
|
||
<p>推动制造业、农业、服务业数字化转型,加快工业互联网创新应用,推进数字农业农村建设,促进数字技术与传统产业深度融合。</p>
|
||
|
||
<h3>(三)培育壮大数字经济核心产业</h3>
|
||
<p>重点发展软件和信息技术服务业、大数据、云计算、人工智能、区块链等核心产业,打造广西数字经济核心产业集群。</p>
|
||
|
||
<h2>四、保障措施</h2>
|
||
<p>加强组织领导,完善工作机制,强化政策支持,健全评估体系,确保各项任务落到实处。</p>
|
||
`,
|
||
tags: ['数字经济', '政策解读', '广西'],
|
||
attachments: [
|
||
{ name: '广西数字经济发展实施意见(全文).pdf', url: '#', size: '2.4MB' },
|
||
{ name: '附件:实施细则.docx', url: '#', size: '856KB' },
|
||
]
|
||
}
|
||
} catch (e: any) {
|
||
message.error('加载失败')
|
||
} finally {
|
||
loading.value = false
|
||
}
|
||
}
|
||
|
||
function goArticle(item: any) {
|
||
router.push(`/article/${item.id}`)
|
||
}
|
||
|
||
onMounted(() => {
|
||
loadArticle()
|
||
})
|
||
|
||
watch(articleId, () => {
|
||
loadArticle()
|
||
})
|
||
</script>
|
||
|
||
<style scoped>
|
||
.article-detail-page {
|
||
background: #f5f7fa;
|
||
min-height: 60vh;
|
||
}
|
||
|
||
.article-loading,
|
||
.article-empty {
|
||
background: #fff;
|
||
border-radius: 12px;
|
||
padding: 40px;
|
||
}
|
||
|
||
.article-container {
|
||
background: #fff;
|
||
border-radius: 12px;
|
||
padding: 40px;
|
||
box-shadow: 0 2px 12px rgba(0,0,0,0.06);
|
||
}
|
||
|
||
.article-cover {
|
||
margin: -40px -40px 32px;
|
||
border-radius: 12px 12px 0 0;
|
||
overflow: hidden;
|
||
height: 320px;
|
||
}
|
||
|
||
.article-cover img {
|
||
width: 100%;
|
||
height: 100%;
|
||
object-fit: cover;
|
||
}
|
||
|
||
.article-header {
|
||
margin-bottom: 24px;
|
||
padding-bottom: 24px;
|
||
border-bottom: 1px solid #f0f0f0;
|
||
}
|
||
|
||
.article-category-tag {
|
||
display: inline-block;
|
||
padding: 3px 12px;
|
||
background: #1e3a5f;
|
||
color: #fff;
|
||
font-size: 12px;
|
||
border-radius: 4px;
|
||
margin-bottom: 12px;
|
||
}
|
||
|
||
.article-title {
|
||
font-size: 26px;
|
||
font-weight: 700;
|
||
color: #1f2937;
|
||
line-height: 1.5;
|
||
margin: 0 0 16px;
|
||
}
|
||
|
||
.article-meta {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 16px;
|
||
}
|
||
|
||
.meta-item {
|
||
font-size: 13px;
|
||
color: #9ca3af;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 4px;
|
||
}
|
||
|
||
.meta-icon {
|
||
font-size: 14px;
|
||
}
|
||
|
||
/* 摘要 */
|
||
.article-summary {
|
||
background: #f8fafc;
|
||
border-left: 4px solid #1e3a5f;
|
||
border-radius: 0 8px 8px 0;
|
||
padding: 16px 20px;
|
||
margin-bottom: 32px;
|
||
}
|
||
|
||
.summary-label {
|
||
font-size: 13px;
|
||
font-weight: 600;
|
||
color: #1e3a5f;
|
||
margin-bottom: 8px;
|
||
}
|
||
|
||
.article-summary p {
|
||
font-size: 14px;
|
||
color: #4b5563;
|
||
line-height: 1.8;
|
||
margin: 0;
|
||
}
|
||
|
||
/* 正文 */
|
||
.article-body {
|
||
font-size: 16px;
|
||
color: #374151;
|
||
line-height: 2;
|
||
}
|
||
|
||
.article-body :deep(h2) {
|
||
font-size: 20px;
|
||
font-weight: 700;
|
||
color: #1e3a5f;
|
||
margin: 32px 0 16px;
|
||
padding-bottom: 8px;
|
||
border-bottom: 2px solid #e5e7eb;
|
||
}
|
||
|
||
.article-body :deep(h3) {
|
||
font-size: 17px;
|
||
font-weight: 600;
|
||
color: #374151;
|
||
margin: 24px 0 12px;
|
||
}
|
||
|
||
.article-body :deep(p) {
|
||
margin: 0 0 16px;
|
||
text-indent: 2em;
|
||
}
|
||
|
||
.article-body :deep(ul), .article-body :deep(ol) {
|
||
padding-left: 24px;
|
||
margin: 12px 0;
|
||
}
|
||
|
||
.article-body :deep(li) {
|
||
margin: 8px 0;
|
||
}
|
||
|
||
/* 附件 */
|
||
.article-attachments {
|
||
margin-top: 32px;
|
||
padding: 20px;
|
||
background: #f9fafb;
|
||
border-radius: 8px;
|
||
}
|
||
|
||
.attachments-title {
|
||
font-size: 15px;
|
||
font-weight: 600;
|
||
color: #374151;
|
||
margin-bottom: 12px;
|
||
}
|
||
|
||
.attachments-list {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 8px;
|
||
}
|
||
|
||
.attachment-item {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
padding: 10px 14px;
|
||
background: #fff;
|
||
border: 1px solid #e5e7eb;
|
||
border-radius: 6px;
|
||
text-decoration: none;
|
||
color: #374151;
|
||
font-size: 14px;
|
||
transition: all 0.2s;
|
||
}
|
||
|
||
.attachment-item:hover {
|
||
border-color: #1e3a5f;
|
||
color: #1e3a5f;
|
||
background: #f0f7ff;
|
||
}
|
||
|
||
.attachment-size {
|
||
margin-left: auto;
|
||
color: #9ca3af;
|
||
font-size: 12px;
|
||
}
|
||
|
||
/* 标签 */
|
||
.article-tags {
|
||
margin-top: 24px;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
flex-wrap: wrap;
|
||
}
|
||
|
||
.tags-label {
|
||
font-size: 13px;
|
||
color: #9ca3af;
|
||
}
|
||
|
||
/* 声明 */
|
||
.article-disclaimer {
|
||
margin-top: 32px;
|
||
padding: 12px 16px;
|
||
background: #fefce8;
|
||
border: 1px solid #fef08a;
|
||
border-radius: 6px;
|
||
}
|
||
|
||
.article-disclaimer p {
|
||
font-size: 12px;
|
||
color: #92400e;
|
||
margin: 0;
|
||
}
|
||
|
||
/* 上下篇 */
|
||
.article-nav {
|
||
margin-top: 32px;
|
||
display: flex;
|
||
gap: 16px;
|
||
padding-top: 20px;
|
||
border-top: 1px solid #f0f0f0;
|
||
}
|
||
|
||
.nav-prev, .nav-next {
|
||
flex: 1;
|
||
padding: 12px 16px;
|
||
background: #f9fafb;
|
||
border-radius: 8px;
|
||
cursor: pointer;
|
||
transition: all 0.2s;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 4px;
|
||
}
|
||
|
||
.nav-prev:hover, .nav-next:hover {
|
||
background: #eff6ff;
|
||
}
|
||
|
||
.nav-next {
|
||
text-align: right;
|
||
align-items: flex-end;
|
||
}
|
||
|
||
.nav-dir {
|
||
font-size: 12px;
|
||
color: #9ca3af;
|
||
}
|
||
|
||
.nav-title {
|
||
font-size: 14px;
|
||
color: #1e3a5f;
|
||
font-weight: 500;
|
||
display: -webkit-box;
|
||
-webkit-line-clamp: 2;
|
||
-webkit-box-orient: vertical;
|
||
overflow: hidden;
|
||
}
|
||
|
||
/* 侧栏 */
|
||
.sidebar {
|
||
position: sticky;
|
||
top: 80px;
|
||
}
|
||
|
||
.sidebar-card {
|
||
background: #fff;
|
||
border-radius: 12px;
|
||
padding: 20px;
|
||
box-shadow: 0 2px 8px rgba(0,0,0,0.06);
|
||
}
|
||
|
||
.sidebar-title {
|
||
font-size: 15px;
|
||
font-weight: 700;
|
||
color: #1e3a5f;
|
||
margin-bottom: 16px;
|
||
padding-bottom: 10px;
|
||
border-bottom: 2px solid #1e3a5f;
|
||
}
|
||
|
||
.related-list {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 10px;
|
||
}
|
||
|
||
.related-item {
|
||
display: flex;
|
||
align-items: flex-start;
|
||
gap: 8px;
|
||
cursor: pointer;
|
||
padding: 6px 8px;
|
||
border-radius: 6px;
|
||
transition: background 0.2s;
|
||
}
|
||
|
||
.related-item:hover {
|
||
background: #f3f4f6;
|
||
}
|
||
|
||
.related-dot {
|
||
color: #1e3a5f;
|
||
font-weight: 600;
|
||
flex-shrink: 0;
|
||
margin-top: 2px;
|
||
}
|
||
|
||
.related-title {
|
||
font-size: 13px;
|
||
color: #374151;
|
||
line-height: 1.5;
|
||
display: -webkit-box;
|
||
-webkit-line-clamp: 2;
|
||
-webkit-box-orient: vertical;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.related-empty {
|
||
font-size: 13px;
|
||
color: #9ca3af;
|
||
text-align: center;
|
||
padding: 12px 0;
|
||
}
|
||
|
||
.hot-item {
|
||
align-items: center;
|
||
}
|
||
|
||
.hot-rank {
|
||
width: 22px;
|
||
height: 22px;
|
||
background: #1e3a5f;
|
||
color: #fff;
|
||
border-radius: 4px;
|
||
font-size: 12px;
|
||
font-weight: 700;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.hot-item:nth-child(-n+3) .hot-rank {
|
||
background: #dc2626;
|
||
}
|
||
|
||
@media (max-width: 768px) {
|
||
.article-container {
|
||
padding: 20px;
|
||
}
|
||
|
||
.article-cover {
|
||
margin: -20px -20px 20px;
|
||
}
|
||
|
||
.article-title {
|
||
font-size: 20px;
|
||
}
|
||
}
|
||
</style>
|