Files
Zhanglj 84bf9db8ba chore: 移除页面中冗余的图片占位标签
删除了无用的span图片占位元素,清理页面冗余代码
2026-07-24 18:56:15 +08:00

789 lines
27 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.
<script setup>
import { gsap } from 'gsap'
import { ScrollTrigger } from 'gsap/ScrollTrigger'
import SiteHeader from '~/components/site/SiteHeader.vue'
import SiteFooter from '~/components/site/SiteFooter.vue'
const pageRoot = ref(null)
const { openConsultation: openGlobalConsultation } = useConsultation()
const { fetchHomepageSections } = useOfficialContent()
const SIMPLE_STAT_VALUE = /^(\d+(?:\.\d+)?)([+%]*)$/
let animationContext
let animationMedia
let counterContext
const { data: homepageSectionRows } = await useAsyncData(
'public-homepage-sections',
() => fetchHomepageSections(),
{ server: false }
)
useRefreshNuxtDataOnResume('public-homepage-sections')
const homepageSections = computed(() => homepageSectionRows.value || [])
const homepageSectionMap = computed(() =>
Object.fromEntries(homepageSections.value.map((section) => [section.key, section]))
)
function homepageSection(key) {
return homepageSectionMap.value[key] || {}
}
function normalizeBackgroundUrl(value) {
const url = String(value || '')
.trim()
.replace(/&quot;|&#34;|&#x22;/gi, '"')
.replace(/&amp;/gi, '&')
const declarationMatch = url.match(/^background-image\s*:\s*(url\(.*\))\s*;?$/i)
const source = declarationMatch ? declarationMatch[1] : url
const urlMatch = source.match(/^url\((.*)\)$/i)
return (urlMatch ? urlMatch[1] : source).trim().replace(/^['"]|['"]$/g, '')
}
function backgroundStyle(url) {
const normalizedUrl = normalizeBackgroundUrl(url)
return normalizedUrl ? { backgroundImage: `url(${normalizedUrl})` } : null
}
const fallbackLogos = []
const fallbackStats = [
['300+', '累计服务客户', '/images/lanhu/slices/home-group37-group70-image5@2x.png'],
['12+', '覆盖行业', '/images/lanhu/slices/home-group36-group71-image5@2x.png'],
['126%', '客户AI搜索曝光平均提升', '/images/lanhu/slices/home-group35-group72-image5@2x.png'],
['65%', '客户转化链路效率平均提升', '/images/lanhu/slices/home-group34-group74-image5@2x.png']
]
const heroBackgroundImage = '/images/lanhu/slices/hero-geo-visual@2x.png'
const fallbackCorePoints = [
['曝光', '打造AI搜索语境下的品牌信任声量,一次布局,长期复用'],
['获客', '让曝光直接沉淀为可跟进的精准线索,告别无效流量']
]
const fallbackRingModels = [
{
title: '声量资产环',
image: '/images/lanhu/slices/home-group80-group83-image11@2x.png',
icon: 'asset',
labels: ['真人内容生产', '全域媒体分发', 'AI信任构建', '品牌资产沉淀']
},
{
title: '获客转化环',
image: '/images/lanhu/slices/home-group86-group87-image12@2x.png',
icon: 'conversion',
labels: ['AI搜索曝光', '精准流量承接', '全链路转化', '数据复盘优化']
}
]
const fallbackAdvantageCards = [
{
heading: '全域媒体资源优势',
bodyOne: '5000+权威官媒+10万+优质自媒体矩阵',
bodyTwo: '提升AI搜索权重 多渠道权威背书,全方位强化品牌 曝光公信力'
},
{
heading: '全链路闭环优势',
bodyOne: '行业唯一打通「纯曝光-内容资产沉淀·转化闭环',
bodyTwo: '从曝光到获客全链路承接,避免流量断层实现 品效合'
},
{
heading: '全客群定制优势',
bodyOne: '覆盖全类型经营主体,一对一专属定制方案',
bodyTwo: '拒绝标准化模板,根据企业阶段、行业、需求匹 記最优 GEO策略'
}
]
const fallbackCaseImages = [
'/images/lanhu/slices/home-group90-image15@2x.png',
'/images/lanhu/slices/home-group91-image16@2x.png',
'/images/lanhu/slices/home-group92-image17@2x.png',
'/images/lanhu/slices/home-group93-image18@2x.png'
]
const fallbackIndustryCards = [
[
'本地生活',
'AI搜索全域曝光提升320%月均精准线索新增80+,持续获得品牌声量与业务线索',
'/images/lanhu/slices/home-group34-group74-image5@2x.png'
],
[
'快消',
'AI搜索全域曝光提升320%月均精准线索新增80+,让内容价值更快兑现',
'/images/lanhu/slices/home-group35-group72-image5@2x.png'
],
[
'美妆',
'AI搜索全域曝光提升320%月均精准线索新增80+,建立多渠道信任背书',
'/images/lanhu/slices/home-group36-group71-image5@2x.png'
]
]
const fallbackDeliverySteps = [
[
'需求诊断',
'一对一沟通,分析品牌现状与增长痛点',
'/images/lanhu/slices/home-group78-image10@2x.png'
],
[
'方案定制',
'定制专属GEO策略,明确交付节点与预期效果',
'/images/lanhu/slices/home-group80-group77-image9@2x.png'
],
[
'内容落地',
'真人内容生产+媒体矩阵投放,全流程同步进度',
'/images/lanhu/slices/home-group86-group76-image8@2x.png'
],
[
'数据优化',
'定期数据复盘,动态调整策略,持续优化效果',
'/images/lanhu/slices/home-group34-group74-image5@2x.png'
],
[
'资产沉淀',
'交付完整内容资产与增长报告,实现长效复用',
'/images/lanhu/slices/home-group35-group72-image5@2x.png'
]
]
const heroSection = computed(() => homepageSection('HERO'))
const customerHallSection = computed(() => homepageSection('CUSTOMER_HALL'))
const capabilitiesSection = computed(() => homepageSection('CAPABILITIES'))
const advantagesSection = computed(() => homepageSection('ADVANTAGES'))
const caseBlockSection = computed(() => homepageSection('CASE_BLOCK'))
const industryBlockSection = computed(() => homepageSection('INDUSTRY_BLOCK'))
const processSection = computed(() => homepageSection('PROCESS'))
const testimonialSection = computed(() => homepageSection('TESTIMONIAL'))
const bottomCtaSection = computed(() => homepageSection('BOTTOM_CTA'))
const heroTitle = computed(() => heroSection.value.title || '大马棒 · 云途全域GEO')
const heroDescription = computed(() => heroSection.value.description || 'AI时代拥有持续增长力')
console.log(heroSection.value.description)
const heroTags = computed(() =>
heroSection.value.tags?.length
? heroSection.value.tags
: ['5万真人内容生产网络', '5000+自媒体资源矩阵', '全链路转化闭环']
)
const logos = computed(() => fallbackLogos)
const stats = computed(() => {
const cards = customerHallSection.value.cards
if (!cards?.length) return fallbackStats
return cards.map((card, index) => [
card.value,
card.label,
fallbackStats[index % fallbackStats.length][2]
])
})
watch(stats, async () => {
if (typeof window === 'undefined') return
await nextTick()
if (window.innerWidth <= 760 || !pageRoot.value) return
counterContext?.revert()
counterContext = gsap.context(() => {
document.querySelectorAll('.stat-item strong').forEach((element) => {
const finalText = element.textContent.trim()
const statMatch = finalText.match(SIMPLE_STAT_VALUE)
if (!statMatch) return
const finalValue = Number.parseFloat(statMatch[1])
const suffix = statMatch[2]
const counter = { value: 0 }
ScrollTrigger.create({
trigger: element,
start: 'top 88%',
once: true,
onEnter: () => {
gsap.to(counter, {
value: finalValue,
duration: 1.45,
ease: 'power2.out',
onUpdate: () => {
element.textContent = `${Math.round(counter.value)}${suffix}`
}
})
}
})
})
}, pageRoot.value)
}, { immediate: true, flush: 'post' })
const corePoints = computed(() => {
const cards = capabilitiesSection.value.cards
if (!cards?.length) return fallbackCorePoints
return cards
.slice(0, 2)
.map((card, index) => [
card.title || fallbackCorePoints[index]?.[0] || '',
card.details?.[0]?.description ||
card.details?.[0]?.heading ||
fallbackCorePoints[index]?.[1] ||
''
])
})
const ringModels = computed(() => fallbackRingModels)
const leadAdvantage = computed(() => {
const groups = advantagesSection.value.groups || []
const lead = groups[0]
return {
title: lead?.heading || '真人内容生产优势',
bodyOne: lead?.bodyOne || '行业独家5万真人分布式内容生产网络',
bodyTwo: lead?.bodyTwo || '用C端爆款内容思维做B端GEO,打造真实可信的AI信任型声量',
image: advantagesSection.value.assetUrl || '/images/lanhu/slices/home-group90-image15@2x.png'
}
})
const advantageCards = computed(() => {
const groups = advantagesSection.value.groups?.slice(1)
console.log(groups);
if (!groups?.length) return fallbackAdvantageCards
return groups
})
const caseCards = computed(() => {
const contents = caseBlockSection.value.contents
if (!contents?.length) {
return fallbackCaseImages.map((image) => ({
image,
description: '某企业服务客户 AI搜索全域曝光提升\n320%月均精准线索新增80+'
}))
}
return contents.map((item, index) => ({
image:
item.imageUrl ||
item.assetUrl ||
item.url ||
item.image ||
fallbackCaseImages[index % fallbackCaseImages.length],
description: item.description || ''
}))
})
const industryCards = computed(() => {
const groups = industryBlockSection.value.groups
if (!groups?.length) return fallbackIndustryCards
return groups.map((group, index) => [
group.title,
group.description,
group.imageUrl || fallbackIndustryCards[index % fallbackIndustryCards.length][2],
group.tags || []
])
})
const deliverySteps = computed(() => {
const groups = processSection.value.groups
if (!groups?.length) return fallbackDeliverySteps
return groups.map((group, index) => [
group.heading,
group.body,
fallbackDeliverySteps[index % fallbackDeliverySteps.length][2]
])
})
const testimonialCarouselEnabled = computed(() => testimonials.value.length > 3)
const testimonialLoopItems = computed(() =>
testimonialCarouselEnabled.value
? [...testimonials.value, ...testimonials.value]
: testimonials.value
)
const testimonials = computed(() => {
const groups = testimonialSection.value.groups
if (!groups?.length) {
return Array.from({ length: 5 }, (_, index) => ({
name: '数据优化:',
date: `2026-12-${42 + index}`,
description:
'创作者每日必读的前沿设计趋势、设计方法论和大厂落地经验之谈,14年的行业专注,脑突入口的优质内容沉淀,为每一个从入行到进阶的设计师、创作者赋能。'
}))
}
return groups
})
function requestConsultation(source, title = '预约免费 GEO 增长诊断') {
openGlobalConsultation({ title, source })
}
onMounted(async () => {
await nextTick()
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches || !pageRoot.value) return
gsap.registerPlugin(ScrollTrigger)
animationContext = gsap.context(() => {
const reveal = (targets, trigger, options = {}) => {
gsap.from(targets, {
autoAlpha: 0,
y: 34,
duration: 0.72,
ease: 'power3.out',
stagger: 0.1,
clearProps: 'transform,opacity,visibility',
scrollTrigger: {
trigger,
start: 'top 82%',
once: true
},
...options
})
}
const heroTimeline = gsap.timeline({ defaults: { ease: 'power3.out' } })
heroTimeline
.from('.hero-copy h1', { autoAlpha: 0, y: 32, duration: 0.7, clearProps: 'all' })
.from(
'.hero-points span',
{ autoAlpha: 0, y: 16, duration: 0.42, stagger: 0.09, clearProps: 'all' },
'-=0.22'
)
.from(
'.button-row > *',
{ autoAlpha: 0, y: 16, duration: 0.42, stagger: 0.1, clearProps: 'all' },
'-=0.12'
)
gsap.to('.hero-art', {
scale: 1.025,
yPercent: 1,
duration: 9,
ease: 'sine.inOut',
repeat: -1,
yoyo: true
})
reveal('.intro-section .section-title', '.intro-section')
reveal('.stats-panel .stat-item', '.stats-panel', { y: 26, stagger: 0.12 })
reveal('.model-layout .ring', '.model-layout', { x: -24, y: 12, stagger: 0.16 })
reveal('.core-value', '.model-layout', { x: 28, y: 0, delay: 0.12 })
reveal('.advantages-section .section-title', '.advantages-section')
reveal('.lead-advantage', '.lead-advantage')
reveal('.advantage-grid article', '.advantage-grid', { y: 24, stagger: 0.12 })
reveal('.cases-section .section-title', '.cases-section')
reveal('.case-panel article', '.case-panel', { y: 24, stagger: 0.1 })
reveal('.industry-grid article', '.industry-grid', { y: 26, stagger: 0.12 })
reveal('.delivery-banner', '.delivery-banner', { y: 22 })
reveal('.delivery-grid article', '.delivery-grid', { y: 22, stagger: 0.1 })
reveal('.testimonial-section .section-title', '.testimonial-section')
reveal('.cta-content', '.cta-section', { y: 26 })
animationMedia = gsap.matchMedia()
animationMedia.add('(max-width: 760px)', () => {
gsap.to('.logo-track', {
xPercent: -50,
duration: 24,
ease: 'none',
repeat: -1
})
gsap.to('.ring:first-child', {
x: -12,
rotation: -2,
ease: 'none',
scrollTrigger: {
trigger: '.model-layout',
start: 'top bottom',
end: 'bottom top',
scrub: 0.8
}
})
gsap.to('.ring:nth-child(2)', {
x: 12,
rotation: 2,
ease: 'none',
scrollTrigger: {
trigger: '.model-layout',
start: 'top bottom',
end: 'bottom top',
scrub: 0.8
}
})
gsap.to('.cta-art', {
yPercent: -6,
ease: 'none',
scrollTrigger: {
trigger: '.cta-section',
start: 'top bottom',
end: 'bottom top',
scrub: 0.8
}
})
})
animationMedia.add('(min-width: 761px)', () => {
const cleanup = []
const addListener = (element, event, handler) => {
element.addEventListener(event, handler)
cleanup.push(() => element.removeEventListener(event, handler))
}
const hero = document.querySelector('.hero-section')
const heroCopy = document.querySelector('.hero-copy')
if (hero && heroCopy) {
const moveX = gsap.quickTo(heroCopy, 'x', { duration: 0.7, ease: 'power3.out' })
const moveY = gsap.quickTo(heroCopy, 'y', { duration: 0.7, ease: 'power3.out' })
addListener(hero, 'pointermove', (event) => {
const bounds = hero.getBoundingClientRect()
moveX(((event.clientX - bounds.left) / bounds.width - 0.5) * 16)
moveY(((event.clientY - bounds.top) / bounds.height - 0.5) * 10)
})
addListener(hero, 'pointerleave', () => {
moveX(0)
moveY(0)
})
}
document.querySelectorAll('.section-title').forEach((title) => {
ScrollTrigger.create({
trigger: title,
start: 'top 84%',
once: true,
onEnter: () => title.classList.add('is-visible')
})
})
const interactiveCards = document.querySelectorAll(
'.stat-item, .advantage-grid article, .case-panel article, .industry-grid article, .delivery-grid article'
)
interactiveCards.forEach((card) => {
card.classList.add('motion-card')
const image = card.querySelector('img')
const imageX = image
? gsap.quickTo(image, 'x', { duration: 0.45, ease: 'power3.out' })
: null
const imageY = image
? gsap.quickTo(image, 'y', { duration: 0.45, ease: 'power3.out' })
: null
addListener(card, 'pointermove', (event) => {
const bounds = card.getBoundingClientRect()
const x = event.clientX - bounds.left
const y = event.clientY - bounds.top
card.style.setProperty('--spot-x', `${x}px`)
card.style.setProperty('--spot-y', `${y}px`)
gsap.to(card, { '--spot-opacity': 1, duration: 0.2 })
imageX?.((x / bounds.width - 0.5) * 9)
imageY?.((y / bounds.height - 0.5) * 9)
})
addListener(card, 'pointerleave', () => {
gsap.to(card, { '--spot-opacity': 0, duration: 0.35 })
imageX?.(0)
imageY?.(0)
})
cleanup.push(() => card.classList.remove('motion-card'))
})
gsap.to('.model-layout .ring:first-child', {
x: -34,
rotation: -4,
ease: 'none',
scrollTrigger: {
trigger: '.model-layout',
start: 'top bottom',
end: 'bottom top',
scrub: 1
}
})
gsap.to('.model-layout .ring:nth-child(2)', {
x: 34,
rotation: 4,
ease: 'none',
scrollTrigger: {
trigger: '.model-layout',
start: 'top bottom',
end: 'bottom top',
scrub: 1
}
})
gsap.to('.case-background', {
xPercent: -4,
scale: 1.08,
ease: 'none',
scrollTrigger: {
trigger: '.cases-section',
start: 'top bottom',
end: 'bottom top',
scrub: 1
}
})
gsap.to('.delivery-banner', {
backgroundPosition: '50% 62%',
ease: 'none',
scrollTrigger: {
trigger: '.delivery-section',
start: 'top bottom',
end: 'bottom top',
scrub: 1
}
})
return () => cleanup.forEach((dispose) => dispose())
})
}, pageRoot.value)
})
onBeforeUnmount(() => {
animationMedia?.revert()
animationContext?.revert()
counterContext?.revert()
})
</script>
<template>
<div id="home" ref="pageRoot" class="site-shell">
<SiteHeader />
<main>
<section class="hero-section">
<div
class="hero-art"
:style="backgroundStyle(heroBackgroundImage)"
aria-hidden="true"
></div>
<div class="page-width hero-content">
<div class="hero-copy">
<h1>{{ heroTitle }}</h1>
<h1>{{ heroDescription }}</h1>
<p class="hero-points">
<span v-for="tag in heroTags" :key="tag"> {{ tag }}</span>
</p>
<div class="button-row">
<button
class="primary-button"
type="button"
@click="requestConsultation('首页 Hero', '免费定制 GEO 增长方案')"
>
免费定制 <b></b></button
><NuxtLink class="outline-button" to="/cases">查看成功案例</NuxtLink>
</div>
</div>
</div>
</section>
<section class="logo-strip" aria-label="合作品牌">
<div class="logo-track">
<span
v-for="(logo, index) in logos"
:key="`${logo}-${index}`"
:class="`logo logo-${index % 6}`"
>{{ logo }}</span
>
</div>
</section>
<section class="intro-section page-width">
<SectionTitle
>{{ customerHallSection.title || '已服务300+全类型经营主体,覆盖12+主流行业'
}}<template #subtitle
>We Have Provided Services To Over 300 Full-spectrum Business Entities<br />Spanning
More Than A Dozen Mainstream Industry Sectors</template
></SectionTitle
>
<div class="stats-panel">
<article v-for="stat in stats" :key="stat[1]" class="stat-item">
<img :src="stat[2]" alt="" /><strong>{{ stat[0] }}</strong
><span>{{ stat[1] }}</span>
</article>
</div>
</section>
<section id="model" class="model-section">
<div class="page-width">
<SectionTitle
>{{ capabilitiesSection.title || '独创「GEO双环增长模型」,实现品效协同长效增长'
}}<template #subtitle
>Original "geo Dual-ring Growth Model" Delivers Long-term Growth Through<br />Integrated
Brand Building And Conversion Performance</template
></SectionTitle
>
<div class="model-layout">
<div
v-for="model in ringModels"
:key="model.title"
class="ring ring-diagram"
role="img"
:aria-label="model.title"
>
<img class="ring-base" :src="model.image" alt="" />
<span
v-for="(label, labelIndex) in model.labels"
:key="label"
:class="`ring-label ring-label-${labelIndex + 1}`"
>{{ label }}</span
>
<div class="ring-center-label">
<svg v-if="model.icon === 'asset'" viewBox="0 0 48 48" aria-hidden="true">
<path d="M10 38V16l14-7 14 7v22H10Z" />
<path d="M17 21h4m6 0h4m-14 7h4m6 0h4M22 38v-6h4v6" />
</svg>
<svg v-else viewBox="0 0 48 48" aria-hidden="true">
<circle cx="15" cy="17" r="6" />
<circle cx="34" cy="14" r="5" />
<circle cx="31" cy="34" r="6" />
<path d="m20 18 9-3m2 4v9m-5 4-7-10" />
</svg>
<strong>{{ model.title }}</strong>
</div>
</div>
<div class="core-value">
<h3>
<img src="/images/lanhu/slices/home-group88-image13@2x.png" alt="" />业务核心价值点
</h3>
<div v-for="point in corePoints" :key="point[0]" class="core-point">
<h4><i></i>{{ point[0] }}</h4>
<p>{{ point[1] }}</p>
</div>
<NuxtLink class="primary-button small" to="/services">了解业务详情 <b></b></NuxtLink>
</div>
</div>
</div>
</section>
<section id="advantages" class="advantages-section">
<div class="page-width">
<SectionTitle
>{{ advantagesSection.title || '四大核心优势'
}}<template #subtitle>Four Core Advantages</template></SectionTitle
>
<div class="lead-advantage">
<img :src="leadAdvantage.image" :alt="leadAdvantage.title" />
<div>
<h3>{{ leadAdvantage.title }}</h3>
<p><b>核心点</b>{{ leadAdvantage.bodyOne }}</p>
<p><b>价值</b>{{ leadAdvantage.bodyTwo }}</p>
</div>
</div>
<div class="advantage-grid">
<article v-for="(item, index) in advantageCards" :key="index">
<img
:src="`/images/lanhu/slices/home-group${37 - index}-group${70 + index}-image5@2x.png`"
alt=""
/>
<div>
<h3>{{ item.heading }}</h3>
<p><b>核心点</b>{{ item.bodyOne }}</p>
<p><b>价值</b>{{ item.bodyTwo }}</p>
</div>
</article>
</div>
</div>
</section>
<section id="cases" class="cases-section">
<div class="case-background" aria-hidden="true"></div>
<div class="page-width case-content">
<SectionTitle
>{{ caseBlockSection.title || '真实落地成果,见证增长实效'
}}<template #subtitle
>Practical Delivered Results That Demonstrate Tangible Growth Outcomes</template
></SectionTitle
>
<div class="case-panel">
<article v-for="(item, index) in caseCards" :key="`${item.image}-${index}`">
<img :src="item.image" :alt="`客户案例 ${index + 1}`" />
<p>{{ item.description }}</p>
</article>
</div>
<div class="center-action">
<NuxtLink class="primary-button small" to="/cases">查看全部案例 <b></b></NuxtLink>
</div>
</div>
</section>
<section id="industries" class="industries-section">
<div class="industries-content">
<SectionTitle
>{{ industryBlockSection.title || '深耕12+行业,定制专属GEO增长方案'
}}<template #subtitle
>With In-depth Experience Across 12+ Industries, We Craft Exclusive Geo Growth
Solutions<br />Tailored To Clients Needs</template
></SectionTitle
>
<div class="industry-grid">
<article v-for="card in industryCards" :key="card[0]">
<h3>{{ card[0] }}</h3>
<img :src="card[2]" alt="" />
<div class="quote"></div>
<p>某企业服务客户<br />{{ card[1] }}</p>
<div class="tags">
<span v-for="tag in card[3] || ['生活服务', '全域曝光']" :key="`${card[0]}-${tag}`"
>#{{ tag }}</span
>
</div>
</article>
</div>
</div>
</section>
<section id="delivery" class="delivery-section">
<div class="page-width">
<SectionTitle
>{{ processSection.title || '5步标准化交付流程,透明可控效果可追溯'
}}<template #subtitle
>With In-depth Experience Across 12+ Industries, We Craft Exclusive Geo Growth
Solutions<br />Tailored To Clients Needs</template
></SectionTitle
>
<div class="delivery-banner" :style="backgroundStyle(processSection.assetUrl)"></div>
<div class="delivery-grid">
<article v-for="step in deliverySteps" :key="step[0]">
<img :src="step[2]" alt="" />
<div>
<h3>{{ step[0] }}</h3>
<p>{{ step[1] }}</p>
</div>
</article>
</div>
</div>
</section>
<section class="testimonial-section">
<div class="page-width">
<SectionTitle
>{{ testimonialSection.title || '客户真实评价,见证服务品质'
}}<template #subtitle
>Genuine Client Testimonials Attest To Our Service Quality</template
></SectionTitle
>
<div
:class="[
'testimonial-track',
testimonialCarouselEnabled ? 'is-carousel' : 'is-centered'
]"
>
<article
v-for="(item, index) in testimonialLoopItems"
:key="`${item.name}-${index}`"
:aria-hidden="
testimonialCarouselEnabled && index >= testimonials.length ? 'true' : null
"
>
<header>
<img
v-if="normalizeBackgroundUrl(item.avatarUrl)"
class="avatar"
:src="normalizeBackgroundUrl(item.avatarUrl)"
:alt="item.name ? `${item.name}头像` : ''"
/><span v-else class="avatar"></span><b>{{ item.name }}</b
><time>{{ item.date }}</time>
</header>
<p>{{ item.description }}</p>
</article>
</div>
</div>
</section>
<section class="cta-section">
<div class="cta-art" :style="backgroundStyle(bottomCtaSection.assetUrl)"></div>
<div class="cta-content">
<h2>
{{
bottomCtaSection.title ||
'AI搜索增长风口已至,大马棒 · 云途全域GEO助力企业打造品效双闭环的长效增长体系'
}}
</h2>
<p>{{ bottomCtaSection.subtitle || '免费获取1对1GEO增长诊断方案' }}</p>
<button class="primary-button" type="button" @click="requestConsultation('首页底部 CTA')">
立即免费咨询 <b></b>
</button>
</div>
</section>
</main>
<SiteFooter />
</div>
</template>