Compare commits

..

2 Commits

Author SHA1 Message Date
xiaoxia d2fbe78c52 Merge branch 'develop' into refactor/tts-api
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 5s
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 55s
CI/CD Pipeline / Validate - Code Quality (pull_request) Failing after 1m36s
CI/CD Pipeline / Frontend Lint (pull_request) Successful in 35s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 13s
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Successful in 1m4s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 1m7s
CI/CD Pipeline / PR Build Web Image (pull_request) Successful in 1m45s
AI Code Review / AI Code Review (pull_request) Successful in 1m24s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 41s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 3m39s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 15m26s
CI/CD Pipeline / CI Gate (pull_request) 失败: CI/CD Pipeline / Validate - Code Quality (pull_request) [frontend-only]
CI/CD Pipeline / Frontend Unit Tests (pull_request) Failing after 20s
ACR Cleanup / ACR Image Cleanup (pull_request_target) Has been cancelled
CI/CD Pipeline / Canary Release to Production (pull_request) Has been cancelled
Preview Cleanup / Cleanup Preview Environment (pull_request) Successful in 17s
CI/CD Pipeline / Production Browser E2E (pull_request) Failing after 1220h39m52s
CI/CD Pipeline / ACR Image Cleanup (pull_request) Failing after 1221h14m15s
CI/CD Pipeline / Staging API Integration Tests (pull_request) Failing after 1221h14m15s
CI/CD Pipeline / Staging E2E Tests (pull_request) Failing after 1221h14m16s
CI/CD Pipeline / Deploy Production (pull_request) Failing after 1221h14m17s
CI/CD Pipeline / Integration Tests (pull_request) Failing after 1222h59m34s
CI/CD Pipeline / Build Production Worker Image (pull_request) Failing after 1222h59m37s
CI/CD Pipeline / Build Production Web Image (pull_request) Failing after 1222h59m39s
CI/CD Pipeline / Unit Tests (pull_request) Failing after 1222h59m44s
CI/CD Pipeline / Build Staging Worker Image (pull_request) Failing after 1225h0m5s
CI/CD Pipeline / Build Staging API Image (pull_request) Failing after 1225h0m9s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Failing after 1223h32m20s
CI/CD Pipeline / Build Production API Image (pull_request) Failing after 1223h32m26s
CI/CD Pipeline / Build Staging Web Image (pull_request) Failing after 1225h32m52s
2026-07-27 12:34:59 +08:00
xiaoxia cfc58bab16 refactor(api): 拆分 tts.ts 为目录结构(types/jobs/index)
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 25s
CI/CD Pipeline / Frontend Lint (pull_request) Failing after 36s
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 1m13s
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Successful in 1m16s
CI/CD Pipeline / Validate - Code Quality (pull_request) Failing after 2m9s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 28s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 59s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 58s
CI/CD Pipeline / PR Build Web Image (pull_request) Failing after 2m13s
AI Code Review / AI Code Review (pull_request) Successful in 1m18s
Preview Deploy / Deploy Preview Environment (pull_request) Failing after 29s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 5m44s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Failing after 17s
CI/CD Pipeline / Canary Release to Production (pull_request) Has been cancelled
CI/CD Pipeline / Production Browser E2E (pull_request) Failing after 1245h15m5s
CI/CD Pipeline / ACR Image Cleanup (pull_request) Failing after 1245h40m41s
CI/CD Pipeline / Staging E2E Tests (pull_request) Failing after 1245h40m43s
CI/CD Pipeline / Deploy Production (pull_request) Failing after 1245h41m9s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Failing after 1245h59m49s
CI/CD Pipeline / Integration Tests (pull_request) Failing after 1245h59m51s
CI/CD Pipeline / Build Production Worker Image (pull_request) Failing after 1246h2m50s
CI/CD Pipeline / Build Production Web Image (pull_request) Failing after 1246h2m50s
CI/CD Pipeline / Unit Tests (pull_request) Failing after 1246h3m9s
CI/CD Pipeline / Build Staging Worker Image (pull_request) Failing after 1246h14m36s
CI/CD Pipeline / Build Staging Web Image (pull_request) Failing after 1246h14m38s
CI/CD Pipeline / Build Staging API Image (pull_request) Failing after 1246h14m38s
CI/CD Pipeline / Staging API Integration Tests (pull_request) Failing after 1246h13m23s
CI/CD Pipeline / Build Production API Image (pull_request) Failing after 1246h35m30s
将 188 行的 tts.ts 拆分为目录化结构:
- types.ts: 类型定义(TTSJob/TTSVoice/各请求响应等)
- jobs.ts: 全部 API 函数(8个)
- index.ts: 统一入口 re-export,保持 @/api/tts 路径向后兼容

