feat(home): 更新首页新闻栏目数据获取逻辑

- 将硬编码的新闻栏目数据替换为从CMS动态获取
- 添加了新闻栏目数据类型定义和解析函数
- 实现了新闻文章链接生成和标题解析功能
- 更新了页面参数传递,将navigationId改为categoryId
- 添加了加载状态和空数据状态的UI显示
- 集成了文章详情页的动态路由跳转功能
This commit is contained in:
2026-01-29 16:55:35 +08:00
parent 682e264a6f
commit 0f5f70522a
2 changed files with 101 additions and 43 deletions

View File

@@ -215,7 +215,7 @@ const {
async () => {
if (!isValidNavigationId.value) return null
return await pageCmsArticle({
navigationId: navigationId.value,
categoryId: navigationId.value,
page: page.value,
limit: limit.value,
keywords: keywords.value || undefined

View File

@@ -26,22 +26,30 @@
</div>
<div class="mt-6 grid grid-cols-12 gap-6">
<div v-for="c in columns" :key="c.title" class="col-span-12 lg:col-span-4">
<div v-for="c in columns" :key="c.key" class="col-span-12 lg:col-span-4">
<div class="panel">
<div class="column-head">
<div class="column-title">{{ c.title }}</div>
<a href="#" class="column-more" @click.prevent>更多 +</a>
<NuxtLink v-if="c.moreTo" :to="c.moreTo" class="column-more">更多 +</NuxtLink>
<span v-else class="column-more opacity-60">更多 +</span>
</div>
<div class="column-list">
<a
v-for="it in c.items"
:key="it"
class="column-item"
href="#"
@click.prevent
>
{{ it }}
</a>
<template v-if="newsPending">
<div class="column-empty">加载中...</div>
</template>
<template v-else-if="!c.items.length">
<div class="column-empty">暂无文章</div>
</template>
<template v-else>
<NuxtLink
v-for="(it, idx) in c.items"
:key="String(it.articleId ?? it.code ?? `${c.key}-${idx}`)"
class="column-item"
:to="resolveArticleLink(it, c.navId)"
>
{{ resolveArticleTitle(it) }}
</NuxtLink>
</template>
</div>
</div>
</div>
@@ -83,6 +91,10 @@ import { usePageSeo } from '@/composables/usePageSeo'
import { getAdByCode } from '@/api/cms/cmsAd'
import type { CmsAd } from '@/api/cms/cmsAd/model'
import { COMPANY } from '@/config/company'
import { listCmsNavigation } from '@/api/cms/cmsNavigation'
import type { CmsNavigation } from '@/api/cms/cmsNavigation/model'
import { pageCmsArticle } from '@/api/cms/cmsArticle'
import type { CmsArticle } from '@/api/cms/cmsArticle/model'
usePageSeo({
title: '首页',
@@ -163,38 +175,78 @@ const services = [
}
]
const columns = [
{
title: '公司动态',
items: [
'桂乐淘官方网站上线公告',
'业务范围与资质信息已更新',
'合作咨询:欢迎留言,我们将尽快联系',
'合规提示:涉及许可项目以许可文件为准',
'更多动态敬请期待...'
]
},
{
title: '行业资讯',
items: [
'生物基材料与绿色低碳趋势速览',
'食品流通与预包装食品合规要点',
'冷链与生鲜品质管理建议',
'更多资讯敬请期待...',
'更多资讯敬请期待...'
]
},
{
title: '合规与公告',
items: [
'一般项目:凭营业执照依法自主开展经营活动',
'许可项目:依法须经批准的项目,经批准后方可开展',
'具体经营项目以相关部门批准文件或许可证件为准',
'更多公告敬请期待...',
'更多公告敬请期待...'
]
const NEWS_PARENT_ID = 4548
type HomeNewsColumn = {
key: string
navId?: number
title: string
moreTo: string | null
items: CmsArticle[]
}
function resolveNavTitle(nav: CmsNavigation) {
return String(nav.title || nav.label || nav.code || '').trim()
}
function resolveArticleTitle(a: CmsArticle) {
return String(a.title || a.code || '未命名文章').trim()
}
function resolveArticleLink(a: CmsArticle, navId?: number) {
const articleId = typeof a.articleId === 'number' && Number.isFinite(a.articleId) ? a.articleId : NaN
const code = String(a.code || '').trim()
return {
path: '/article-item',
query: {
id: Number.isFinite(articleId) ? String(articleId) : undefined,
code: !Number.isFinite(articleId) && code ? code : undefined,
navId: typeof navId === 'number' && Number.isFinite(navId) ? String(navId) : undefined
}
}
]
}
const { data: newsRaw, pending: newsPending } = await useAsyncData<HomeNewsColumn[]>(
`home-news-${NEWS_PARENT_ID}`,
async () => {
const navs = await listCmsNavigation({ parentId: NEWS_PARENT_ID }).catch(() => [])
const top3 = [...navs]
.filter((it) => typeof it?.navigationId === 'number')
.sort((a, b) => (a.sortNumber ?? 0) - (b.sortNumber ?? 0))
.slice(0, 3)
const bundles = await Promise.all(
top3.map(async (nav) => {
const navId = nav.navigationId
const title = resolveNavTitle(nav) || (typeof navId === 'number' ? `栏目 ${navId}` : '栏目')
const page = typeof navId === 'number'
? await pageCmsArticle({ categoryId: navId, page: 1, limit: 10 }).catch(() => null)
: null
return {
key: String(navId ?? title),
navId,
title,
moreTo: typeof navId === 'number' ? `/article/${navId}` : null,
items: page?.list ?? []
} satisfies HomeNewsColumn
})
)
return bundles
}
)
const columns = computed<HomeNewsColumn[]>(() => {
if (newsRaw.value?.length) return newsRaw.value
if (!newsPending.value) return []
// Keep layout stable while SSR/client is loading.
return [
{ key: 'news-loading-1', navId: undefined, title: '加载中', moreTo: null, items: [] },
{ key: 'news-loading-2', navId: undefined, title: '加载中', moreTo: null, items: [] },
{ key: 'news-loading-3', navId: undefined, title: '加载中', moreTo: null, items: [] }
]
})
const compliance = [
{
@@ -393,6 +445,12 @@ function scrollToCompany() {
padding: 10px 14px 14px;
}
.column-empty {
padding: 12px 0;
font-size: 13px;
color: rgba(0, 0, 0, 0.45);
}
.column-item {
display: block;
padding: 8px 0;