703 lines
17 KiB
Vue
703 lines
17 KiB
Vue
<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>
|