|
- <template>
- <div>
- <div class="mx-auto max-w-7xl px-4 py-6 sm:px-6 lg:px-8 lg:py-10">
- <div class="lg:grid lg:grid-cols-12 lg:gap-8">
- <!-- 正文 -->
- <div class="lg:col-span-8">
- <article class="overflow-hidden rounded-2xl bg-[var(--c-ffffff)] p-5 shadow-[0_8px_28px_var(--sh-34-68-112-50)] sm:p-8">
- <!-- 面包屑 -->
- <nav aria-label="面包屑" class="flex flex-wrap items-center gap-1.5 text-xs text-[var(--c-8a97a8)]">
- <NuxtLink to="/" class="hover:text-[var(--c-0b8cff)]">首页</NuxtLink>
- <span>/</span>
- <NuxtLink to="/info" class="hover:text-[var(--c-0b8cff)]">AI 资讯</NuxtLink>
- <span>/</span>
- <NuxtLink :to="colPath(detail.col_id)" class="hover:text-[var(--c-0b8cff)]">{{ colName(detail.col_id) }}</NuxtLink>
- </nav>
-
- <h1 class="mt-4 text-2xl font-extrabold leading-snug text-[var(--c-172033)] sm:text-3xl">
- {{ detail.title }}
- </h1>
-
- <!-- 元信息 -->
- <div class="mt-4 flex flex-wrap items-center gap-4 border-b border-[var(--c-eef2f7)] pb-5 text-xs text-[var(--c-a0aabb)]">
- <span class="inline-flex items-center gap-1">
- <UIcon name="i-lucide-calendar" class="size-3.5" />
- {{ formatTime(detail.publish_time) }}
- </span>
- <span class="inline-flex items-center gap-1">
- <UIcon name="i-lucide-eye" class="size-3.5" />
- {{ detail.view_num || 0 }} 次浏览
- </span>
- <span class="rounded-full bg-[var(--c-eef7ff)] px-2 py-0.5 font-bold text-[var(--c-0b8cff)]">
- {{ colName(detail.col_id) }}
- </span>
- </div>
-
- <!-- 摘要 -->
- <p v-if="detail.info" class="mt-5 rounded-xl bg-[var(--c-f7f9fd)] p-4 text-sm leading-relaxed text-[var(--c-4b5b70)]">
- {{ detail.info }}
- </p>
-
- <!-- 正文富文本 -->
- <!-- eslint-disable-next-line vue/no-v-html -->
- <div class="article-content mt-6" v-html="detail.content" />
-
- <!-- 标签 -->
- <div v-if="detail.tag_list?.length" class="mt-8 flex flex-wrap items-center gap-2 border-t border-[var(--c-eef2f7)] pt-6">
- <span class="text-xs font-bold text-[var(--c-8a97a8)]">标签:</span>
- <NuxtLink
- v-for="tag in detail.tag_list"
- :key="tag.id"
- :to="`/info/tag/${tag.tag_key}.html`"
- class="rounded-full bg-[var(--c-f7f9fd)] px-3 py-1.5 text-xs font-bold text-[var(--c-5b6b80)] transition-colors hover:bg-[var(--c-eef7ff)] hover:text-[var(--c-0b8cff)]"
- >
- {{ tag.name }}
- </NuxtLink>
- </div>
-
- <!-- 上一篇 / 下一篇 -->
- <div v-if="detail.prev_id || detail.next_id" class="mt-8 grid gap-3 border-t border-[var(--c-eef2f7)] pt-6 sm:grid-cols-2">
- <NuxtLink
- v-if="detail.prev_id"
- :to="`${colPath(detail.col_id)}/${detail.prev_id}.html`"
- rel="prev"
- class="group rounded-xl bg-[var(--c-f7f9fd)] p-4 transition-colors hover:bg-[var(--c-eef7ff)]"
- >
- <div class="text-xs text-[var(--c-a0aabb)]">上一篇</div>
- <div class="mt-1 text-sm font-bold text-[var(--c-4b5b70)] transition-colors group-hover:text-[var(--c-0b8cff)]">查看上一篇</div>
- </NuxtLink>
- <NuxtLink
- v-if="detail.next_id"
- :to="`${colPath(detail.col_id)}/${detail.next_id}.html`"
- rel="next"
- class="group rounded-xl bg-[var(--c-f7f9fd)] p-4 text-right transition-colors hover:bg-[var(--c-eef7ff)] sm:col-start-2"
- >
- <div class="text-xs text-[var(--c-a0aabb)]">下一篇</div>
- <div class="mt-1 text-sm font-bold text-[var(--c-4b5b70)] transition-colors group-hover:text-[var(--c-0b8cff)]">查看下一篇</div>
- </NuxtLink>
- </div>
- </article>
- </div>
-
- <!-- 侧栏 -->
- <div class="mt-8 lg:col-span-4 lg:mt-0">
- <div class="lg:sticky lg:top-20">
- <NewsSidebar :hot-list="hotList" :tag-list="tagList" />
- </div>
- </div>
- </div>
- </div>
- </div>
- </template>
-
- <script setup>
- import { colName, colPath, formatTime } from '~/composables/useArticle'
-
- defineProps({
- detail: { type: Object, required: true },
- hotList: { type: Array, default: () => [] },
- tagList: { type: Array, default: () => [] },
- })
- </script>
-
- <style scoped>
- /* 富文本正文排版(后台编辑器输出的 HTML) */
- .article-content {
- font-size: 15px;
- line-height: 1.9;
- color: var(--c-3f4c60);
- word-break: break-word;
- }
- .article-content :deep(h1),
- .article-content :deep(h2),
- .article-content :deep(h3),
- .article-content :deep(h4) {
- margin: 1.6em 0 0.7em;
- font-weight: 800;
- color: var(--c-172033);
- line-height: 1.4;
- }
- .article-content :deep(h1) { font-size: 1.5em; }
- .article-content :deep(h2) { font-size: 1.3em; }
- .article-content :deep(h3) { font-size: 1.15em; }
- .article-content :deep(p) { margin: 0.9em 0; }
- .article-content :deep(img) {
- max-width: 100%;
- height: auto;
- margin: 1.2em auto;
- display: block;
- border-radius: 12px;
- }
- .article-content :deep(a) {
- color: var(--c-0b8cff);
- text-decoration: underline;
- }
- .article-content :deep(ul),
- .article-content :deep(ol) {
- margin: 0.9em 0;
- padding-left: 1.4em;
- }
- .article-content :deep(ul) { list-style: disc; }
- .article-content :deep(ol) { list-style: decimal; }
- .article-content :deep(li) { margin: 0.4em 0; }
- .article-content :deep(blockquote) {
- margin: 1.2em 0;
- padding: 12px 16px;
- border-left: 4px solid var(--c-0b8cff);
- border-radius: 0 10px 10px 0;
- background: var(--c-f7fbff);
- color: var(--c-4b5b70);
- }
- .article-content :deep(pre) {
- margin: 1.2em 0;
- padding: 14px 16px;
- overflow-x: auto;
- border-radius: 12px;
- background: var(--c-0e1524);
- color: var(--c-eaf0ff);
- font-size: 13px;
- line-height: 1.7;
- }
- .article-content :deep(code) {
- padding: 2px 6px;
- border-radius: 5px;
- background: var(--c-f1f5fa);
- font-size: 0.92em;
- }
- .article-content :deep(pre code) {
- padding: 0;
- background: transparent;
- }
- .article-content :deep(table) {
- width: 100%;
- margin: 1.2em 0;
- border-collapse: collapse;
- font-size: 14px;
- }
- .article-content :deep(th),
- .article-content :deep(td) {
- border: 1px solid var(--c-e8eef7);
- padding: 8px 12px;
- }
- .article-content :deep(th) {
- background: var(--c-f7f9fd);
- font-weight: 700;
- }
- .article-content :deep(hr) {
- margin: 1.6em 0;
- border: 0;
- border-top: 1px solid var(--c-eef2f7);
- }
- </style>
|