feat:接口联调

This commit is contained in:
2026-07-21 11:09:17 +08:00
parent eea6319c41
commit 538194d8a5
39 changed files with 3553 additions and 3386 deletions
+426 -600
View File
File diff suppressed because it is too large Load Diff
+203 -65
View File
@@ -1,12 +1,43 @@
<script setup>
import SiteHeader from '~/components/site/SiteHeader.vue'
import SiteFooter from '~/components/site/SiteFooter.vue'
import { featuredInsight, insightCategories, insights } from '~/data/insights'
const activeCategory = ref('全部')
const {
ALL_LABEL,
LATEST_LABEL,
bySortAndTime,
categoryList,
fetchPublicContent,
normalizeArticle,
normalizeWhitepaper
} = useOfficialContent()
const activeCategory = ref(ALL_LABEL)
const visibleCount = ref(4)
const { openConsultation } = useConsultation()
const { data: pageData } = await useAsyncData('public-insights', async () => {
const [articleRows, whitepaperRows] = await Promise.all([
fetchPublicContent('ARTICLE'),
fetchPublicContent('WHITEPAPER')
])
return {
articles: articleRows.sort(bySortAndTime).map((item) => normalizeArticle(item, new Map())),
whitepapers: whitepaperRows.sort(bySortAndTime).map((item) => normalizeWhitepaper(item, new Map()))
}
})
const insights = computed(() => pageData.value?.articles || [])
const whitepapers = computed(() => pageData.value?.whitepapers || [])
const featuredInsight = computed(
() => insights.value.find((item) => item.featured || item.isFeatured) || insights.value[0] || null
)
const featuredWhitepaper = computed(
() => whitepapers.value.find((item) => item.isFeatured) || whitepapers.value[0] || null
)
const insightCategories = computed(() => categoryList(insights.value, 'category', [LATEST_LABEL]))
useHead({
title: '增长洞察|大马棒·云途全域 GEO',
meta: [
@@ -19,9 +50,9 @@ useHead({
})
const filteredInsights = computed(() => {
const list = insights.filter((item) => !item.featured)
if (activeCategory.value === '全部') return list
if (activeCategory.value === '最新文章') return list.slice(0, 3)
const list = insights.value.filter((item) => item.slug !== featuredInsight.value?.slug)
if (activeCategory.value === ALL_LABEL) return list
if (activeCategory.value === LATEST_LABEL) return list.slice(0, 3)
return list.filter((item) => item.category === activeCategory.value)
})
const visibleInsights = computed(() => filteredInsights.value.slice(0, visibleCount.value))
@@ -36,12 +67,25 @@ function requestDiagnosis(source) {
}
function requestWhitepaper(source) {
if (featuredWhitepaper.value?.documentUrl) {
window.open(featuredWhitepaper.value.documentUrl, '_blank', 'noopener,noreferrer')
return
}
openConsultation({
title: '获取 2026 AI 搜索与品牌增长观察',
need: '行业白皮书',
title: featuredWhitepaper.value?.title || '获取行业白皮书',
need: featuredWhitepaper.value?.title || '行业白皮书',
source
})
}
function articleTags(article) {
if (article.tags?.length) return article.tags
const baseTags = ['GEO', '生成式引擎优化', 'AI搜索营销', 'SEO', '内容营销']
if (article.slug?.includes('report')) return [...baseTags, '行业报告']
if (article.slug?.includes('matrix')) return [...baseTags, '信源建设']
if (article.slug?.includes('metrics')) return [...baseTags, '增长指标']
return baseTags
}
</script>
<template>
@@ -88,7 +132,7 @@ function requestWhitepaper(source) {
<p>AI 不只是新的流量入口它正在改变用户理解品牌比较方案和做出选择的方式</p>
</header>
<article class="featured-insight">
<article v-if="featuredInsight" class="featured-insight">
<NuxtLink :to="`/insights/${featuredInsight.slug}`" class="featured-image">
<img :src="featuredInsight.cover" :alt="featuredInsight.title" />
<span>EDITOR'S PICK</span>
@@ -142,10 +186,16 @@ function requestWhitepaper(source) {
<time>{{ article.date }}</time
><span>{{ article.views }} 阅读</span>
</div>
<span class="article-category">{{ article.category }}</span>
<h3>
<NuxtLink :to="`/insights/${article.slug}`">{{ article.title }}</NuxtLink>
</h3>
<p>{{ article.excerpt }}</p>
<div class="article-tags" aria-label="文章标签">
<span v-for="tag in articleTags(article)" :key="`${article.slug}-${tag}`">{{
tag
}}</span>
</div>
<NuxtLink :to="`/insights/${article.slug}`" class="read-link"
>查看详情 <AppIcon name="arrow-right"
/></NuxtLink>
@@ -177,12 +227,12 @@ function requestWhitepaper(source) {
<div class="page-width whitepaper-grid">
<div class="whitepaper-number"><span>REPORT</span><strong>2026</strong></div>
<div class="whitepaper-copy">
<p>行业白皮书</p>
<h2>AI 搜索与品牌增长观察</h2>
<span>用户行为 · 内容信源 · 衡量框架 · 行业实践</span>
<p>{{ featuredWhitepaper?.category || '行业白皮书' }}</p>
<h2>{{ featuredWhitepaper?.title || 'AI 搜索与品牌增长观察' }}</h2>
<span>{{ featuredWhitepaper?.excerpt || '用户行为 · 内容信源 · 衡量框架 · 行业实践' }}</span>
</div>
<button type="button" @click="requestWhitepaper('增长洞察页白皮书')">
获取白皮书 <AppIcon name="arrow-right" />
{{ featuredWhitepaper?.cta || '获取白皮书' }} <AppIcon name="arrow-right" />
</button>
</div>
</section>
@@ -493,43 +543,78 @@ function requestWhitepaper(source) {
}
.insight-filters {
display: flex;
flex-wrap: wrap;
gap: 10px;
margin-top: 75px;
padding-bottom: 18px;
border-bottom: 1px solid #e0e9f3;
flex-wrap: nowrap;
justify-content: center;
gap: clamp(20px, 3.2vw, 44px);
width: min(100%, 980px);
margin: 72px auto 0;
padding: 0 6px;
overflow-x: auto;
border-bottom: 0;
scrollbar-width: none;
}
.insight-filters::-webkit-scrollbar {
display: none;
}
.insight-filters button {
height: 38px;
padding: 0 17px;
border-radius: 4px;
color: #61758c;
background: #f1f6fc;
font-size: 12px;
position: relative;
flex: 0 0 auto;
height: 54px;
padding: 0 8px;
border-radius: 0;
color: #101827;
background: transparent;
font-size: 22px !important;
font-weight: 500;
transition: 0.2s;
}
.insight-filters button:hover,
.insight-filters button.active {
color: #fff;
background: #287ff0;
box-shadow: 0 8px 18px rgba(39, 126, 238, 0.2);
transform: translateY(-1px);
color: #0978ff;
background: transparent;
box-shadow: none;
transform: none;
}
.insight-filters button::after {
position: absolute;
right: 0;
bottom: 0;
left: 0;
height: 2px;
content: '';
background: currentColor;
opacity: 0;
transform: scaleX(0.75);
transition:
opacity 0.2s,
transform 0.2s;
}
.insight-filters button.active::after {
opacity: 1;
transform: scaleX(1);
}
.article-results {
margin-top: 40px;
margin-top: 28px;
}
.article-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 28px;
grid-template-columns: 1fr;
gap: 20px;
width: min(100%, 1180px);
margin: 0 auto;
}
.article-grid article {
display: grid;
grid-template-columns: 0.85fr 1.15fr;
min-height: 285px;
grid-template-columns: minmax(280px, 332px) 1fr;
gap: 48px;
align-items: center;
min-height: 268px;
overflow: hidden;
border-radius: 14px 4px 14px 4px;
background: #f3f7fc;
padding: 18px 48px 18px 28px;
border: 1px solid #edf1f6;
border-radius: 18px;
background: linear-gradient(100deg, #fff 0%, #fff 72%, #f0f7ff 100%);
box-shadow: 0 10px 26px rgba(43, 83, 130, 0.05);
transition:
transform 0.25s,
box-shadow 0.25s,
@@ -537,18 +622,20 @@ function requestWhitepaper(source) {
}
.article-grid article:hover {
background: #fff;
box-shadow: 0 20px 42px rgba(61, 119, 189, 0.13);
transform: translateY(-5px);
box-shadow: 0 18px 42px rgba(61, 119, 189, 0.12);
transform: translateY(-3px);
}
.article-grid article.lead {
grid-column: 1 / -1;
grid-template-columns: 1.05fr 0.95fr;
min-height: 350px;
grid-column: auto;
grid-template-columns: minmax(280px, 332px) 1fr;
min-height: 268px;
}
.article-cover {
position: relative;
min-height: 100%;
height: 232px;
min-height: 0;
overflow: hidden;
border-radius: 14px;
}
.article-cover img {
width: 100%;
@@ -561,40 +648,64 @@ function requestWhitepaper(source) {
transform: scale(1.04);
}
.article-cover > span {
position: absolute;
top: 18px;
left: 18px;
padding: 7px 9px;
border-radius: 3px;
color: #fff;
background: rgba(32, 105, 200, 0.82);
font-size: 9px;
display: none;
}
.article-copy {
display: flex;
flex-direction: column;
justify-content: center;
padding: 28px;
min-width: 0;
padding: 4px 0;
}
.article-grid .article-meta,
.article-grid .read-link {
display: none;
}
.article-category {
color: #50667f;
font-size: 13px;
line-height: 1.4;
}
.article-copy h3 {
margin: 15px 0 0;
font-size: 20px;
line-height: 1.45;
margin: 12px 0 0;
color: #142033;
font-size: 24px;
font-weight: 500;
line-height: 1.38;
}
.article-grid article.lead h3 {
font-size: 28px;
font-size: 24px;
}
.article-copy h3 a {
color: inherit;
text-decoration: none;
}
.article-copy > p {
margin: 12px 0 0;
color: #75859a;
font-size: 11px;
line-height: 1.75;
display: -webkit-box;
overflow: hidden;
margin: 16px 0 0;
color: #435776;
font-size: 16px;
line-height: 1.68;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
}
.read-link {
align-self: flex-start;
margin-top: 22px;
padding-bottom: 4px;
border-bottom: 1px solid #9bc1e9;
.article-tags {
display: flex;
flex-wrap: wrap;
gap: 10px;
margin-top: 24px;
}
.article-tags span {
min-height: 34px;
display: inline-flex;
align-items: center;
padding: 0 13px;
border: 1px solid #d9e5f5;
border-radius: 5px;
color: #096fff;
background: #f7fbff;
font-size: 12px;
}
.load-more {
min-width: 150px;
@@ -736,10 +847,13 @@ function requestWhitepaper(source) {
url('/images/lanhu/slices/home-group105-image31@2x.png') center / cover no-repeat;
}
.insights-cta-content {
display: grid;
justify-items: center;
padding: 74px 0;
text-align: center;
}
.insights-cta-content p {
width: 100%;
margin: 0;
color: #237df0;
font-size: 12px;
@@ -850,24 +964,48 @@ function requestWhitepaper(source) {
flex-direction: column;
}
.insight-filters {
justify-content: flex-start;
gap: 30px;
width: 100%;
margin-top: 52px;
padding-bottom: 4px;
flex-wrap: nowrap;
overflow-x: auto;
}
.insight-filters button {
flex: 0 0 auto;
height: 48px;
font-size: 18px !important;
}
.article-grid article,
.article-grid article.lead {
grid-template-columns: 1fr;
gap: 18px;
padding: 14px;
border-radius: 16px;
}
.article-cover {
min-height: 210px;
height: auto;
min-height: 0;
aspect-ratio: 1.43;
}
.article-copy {
padding: 25px 22px;
padding: 0 4px 4px;
}
.article-copy h3,
.article-grid article.lead h3 {
font-size: 22px;
font-size: 21px;
}
.article-copy > p {
font-size: 14px;
}
.article-tags {
gap: 8px;
margin-top: 18px;
}
.article-tags span {
min-height: 30px;
padding: 0 10px;
}
.whitepaper-grid {
grid-template-columns: 1fr;