feat: init longde website project

- Astro 静态网站
- 首页、学习产品介绍
- 等待名单表单
- 隐私政策、用户协议、支持、下载、更新日志页面
- SEO 优化 (sitemap, robots, OG)
This commit is contained in:
WangDL 2026-05-04 15:22:20 +08:00
commit bd570d5fb3
24 changed files with 6978 additions and 0 deletions

11
.env.example Normal file
View File

@ -0,0 +1,11 @@
# Environment Variables
# Site URL (for SEO and sitemap)
SITE_URL=http://localhost:4321
# Server Configuration
HOST=localhost
PORT=4321
# API endpoints (for future backend integration)
# API_URL=https://api.longde.cloud

5
.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
node_modules/
.DS_Store
.astro/
.env
dist/

93
README.md Normal file
View File

@ -0,0 +1,93 @@
# 龙德AI学习 官网
AI 学习产品的官方网站,基于 Astro + TypeScript 构建。
## 技术栈
- **框架**: Astro 5.x
- **语言**: TypeScript
- **样式**: 原生 CSSApple 风格)
## 页面结构
- `/` - 首页
- `/privacy` - 隐私政策
- `/terms` - 用户协议
- `/support` - 支持与反馈
- `/waitlist` - 等待名单
- `/changelog` - 更新日志
- `/download` - Mac 下载页
## 快速开始
### 安装依赖
```bash
npm install
```
### 启动开发服务器
```bash
npm run dev
```
访问 http://localhost:4321
### 其他命令
| 命令 | 说明 |
|------|------|
| `npm run dev` | 启动开发服务器 |
| `npm run build` | 构建生产版本 |
| `npm run preview` | 预览生产版本 |
| `npm run astro` | 运行 Astro CLI |
## 环境变量
复制 `.env.example``.env` 并根据需要修改:
```bash
cp .env.example .env
```
| 变量 | 默认值 | 说明 |
|------|--------|------|
| `SITE_URL` | http://localhost:4321 | 站点地址SEO 和 sitemap 使用)|
| `HOST` | localhost | 服务器主机 |
| `PORT` | 4321 | 服务器端口 |
## 项目结构
```
├── src/
│ ├── components/ # 组件
│ │ ├── Header.astro
│ │ ├── Footer.astro
│ │ ├── Hero.astro
│ │ ├── FeatureCard.astro
│ │ ├── CTA.astro
│ │ └── WaitlistForm.astro
│ ├── layouts/ # 布局
│ │ └── BaseLayout.astro
│ └── pages/ # 页面
│ ├── index.astro
│ ├── privacy.astro
│ ├── terms.astro
│ ├── support.astro
│ ├── waitlist.astro
│ ├── changelog.astro
│ └── download.astro
├── public/ # 静态文件
│ ├── favicon.svg
│ └── robots.txt
├── astro.config.mjs
├── tsconfig.json
└── package.json
```
## 注意事项
- 域名 `longde.cloud` 正在备案中,所有 URL 使用环境变量管理
- 等待名单表单当前为 Mock 模式,后端接入后修改 `WaitlistForm.astro`
- SEO 配置在 `BaseLayout.astro` 中统一管理

12
astro.config.mjs Normal file
View File

@ -0,0 +1,12 @@
import { defineConfig } from 'astro/config';
const host = process.env.HOST || 'localhost';
const port = process.env.PORT || '4321';
export default defineConfig({
site: process.env.SITE_URL || `http://${host}:${port}`,
server: {
host,
port: parseInt(port, 10),
},
});

5457
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

15
package.json Normal file
View File

@ -0,0 +1,15 @@
{
"name": "longde-website",
"type": "module",
"version": "0.1.0",
"scripts": {
"dev": "astro dev",
"start": "astro dev",
"build": "astro build",
"preview": "astro preview",
"astro": "astro"
},
"dependencies": {
"astro": "^5.0.0"
}
}

4
public/favicon.svg Normal file
View File

@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
<circle cx="16" cy="16" r="14" fill="none" stroke="#0071e3" stroke-width="2"/>
<path d="M10 16h12M16 10v12" stroke="#0071e3" stroke-width="2" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 261 B

4
public/robots.txt Normal file
View File

@ -0,0 +1,4 @@
User-agent: *
Allow: /
Sitemap: http://localhost:4321/sitemap.xml

60
src/components/CTA.astro Normal file
View File

