This commit is contained in:
ljy
2026-08-11 01:33:33 +08:00
parent cfafec8a3c
commit 738f00d442
129 changed files with 0 additions and 11642 deletions
-650
View File
@@ -1,650 +0,0 @@
import { useCallback, useEffect, useMemo, useState, type CSSProperties } from 'react'
import './App.css'
const sections = [
{ id: 'hero', label: 'LP-H 首屏', title: '你请客,我买单', summary: '杜康好客:你请客,我买单。买酒送等价好客权益,到合作门店抵餐。' },
{ id: 'brand', label: 'LP-01 品牌传承', title: '品牌传承', summary: '杜康被尊为秫酒酒宗、酒祖与酒神,是华夏酿造文明的精神图腾。' },
{ id: 'poetry', label: 'LP-02 诗酒文化', title: '诗酒文化', summary: '何以解忧,唯有杜康。曹操《短歌行》主诗签,苏轼、白居易、辛弃疾辅诗签。' },
{ id: 'qingxiang', label: 'LP-03 清香宝典', title: '清香', summary: '千年杜康,醉美清香。绵·净·雅,杏花村同源同脉,一清到底。' },
{ id: 'haoke', label: 'LP-04 好客模式', title: '好客模式', summary: '买酒送等价好客权益;到合作门店按面额抵餐。酒餐同赴,线上线下一体。' },
{ id: 'system', label: 'LP-05 三端协同', title: '三端协同', summary: '城市合伙人拓店 → 用户购酒得权益 → 门店核销结算 → 再反哺城市运营。' },
{ id: 'tech', label: 'LP-06 科技驱动', title: '科技驱动', summary: '四端一体、全链路闭环、宴请场景数字化、城市运营中台。' },
{ id: 'subsidy', label: 'LP-07 酒厂实力', title: '酒厂实力', summary: '十亿级支持,与餐饮共度寒冬。支持细则以合作政策为准。' },
{ id: 'partner', label: 'LP-08 城市合伙人', title: '城市合伙人', summary: '携酒祖入城,共建一座城的好客。了解合作方式,不承诺收益。' },
{ id: 'tech-detail', label: 'LP-09 科技板块', title: '科技板块', summary: '可运营、可结算、可规模化的好客系统:用户端、门店核销端、合伙人端、总部管控。' },
{ id: 'contact', label: 'LP-10 联系我们', title: '联系我们', summary: '扫码咨询项目负责人吕丹,电话 13073729990。无留资表单。' },
]
const navItems = [
{ href: '#brand', label: '品牌传承' },
{ href: '#poetry', label: '诗酒文化' },
{ href: '#qingxiang', label: '清香' },
{ href: '#haoke', label: '好客模式' },
{ href: '#system', label: '三端协同' },
{ href: '#tech', label: '科技驱动' },
{ href: '#subsidy', label: '酒厂实力' },
{ href: '#partner', label: '城市合伙人' },
{ href: '#tech-detail', label: '科技板块' },
{ href: '#contact', label: '联系我们' },
]
const heritageCards = [
{
title: '导语',
text: '杜康,被尊为秫酒「酒宗」「酒祖」,更被世人敬奉为「酒神」。从典籍到诗篇,从故里到祠祀,杜康不只是一杯酒的名字——它是华夏酿造文明的精神图腾。',
},
{
title: '典籍立祖',
text: '《世本》载「杜康作秫酒」;《说文解字》谓「少康,杜康也」。酒之所兴,肇自上皇——杜康造酒,写入中华文明的源头叙事。',
},
{
title: '故里承源',
text: '杜康村、杜康泉、空桑涧……酒祖故里的山水与作坊,延续着「以秫为骨、以曲为魂」的古老智慧,让技进乎道,香传千年。',
},
{
title: '酒以成礼',
text: '从封坛祭祖到「饮惟祀」的酒德观念,杜康文化把粮食之精,升华为礼乐之器——国之大事,在祀在戎,酒以成礼。',
},
]
const timeline = [
'先秦典籍立名',
'魏晋诗性转化',
'杜康祠祀千年',
'当代酒祖品牌复兴',
]
const qingxiangPositions = [
{ title: '企业定位', text: '承千年清韵,铸清香白酒标杆企业。' },
{ title: '品质定位', text: '守古法清酿,酿纯粹绵柔清雅好酒。' },
{ title: '品牌定位', text: '传酒祖清魂,塑高端清香文化名酒。' },
{ title: '营销定位', text: '拓清香赛道,引全民清雅饮酒新风尚。' },
]
const tastingSteps = [
{ title: '抿', text: '微启触香,品粮香本味' },
{ title: '咂', text: '舌卷含香,品绵柔入喉' },
{ title: '呵', text: '缓缓呵气,品回甘生津' },
]
const systemEnds = [
{
title: '城市合伙人',
role: '开城运营者:拓店、组仓配、带团队,让品牌在一座城真正落地。',
caps: '门店拓展与入驻协同 · 推广与代下单 · 佣金与经营数据可见',
},
{
title: '合作门店',
role: '好客权益核销端:把用户带回餐桌,用核销结算反哺门店经营。',
caps: '手机号/核销码核销 · 核销记录可查 · 结算路径清晰',
},
{
title: '用户',
role: '购酒与权益的享受者:小程序下单,酒到席、权益到店。',
caps: '清香选购 · 1:1 好客权益 · 同城配送/现场提货',
},
]
const techPoints = [
{
title: '四端一体',
text: '用户小程序、门店核销、城市合伙人、总部管控——权限分离、数据同源,开城可复制。',
},
{
title: '全链路闭环',
text: '下单履约、好客权益发放、门店核销、结算回款可视可追,减少「口头承诺、纸面对账」。',
},
{
title: '宴请场景数字化',
text: '面向商务宴请高频人群:酒到席、权益到店,把一次消费同时服务「喝」与「吃」。',
},
{
title: '城市运营中台',
text: '拓店、推广归因、经营数据与热力洞察,服务合伙人把一座城「盘清楚、推得动」。',
},
]
const techPorts = [
{
title: '用户端',
text: '清香选购与展示 · 下单履约(同城配送/现场提货) · 好客权益领取与使用 · 售后工单与发票入口',
},
{
title: '门店核销端',
text: '手机号/核销码核销 · 核销记录可查 · 结算与提现协同(细则以政策为准)',
},
{
title: '城市合伙人端',
text: '门店资料与入驻协同 · 推广/代下单 · 佣金与经营数据可见 · 子账号与权限管理',
},
{
title: '总部管控与数据',
text: '城市开通与审核 · 全链路订单/核销洞察 · 热力与经营分析 · 结算打款协同',
},
]
function App() {
const isStageMode = useMemo(() => new URLSearchParams(window.location.search).get('mode') === 'stage', [])
const [activeIndex, setActiveIndex] = useState(0)
const [autoPlay, setAutoPlay] = useState(false)
const [panelOpen, setPanelOpen] = useState(true)
const activeSection = sections[activeIndex] ?? sections[0]
useEffect(() => {
document.body.classList.toggle('stage-mode', isStageMode)
return () => document.body.classList.remove('stage-mode')
}, [isStageMode])
useEffect(() => {
if (!isStageMode) return
const hashIndex = sections.findIndex((section) => `#${section.id}` === window.location.hash)
if (hashIndex >= 0) setActiveIndex(hashIndex)
}, [isStageMode])
useEffect(() => {
if (isStageMode) return
const observer = new IntersectionObserver(
(entries) => {
const visible = entries
.filter((entry) => entry.isIntersecting)
.sort((a, b) => b.intersectionRatio - a.intersectionRatio)[0]
if (visible?.target.id) {
const nextIndex = sections.findIndex((section) => section.id === visible.target.id)
if (nextIndex >= 0) setActiveIndex(nextIndex)
}
},
{ threshold: [0.35, 0.6, 0.8] },
)
sections.forEach((section) => {
const node = document.getElementById(section.id)
if (node) observer.observe(node)
})
return () => observer.disconnect()
}, [isStageMode])
const goToSection = useCallback((index: number) => {
const safeIndex = Math.max(0, Math.min(index, sections.length - 1))
setActiveIndex(safeIndex)
if (isStageMode) {
window.history.replaceState(null, '', `?mode=stage#${sections[safeIndex].id}`)
return
}
document.getElementById(sections[safeIndex].id)?.scrollIntoView({ behavior: 'smooth', block: 'start' })
}, [isStageMode])
useEffect(() => {
if (!isStageMode) return
const root = document.getElementById('root')
root?.setAttribute('tabindex', '-1')
root?.focus({ preventScroll: true })
const handleKeyDown = (event: KeyboardEvent) => {
const key = event.key
const code = event.code
const isNext = key === 'ArrowRight' || code === 'ArrowRight' || key === 'ArrowDown' || code === 'ArrowDown' || key === 'PageDown' || code === 'PageDown' || key === ' '
const isPrev = key === 'ArrowLeft' || code === 'ArrowLeft' || key === 'ArrowUp' || code === 'ArrowUp' || key === 'PageUp' || code === 'PageUp'
const isHome = key === 'Home' || code === 'Home'
const isEnd = key === 'End' || code === 'End'
if (!isNext && !isPrev && !isHome && !isEnd) return
event.preventDefault()
event.stopPropagation()
setActiveIndex((current) => {
let next = current
if (isNext) next = Math.min(current + 1, sections.length - 1)
if (isPrev) next = Math.max(current - 1, 0)
if (isHome) next = 0
if (isEnd) next = sections.length - 1
window.history.replaceState(null, '', `?mode=stage#${sections[next].id}`)
return next
})
}
window.addEventListener('keydown', handleKeyDown, true)
return () => {
window.removeEventListener('keydown', handleKeyDown, true)
root?.removeAttribute('tabindex')
}
}, [isStageMode])
useEffect(() => {
if (!isStageMode || !autoPlay) return
const timer = window.setInterval(() => {
setActiveIndex((current) => {
const next = current >= sections.length - 1 ? 0 : current + 1
window.history.replaceState(null, '', `?mode=stage#${sections[next].id}`)
return next
})
}, 8000)
return () => window.clearInterval(timer)
}, [autoPlay, isStageMode])
const progress = ((activeIndex + 1) / sections.length) * 100
const techSectionIndex = sections.findIndex((s) => s.id === 'tech')
const isTechTopbar = !isStageMode && techSectionIndex >= 0 && activeIndex >= techSectionIndex
return (
<div className={isStageMode ? 'site is-stage' : 'site'}>
<header className={isTechTopbar ? 'topbar is-tech-theme' : 'topbar'}>
<a className="brand-lockup" href="#hero" onClick={() => goToSection(0)}>
<img className="haoke-logo-img" src="/assets/haoke-logo.png" alt="杜康好客" />
<span>
<strong></strong>
<small></small>
</span>
</a>
<nav className="nav-links" aria-label="主导航">
{navItems.map((item) => (
<a href={item.href} key={item.href}>{item.label}</a>
))}
</nav>
<a className="nav-cta" href="#contact"></a>
</header>
<main
className={isStageMode ? 'stage-track' : undefined}
style={isStageMode ? { transform: `translate3d(-${activeIndex * 100}vw, 0, 0)` } : undefined}
>
<section className="page-section hero-section poetic-section reveal-section" id="hero">
<div className="ink-cloud ink-cloud-one" />
<div className="hero-copy">
<p className="brand-main"></p>
<p className="brand-sub"> · </p>
<h1></h1>
<p className="hero-text"></p>
<div className="hero-actions">
<a className="primary-button" href="#haoke"></a>
<a className="secondary-button" href="#partner"></a>
</div>
</div>
<div className="hero-visual" aria-label="杜康好客清香系列 T30 产品视觉">
<img className="hero-bottle" src="/assets/t30-hero-bottle.png" alt="杜康好客清香系列 T30" />
</div>
</section>
<section className={`page-section poetic-section brand-section${activeIndex === 1 ? ' is-entered' : ''}`} id="brand">
<div className="section-heading">
<img className="ornament haoke-mark" src="/assets/haoke-logo.png" alt="杜康好客" />
<span className="eyebrow"></span>
<h2></h2>
</div>
<div className="brand-layout">
<div className="heritage-grid">
{heritageCards.map((card, index) => (
<article className="paper-card" key={card.title} style={{ '--slide-i': index } as CSSProperties}>
<span>{card.title}</span>
<p>{card.text}</p>
</article>
))}
</div>
<figure className="heritage-figure">
<img src="/assets/heritage-scene.png" alt="酒祖杜康祠堂造像" />
<figcaption> · </figcaption>
</figure>
</div>
<div className="timeline" aria-label="品牌传承时间线">
{timeline.map((item, index) => (
<div className="timeline-item" key={item} style={{ '--slide-i': index } as CSSProperties}>{item}</div>
))}
</div>
<p className="closing-line"></p>
</section>
<section className={`page-section poetic-section poetry-section${activeIndex === 2 ? ' is-entered' : ''}`} id="poetry">
<div className="poetry-main-card reveal-card">
<img className="ornament haoke-mark" src="/assets/haoke-logo.png" alt="杜康好客" />
<span className="eyebrow"></span>
<h2></h2>
<p className="lead"></p>
<blockquote>
<span></span>
<strong></strong>
<cite> · </cite>
</blockquote>
<p></p>
<p className="closing-inline">穿</p>
</div>
<div className="poetry-tags">
<article style={{ '--slide-i': 0 } as CSSProperties}>
<b></b>
<span></span>
<small> · </small>
</article>
<article style={{ '--slide-i': 1 } as CSSProperties}>
<b></b>
<span>便</span>
<small> · </small>
</article>
<article style={{ '--slide-i': 2 } as CSSProperties}>
<b></b>
<span></span>
<small> · </small>
</article>
</div>
</section>
<section className="page-section poetic-section qingxiang-section" id="qingxiang">
<div className="section-heading">
<img className="ornament haoke-mark" src="/assets/haoke-logo.png" alt="杜康好客" />
<span className="eyebrow"></span>
<h2></h2>
<p>西</p>
</div>
<div className="data-band">
<div>
<strong>1000亿</strong>
<span>202413%350亿</span>
<small>2025</small>
</div>
<div>
<strong>45.6%</strong>
<span>202545.6%43.04%41.76%</span>
<small></small>
</div>
</div>
<div className="feature-layout">
<div className="position-grid">
{qingxiangPositions.map((item) => (
<article key={item.title}>
<h3>{item.title}</h3>
<p>{item.text}</p>
</article>
))}
</div>
<div className="craft-card">
<h3> · · </h3>
<p></p>
<p className="craft-extra">7321</p>
<div className="tasting-detail">
{tastingSteps.map((step) => (
<div key={step.title}>
<strong>{step.title}</strong>
<p>{step.text}</p>
</div>
))}
</div>
<p className="mission-line"></p>
<p className="product-tag"> · T30</p>
</div>
</div>
<figure className="store-figure">
<img src="/assets/store-preview.png" alt="杜康清香酒文馆效果示意" />
<figcaption> · </figcaption>
</figure>
<p className="footnote"></p>
</section>
<section className="page-section poetic-section haoke-section" id="haoke">
<div className="section-heading centered">
<span className="eyebrow"></span>
<h2></h2>
<p>广 + </p>
<p className="mode-line"></p>
</div>
<div className="three-columns">
<article>
<span>01</span>
<h3></h3>
<p></p>
</article>
<article>
<span>02</span>
<h3>线线</h3>
<p></p>
</article>
<article>
<span>03</span>
<h3></h3>
<p></p>
</article>
</div>
<p className="closing-line"></p>
<div className="section-cta">
<a className="primary-button" href="#system"></a>
</div>
</section>
<section className="page-section poetic-section system-section" id="system">
<div className="section-heading">
<span className="eyebrow"></span>
<h2> · · </h2>
<p></p>
</div>
<div className="system-grid">
{systemEnds.map((item) => (
<article key={item.title}>
<h3>{item.title}</h3>
<p>{item.role}</p>
<small>{item.caps}</small>
</article>
))}
</div>
<p className="loop-line"> </p>
<p className="closing-line">线</p>
<div className="section-cta">
<a className="primary-button" href="#tech"></a>
</div>
</section>
<section className="page-section tech-section tech-drive-section" id="tech">
<div className="tech-orbit" />
<div className="section-heading dark-heading">
<span className="eyebrow"></span>
<h2></h2>
<p>线</p>
</div>
<div className="tech-grid">
{techPoints.map((item) => (
<article key={item.title}>
<span>{item.title}</span>
<p>{item.text}</p>
</article>
))}
</div>
<p className="industry-claim">穿</p>
<div className="section-cta">
<a className="primary-button tech-button" href="#tech-detail"></a>
</div>
</section>
<section className="page-section tech-section subsidy-section" id="subsidy">
<div className="section-heading dark-heading centered">
<span className="eyebrow"></span>
<h2>亿</h2>
<p className="sub-title"> · </p>
<p></p>
</div>
<div className="subsidy-number reveal-number">亿</div>
<p className="number-desc">亿</p>
<div className="subsidy-points">
<article>
<h3></h3>
<p></p>
</article>
<article>
<h3></h3>
<p></p>
</article>
<article>
<h3></h3>
<p></p>
</article>
</div>
<p className="closing-line dark-closing"></p>
<p className="footnote dark-footnote"></p>
</section>
<section className="page-section tech-section partner-section" id="partner">
<div className="section-heading dark-heading">
<span className="eyebrow"></span>
<h2></h2>
<p> + </p>
</div>
<div className="partner-detail-grid">
<article><h3></h3><p> + + 亿</p></article>
<article><h3></h3><p></p></article>
<article><h3></h3><p></p></article>
<article><h3></h3><p>/ · · </p></article>
<article className="span-2"><h3></h3><p> · · · </p></article>
</div>
<div className="hero-actions">
<a className="primary-button tech-button" href="#contact"></a>
<a className="secondary-button tech-secondary" href="#tech-detail"></a>
</div>
</section>
<section className="page-section tech-section tech-detail-section" id="tech-detail">
<div className="section-heading dark-heading centered">
<span className="eyebrow"></span>
<h2></h2>
<p></p>
</div>
<div className="ports-grid">
{techPorts.map((port) => (
<article key={port.title}>
<h3>{port.title}</h3>
<p>{port.text}</p>
</article>
))}
</div>
<div className="tech-highlights">
<span>穿</span>
<span></span>
<span></span>
</div>
<p className="closing-line dark-closing"></p>
<p className="contact-hint"> · </p>
</section>
<section className="page-section tech-section contact-section" id="contact">
<div className="section-heading dark-heading">
<span className="eyebrow"></span>
<h2> · </h2>
<p></p>
<p className="contact-role"> · </p>
<div className="contact-person">
<strong></strong>
<a className="contact-phone" href="tel:+8613073729990" aria-label="拨打吕丹电话 13073729990">
13073729990
</a>
</div>
</div>
<div className="qr-card">
<img src="/assets/qr-contact.png" alt="项目负责人微信二维码" />
<span></span>
</div>
<footer className="footer in-section">
<strong> · </strong>
<span></span>
<span>© 2026</span>
<span>ICP备2026004970号</span>
<p></p>
</footer>
</section>
</main>
{isStageMode && (
<>
<button
aria-label="上一节"
className="stage-click-zone stage-click-prev"
disabled={activeIndex <= 0}
onClick={() => goToSection(activeIndex - 1)}
type="button"
>
<span></span>
</button>
<button
aria-label="下一节"
className="stage-click-zone stage-click-next"
disabled={activeIndex >= sections.length - 1}
onClick={() => goToSection(activeIndex + 1)}
type="button"
>
<span></span>
</button>
{panelOpen ? (
<aside className="stage-panel is-open" aria-label="展控模式控制面板">
<div className="stage-status">
<span> · </span>
<div className="stage-status-right">
<b>{String(activeIndex + 1).padStart(2, '0')} / {String(sections.length).padStart(2, '0')}</b>
<button
type="button"
className="stage-collapse-btn"
onClick={() => setPanelOpen(false)}
aria-label="收起展控面板"
>
</button>
</div>
</div>
<p>{activeSection.summary}</p>
<div className="autoplay-row">
<button
type="button"
className={autoPlay ? 'is-on' : ''}
onClick={() => setAutoPlay((value) => !value)}
>
{autoPlay ? '自动播:开' : '自动播:关'}
</button>
<button type="button" onClick={() => goToSection(0)}></button>
</div>
<div className="highlight-jumps">
<button type="button" onClick={() => goToSection(2)}></button>
<button type="button" onClick={() => goToSection(7)}>亿</button>
<button type="button" onClick={() => goToSection(6)}></button>
</div>
<div className="stage-qr-grid">
<div>
<img src="/assets/qr-host.png" alt="控场码:打开展控模式" />
<span> · </span>
</div>
<div>
<img src="/assets/qr-audience.png" alt="观展码:打开官网首页" />
<span> · </span>
</div>
</div>
<div className="stage-keys"> · · · Home / End</div>
</aside>
) : (
<button
type="button"
className="stage-panel-toggle"
onClick={() => setPanelOpen(true)}
aria-label="展开展控面板"
>
</button>
)}
</>
)}
<div className={`progress-bar ${isStageMode ? 'with-label' : ''}`} aria-hidden={!isStageMode}>
<div style={{ width: `${progress}%` }} />
{isStageMode && (
<span className="progress-label">{activeSection.label}</span>
)}
</div>
{isStageMode && (
<div className="stage-dots" aria-label="展控章节导航">
{sections.map((section, index) => (
<button
aria-label={`跳转到${section.title}`}
className={index === activeIndex ? 'active' : ''}
key={section.id}
onClick={() => goToSection(index)}
type="button"
/>
))}
</div>
)}
</div>
)
}
export default App