Files
jczxw-pc/app/pages/article/[id].vue
2026-04-29 10:05:55 +08:00

703 lines
17 KiB
Vue
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="article-page">
<PortalHeader :active-path="article.categoryPath || route.path" />
<main class="container article-main">
<div class="article-layout">
<section class="article-primary">
<div class="breadcrumb-bar">
<NuxtLink to="/">首页</NuxtLink>
<span>/</span>
<NuxtLink v-if="article.categoryPath" :to="article.categoryPath">{{ article.categoryName }}</NuxtLink>
<template v-if="article.categoryPath">
<span>/</span>
</template>
<span>{{ article.title || '文章详情' }}</span>
</div>
<div v-if="loading" class="article-state">
<a-skeleton active :paragraph="{ rows: 12 }" />
</div>
<div v-else-if="!article.id" class="article-state article-state-empty">
<a-result status="404" title="文章不存在" sub-title="您查找的文章不存在或已被删除">
<template #extra>
<a-button type="primary" @click="$router.back()">返回上一页</a-button>
</template>
</a-result>
</div>
<article v-else class="article-card">
<div v-if="article.cover" class="article-cover">
<img :src="article.cover" :alt="article.title" />
</div>
<div class="article-content-wrap">
<div class="article-heading">
<span v-if="article.categoryName" class="article-category">{{ article.categoryName }}</span>
<h1 class="article-title">{{ article.title }}</h1>
<div class="article-meta">
<span v-if="article.source">来源{{ article.source }}</span>
<span v-if="article.author">作者{{ article.author }}</span>
<span v-if="article.publishTime">{{ article.publishTime }}</span>
<span v-if="article.views">{{ article.views }} 次阅读</span>
</div>
</div>
<div v-if="article.summary" class="article-summary">
<strong>摘要</strong>
<p>{{ article.summary }}</p>
</div>
<div class="article-body" v-html="article.content" />
<div v-if="article.attachments?.length" class="article-attachments">
<div class="section-bar">
<span>相关附件</span>
</div>
<div class="attachments-list">
<a
v-for="file in article.attachments"
:key="file.url"
:href="file.url"
target="_blank"
class="attachment-item"
>
<span class="attachment-name">{{ file.name }}</span>
<span class="attachment-size">{{ file.size }}</span>
</a>
</div>
</div>
<div v-if="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">
声明本文内容仅代表作者本人观点不代表本网站立场如有侵权请联系我们删除
</div>
<div class="article-nav">
<div v-if="prevArticle" class="article-nav-item" @click="goArticle(prevArticle)">
<small>上一篇</small>
<span>{{ prevArticle.title }}</span>
</div>
<div v-if="nextArticle" class="article-nav-item article-nav-item-next" @click="goArticle(nextArticle)">
<small>下一篇</small>
<span>{{ nextArticle.title }}</span>
</div>
</div>
</div>
</article>
</section>
<aside class="article-sidebar">
<div class="sidebar-panel">
<div class="panel-heading">
<span>相关文章</span>
</div>
<div class="sidebar-list">
<button
v-for="item in relatedArticles"
:key="item.id"
type="button"
class="sidebar-link"
@click="goArticle(item)"
>
{{ item.title }}
</button>
<div v-if="!relatedArticles.length" class="sidebar-empty">暂无相关文章</div>
</div>
</div>
<div class="sidebar-panel">
<div class="panel-heading">
<span>热门推荐</span>
</div>
<div class="sidebar-list">
<button
v-for="item in hotArticles"
:key="item.id"
type="button"
class="sidebar-link sidebar-link-rank"
@click="goArticle(item)"
>
<strong>{{ item.rank }}</strong>
<span>{{ item.title }}</span>
</button>
</div>
</div>
</aside>
</div>
</main>
<footer class="site-footer">
<div class="footer-nav">
<div class="container footer-nav-inner">
<NuxtLink to="/about">友情链接</NuxtLink>
<NuxtLink to="/consultation">咨询服务</NuxtLink>
<NuxtLink to="/reference">决策成果研究文库</NuxtLink>
<NuxtLink to="/expert">政府智库学者库</NuxtLink>
</div>
</div>
<PortalFooter />
</footer>
</div>
</template>
<script setup lang="ts">
import { message } from 'ant-design-vue'
import { usePageSeo } from '@/composables/usePageSeo'
definePageMeta({
layout: 'blank'
})
type ArticleAttachment = {
name: string
url: string
size: string
}
type ArticleRecord = {
id?: string
title: string
cover?: string
categoryName?: string
categoryPath?: string
source?: string
author?: string
publishTime?: string
views?: number
summary?: string
content?: string
tags?: string[]
attachments?: ArticleAttachment[]
}
type ArticleLink = {
id: number | string
title: string
rank?: number
}
const route = useRoute()
const router = useRouter()
const articleId = computed(() => route.params.id as string)
const loading = ref(true)
const article = ref<ArticleRecord>({ title: '' })
const prevArticle = ref<ArticleLink | null>(null)
const nextArticle = ref<ArticleLink | null>(null)
const relatedArticles = ref<ArticleLink[]>([])
const hotArticles = ref<ArticleLink[]>([
{ 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 }
])
usePageSeo({
title: '文章详情',
description: '广西决策咨询网文章详情页'
})
useHead({
title: computed(() => `${article.value?.title || '文章详情'} - 广西决策咨询网`),
meta: [
{ name: 'description', content: computed(() => article.value?.summary || '广西决策咨询网文章详情页') }
]
})
async function loadArticle() {
loading.value = true
try {
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' }
]
}
prevArticle.value = { id: Number(articleId.value) - 1 || 1, title: '广西人工智能产业发展三年行动计划解读' }
nextArticle.value = { id: Number(articleId.value) + 1 || 2, title: '关于推动平台经济规范健康持续发展的若干措施' }
relatedArticles.value = [
{ id: 11, title: '广西数字政府建设重点任务清单发布' },
{ id: 12, title: '自治区大数据发展战略阶段性成果综述' },
{ id: 13, title: '面向东盟的数据跨境合作政策观察' },
{ id: 14, title: '产业数字化转型标杆案例分析' },
{ id: 15, title: '推动数字基础设施提质增效的实施路径' }
]
} catch {
message.error('加载失败')
} finally {
loading.value = false
}
}
function goArticle(item: ArticleLink) {
router.push(`/article/${item.id}`)
}
onMounted(() => {
loadArticle()
})
watch(articleId, () => {
loadArticle()
})
</script>
<style scoped>
.article-page {
min-height: 100vh;
background: #ffffff;
color: #1f2d3d;
}
.container {
width: min(1200px, calc(100% - 32px));
margin: 0 auto;
}
.article-main {
padding: 18px 0 40px;
}
.article-layout {
display: grid;
grid-template-columns: minmax(0, 1fr) 300px;
gap: 24px;
align-items: start;
}
.article-primary,
.article-sidebar {
min-width: 0;
}
.breadcrumb-bar {
display: flex;
flex-wrap: wrap;
gap: 8px;
align-items: center;
min-height: 44px;
padding: 0 18px;
margin-bottom: 16px;
background: #f7fbff;
border: 1px solid #e3edf6;
color: #6c7f92;
font-size: 13px;
}
.breadcrumb-bar a {
color: #2c6eaa;
}
.article-state {
background: #fff;
border: 1px solid #e3edf6;
padding: 28px;
}
.article-state-empty {
padding: 12px;
}
.article-card {
background: #fff;
border: 1px solid #e3edf6;
}
.article-cover {
height: 360px;
overflow: hidden;
border-bottom: 1px solid #e3edf6;
}
.article-cover img {
width: 100%;
height: 100%;
object-fit: cover;
display: block;
}
.article-content-wrap {
padding: 28px 34px 36px;
}
.article-heading {
padding-bottom: 20px;
margin-bottom: 22px;
border-bottom: 1px solid #e6eef5;
text-align: center;
}
.article-category {
display: inline-block;
padding: 4px 12px;
margin-bottom: 14px;
background: #edf5fc;
color: #2c6eaa;
font-size: 12px;
border: 1px solid #c9dff1;
}
.article-title {
margin: 0 0 16px;
color: #203040;
font-size: 32px;
font-weight: 700;
line-height: 1.45;
}
.article-meta {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 10px 22px;
color: #8a97a4;
font-size: 13px;
}
.article-summary {
padding: 16px 18px;
margin-bottom: 28px;
background: #f7fbff;
border-left: 4px solid #2c6eaa;
}
.article-summary strong {
display: block;
margin-bottom: 8px;
color: #2c6eaa;
font-size: 14px;
}
.article-summary p {
margin: 0;
color: #536271;
font-size: 14px;
line-height: 1.9;
}
.article-body {
color: #33485c;
font-size: 16px;
line-height: 2;
}
.article-body :deep(h2) {
margin: 32px 0 16px;
padding-left: 12px;
border-left: 4px solid #2c6eaa;
color: #214e7f;
font-size: 22px;
font-weight: 700;
line-height: 1.4;
}
.article-body :deep(h3) {
margin: 24px 0 12px;
color: #2d5f92;
font-size: 18px;
font-weight: 700;
}
.article-body :deep(p) {
margin: 0 0 16px;
text-indent: 2em;
}
.article-body :deep(ul),
.article-body :deep(ol) {
margin: 14px 0;
padding-left: 24px;
}
.article-body :deep(li) {
margin: 8px 0;
}
.article-attachments,
.article-tags,
.article-nav {
margin-top: 32px;
}
.section-bar {
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 0 10px;
border-bottom: 1px solid #e6eef5;
}
.section-bar span {
color: #2c6eaa;
font-size: 20px;
font-weight: 700;
}
.attachments-list {
padding-top: 10px;
}
.attachment-item {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
padding: 14px 0;
border-bottom: 1px dashed #e8edf2;
color: #3e5162;
font-size: 14px;
}
.attachment-item:hover {
color: #2c6eaa;
}
.attachment-size {
color: #98a3ac;
font-size: 12px;
flex-shrink: 0;
}
.article-tags {
display: flex;
align-items: center;
gap: 8px;
flex-wrap: wrap;
}
.tags-label {
color: #7e8c98;
font-size: 13px;
}
.article-disclaimer {
margin-top: 28px;
padding: 12px 16px;
background: #fbf8ec;
border: 1px solid #eee2b3;
color: #8d7331;
font-size: 12px;
line-height: 1.7;
}
.article-nav {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 18px;
padding-top: 24px;
border-top: 1px solid #e6eef5;
}
.article-nav-item {
display: flex;
flex-direction: column;
gap: 8px;
min-height: 88px;
padding: 16px 18px;
background: #f8fbfd;
border: 1px solid #e3edf6;
cursor: pointer;
transition: all 0.2s ease;
}
.article-nav-item:hover {
border-color: #b8d4ea;
background: #f1f8fd;
}
.article-nav-item small {
color: #97a3ae;
font-size: 12px;
}
.article-nav-item span {
color: #2a3d50;
font-size: 14px;
line-height: 1.7;
}
.article-nav-item-next {
text-align: right;
}
.article-sidebar {
display: flex;
flex-direction: column;
gap: 18px;
}
.sidebar-panel {
background: #fff;
border: 1px solid #e3edf6;
padding: 16px 18px 18px;
}
.panel-heading {
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 0 10px;
border-bottom: 1px solid #e6eef5;
}
.panel-heading span {
color: #2c6eaa;
font-size: 20px;
font-weight: 700;
}
.sidebar-list {
padding-top: 8px;
}
.sidebar-link {
display: block;
width: 100%;
padding: 12px 0;
border: 0;
border-bottom: 1px dashed #e8edf2;
background: transparent;
color: #32485b;
font-size: 14px;
line-height: 1.7;
text-align: left;
cursor: pointer;
}
.sidebar-link:hover {
color: #2c6eaa;
}
.sidebar-link-rank {
display: grid;
grid-template-columns: 24px 1fr;
gap: 10px;
align-items: start;
}
.sidebar-link-rank strong {
display: flex;
align-items: center;
justify-content: center;
width: 24px;
height: 24px;
background: #2c6eaa;
color: #fff;
font-size: 12px;
}
.sidebar-empty {
padding: 18px 0 8px;
color: #99a5af;
font-size: 13px;
text-align: center;
}
.site-footer {
margin-top: 8px;
background: #fff;
border-top: 1px solid #e5eef6;
}
.footer-nav {
border-bottom: 1px solid #eef3f7;
}
.footer-nav-inner {
display: flex;
justify-content: center;
gap: 60px;
padding: 18px 0;
font-size: 14px;
}
.footer-nav-inner a {
color: #5c6974;
}
@media (max-width: 1100px) {
.article-layout {
grid-template-columns: 1fr;
}
.article-sidebar {
order: 2;
}
}
@media (max-width: 768px) {
.container {
width: min(100%, calc(100% - 24px));
}
.article-main {
padding-top: 14px;
}
.article-cover {
height: 220px;
}
.article-content-wrap {
padding: 20px 18px 28px;
}
.article-title {
font-size: 24px;
}
.article-meta {
justify-content: flex-start;
}
.article-nav {
grid-template-columns: 1fr;
}
.footer-nav-inner {
flex-wrap: wrap;
gap: 18px 28px;
padding: 16px 0;
}
}
</style>