Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 91902d66ac | |||
| 3b97eb7bf2 |
@@ -0,0 +1,174 @@
|
||||
/**
|
||||
* 成品 / 视频相关 API
|
||||
* 后端实际接口:/videos
|
||||
*/
|
||||
import apiClient from "./client"
|
||||
|
||||
/** 复核状态 */
|
||||
export type ReviewStatus = "pending_review" | "approved" | "rejected"
|
||||
|
||||
/** 成品条目 */
|
||||
export interface ProductItem {
|
||||
id: string
|
||||
title: string
|
||||
video_url?: string
|
||||
thumbnail_url?: string
|
||||
duration_seconds?: number
|
||||
file_size?: number
|
||||
resolution?: string
|
||||
status: "processing" | "completed" | "failed"
|
||||
/** 复核状态 */
|
||||
review_status?: ReviewStatus
|
||||
/** 所属项目 ID */
|
||||
project_id?: string
|
||||
/** 所属项目名称 */
|
||||
project_name?: string
|
||||
/** 查重率(百分比) */
|
||||
duplicate_rate?: number
|
||||
created_at?: string
|
||||
updated_at?: string
|
||||
}
|
||||
|
||||
/** 列表查询参数 */
|
||||
export interface ProductListParams {
|
||||
page?: number
|
||||
page_size?: number
|
||||
project_id?: string
|
||||
review_status?: ReviewStatus | "all"
|
||||
}
|
||||
|
||||
/** 分页响应 */
|
||||
export interface ProductListResponse {
|
||||
items: ProductItem[]
|
||||
total: number
|
||||
page: number
|
||||
page_size: number
|
||||
}
|
||||
|
||||
/** 批量下载任务状态 */
|
||||
export interface BatchDownloadStatus {
|
||||
job_id: string
|
||||
status: "processing" | "completed" | "failed"
|
||||
/** 完成后返回的下载 URL */
|
||||
download_url?: string
|
||||
/** 进度百分比 */
|
||||
progress?: number
|
||||
}
|
||||
|
||||
/** 后端 /videos 接口返回的原始视频条目 */
|
||||
interface VideoItem {
|
||||
id: string
|
||||
project_id: string
|
||||
generation_task_id: string
|
||||
name: string
|
||||
file_url: string
|
||||
file_size: number
|
||||
duration: number
|
||||
thumbnail_url: string | null
|
||||
width: number
|
||||
height: number
|
||||
fps: number
|
||||
status: string
|
||||
review_status: ReviewStatus
|
||||
generation_params: Record<string, unknown>
|
||||
download_url: string
|
||||
generated_at: string
|
||||
}
|
||||
|
||||
/**
|
||||
* 将后端 VideoItem 映射为 ProductItem 格式
|
||||
*/
|
||||
function mapVideoToProductItem(video: VideoItem): ProductItem {
|
||||
return {
|
||||
id: video.id,
|
||||
title: video.name || "未命名视频",
|
||||
// 优先用 download_url(带签名)播放,file_url 无签名无法访问
|
||||
video_url: video.download_url || video.file_url,
|
||||
thumbnail_url: video.thumbnail_url || undefined,
|
||||
duration_seconds: video.duration,
|
||||
file_size: video.file_size,
|
||||
resolution: video.width && video.height ? `${video.width}x${video.height}` : undefined,
|
||||
status:
|
||||
video.status === "completed"
|
||||
? "completed"
|
||||
: video.status === "failed"
|
||||
? "failed"
|
||||
: "processing",
|
||||
review_status: video.review_status,
|
||||
project_id: video.project_id,
|
||||
// 后端 /videos 接口暂无 project_name 字段
|
||||
project_name: undefined,
|
||||
// 后端字段名为 generated_at,映射为 created_at 供前端统一使用
|
||||
created_at: video.generated_at,
|
||||
updated_at: video.generated_at,
|
||||
// 后端 /videos 接口暂无 duplicate_rate 字段
|
||||
duplicate_rate: undefined,
|
||||
}
|
||||
}
|
||||
|
||||
/** 获取成品列表(支持分页和筛选) */
|
||||
export const getProducts = async (params?: ProductListParams): Promise<ProductItem[]> => {
|
||||
const response = await apiClient.get("/videos", { params })
|
||||
const data = response.data
|
||||
const videos: VideoItem[] = Array.isArray(data?.items)
|
||||
? data.items
|
||||
: Array.isArray(data)
|
||||
? data
|
||||
: []
|
||||
return videos.map(mapVideoToProductItem)
|
||||
}
|
||||
|
||||
/** 获取单个成品详情 */
|
||||
export const getProduct = async (productId: string): Promise<ProductItem> => {
|
||||
const response = await apiClient.get(`/videos/${productId}`)
|
||||
return mapVideoToProductItem(response.data as VideoItem)
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除成品
|
||||
* 注意:后端暂未实现 /videos DELETE 接口,调用会返回 405
|
||||
* 待后端实现后自动生效
|
||||
*/
|
||||
export const deleteProduct = async (productId: string): Promise<void> => {
|
||||
await apiClient.delete(`/videos/${productId}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取成品下载链接
|
||||
* 直接使用列表返回的 download_url(带OSS签名)
|
||||
*/
|
||||
export const getProductDownloadUrl = async (
|
||||
productId: string,
|
||||
): Promise<{ url: string; expires_at: string }> => {
|
||||
// 优先从列表缓存取;如果没有则调详情接口
|
||||
const product = await getProduct(productId)
|
||||
if (!product.video_url) throw new Error("下载链接不可用")
|
||||
return { url: product.video_url, expires_at: "" }
|
||||
}
|
||||
|
||||
/** 更新复核状态 — TODO: 后端暂无对应端点,暂存本地状态 */
|
||||
export const updateReviewStatus = async (
|
||||
productId: string,
|
||||
status: ReviewStatus,
|
||||
): Promise<ProductItem> => {
|
||||
// 后端暂无 /videos/{id}/review 端点
|
||||
// 暂时返回当前状态,后续可扩展
|
||||
const product = await getProduct(productId)
|
||||
return { ...product, review_status: status }
|
||||
}
|
||||
|
||||
/** 发起批量下载 — TODO: 后端暂无对应端点 */
|
||||
export const batchDownload = async (videoIds: string[]): Promise<{ job_id: string }> => {
|
||||
// 后端暂无 /videos/batch-download 端点
|
||||
// 暂时返回模拟 job_id,后续可扩展
|
||||
console.warn("[batchDownload] 后端暂无批量下载端点", videoIds)
|
||||
return { job_id: `mock-${Date.now()}` }
|
||||
}
|
||||
|
||||
/** 查询批量下载状态 — TODO: 后端暂无对应端点 */
|
||||
export const getBatchDownloadStatus = async (jobId: string): Promise<BatchDownloadStatus> => {
|
||||
// 后端暂无 /videos/batch-download/{jobId} 端点
|
||||
// 暂时返回模拟状态,后续可扩展
|
||||
console.warn("[getBatchDownloadStatus] 后端暂无批量下载状态端点", jobId)
|
||||
return { job_id: jobId, status: "processing", progress: 0 }
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
/**
|
||||
* 成品 / 视频相关 API — 目录化入口
|
||||
* 保持与原 products.ts 相同导出,向后兼容
|
||||
*/
|
||||
|
||||
// 类型
|
||||
export type {
|
||||
ReviewStatus,
|
||||
ProductItem,
|
||||
ProductListParams,
|
||||
ProductListResponse,
|
||||
BatchDownloadStatus,
|
||||
VideoItem,
|
||||
} from "./types"
|
||||
|
||||
// 工具函数
|
||||
export { mapVideoToProductItem } from "./utils"
|
||||
|
||||
// API 函数
|
||||
export {
|
||||
getProducts,
|
||||
getProduct,
|
||||
deleteProduct,
|
||||
getProductDownloadUrl,
|
||||
updateReviewStatus,
|
||||
batchDownload,
|
||||
getBatchDownloadStatus,
|
||||
} from "./products"
|
||||
@@ -1,80 +0,0 @@
|
||||
/**
|
||||
* 成品 / 视频相关 API 函数
|
||||
* 后端实际接口:/videos
|
||||
*/
|
||||
import apiClient from "../client"
|
||||
import type {
|
||||
BatchDownloadStatus,
|
||||
ProductItem,
|
||||
ProductListParams,
|
||||
VideoItem,
|
||||
ReviewStatus,
|
||||
} from "./types"
|
||||
import { mapVideoToProductItem } from "./utils"
|
||||
|
||||
/** 获取成品列表(支持分页和筛选) */
|
||||
export const getProducts = async (params?: ProductListParams): Promise<ProductItem[]> => {
|
||||
const response = await apiClient.get("/videos", { params })
|
||||
const data = response.data
|
||||
const videos: VideoItem[] = Array.isArray(data?.items)
|
||||
? data.items
|
||||
: Array.isArray(data)
|
||||
? data
|
||||
: []
|
||||
return videos.map(mapVideoToProductItem)
|
||||
}
|
||||
|
||||
/** 获取单个成品详情 */
|
||||
export const getProduct = async (productId: string): Promise<ProductItem> => {
|
||||
const response = await apiClient.get(`/videos/${productId}`)
|
||||
return mapVideoToProductItem(response.data as VideoItem)
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除成品
|
||||
* 注意:后端暂未实现 /videos DELETE 接口,调用会返回 405
|
||||
* 待后端实现后自动生效
|
||||
*/
|
||||
export const deleteProduct = async (productId: string): Promise<void> => {
|
||||
await apiClient.delete(`/videos/${productId}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取成品下载链接
|
||||
* 直接使用列表返回的 download_url(带OSS签名)
|
||||
*/
|
||||
export const getProductDownloadUrl = async (
|
||||
productId: string,
|
||||
): Promise<{ url: string; expires_at: string }> => {
|
||||
// 优先从列表缓存取;如果没有则调详情接口
|
||||
const product = await getProduct(productId)
|
||||
if (!product.video_url) throw new Error("下载链接不可用")
|
||||
return { url: product.video_url, expires_at: "" }
|
||||
}
|
||||
|
||||
/** 更新复核状态 — TODO: 后端暂无对应端点,暂存本地状态 */
|
||||
export const updateReviewStatus = async (
|
||||
productId: string,
|
||||
status: ReviewStatus,
|
||||
): Promise<ProductItem> => {
|
||||
// 后端暂无 /videos/{id}/review 端点
|
||||
// 暂时返回当前状态,后续可扩展
|
||||
const product = await getProduct(productId)
|
||||
return { ...product, review_status: status }
|
||||
}
|
||||
|
||||
/** 发起批量下载 — TODO: 后端暂无对应端点 */
|
||||
export const batchDownload = async (videoIds: string[]): Promise<{ job_id: string }> => {
|
||||
// 后端暂无 /videos/batch-download 端点
|
||||
// 暂时返回模拟 job_id,后续可扩展
|
||||
console.warn("[batchDownload] 后端暂无批量下载端点", videoIds)
|
||||
return { job_id: `mock-${Date.now()}` }
|
||||
}
|
||||
|
||||
/** 查询批量下载状态 — TODO: 后端暂无对应端点 */
|
||||
export const getBatchDownloadStatus = async (jobId: string): Promise<BatchDownloadStatus> => {
|
||||
// 后端暂无 /videos/batch-download/{jobId} 端点
|
||||
// 暂时返回模拟状态,后续可扩展
|
||||
console.warn("[getBatchDownloadStatus] 后端暂无批量下载状态端点", jobId)
|
||||
return { job_id: jobId, status: "processing", progress: 0 }
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
/**
|
||||
* 成品 / 视频相关类型定义
|
||||
*/
|
||||
|
||||
/** 复核状态 */
|
||||
export type ReviewStatus = "pending_review" | "approved" | "rejected"
|
||||
|
||||
/** 成品条目 */
|
||||
export interface ProductItem {
|
||||
id: string
|
||||
title: string
|
||||
video_url?: string
|
||||
thumbnail_url?: string
|
||||
duration_seconds?: number
|
||||
file_size?: number
|
||||
resolution?: string
|
||||
status: "processing" | "completed" | "failed"
|
||||
/** 复核状态 */
|
||||
review_status?: ReviewStatus
|
||||
/** 所属项目 ID */
|
||||
project_id?: string
|
||||
/** 所属项目名称 */
|
||||
project_name?: string
|
||||
/** 查重率(百分比) */
|
||||
duplicate_rate?: number
|
||||
created_at?: string
|
||||
updated_at?: string
|
||||
}
|
||||
|
||||
/** 列表查询参数 */
|
||||
export interface ProductListParams {
|
||||
page?: number
|
||||
page_size?: number
|
||||
project_id?: string
|
||||
review_status?: ReviewStatus | "all"
|
||||
}
|
||||
|
||||
/** 分页响应 */
|
||||
export interface ProductListResponse {
|
||||
items: ProductItem[]
|
||||
total: number
|
||||
page: number
|
||||
page_size: number
|
||||
}
|
||||
|
||||
/** 批量下载任务状态 */
|
||||
export interface BatchDownloadStatus {
|
||||
job_id: string
|
||||
status: "processing" | "completed" | "failed"
|
||||
/** 完成后返回的下载 URL */
|
||||
download_url?: string
|
||||
/** 进度百分比 */
|
||||
progress?: number
|
||||
}
|
||||
|
||||
/** 后端 /videos 接口返回的原始视频条目 */
|
||||
export interface VideoItem {
|
||||
id: string
|
||||
project_id: string
|
||||
generation_task_id: string
|
||||
name: string
|
||||
file_url: string
|
||||
file_size: number
|
||||
duration: number
|
||||
thumbnail_url: string | null
|
||||
width: number
|
||||
height: number
|
||||
fps: number
|
||||
status: string
|
||||
review_status: ReviewStatus
|
||||
generation_params: Record<string, unknown>
|
||||
download_url: string
|
||||
generated_at: string
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
/**
|
||||
* 成品数据转换工具函数
|
||||
*/
|
||||
import type { ProductItem, VideoItem } from "./types"
|
||||
|
||||
/**
|
||||
* 将后端 VideoItem 映射为 ProductItem 格式
|
||||
*/
|
||||
export function mapVideoToProductItem(video: VideoItem): ProductItem {
|
||||
return {
|
||||
id: video.id,
|
||||
title: video.name || "未命名视频",
|
||||
// 优先用 download_url(带签名)播放,file_url 无签名无法访问
|
||||
video_url: video.download_url || video.file_url,
|
||||
thumbnail_url: video.thumbnail_url || undefined,
|
||||
duration_seconds: video.duration,
|
||||
file_size: video.file_size,
|
||||
resolution: video.width && video.height ? `${video.width}x${video.height}` : undefined,
|
||||
status:
|
||||
video.status === "completed"
|
||||
? "completed"
|
||||
: video.status === "failed"
|
||||
? "failed"
|
||||
: "processing",
|
||||
review_status: video.review_status,
|
||||
project_id: video.project_id,
|
||||
// 后端 /videos 接口暂无 project_name 字段
|
||||
project_name: undefined,
|
||||
// 后端字段名为 generated_at,映射为 created_at 供前端统一使用
|
||||
created_at: video.generated_at,
|
||||
updated_at: video.generated_at,
|
||||
// 后端 /videos 接口暂无 duplicate_rate 字段
|
||||
duplicate_rate: undefined,
|
||||
}
|
||||
}
|
||||
Regular → Executable
+8
-264
@@ -8,249 +8,15 @@
|
||||
*/
|
||||
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()
|
||||
@@ -264,35 +30,13 @@ const HomePage: React.FC = () => {
|
||||
|
||||
return (
|
||||
<div className="hp-page">
|
||||
{/* 顶部导航栏 */}
|
||||
<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 个区域 */}
|
||||
<NavBar />
|
||||
<HeroSection />
|
||||
<FeatureSection />
|
||||
<WorkflowSection />
|
||||
<PricingSection />
|
||||
<CTASection />
|
||||
|
||||
{/* 底部 */}
|
||||
<footer className="hp-footer">
|
||||
<p>© 2026 小虾智剪 · AI智能视频创作平台</p>
|
||||
</footer>
|
||||
<Footer />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
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>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
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>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
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>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
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>
|
||||
)
|
||||
@@ -0,0 +1,102 @@
|
||||
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>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
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