Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d2fbe78c52 | |||
| cfc58bab16 |
@@ -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
|
||||
}
|
||||
@@ -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"
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
Executable → Regular
+264
-8
@@ -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>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user