主入口从 188 行减少到 31 行,按业务域清晰分层。
2026-07-26 17:04:23 +08:00
11 changed files with 479 additions and 483 deletions
-188
View File
@@ -1,188 +0,0 @@
/**
* TTS 语音合成 API
* 对接后端 /api/v1/tts/* 端点
*
* 任务 3.14 新增
*/
import apiClient from "./client"
/* ── 类型定义 ──────────────────────────────────── */
/** TTS 元数据(合成时附带的扩展信息) */
export interface TTSMetadata {
/** 语音时长(秒) */
duration?: number
/** 采样率(Hz */
sample_rate?: number
/** 语言 */
language?: string
/** 其他扩展字段 */
[key: string]: unknown
}
/** TTS 合成请求参数 */
export interface TTSSynthesizeRequest {
text: string
voice_id?: string
output_name?: string
language?: string
speed?: number
voice_model?: string
voice_clone_profile_id?: string
format?: string
metadata?: TTSMetadata
}
/** TTS 合成创建响应 */
export interface TTSSynthesizeResponse {
job_id: string
status: string
message: string
}
/** TTS 任务详情 */
export interface TTSJob {
id: string
user_id: string
project_id: string | null
text: string
voice_id: string | null
voice_model: string | null
voice_clone_profile_id: string | null
language: string
speed: number
output_name: string | null
output_audio_url: string | null
output_format: string
duration_seconds: number | null
file_size_bytes: number | null
sample_rate: number | null
status: string
error_message: string | null
retry_count: number
max_retries: number
metadata_: TTSMetadata | null
created_at: string
updated_at: string
}
/** TTS 任务状态(轻量轮询用) */
export interface TTSJobStatus {
id: string
status: string
output_audio_url: string | null
error_message: string | null
duration_seconds: number | null
retry_count: number
}
/** TTS 任务列表响应 */
export interface TTSJobListResponse {
items: TTSJob[]
total: number
skip: number
limit: number
}
/** TTS 任务列表查询参数 */
export interface TTSJobListParams {
status?: string
skip?: number
limit?: number
}
/* ── API 函数 ──────────────────────────────────── */
/** 创建 TTS 合成任务 */
export const synthesizeSpeech = async (
data: TTSSynthesizeRequest,
): Promise<TTSSynthesizeResponse> => {
const response = await apiClient.post<TTSSynthesizeResponse>("/tts/synthesize", data)
return response.data
}
/** 获取 TTS 任务详情 */
export const getTTSJob = async (jobId: string): Promise<TTSJob> => {
const response = await apiClient.get<TTSJob>(`/tts/jobs/${jobId}`)
return response.data
}
/** 获取 TTS 任务状态(轻量轮询) */
export const getTTSJobStatus = async (jobId: string): Promise<TTSJobStatus> => {
const response = await apiClient.get<TTSJobStatus>(`/tts/jobs/${jobId}/status`)
return response.data
}
/** 获取 TTS 任务列表 */
export const getTTSJobs = async (params?: TTSJobListParams): Promise<TTSJobListResponse> => {
const searchParams = new URLSearchParams()
if (params?.status) searchParams.set("status", params.status)
if (params?.skip !== undefined) searchParams.set("skip", String(params.skip))
if (params?.limit !== undefined) searchParams.set("limit", String(params.limit))
const qs = searchParams.toString()
const response = await apiClient.get<TTSJobListResponse>(`/tts/jobs${qs ? `?${qs}` : ""}`)
return response.data
}
/** 存为素材请求参数 */
export interface SaveTtsToLibraryRequest {
name?: string
tag_ids?: string[]
}
/** 将 TTS 合成结果保存到配音库 */
export const saveTtsToLibrary = async (
jobId: string,
data?: SaveTtsToLibraryRequest,
): Promise<void> => {
await apiClient.post(`/tts/jobs/${jobId}/save-to-library`, data ?? {})
}
/** 删除 TTS 任务 */
export const deleteTTSJob = async (jobId: string): Promise<void> => {
await apiClient.delete(`/tts/jobs/${jobId}`)
}
/* ── 音色列表 ──────────────────────────────────── */
/** TTS 音色 */
export interface TTSVoice {
id: string
name: string
/** 音色分类标签:male/female/young/service/news/emotion */
category?: string
/** 语言 */
language?: string
/** 试听 URL */
preview_url?: string
/** 描述 */
description?: string
}
/** 获取 TTS 音色列表 */
export const getTtsVoices = async (): Promise<TTSVoice[]> => {
const response = await apiClient.get<TTSVoice[]>("/tts/voices")
return response.data
}
/* ── TTS 试听 ──────────────────────────────────── */
/** TTS 试听请求参数 */
export interface TTSPreviewRequest {
text: string
voice_id: string
speed?: number
pitch?: number
}
/** TTS 试听响应 */
export interface TTSPreviewResponse {
audio_url: string
duration?: number
}
/** TTS 试听 */
export const previewTts = async (data: TTSPreviewRequest): Promise<TTSPreviewResponse> => {
const response = await apiClient.post<TTSPreviewResponse>("/tts/preview", data)
return response.data
}
+31
View File
@@ -0,0 +1,31 @@
/**
* TTS 语音合成 API — 目录化入口
* 保持与原 tts.ts 相同导出,向后兼容
*/
// 类型
export type {
TTSMetadata,
TTSSynthesizeRequest,
TTSSynthesizeResponse,
TTSJob,
TTSJobStatus,
TTSJobListResponse,
TTSJobListParams,
SaveTtsToLibraryRequest,
TTSVoice,
TTSPreviewRequest,
TTSPreviewResponse,
} from "./types"
// API 函数
export {
synthesizeSpeech,
getTTSJob,
getTTSJobStatus,
getTTSJobs,
saveTtsToLibrary,
deleteTTSJob,
getTtsVoices,
previewTts,
} from "./jobs"
+72
View File
@@ -0,0 +1,72 @@
/**
* TTS 语音合成 API 函数
*/
import apiClient from "../client"
import type {
TTSSynthesizeRequest,
TTSSynthesizeResponse,
TTSJob,
TTSJobStatus,
TTSJobListResponse,
TTSJobListParams,
SaveTtsToLibraryRequest,
TTSVoice,
TTSPreviewRequest,
TTSPreviewResponse,
} from "./types"
/** 创建 TTS 合成任务 */
export const synthesizeSpeech = async (
data: TTSSynthesizeRequest,
): Promise<TTSSynthesizeResponse> => {
const response = await apiClient.post<TTSSynthesizeResponse>("/tts/synthesize", data)
return response.data
}
/** 获取 TTS 任务详情 */
export const getTTSJob = async (jobId: string): Promise<TTSJob> => {
const response = await apiClient.get<TTSJob>(`/tts/jobs/${jobId}`)
return response.data
}
/** 获取 TTS 任务状态(轻量轮询) */
export const getTTSJobStatus = async (jobId: string): Promise<TTSJobStatus> => {
const response = await apiClient.get<TTSJobStatus>(`/tts/jobs/${jobId}/status`)
return response.data
}
/** 获取 TTS 任务列表 */
export const getTTSJobs = async (params?: TTSJobListParams): Promise<TTSJobListResponse> => {
const searchParams = new URLSearchParams()
if (params?.status) searchParams.set("status", params.status)
if (params?.skip !== undefined) searchParams.set("skip", String(params.skip))
if (params?.limit !== undefined) searchParams.set("limit", String(params.limit))
const qs = searchParams.toString()
const response = await apiClient.get<TTSJobListResponse>(`/tts/jobs${qs ? `?${qs}` : ""}`)
return response.data
}
/** 将 TTS 合成结果保存到配音库 */
export const saveTtsToLibrary = async (
jobId: string,
data?: SaveTtsToLibraryRequest,
): Promise<void> => {
await apiClient.post(`/tts/jobs/${jobId}/save-to-library`, data ?? {})
}
/** 删除 TTS 任务 */
export const deleteTTSJob = async (jobId: string): Promise<void> => {
await apiClient.delete(`/tts/jobs/${jobId}`)
}
/** 获取 TTS 音色列表 */
export const getTtsVoices = async (): Promise<TTSVoice[]> => {
const response = await apiClient.get<TTSVoice[]>("/tts/voices")
return response.data
}
/** TTS 试听 */
export const previewTts = async (data: TTSPreviewRequest): Promise<TTSPreviewResponse> => {
const response = await apiClient.post<TTSPreviewResponse>("/tts/preview", data)
return response.data
}
+112
View File
@@ -0,0 +1,112 @@
/**
* TTS 语音合成类型定义
*/
/** TTS 元数据 */
export interface TTSMetadata {
duration?: number
sample_rate?: number
language?: string
[key: string]: unknown
}
/** TTS 合成请求参数 */
export interface TTSSynthesizeRequest {
text: string
voice_id?: string
output_name?: string
language?: string
speed?: number
voice_model?: string
voice_clone_profile_id?: string
format?: string
metadata?: TTSMetadata
}
/** TTS 合成创建响应 */
export interface TTSSynthesizeResponse {
job_id: string
status: string
message: string
}
/** TTS 任务详情 */
export interface TTSJob {
id: string
user_id: string
project_id: string | null
text: string
voice_id: string | null
voice_model: string | null
voice_clone_profile_id: string | null
language: string
speed: number
output_name: string | null
output_audio_url: string | null
output_format: string
duration_seconds: number | null
file_size_bytes: number | null
sample_rate: number | null
status: string
error_message: string | null
retry_count: number
max_retries: number
metadata_: TTSMetadata | null
created_at: string
updated_at: string
}
/** TTS 任务状态(轻量轮询用) */
export interface TTSJobStatus {
id: string
status: string
output_audio_url: string | null
error_message: string | null
duration_seconds: number | null
retry_count: number
}
/** TTS 任务列表响应 */
export interface TTSJobListResponse {
items: TTSJob[]
total: number
skip: number
limit: number
}
/** TTS 任务列表查询参数 */
export interface TTSJobListParams {
status?: string
skip?: number
limit?: number
}
/** 存为素材请求参数 */
export interface SaveTtsToLibraryRequest {
name?: string
tag_ids?: string[]
}
/** TTS 音色 */
export interface TTSVoice {
id: string
name: string
category?: string
language?: string
preview_url?: string
description?: string
}
/** TTS 试听请求参数 */
export interface TTSPreviewRequest {
text: string
voice_id: string
speed?: number
pitch?: number
}
/** TTS 试听响应 */
export interface TTSPreviewResponse {
audio_url: string
duration?: number
}
+264 -8
View File
@@ -8,15 +8,249 @@
*/
import React from "react"
import { useNavigate } from "react-router-dom"
import { Button } from "@/components/ui"
import { useAuthStore } from "@/store/authStore"
import { NavBar, Footer } from "./sections/Layout"
import { HeroSection } from "./sections/HeroSection"
import { FeatureSection } from "./sections/FeatureSection"
import { WorkflowSection } from "./sections/WorkflowSection"
import { PricingSection } from "./sections/PricingSection"
import { CTASection } from "./sections/CTASection"
import "./home-page.css"
/* ── HeroSection ─────────────────────────────────────────── */
const HeroSection: React.FC = () => {
const navigate = useNavigate()
return (
<section className="hp-hero">
<div className="hp-hero-inner">
{/* 左侧文案 */}
<div className="hp-hero-content">
<span className="hp-hero-tag">🦐 · AI智能视频创作平台</span>
<h1 className="hp-hero-title">
AI自动剪辑
<br />
</h1>
<p className="hp-hero-desc">
AI 30
</p>
<div className="hp-hero-actions">
<Button buttonType="primary" buttonSize="lg" onClick={() => navigate("/register")}>
</Button>
<Button buttonType="secondary" buttonSize="lg" onClick={() => navigate("/pricing")}>
</Button>
</div>
</div>
{/* 右侧视觉 */}
<div className="hp-hero-visual">
<div className="hp-hero-video">
<div className="hp-hero-video-inner">
<span className="hp-hero-video-placeholder">🎬</span>
</div>
<button className="hp-hero-play" type="button" aria-label="播放演示视频">
</button>
</div>
<div className="hp-hero-info">
<div className="hp-hero-badges">
<span className="hp-hero-badge"> AI智能剪辑</span>
<span className="hp-hero-badge"> 30</span>
</div>
<span className="hp-hero-pill"></span>
</div>
</div>
</div>
</section>
)
}
/* ── FeatureSection ──────────────────────────────────────── */
const FEATURES = [
{
icon: "🤖",
title: "AI 智能剪辑",
desc: "自动识别视频高光片段,智能去除冗余内容,智能剪辑精彩短视频。",
},
{
icon: "🎙️",
title: "AI 配音克隆",
desc: "克隆您的声音,支持多种音色风格,自动生成自然流畅的配音。",
},
{
icon: "📝",
title: "智能字幕标题",
desc: "自动语音识别生成精准字幕,AI 创作吸睛标题,提升内容传播力。",
},
{
icon: "📱",
title: "多平台一键发布",
desc: "支持抖音、快手、小红书、微信视频号等主流平台,一键同步发布。",
},
]
const FeatureSection: React.FC = () => {
return (
<section className="hp-features">
<div className="hp-section-inner">
<h2 className="hp-section-title"></h2>
<p className="hp-section-desc"> AI </p>
<div className="hp-feature-grid">
{FEATURES.map((f) => (
<div key={f.title} className="hp-feature-card">
<div className="hp-feature-icon">{f.icon}</div>
<h3 className="hp-feature-title">{f.title}</h3>
<p className="hp-feature-desc">{f.desc}</p>
</div>
))}
</div>
</div>
</section>
)
}
/* ── WorkflowSection ─────────────────────────────────────── */
const STEPS = [
{ icon: "📤", title: "上传素材", desc: "拖拽或选择视频素材,支持批量上传" },
{ icon: "🧠", title: "AI 处理", desc: "AI 自动分析、剪辑、配音、加字幕" },
{ icon: "👀", title: "预览调整", desc: "在线预览生成结果,支持微调编辑" },
{ icon: "🚀", title: "一键发布", desc: "多平台同步发布,追踪数据表现" },
]
const WorkflowSection: React.FC = () => {
return (
<section className="hp-workflow">
<div className="hp-section-inner">
<h2 className="hp-section-title"></h2>
<p className="hp-section-desc"> 30 </p>
<div className="hp-step-grid">
{STEPS.map((step, idx) => (
<div key={step.title} className="hp-step-card">
<div className="hp-step-number">{idx + 1}</div>
<div className="hp-step-icon">{step.icon}</div>
<h3 className="hp-step-title">{step.title}</h3>
<p className="hp-step-desc">{step.desc}</p>
{idx < STEPS.length - 1 && <div className="hp-step-arrow"></div>}
</div>
))}
</div>
</div>
</section>
)
}
/* ── PricingSection ──────────────────────────────────────── */
const PLANS = [
{
name: "基础版",
price: "免费",
period: "",
desc: "适合个人体验,快速上手",
features: ["每月 5 次 AI 生成", "720p 视频导出", "基础模板库", "1 个平台账号绑定"],
highlighted: false,
cta: "免费开始",
},
{
name: "专业版",
price: "¥99",
period: "/月",
desc: "适合内容创作者,高效产出",
features: [
"每月 100 次 AI 生成",
"1080p 视频导出",
"全部模板库",
"4 个平台账号绑定",
"AI 配音克隆",
"优先客服支持",
],
highlighted: true,
cta: "立即订阅",
},
{
name: "企业版",
price: "¥399",
period: "/月",
desc: "适合团队与企业,规模化运营",
features: [
"无限次 AI 生成",
"4K 视频导出",
"全部模板 + 定制模板",
"无限平台账号绑定",
"团队协作管理",
"API 接入支持",
"专属客户经理",
],
highlighted: false,
cta: "联系销售",
},
]
const PricingSection: React.FC = () => {
const navigate = useNavigate()
return (
<section className="hp-pricing">
<div className="hp-section-inner">
<h2 className="hp-section-title"></h2>
<p className="hp-section-desc"></p>
<div className="hp-pricing-grid">
{PLANS.map((plan) => (
<div
key={plan.name}
className={`hp-pricing-card${plan.highlighted ? " hp-pricing-card--highlight" : ""}`}
>
{plan.highlighted && <div className="hp-pricing-badge"></div>}
<h3 className="hp-pricing-name">{plan.name}</h3>
<div className="hp-pricing-price">
{plan.price}
{plan.period && <span className="hp-pricing-period">{plan.period}</span>}
</div>
<p className="hp-pricing-desc">{plan.desc}</p>
<ul className="hp-pricing-features">
{plan.features.map((f) => (
<li key={f}>
<span className="hp-pricing-check"></span>
{f}
</li>
))}
</ul>
<Button
buttonType={plan.highlighted ? "primary" : "secondary"}
onClick={() => navigate("/register")}
>
{plan.cta}
</Button>
</div>
))}
</div>
</div>
</section>
)
}
/* ── CTASection ──────────────────────────────────────────── */
const CTASection: React.FC = () => {
const navigate = useNavigate()
return (
<section className="hp-cta">
<div className="hp-cta-inner">
<h2 className="hp-cta-title"> AI </h2>
<p className="hp-cta-desc"> AI </p>
<Button buttonType="primary" buttonSize="lg" onClick={() => navigate("/register")}>
</Button>
</div>
</section>
)
}
/* ── 主页面 ─────────────────────────────────────────────── */
const HomePage: React.FC = () => {
const isAuthenticated = useAuthStore((state) => state.isAuthenticated)
const navigate = useNavigate()
@@ -30,13 +264,35 @@ const HomePage: React.FC = () => {
return (
<div className="hp-page">
<NavBar />
{/* 顶部导航栏 */}
<header className="hp-nav">
<div className="hp-nav-inner">
<button className="hp-nav-brand" type="button" onClick={() => navigate("/")}>
<span className="hp-nav-logo">🦐</span>
<span className="hp-nav-brand-text"></span>
</button>
<div className="hp-nav-actions">
<Button buttonType="ghost" buttonSize="sm" onClick={() => navigate("/login")}>
</Button>
<Button buttonType="primary" buttonSize="sm" onClick={() => navigate("/register")}>
</Button>
</div>
</div>
</header>
{/* 5 个区域 */}
<HeroSection />
<FeatureSection />
<WorkflowSection />
<PricingSection />
<CTASection />
<Footer />
{/* 底部 */}
<footer className="hp-footer">
<p>© 2026 · AI智能视频创作平台</p>
</footer>
</div>
)
}
@@ -1,20 +0,0 @@
import React from "react"
import { useNavigate } from "react-router-dom"
import { Button } from "@/components/ui"
/** CTA 行动号召区域 */
export const CTASection: React.FC = () => {
const navigate = useNavigate()
return (
<section className="hp-cta">
<div className="hp-cta-inner">
<h2 className="hp-cta-title"> AI </h2>
<p className="hp-cta-desc"> AI </p>
<Button buttonType="primary" buttonSize="lg" onClick={() => navigate("/register")}>
</Button>
</div>
</section>
)
}
@@ -1,45 +0,0 @@
import React from "react"
const FEATURES = [
{
icon: "🤖",
title: "AI 智能剪辑",
desc: "自动识别视频高光片段,智能去除冗余内容,智能剪辑精彩短视频。",
},
{
icon: "🎙️",
title: "AI 配音克隆",
desc: "克隆您的声音,支持多种音色风格,自动生成自然流畅的配音。",
},
{
icon: "📝",
title: "智能字幕标题",
desc: "自动语音识别生成精准字幕,AI 创作吸睛标题,提升内容传播力。",
},
{
icon: "📱",
title: "多平台一键发布",
desc: "支持抖音、快手、小红书、微信视频号等主流平台,一键同步发布。",
},
]
/** 核心功能区域 */
export const FeatureSection: React.FC = () => {
return (
<section className="hp-features">
<div className="hp-section-inner">
<h2 className="hp-section-title"></h2>
<p className="hp-section-desc"> AI </p>
<div className="hp-feature-grid">
{FEATURES.map((f) => (
<div key={f.title} className="hp-feature-card">
<div className="hp-feature-icon">{f.icon}</div>
<h3 className="hp-feature-title">{f.title}</h3>
<p className="hp-feature-desc">{f.desc}</p>
</div>
))}
</div>
</div>
</section>
)
}
@@ -1,55 +0,0 @@
import React from "react"
import { useNavigate } from "react-router-dom"
import { Button } from "@/components/ui"
/** Hero 首屏区域 */
export const HeroSection: React.FC = () => {
const navigate = useNavigate()
return (
<section className="hp-hero">
<div className="hp-hero-inner">
{/* 左侧文案 */}
<div className="hp-hero-content">
<span className="hp-hero-tag">🦐 · AI智能视频创作平台</span>
<h1 className="hp-hero-title">
AI自动剪辑
<br />
</h1>
<p className="hp-hero-desc">
AI 30
</p>
<div className="hp-hero-actions">
<Button buttonType="primary" buttonSize="lg" onClick={() => navigate("/register")}>
</Button>
<Button buttonType="secondary" buttonSize="lg" onClick={() => navigate("/pricing")}>
</Button>
</div>
</div>
{/* 右侧视觉 */}
<div className="hp-hero-visual">
<div className="hp-hero-video">
<div className="hp-hero-video-inner">
<span className="hp-hero-video-placeholder">🎬</span>
</div>
<button className="hp-hero-play" type="button" aria-label="播放演示视频">
</button>
</div>
<div className="hp-hero-info">
<div className="hp-hero-badges">
<span className="hp-hero-badge"> AI智能剪辑</span>
<span className="hp-hero-badge"> 30</span>
</div>
<span className="hp-hero-pill"></span>
</div>
</div>
</div>
</section>
)
}
@@ -1,34 +0,0 @@
import React from "react"
import { useNavigate } from "react-router-dom"
import { Button } from "@/components/ui"
/** 顶部导航栏 */
export const NavBar: React.FC = () => {
const navigate = useNavigate()
return (
<header className="hp-nav">
<div className="hp-nav-inner">
<button className="hp-nav-brand" type="button" onClick={() => navigate("/")}>
<span className="hp-nav-logo">🦐</span>
<span className="hp-nav-brand-text"></span>
</button>
<div className="hp-nav-actions">
<Button buttonType="ghost" buttonSize="sm" onClick={() => navigate("/login")}>
</Button>
<Button buttonType="primary" buttonSize="sm" onClick={() => navigate("/register")}>
</Button>
</div>
</div>
</header>
)
}
/** 页脚 */
export const Footer: React.FC = () => (
<footer className="hp-footer">
<p>© 2026 · AI智能视频创作平台</p>
</footer>
)
@@ -1,102 +0,0 @@
import React from "react"
import { useNavigate } from "react-router-dom"
import { Button } from "@/components/ui"
interface Plan {
name: string
price: string
period: string
desc: string
features: string[]
highlighted: boolean
cta: string
}
const PLANS: Plan[] = [
{
name: "基础版",
price: "免费",
period: "",
desc: "适合个人体验,快速上手",
features: ["每月 5 次 AI 生成", "720p 视频导出", "基础模板库", "1 个平台账号绑定"],
highlighted: false,
cta: "免费开始",
},
{
name: "专业版",
price: "¥99",
period: "/月",
desc: "适合内容创作者,高效产出",
features: [
"每月 100 次 AI 生成",
"1080p 视频导出",
"全部模板库",
"4 个平台账号绑定",
"AI 配音克隆",
"优先客服支持",
],
highlighted: true,
cta: "立即订阅",
},
{
name: "企业版",
price: "¥399",
period: "/月",
desc: "适合团队与企业,规模化运营",
features: [
"无限次 AI 生成",
"4K 视频导出",
"全部模板 + 定制模板",
"无限平台账号绑定",
"团队协作管理",
"API 接入支持",
"专属客户经理",
],
highlighted: false,
cta: "联系销售",
},
]
/** 定价方案区域 */
export const PricingSection: React.FC = () => {
const navigate = useNavigate()
return (
<section className="hp-pricing">
<div className="hp-section-inner">
<h2 className="hp-section-title"></h2>
<p className="hp-section-desc"></p>
<div className="hp-pricing-grid">
{PLANS.map((plan) => (
<div
key={plan.name}
className={`hp-pricing-card${plan.highlighted ? " hp-pricing-card--highlight" : ""}`}
>
{plan.highlighted && <div className="hp-pricing-badge"></div>}
<h3 className="hp-pricing-name">{plan.name}</h3>
<div className="hp-pricing-price">
{plan.price}
{plan.period && <span className="hp-pricing-period">{plan.period}</span>}
</div>
<p className="hp-pricing-desc">{plan.desc}</p>
<ul className="hp-pricing-features">
{plan.features.map((f) => (
<li key={f}>
<span className="hp-pricing-check"></span>
{f}
</li>
))}
</ul>
<Button
buttonType={plan.highlighted ? "primary" : "secondary"}
onClick={() => navigate("/register")}
>
{plan.cta}
</Button>
</div>
))}
</div>
</div>
</section>
)
}
@@ -1,31 +0,0 @@
import React from "react"
const STEPS = [
{ icon: "📤", title: "上传素材", desc: "拖拽或选择视频素材,支持批量上传" },
{ icon: "🧠", title: "AI 处理", desc: "AI 自动分析、剪辑、配音、加字幕" },
{ icon: "👀", title: "预览调整", desc: "在线预览生成结果,支持微调编辑" },
{ icon: "🚀", title: "一键发布", desc: "多平台同步发布,追踪数据表现" },
]
/** 工作流程区域 */
export const WorkflowSection: React.FC = () => {
return (
<section className="hp-workflow">
<div className="hp-section-inner">
<h2 className="hp-section-title"></h2>
<p className="hp-section-desc"> 30 </p>
<div className="hp-step-grid">
{STEPS.map((step, idx) => (
<div key={step.title} className="hp-step-card">
<div className="hp-step-number">{idx + 1}</div>
<div className="hp-step-icon">{step.icon}</div>
<h3 className="hp-step-title">{step.title}</h3>
<p className="hp-step-desc">{step.desc}</p>
{idx < STEPS.length - 1 && <div className="hp-step-arrow"></div>}
</div>
))}
</div>
</div>
</section>
)
}