Files
Endoleap/src/components/landing-page.tsx
T

80 lines
11 KiB
TypeScript
Raw 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.
/**
* 公开落地页的客户端展示组件。包含品牌导航、可点击的创作示意图、功能说明、工作方式、FAQ与登录/演示入口。
* 示意卡片只改变本页高亮,不创建真实画布元素;本模块不读取账号、不调用编辑器,也不持久化状态。
*/
"use client";
import { useState } from "react";
import Link from "next/link";
import { ArrowUpRight, ArrowRight, ChevronDown, FileText, Image as ImageIcon, Layers, MousePointer2, Plus, Sparkles, Type } from "lucide-react";
import { Brand } from "@/components/brand";
import styles from "./landing-page.module.css";
/** 三个示意卡片对应的页脚说明;数组索引与active及预览按钮一一对应。 */
const cards = ["先留住一个念头", "让不同的线索相遇", "慢慢成为你的作品"];
/** FAQ静态问答数据;这里只呈现文案,访问权限与演示存储的实际执行在对应业务模块中。 */
const questions = [
["还没想好要做什么,也可以开始吗?", "当然。一个词、一张参考图、一段还不成形的想法,都可以成为起点。先放下来,再慢慢找到它们之间的联系。"],
["我可以放些什么在画布里?", "文字、笔记、图片、脚本和图标。你可以自由移动、调整样式、用线连接,也可以用分区把相关内容放在一起。"],
["我的创作会自动公开吗?", "不会。工作空间需要登录访问,内容的访问范围由空间和材料权限控制。"],
["可以先体验,再注册吗?", "可以。点击「先逛逛工作台」进入本地演示。演示内容保留在当前浏览器,登录后可以建立自己的工作空间。"],
];
/** 本页只有两个交互状态:预览卡片单选,以及最多展开一个问题的FAQ。
* 导航锚点定位本页三个section;登录入口进入/login,本地演示入口携带demo=1进入工作台。
* 主体的SVG连线、花朵、光标和功能插图是展示图形,不接入画布命中、拖动或同步链路。 */
export default function LandingPage() {
// 初始选中第一张预览卡;三张卡片与底部三个圆点均直接设置同一个索引,aria-pressed同步表达选择。
const [active, setActive] = useState(0);
// null表示全部收起;点击当前问题置null,点击其它问题改索引。hidden与aria-expanded/controls对应同一状态。
const [faq, setFaq] = useState<number | null>(null);
// 页面顺序:导航→首屏交互预览→理念横条→三个功能说明→工作步骤→FAQ→行动入口→页脚。
// 功能卡与步骤通过静态内容展示;FAQ以问题文本作key,步骤以序号作key,预览以说明作key。
// 状态仅在本次组件挂载期间保留;切换预览不自动轮播,FAQ展开不发网络请求。
return <div className="landing"><div className={styles.page}>
<header className={styles.nav}>
<Brand />
<nav aria-label="主导航"><a href="#space">创作空间</a><a href="#workflow">工作方式</a><a href="#questions">一些问题</a></nav>
<div className={styles.navActions}><Link href="/login">登录</Link><Link href="/login" className={styles.smallCta}>开始创作 <ArrowUpRight size={15} /></Link></div>
</header>
<main>
<section className={styles.hero}>
<div className={styles.heroCopy}>
<div className={styles.eyebrow}><span className={styles.statusDot} /> A LITTLE SPACE. ENDLESS POSSIBILITIES.</div>
<h1>给想法一点<br /><span>自由生长</span>的空间。</h1>
<p>不必一开始就井井有条。<br />把零散的灵感放下来,连接起来,<br className={styles.mobileBreak} />慢慢做成自己的作品。</p>
<div className={styles.heroActions}><Link href="/login" className={styles.primary}>打开你的创作空间 <ArrowUpRight size={18} /></Link><Link href="/dashboard?demo=1" className={styles.textLink}>先逛逛工作台 <ArrowRight size={16} /></Link></div>
<div className={styles.heroNote}><span className={styles.scribbleStar}>✳</span> 留一点空白,给还没发生的好想法。</div>
</div>
<div className={styles.preview} aria-label="创作空间交互预览">
<div className={styles.previewHeading}><span><span className={styles.tinyLogo}>↗</span> 一些还不错的想法 <ChevronDown size={11} /></span><span>PRIVATE SPACE</span></div>
<div className={styles.scene}>
<svg className={styles.wires} viewBox="0 0 600 440" fill="none" aria-hidden="true"><path d="M250 177 C335 177 315 137 393 137" /><path d="M416 220 C416 300 255 290 255 336" /><circle cx="250" cy="177" r="3" /><circle cx="393" cy="137" r="3" /><circle cx="416" cy="220" r="3" /><circle cx="255" cy="336" r="3" /></svg>
<button className={`${styles.ideaCard} ${active === 0 ? styles.activeCard : ""}`} aria-pressed={active === 0} onClick={() => setActive(0)}><span className={styles.cardLabel}><FileText size={12} /> 随手记下</span><strong>如果灵感<br />有自己的形状?</strong><span className={styles.cardText}>也许从一片叶子,<br />或一个突然冒出来的问题开始。</span><span className={styles.paperCorner} /></button>
<button className={`${styles.artCard} ${active === 1 ? styles.activeCard : ""}`} aria-pressed={active === 1} onClick={() => setActive(1)}><span className={styles.cardLabel}><ImageIcon size={12} /> 一点视觉线索</span><svg viewBox="0 0 180 200" aria-hidden="true"><path d="M92 164C89 133 94 111 94 88M94 125C72 125 59 110 57 94C78 91 90 105 94 125M94 143C118 143 134 129 137 110C112 107 96 123 94 143" /><path d="M93 88C47 109 32 78 60 62C35 32 67 11 87 43C96 7 130 18 116 50C151 37 168 66 132 79C158 104 119 121 101 89" /><circle cx="95" cy="68" r="13" /><path d="M67 173C85 168 108 170 126 175" /></svg><span className={styles.artCaption}>ordinary things, new eyes.</span></button>
<button className={`${styles.noteCard} ${active === 2 ? styles.activeCard : ""}`} aria-pressed={active === 2} onClick={() => setActive(2)}><span className={styles.cardLabel}><Sparkles size={12} /> 连接之后</span><strong>把日常,<br />做成一点不日常。</strong><span className={styles.noteTag}>下一个小作品 ↗</span></button>
<span className={styles.looseNote}>keep wondering.</span><div className={styles.cursor} aria-hidden="true"><MousePointer2 size={21} fill="currentColor" /><span>你</span></div>
</div>
<div className={styles.previewFooter}><div aria-label="预览选择">{cards.map((label, index) => <button key={label} aria-label={label} aria-pressed={active === index} onClick={() => setActive(index)} />)}</div><span>{cards[active]}</span><span>100%</span></div>
</div>
</section>
<div className={styles.manifestoStrip}><span>FOR THE CURIOUS MINDS.</span><p>认真生活的人,<span>总有一些想法值得留下。</span></p><span>COLLECT / CONNECT / CREATE</span></div>
<section id="space" className={styles.space}>
<div className={styles.sectionHeading}><div><div className={styles.eyebrow}>01 — YOUR THINKING SPACE</div><h2>思路可以自由,<br />也可以有迹可循。</h2></div><p>从随手收集,到看见联系。<br />把整理变成创作的一部分。</p></div>
<div className={styles.featureGrid}>
<article><div className={`${styles.featureArt} ${styles.collectArt}`} aria-hidden="true"><span><Type size={18} /> 一句还没写完的话</span><span><ImageIcon size={18} /> 某个让你停下来的画面</span><span><Sparkles size={18} /> 一点突如其来的好奇心</span></div><span className={styles.featureNumber}>01 / COLLECT</span><h3>先留下,再想一想。</h3><p>文字、图片、参考和碎片。灵感来的时候,<br />不需要先决定它应该放在哪个文件夹。</p></article>
<article><div className={`${styles.featureArt} ${styles.connectArt}`} aria-hidden="true"><svg viewBox="0 0 320 170" fill="none"><path d="M68 57C150 57 130 115 220 115M220 115C240 115 250 60 280 60" /></svg><span>一个念头</span><span>另一种可能</span><i>✳</i></div><span className={styles.featureNumber}>02 / CONNECT</span><h3>让线索,自己说话。</h3><p>在无限画布上自由摆放、连接与分组。<br />原本无关的东西,也许正好能碰出火花。</p></article>
<article><div className={`${styles.featureArt} ${styles.createArt}`} aria-hidden="true"><div><span>UNTITLED PROJECT</span><strong>想法,有形。</strong><span>文字 × 图像 × 你自己的表达</span></div><span className={styles.editBadge}><Layers size={13} /> 持续打磨中</span></div><span className={styles.featureNumber}>03 / CREATE</span><h3>慢慢变成,你的作品。</h3><p>调整文字、图标和画面细节。<br />让收集的每一点灵感,都有继续生长的可能。</p></article>
</div>
</section>
<section id="workflow" className={styles.workflow}>
<div className={styles.quote}><span className={styles.quoteMark}>“</span><h2>不急着有答案。<br />先和自己的想法,<em>待一会儿。</em></h2><p>一个属于你的工作空间,<br />也是一张可以反复涂改的草稿纸。</p><span className={styles.signature}>a work in progress, always.</span></div>
<div className={styles.workflowList}>{[["01", "带着一点好奇,开始。", "从一张空白画布,或一个还没想好的念头出发。"], ["02", "把线索,放在一起。", "拖动、连接、分区,让自己的思路慢慢清晰。"], ["03", "继续打磨,不必着急完成。", "随时回来,加一点新想法,留下新的可能。"]].map(([number, title, copy]) => <div key={number}><span>{number}</span><div><h3>{title}</h3><p>{copy}</p></div><ArrowUpRight size={18} /></div>)}</div>
</section>
<section id="questions" className={styles.faq}><div><div className={styles.eyebrow}>A FEW THINGS TO KNOW</div><h2>开始之前,<br />你也许想知道。</h2></div><div>{questions.map(([question, answer], index) => <div className={styles.faqItem} key={question}><button aria-expanded={faq === index} aria-controls={`landing-faq-${index}`} onClick={() => setFaq(faq === index ? null : index)}><span>{question}</span><Plus size={17} style={{ rotate: faq === index ? "45deg" : "0deg" }} /></button><div id={`landing-faq-${index}`} hidden={faq !== index}><p>{answer}</p></div></div>)}</div></section>
<section className={styles.closing}><span className={styles.closingDoodle} aria-hidden="true">✳</span><div className={styles.eyebrow}>YOUR NEXT IDEA STARTS HERE</div><h2>好想法,<br className={styles.mobileBreak} />值得一个好地方。</h2><p>不用准备好全部。带着一点好奇,先开始。</p><Link href="/login" className={styles.primary}>给想法留个位置 <ArrowUpRight size={18} /></Link></section>
</main>
<footer className={styles.footer}><Brand /><p>给想法留白,给创作空间。</p><a href="#space">回到创作空间 <ArrowUpRight size={14} /></a><span>© 2026 ENDOLEAP</span></footer>
</div></div>;
}