@ -0,0 +1,60 @@
---
interface Props {
title: string;
description?: string;
href: string;
variant?: 'primary' | 'secondary';
}
const { title, description, href, variant = 'primary' } = Astro.props;
---
<div class="cta">
<a href={href} class:list={['button', variant]}>{title}</a>
{description && <p class="description">{description}</p>}
</div>
<style>
.cta {
text-align: center;
}
.button {
display: inline-block;
padding: 0.75rem 1.5rem;
font-size: 1rem;
font-weight: 500;
text-decoration: none;
border-radius: 8px;
transition: all 0.2s ease;
}
.button.primary {
background: var(--color-accent);
color: #ffffff;
border: none;
}
.button.primary:hover {
background: var(--color-accent-hover);
text-decoration: none;
}
.button.secondary {
background: transparent;
color: var(--color-accent);
border: 1px solid var(--color-accent);
}
.button.secondary:hover {
background: var(--color-accent);
color: #ffffff;
text-decoration: none;
}
.description {
margin-top: 0.75rem;
font-size: 0.875rem;
color: var(--color-text-secondary);
}
</style>

View File

@ -0,0 +1,52 @@
---
interface Props {
title: string;
description: string;
icon?: string;
}
const { title, description, icon } = Astro.props;
---
<div class="feature-card">
{icon && <div class="icon">{icon}</div>}
<h3 class="title">{title}</h3>
<p class="description">{description}</p>
</div>
<style>
.feature-card {
padding: 1.5rem;
background: var(--color-bg);
border: 1px solid var(--color-border);
border-radius: 12px;
transition: border-color 0.2s ease, box-shadow 0.2s ease;
}
.feature-card:hover {
border-color: var(--color-text-secondary);
}
.icon {
width: 40px;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 1rem;
font-size: 1.5rem;
}
.title {
font-size: 1.125rem;
font-weight: 600;
color: var(--color-text);
margin-bottom: 0.5rem;
}
.description {
font-size: 0.9375rem;
color: var(--color-text-secondary);
line-height: 1.6;
}
</style>

View File

@ -0,0 +1,71 @@
---
const currentYear = new Date().getFullYear();
const footerLinks = [
{ href: '/privacy', label: '隐私政策' },
{ href: '/terms', label: '用户协议' },
{ href: '/support', label: '支持与反馈' },
];
---
<footer class="footer">
<div class="footer-content">
<div class="footer-brand">
<span class="copyright">© {currentYear} 龙德AI学习</span>
</div>
<nav class="footer-links">
{footerLinks.map(link => (
<a href={link.href} class="footer-link">{link.label}</a>
))}
</nav>
</div>
</footer>
<style>
.footer {
margin-top: auto;
padding: 2rem var(--page-padding);
border-top: 1px solid var(--color-border);
background: var(--color-bg);
}
.footer-content {
max-width: var(--max-width);
margin: 0 auto;
display: flex;
flex-direction: column;
align-items: center;
gap: 1rem;
text-align: center;
}
.copyright {
font-size: 0.75rem;
color: var(--color-text-secondary);
}
.footer-links {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 1.5rem;
}
.footer-link {
font-size: 0.8125rem;
color: var(--color-text-secondary);
text-decoration: none;
}
.footer-link:hover {
color: var(--color-text);
text-decoration: none;
}
@media (min-width: 640px) {
.footer-content {
flex-direction: row;
justify-content: space-between;
}
}
</style>

View File

@ -0,0 +1,99 @@
---
const pathname = Astro.url.pathname;
const navItems = [
{ href: '/waitlist', label: '等待名单' },
{ href: '/changelog', label: '更新日志' },
{ href: '/support', label: '支持' },
];
---
<header class="header">
<nav class="nav">
<a href="/" class="logo">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="1.5"/>
<path d="M8 12h8M12 8v8" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
</svg>
<span>龙德</span>
</a>
<div class="nav-links">
{navItems.map(item => (
<a
href={item.href}
class:list={['nav-link', { active: pathname === item.href }]}
>
{item.label}
</a>
))}
</div>
</nav>
</header>
<style>
.header {
position: sticky;
top: 0;
z-index: 100;
background: rgba(255, 255, 255, 0.8);
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
border-bottom: 1px solid var(--color-border);
}
.nav {
max-width: var(--max-width);
margin: 0 auto;
padding: 0 var(--page-padding);
height: 52px;
display: flex;
align-items: center;
justify-content: space-between;
}
.logo {
display: flex;
align-items: center;
gap: 0.5rem;
font-size: 1.125rem;
font-weight: 600;
color: var(--color-text);
text-decoration: none;
}
.logo:hover {
text-decoration: none;
}
.logo svg {
color: var(--color-accent);
}
.nav-links {
display: flex;
gap: 2rem;
}
.nav-link {
font-size: 0.875rem;
color: var(--color-text-secondary);
text-decoration: none;
transition: color 0.2s ease;
}
.nav-link:hover,
.nav-link.active {
color: var(--color-text);
text-decoration: none;
}
@media (max-width: 640px) {
.nav-links {
gap: 1rem;
}
.nav-link {
font-size: 0.8125rem;
}
}
</style>

42
src/components/Hero.astro Normal file
View File

@ -0,0 +1,42 @@
---
interface Props {
title: string;
subtitle?: string;
}
const { title, subtitle } = Astro.props;
---
<section class="hero">
<div class="hero-content">
<h1 class="title">{title}</h1>
{subtitle && <p class="subtitle">{subtitle}</p>}
</div>
</section>
<style>
.hero {
padding: clamp(4rem, 10vw, 6rem) var(--page-padding);
text-align: center;
}
.hero-content {
max-width: 720px;
margin: 0 auto;
}
.title {
font-size: clamp(2rem, 6vw, 3rem);
font-weight: 600;
letter-spacing: -0.02em;
line-height: 1.1;
color: var(--color-text);
}
.subtitle {
margin-top: 1rem;
font-size: clamp(1rem, 2.5vw, 1.25rem);
color: var(--color-text-secondary);
line-height: 1.6;
}
</style>

View File

@ -0,0 +1,254 @@
---
interface Props {
title?: string;
description?: string;
}
const {
title = "加入等待名单",
description = "留下你的邮箱,我们会第一时间通知你"
} = Astro.props;
---
<div class="waitlist-form-container">
<form id="waitlist-form" class="waitlist-form">
<div class="form-header">
<h2 class="title">{title}</h2>
<p class="description">{description}</p>
</div>
<div class="form-fields">
<div class="field">
<label for="nickname">昵称(可选)</label>
<input type="text" id="nickname" name="nickname" placeholder="你的昵称" />
</div>
<div class="field required">
<label for="email">邮箱</label>
<input type="email" id="email" name="email" placeholder="your@email.com" required />
</div>
<div class="field">
<label for="device">使用设备</label>
<select id="device" name="device">
<option value="">请选择</option>
<option value="iphone">iPhone</option>
<option value="android">Android</option>
<option value="ipad">iPad</option>
<option value="mac">Mac</option>
</select>
</div>
<div class="field">
<label for="interest">感兴趣的方向</label>
<select id="interest" name="interest">
<option value="">请选择</option>
<option value="gongkai">公考申论 AI 学习教练</option>
<option value="ai-tools">AI 工具学习知识库</option>
<option value="frontend-interview">程序员前端面试学习助手</option>
<option value="other">其他</option>
</select>
</div>
<div class="field">
<label for="pain-point">当前最大痛点</label>
<textarea id="pain-point" name="pain-point" rows="3" placeholder="描述你当前学习中遇到的最大困难..."></textarea>
</div>
<div class="field checkbox-field">
<label class="checkbox-label">
<input type="checkbox" name="beta" value="yes" />
<span>愿意参加内测</span>
</label>
</div>
<div class="field checkbox-field">
<label class="checkbox-label">
<input type="checkbox" name="notify" value="yes" checked />
<span>接受后续邮件通知</span>
</label>
</div>
</div>
<button type="submit" class="submit-button">提交</button>
</form>
<div id="success-message" class="success-message" hidden>
<div class="success-icon">✓</div>
<h3>提交成功!</h3>
<p>感谢你的关注,我们会在产品上线后第一时间通知你。</p>
</div>
</div>
<script>
const form = document.getElementById('waitlist-form') as HTMLFormElement;
const successMessage = document.getElementById('success-message');
form?.addEventListener('submit', (e) => {
e.preventDefault();
// Mock successful submission
form.hidden = true;
if (successMessage) {
successMessage.hidden = false;
}
});
</script>
<style>
.waitlist-form-container {
max-width: 480px;
margin: 0 auto;
padding: 2rem;
background: var(--color-bg-secondary);
border-radius: 16px;
}
.form-header {
text-align: center;
margin-bottom: 2rem;
}
.title {
font-size: 1.5rem;
font-weight: 600;
color: var(--color-text);
}
.description {
margin-top: 0.5rem;
font-size: 0.9375rem;
color: var(--color-text-secondary);
}
.form-fields {
display: flex;
flex-direction: column;
gap: 1.25rem;
}
.field {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.field.required label::after {
content: " *";
color: var(--color-text-secondary);
}
label {
font-size: 0.875rem;
font-weight: 500;
color: var(--color-text);
}
input[type="text"],
input[type="email"],
select,
textarea {
padding: 0.75rem 1rem;
font-size: 1rem;
font-family: inherit;
color: var(--color-text);
background: var(--color-bg);
border: 1px solid var(--color-border);
border-radius: 8px;
transition: border-color 0.2s ease, box-shadow 0.2s ease;
}
input:focus,
select:focus,
textarea:focus {
outline: none;
border-color: var(--color-accent);
box-shadow: 0 0 0 3px rgba(0, 113, 227, 0.1);
}
select {
cursor: pointer;
appearance: none;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%2386868b' d='M6 8L1 3h10z'/%3E%3C/svg%3E");
background-repeat: no-repeat;
background-position: right 1rem center;
padding-right: 2.5rem;
}
textarea {
resize: vertical;
min-height: 80px;
}
.checkbox-field {
flex-direction: row;
}
.checkbox-label {
display: flex;
align-items: center;
gap: 0.5rem;
cursor: pointer;
font-weight: 400;
}
.checkbox-label input[type="checkbox"] {
width: 18px;
height: 18px;
cursor: pointer;
accent-color: var(--color-accent);
}
.submit-button {
margin-top: 1.5rem;
width: 100%;
padding: 0.875rem 1.5rem;
font-size: 1rem;
font-weight: 500;
color: #ffffff;
background: var(--color-accent);
border: none;
border-radius: 8px;
cursor: pointer;
transition: background 0.2s ease;
}
.submit-button:hover {
background: var(--color-accent-hover);
}
.submit-button:disabled {
opacity: 0.6;
cursor: not-allowed;
}
.success-message {
text-align: center;
padding: 2rem;
}
.success-icon {
width: 48px;
height: 48px;
margin: 0 auto 1rem;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.5rem;
color: #ffffff;
background: #34c759;
border-radius: 50%;
}
.success-message h3 {
font-size: 1.25rem;
font-weight: 600;
color: var(--color-text);
margin-bottom: 0.5rem;
}
.success-message p {
font-size: 0.9375rem;
color: var(--color-text-secondary);
}
</style>

View File

@ -0,0 +1,107 @@
---
interface Props {
title: string;
description: string;
ogImage?: string;
}
const { title, description, ogImage } = Astro.props;
const siteUrl = import.meta.env.SITE_URL || 'http://localhost:4321';
const ogImageUrl = ogImage || `${siteUrl}/og-default.png`;
---
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>{title}</title>
<meta name="description" content={description} />
<meta name="robots" content="index, follow" />
<link rel="canonical" href={Astro.url.href} />
<meta property="og:type" content="website" />
<meta property="og:title" content={title} />
<meta property="og:description" content={description} />
<meta property="og:url" content={Astro.url.href} />
<meta property="og:image" content={ogImageUrl} />
<meta property="og:site_name" content="龙德AI学习" />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content={title} />
<meta name="twitter:description" content={description} />
<meta name="twitter:image" content={ogImageUrl} />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
</head>
<body>
<slot />
</body>
</html>
<style is:global>
*, *::before, *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
:root {
--color-bg: #ffffff;
--color-bg-secondary: #f5f5f7;
--color-text: #1d1d1f;
--color-text-secondary: #86868b;
--color-accent: #0071e3;
--color-accent-hover: #0077ed;
--color-border: #d2d2d7;
--font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text", "SF Pro Display", "Helvetica Neue", Helvetica, Arial, sans-serif;
--font-mono: ui-monospace, SFMono-Regular, "SF Mono", monospace;
--max-width: 980px;
--page-padding: clamp(1rem, 5vw, 2rem);
}
html {
font-family: var(--font-family);
font-size: 16px;
line-height: 1.5;
color: var(--color-text);
background: var(--color-bg);
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
scroll-behavior: smooth;
}
body {
min-height: 100vh;
display: flex;
flex-direction: column;
}
main {
flex: 1;
}
a {
color: var(--color-accent);
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
h1, h2, h3, h4, h5, h6 {
line-height: 1.2;
font-weight: 600;
}
img {
max-width: 100%;
height: auto;
}
button {
cursor: pointer;
font-family: inherit;
font-size: inherit;
}
</style>

125
src/pages/changelog.astro Normal file
View File

@ -0,0 +1,125 @@
---
import BaseLayout from '../layouts/BaseLayout.astro';
import Header from '../components/Header.astro';
import Footer from '../components/Footer.astro';
import Hero from '../components/Hero.astro';
const changelog = [
{
version: 'v0.1.0',
date: '2024年5月4日',
changes: ['等待名单页面上线', '基础官网框架搭建'],
status: 'planning'
}
];
---
<BaseLayout
title="更新日志 - 龙德AI学习"
description="龙德AI学习的最新更新和开发进度。"
>
<Header />
<main>
<Hero
title="更新日志"
subtitle="产品开发进度与更新记录"
/>
<section class="changelog-section">
<div class="changelog-container">
{changelog.map(item => (
<div class="changelog-item">
<div class="version-header">
<span class="version">{item.version}</span>
<span class="date">{item.date}</span>
<span class:list={['status', item.status]}>{item.status === 'planning' ? '计划中' : item.status}</span>
</div>
<ul class="changes">
{item.changes.map(change => (
<li>{change}</li>
))}
</ul>
</div>
))}
</div>
</section>
</main>
<Footer />
</BaseLayout>
<style>
.changelog-section {
padding: 0 var(--page-padding) 4rem;
}
.changelog-container {
max-width: 720px;
margin: 0 auto;
display: flex;
flex-direction: column;
gap: 2rem;
}
.changelog-item {
padding: 1.5rem;
background: var(--color-bg-secondary);
border-radius: 12px;
}
.version-header {
display: flex;
align-items: center;
gap: 1rem;
margin-bottom: 1rem;
flex-wrap: wrap;
}
.version {
font-size: 1.125rem;
font-weight: 600;
color: var(--color-text);
}
.date {
font-size: 0.875rem;
color: var(--color-text-secondary);
}
.status {
font-size: 0.75rem;
padding: 0.25rem 0.75rem;
border-radius: 4px;
font-weight: 500;
}
.status.planning {
background: #e8f4fd;
color: #0071e3;
}
.status.current {
background: #d4edda;
color: #28a745;
}
.changes {
list-style: none;
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.changes li {
font-size: 0.9375rem;
color: var(--color-text-secondary);
padding-left: 1.25rem;
position: relative;
}
.changes li::before {
content: "•";
position: absolute;
left: 0;
color: var(--color-text-secondary);
}
</style>

92
src/pages/download.astro Normal file
View File

@ -0,0 +1,92 @@
---
import BaseLayout from '../layouts/BaseLayout.astro';
import Header from '../components/Header.astro';
import Footer from '../components/Footer.astro';
import Hero from '../components/Hero.astro';
import CTA from '../components/CTA.astro';
---
<BaseLayout
title="下载 - 龙德AI学习"
description="龙德AI学习 Mac 版下载,敬请期待。"
>
<Header />
<main>
<Hero
title="Mac 应用"
subtitle="龙德AI学习 Mac 版正在开发中"
/>
<section class="download-section">
<div class="download-card">
<div class="icon">
<svg width="64" height="64" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="8" y="8" width="48" height="48" rx="8" stroke="currentColor" stroke-width="2"/>
<path d="M24 28c0-4.418 3.582-8 8-8s8 3.582 8 8" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
<circle cx="32" cy="38" r="4" fill="currentColor"/>
</svg>
</div>
<h2>即将上线</h2>
<p>Mac 版应用正在积极开发中,请先加入等待名单获取最新消息。</p>
<div class="version-info">
<span>支持 macOS 12+</span>
</div>
<CTA
title="加入等待名单"
href="/waitlist"
variant="primary"
/>
</div>
</section>
</main>
<Footer />
</BaseLayout>
<style>
.download-section {
padding: 0 var(--page-padding) 4rem;
}
.download-card {
max-width: 400px;
margin: 0 auto;
padding: 3rem 2rem;
background: var(--color-bg-secondary);
border-radius: 16px;
text-align: center;
}
.icon {
color: var(--color-text-secondary);
margin-bottom: 1.5rem;
}
.download-card h2 {
font-size: 1.5rem;
font-weight: 600;
color: var(--color-text);
margin-bottom: 0.75rem;
}
.download-card p {
font-size: 0.9375rem;
color: var(--color-text-secondary);
line-height: 1.6;
margin-bottom: 1.5rem;
}
.version-info {
display: flex;
justify-content: center;
gap: 1rem;
margin-bottom: 2rem;
}
.version-info span {
font-size: 0.8125rem;
color: var(--color-text-secondary);
padding: 0.375rem 0.875rem;
background: var(--color-bg);
border-radius: 4px;
}
</style>

70
src/pages/index.astro Normal file
View File

@ -0,0 +1,70 @@
---
import BaseLayout from '../layouts/BaseLayout.astro';
import Header from '../components/Header.astro';
import Footer from '../components/Footer.astro';
import Hero from '../components/Hero.astro';
import FeatureCard from '../components/FeatureCard.astro';
import CTA from '../components/CTA.astro';
---
<BaseLayout
title="龙德AI学习 - 智能学习助手"
description="龙德AI学习专注公考申论、AI工具学习、前端面试的智能学习助手让学习更高效。"
>
<Header />
<main>
<Hero
title="让学习更高效"
subtitle="智能学习助手专注于公考申论、AI工具学习、前端面试等领域"
/>
<section class="features">
<div class="features-container">
<FeatureCard
title="公考申论 AI 教练"
description="基于 AI 的申论批改与反馈,帮你找到提升空间,提高考试成绩。"
icon="📝"
/>
<FeatureCard
title="AI 工具知识库"
description="系统整理 AI 工具使用技巧,让你在工作和学习中快人一步。"
icon="🛠️"
/>
<FeatureCard
title="前端面试助手"
description="针对程序员的前端面试准备工具,模拟真实面试场景。"
icon="💻"
/>
</div>
</section>
<section class="cta-section">
<CTA
title="加入等待名单"
description="抢先体验更多功能"
href="/waitlist"
variant="primary"
/>
</section>
</main>
<Footer />
</BaseLayout>
<style>
.features {
padding: 4rem var(--page-padding);
}
.features-container {
max-width: var(--max-width);
margin: 0 auto;
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 1.5rem;
}
.cta-section {
padding: 2rem var(--page-padding) 4rem;
text-align: center;
}
</style>

94
src/pages/privacy.astro Normal file
View File

@ -0,0 +1,94 @@
---
import BaseLayout from '../layouts/BaseLayout.astro';
import Header from '../components/Header.astro';
import Footer from '../components/Footer.astro';
import Hero from '../components/Hero.astro';
const lastUpdated = '2024年5月4日';
---
<BaseLayout
title="隐私政策 - 龙德AI学习"
description="龙德AI学习的隐私政策说明我们如何收集、使用和保护您的个人信息。"
>
<Header />
<main>
<Hero
title="隐私政策"
subtitle={`最后更新:${lastUpdated}`}
/>
<article class="content">
<div class="content-inner">
<section class="section">
<h2>信息收集</h2>
<p>我们收集您主动提供的信息,包括但不限于:昵称、邮箱地址、设备类型、使用偏好等。这些信息仅用于改进我们的服务和向您提供相关更新。</p>
</section>
<section class="section">
<h2>信息使用</h2>
<p>我们使用收集的信息用于:提供和维护服务、发送产品更新和通知、改进产品功能、分析服务使用情况。您可以随时选择退出邮件通知。</p>
</section>
<section class="section">
<h2>信息保护</h2>
<p>我们采取合理的安全措施保护您的个人信息防止未经授权的访问、使用或泄露。但互联网传输无法保证100%安全,我们会持续努力保护您的数据。</p>
</section>
<section class="section">
<h2>Cookie 使用</h2>
<p>我们的网站可能使用 Cookie 来改善用户体验。您可以通过浏览器设置禁用 Cookie但这可能影响部分功能。</p>
</section>
<section class="section">
<h2>第三方服务</h2>
<p>我们可能使用第三方服务提供商来辅助运营服务,他们会按照各自的隐私政策处理您的信息。</p>
</section>
<section class="section">
<h2>用户权利</h2>
<p>您有权访问、更正或删除您的个人信息。如有任何疑问,请通过支持页面联系我们。</p>
</section>
<section class="section">
<h2>政策更新</h2>
<p>我们可能会不时更新本隐私政策。更新后我们会通过网站公告或邮件通知您。</p>
</section>
<section class="section">
<h2>联系我们</h2>
<p>如对本隐私政策有任何疑问,请访问我们的 <a href="/support">支持页面</a>。</p>
</section>
</div>
</article>
</main>
<Footer />
</BaseLayout>
<style>
.content {
padding: 0 var(--page-padding) 4rem;
}
.content-inner {
max-width: 720px;
margin: 0 auto;
}
.section {
margin-bottom: 2rem;
}
.section h2 {
font-size: 1.25rem;
font-weight: 600;
color: var(--color-text);
margin-bottom: 0.75rem;
}
.section p {
font-size: 0.9375rem;
color: var(--color-text-secondary);
line-height: 1.7;
}
</style>

View File

@ -0,0 +1,29 @@
---
import BaseLayout from '../layouts/BaseLayout.astro';
import Header from '../components/Header.astro';
import Footer from '../components/Footer.astro';
const siteUrl = import.meta.env.SITE_URL || 'http://localhost:4321';
const pages = [
{ url: '/', priority: '1.0' },
{ url: '/privacy', priority: '0.8' },
{ url: '/terms', priority: '0.8' },
{ url: '/support', priority: '0.8' },
{ url: '/waitlist', priority: '0.9' },
{ url: '/changelog', priority: '0.6' },
{ url: '/download', priority: '0.7' },
];
const today = new Date().toISOString().split('T')[0];
---
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
{pages.map(page => (
<url>
<loc>{siteUrl}{page.url}</loc>
<lastmod>{today}</lastmod>
<changefreq>weekly</changefreq>
<priority>{page.priority}</priority>
</url>
))}
</urlset>

151
src/pages/support.astro Normal file
View File

@ -0,0 +1,151 @@
---
import BaseLayout from '../layouts/BaseLayout.astro';
import Header from '../components/Header.astro';
import Footer from '../components/Footer.astro';
import Hero from '../components/Hero.astro';
---
<BaseLayout
title="支持与反馈 - 龙德AI学习"
description="获取龙德AI学习的帮助与支持反馈问题或建议。"
>
<Header />
<main>
<Hero
title="支持与反馈"
subtitle="我们随时为您服务"
/>
<section class="content">
<div class="content-inner">
<div class="support-card">
<div class="icon">💬</div>
<h2>联系我们</h2>
<p>有问题或建议?我们很乐意听到您的声音。</p>
<a href="mailto:support@longde.cloud" class="contact-link">support@longde.cloud</a>
</div>
<div class="faq-section">
<h3>常见问题</h3>
<div class="faq-list">
<details class="faq-item">
<summary>龙德AI学习是什么</summary>
<p>龙德AI学习是一个专注于公考申论、AI工具学习、前端面试的智能学习助手。</p>
</details>
<details class="faq-item">
<summary>产品什么时候上线?</summary>
<p>我们正在积极开发中,可以加入等待名单获取最新消息。</p>
</details>
<details class="faq-item">
<summary>支持哪些平台?</summary>
<p>我们计划支持 Web、iOS、Android、Mac 等平台。</p>
</details>
<details class="faq-item">
<summary>如何反馈问题?</summary>
<p>请发送邮件至 support@longde.cloud我们会尽快回复。</p>
</details>
</div>
</div>
</div>
</section>
</main>
<Footer />
</BaseLayout>
<style>
.content {
padding: 0 var(--page-padding) 4rem;
}
.content-inner {
max-width: 720px;
margin: 0 auto;
display: flex;
flex-direction: column;
gap: 3rem;
}
.support-card {
text-align: center;
padding: 3rem 2rem;
background: var(--color-bg-secondary);
border-radius: 16px;
}
.icon {
font-size: 3rem;
margin-bottom: 1rem;
}
.support-card h2 {
font-size: 1.5rem;
font-weight: 600;
color: var(--color-text);
margin-bottom: 0.5rem;
}
.support-card p {
font-size: 0.9375rem;
color: var(--color-text-secondary);
margin-bottom: 1.5rem;
}
.contact-link {
display: inline-block;
padding: 0.75rem 1.5rem;
font-size: 1rem;
font-weight: 500;
color: var(--color-accent);
background: var(--color-bg);
border: 1px solid var(--color-accent);
border-radius: 8px;
text-decoration: none;
transition: all 0.2s ease;
}
.contact-link:hover {
background: var(--color-accent);
color: #ffffff;
text-decoration: none;
}
.faq-section h3 {
font-size: 1.25rem;
font-weight: 600;
color: var(--color-text);
margin-bottom: 1.5rem;
}
.faq-list {
display: flex;
flex-direction: column;
gap: 0.75rem;
}
.faq-item {
padding: 1rem 1.25rem;
background: var(--color-bg-secondary);
border-radius: 8px;
cursor: pointer;
}
.faq-item summary {
font-weight: 500;
color: var(--color-text);
list-style: none;
}
.faq-item summary::-webkit-details-marker {
display: none;
}
.faq-item[open] summary {
margin-bottom: 0.75rem;
}
.faq-item p {
font-size: 0.9375rem;
color: var(--color-text-secondary);
line-height: 1.6;
}
</style>

94
src/pages/terms.astro Normal file
View File

@ -0,0 +1,94 @@
---
import BaseLayout from '../layouts/BaseLayout.astro';
import Header from '../components/Header.astro';
import Footer from '../components/Footer.astro';
import Hero from '../components/Hero.astro';
const lastUpdated = '2024年5月4日';
---
<BaseLayout
title="用户协议 - 龙德AI学习"
description="龙德AI学习的用户协议明确用户与服务商之间的权利与义务。"
>
<Header />
<main>
<Hero
title="用户协议"
subtitle={`最后更新:${lastUpdated}`}
/>
<article class="content">
<div class="content-inner">
<section class="section">
<h2>服务说明</h2>
<p>龙德AI学习是一个 AI 驱动的学习辅助平台,旨在帮助用户提升学习效率。用户使用我们的服务即表示同意本协议的所有条款。</p>
</section>
<section class="section">
<h2>用户账户</h2>
<p>您需要保证提供给我们的信息真实、准确、完整。您有责任妥善保管账户信息,因账户被盗用造成的损失由您自行承担。</p>
</section>
<section class="section">
<h2>使用规范</h2>
<p>您同意不会利用服务从事任何违法活动,不发布危害国家安全、侵犯他人权益的内容。我们有权对违规行为采取相应措施。</p>
</section>
<section class="section">
<h2>知识产权</h2>
<p>服务中的内容、代码、界面设计等知识产权归我们所有。未经授权,用户不得复制、修改或传播。</p>
</section>
<section class="section">
<h2>服务变更</h2>
<p>我们保留随时修改或暂停服务的权利,并会提前通知用户。因服务变更造成的损失,我们不承担责任。</p>
</section>
<section class="section">
<h2>免责声明</h2>
<p>服务按「现状」提供,我们不对服务的准确性、完整性、可靠性做任何承诺。用户需自行承担使用风险。</p>
</section>
<section class="section">
<h2>争议解决</h2>
<p>本协议的解释和执行均适用中华人民共和国法律。如有争议,双方应协商解决,协商不成的可向法院提起诉讼。</p>
</section>
<section class="section">
<h2>联系我们</h2>
<p>如对用户协议有任何疑问,请访问我们的 <a href="/support">支持页面</a>。</p>
</section>
</div>
</article>
</main>
<Footer />
</BaseLayout>
<style>
.content {
padding: 0 var(--page-padding) 4rem;
}
.content-inner {
max-width: 720px;
margin: 0 auto;
}
.section {
margin-bottom: 2rem;
}
.section h2 {
font-size: 1.25rem;
font-weight: 600;
color: var(--color-text);
margin-bottom: 0.75rem;
}
.section p {
font-size: 0.9375rem;
color: var(--color-text-secondary);
line-height: 1.7;
}
</style>

31
src/pages/waitlist.astro Normal file
View File

@ -0,0 +1,31 @@
---
import BaseLayout from '../layouts/BaseLayout.astro';
import Header from '../components/Header.astro';
import Footer from '../components/Footer.astro';
import Hero from '../components/Hero.astro';
import WaitlistForm from '../components/WaitlistForm.astro';
---
<BaseLayout
title="等待名单 - 龙德AI学习"
description="加入龙德AI学习等待名单抢先体验获取最新产品资讯和内测资格。"
>
<Header />
<main>
<Hero
title="加入等待名单"
subtitle="留下你的信息,我们会第一时间通知你"
/>
<section class="waitlist-section">
<WaitlistForm />
</section>
</main>
<Footer />
</BaseLayout>
<style>
.waitlist-section {
padding: 0 var(--page-padding) 4rem;
}
</style>

6
tsconfig.json Normal file
View File

@ -0,0 +1,6 @@
{
"extends": "astro/tsconfigs/strict",
"compilerOptions": {
"strictNullChecks": true
}
}