Chore: remove promotional copy and home-training wording

这个提交包含在:
cryptocommuniums-afk
2026-03-14 21:50:09 +08:00
父节点 914f015c30
当前提交 ba35e50528
修改 11 个文件,包含 50 行新增50 行删除

查看文件

@@ -1,6 +1,6 @@
# Tennis Training Hub # Tennis Training Hub
AI 网球训练助手,提供训练计划、姿势分析、实时摄像头分析、在线视频录制与视频库管理。当前版本新增独立 Go 媒体服务,用于处理在线录制、分段上传、实时推流信令和归档回放。 网球训练管理与分析应用,提供训练计划、姿势分析、实时摄像头分析、在线视频录制与视频库管理。当前版本新增独立 Go 媒体服务,用于处理在线录制、分段上传、实时推流信令和归档回放。
## Architecture ## Architecture

查看文件

@@ -67,7 +67,7 @@ export default function Dashboard() {
<div className="flex gap-2"> <div className="flex gap-2">
<Button data-testid="dashboard-training-button" onClick={() => setLocation("/training")} className="gap-2"> <Button data-testid="dashboard-training-button" onClick={() => setLocation("/training")} className="gap-2">
<Target className="h-4 w-4" /> <Target className="h-4 w-4" />
</Button> </Button>
<Button variant="outline" onClick={() => setLocation("/analysis")} className="gap-2"> <Button variant="outline" onClick={() => setLocation("/analysis")} className="gap-2">
<Video className="h-4 w-4" /> <Video className="h-4 w-4" />
@@ -220,7 +220,7 @@ export default function Dashboard() {
<div className="h-[200px] flex items-center justify-center text-muted-foreground text-sm"> <div className="h-[200px] flex items-center justify-center text-muted-foreground text-sm">
<div className="text-center"> <div className="text-center">
<Video className="h-8 w-8 mx-auto mb-2 opacity-30" /> <Video className="h-8 w-8 mx-auto mb-2 opacity-30" />
<p>AI分析</p> <p></p>
</div> </div>
</div> </div>
)} )}
@@ -231,7 +231,7 @@ export default function Dashboard() {
{/* Quick actions */} {/* Quick actions */}
<Card className="border-0 shadow-sm"> <Card className="border-0 shadow-sm">
<CardHeader className="pb-2"> <CardHeader className="pb-2">
<CardTitle className="text-base font-semibold"></CardTitle> <CardTitle className="text-base font-semibold"></CardTitle>
</CardHeader> </CardHeader>
<CardContent> <CardContent>
<div className="grid grid-cols-1 sm:grid-cols-3 gap-3"> <div className="grid grid-cols-1 sm:grid-cols-3 gap-3">
@@ -244,7 +244,7 @@ export default function Dashboard() {
</div> </div>
<div> <div>
<p className="font-medium text-sm"></p> <p className="font-medium text-sm"></p>
<p className="text-xs text-muted-foreground">AI定制个人训练方案</p> <p className="text-xs text-muted-foreground"></p>
</div> </div>
</button> </button>
<button <button
@@ -256,7 +256,7 @@ export default function Dashboard() {
</div> </div>
<div> <div>
<p className="font-medium text-sm"></p> <p className="font-medium text-sm"></p>
<p className="text-xs text-muted-foreground">MediaPipe AI姿势识别</p> <p className="text-xs text-muted-foreground">姿</p>
</div> </div>
</button> </button>
<button <button
@@ -268,7 +268,7 @@ export default function Dashboard() {
</div> </div>
<div> <div>
<p className="font-medium text-sm">NTRP评分</p> <p className="font-medium text-sm">NTRP评分</p>
<p className="text-xs text-muted-foreground"></p> <p className="text-xs text-muted-foreground"></p>
</div> </div>
</button> </button>
</div> </div>

查看文件

@@ -30,18 +30,18 @@ export default function Home() {
<div className="max-w-3xl mx-auto text-center"> <div className="max-w-3xl mx-auto text-center">
<div className="inline-flex items-center gap-2 px-3 py-1 rounded-full bg-primary/10 text-primary text-sm font-medium mb-6"> <div className="inline-flex items-center gap-2 px-3 py-1 rounded-full bg-primary/10 text-primary text-sm font-medium mb-6">
<Zap className="h-3.5 w-3.5" /> <Zap className="h-3.5 w-3.5" />
AI网球训练助手
</div> </div>
<h1 className="text-4xl md:text-5xl lg:text-6xl font-bold tracking-tight leading-tight"> <h1 className="text-4xl md:text-5xl lg:text-6xl font-bold tracking-tight leading-tight">
<span className="text-primary block mt-1"></span> <span className="text-primary block mt-1"></span>
</h1> </h1>
<p className="text-lg text-muted-foreground mt-6 max-w-xl mx-auto leading-relaxed"> <p className="text-lg text-muted-foreground mt-6 max-w-xl mx-auto leading-relaxed">
AI姿势识别 · · · · 姿 · ·
</p> </p>
<div className="flex items-center justify-center gap-3 mt-8"> <div className="flex items-center justify-center gap-3 mt-8">
<Button onClick={() => setLocation("/login")} size="lg" className="gap-2 h-12 px-6"> <Button onClick={() => setLocation("/login")} size="lg" className="gap-2 h-12 px-6">
<ChevronRight className="h-4 w-4" /> <ChevronRight className="h-4 w-4" />
</Button> </Button>
</div> </div>
@@ -50,43 +50,43 @@ export default function Home() {
{/* Features */} {/* Features */}
<section className="container py-16"> <section className="container py-16">
<h2 className="text-2xl font-bold text-center mb-12"></h2> <h2 className="text-2xl font-bold text-center mb-12"></h2>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 max-w-5xl mx-auto"> <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 max-w-5xl mx-auto">
{[ {[
{ {
icon: Video, icon: Video,
title: "AI姿势识别", title: "姿势识别",
desc: "MediaPipe实时分析33个关键点,精准评估挥拍动作", desc: "使用 MediaPipe 分析 33 个关键点并记录挥拍数据",
color: "bg-blue-50 text-blue-600", color: "bg-blue-50 text-blue-600",
}, },
{ {
icon: Target, icon: Target,
title: "智能训练计划", title: "训练计划",
desc: "根据水平和分析结果,AI自动生成和调整训练方案", desc: "根据水平和分析结果生成训练安排",
color: "bg-green-50 text-green-600", color: "bg-green-50 text-green-600",
}, },
{ {
icon: Award, icon: Award,
title: "NTRP自动评分", title: "NTRP自动评分",
desc: "USTA标准五维度评估,每次分析自动更新评分", desc: "USTA 维度记录评分结果",
color: "bg-purple-50 text-purple-600", color: "bg-purple-50 text-purple-600",
}, },
{ {
icon: Zap, icon: Zap,
title: "击球统计", title: "击球统计",
desc: "击球次数、挥拍速度一致性,量化训练效果", desc: "记录击球次数、挥拍速度一致性",
color: "bg-orange-50 text-orange-600", color: "bg-orange-50 text-orange-600",
}, },
{ {
icon: Footprints, icon: Footprints,
title: "运动轨迹", title: "运动轨迹",
desc: "重心移动轨迹分析,优化脚步移动模式", desc: "记录重心移动轨迹和脚步变化",
color: "bg-teal-50 text-teal-600", color: "bg-teal-50 text-teal-600",
}, },
{ {
icon: TrendingUp, icon: TrendingUp,
title: "进度追踪", title: "进度追踪",
desc: "训练历史、能力趋势评分变化一目了然", desc: "查看训练历史、趋势评分变化",
color: "bg-indigo-50 text-indigo-600", color: "bg-indigo-50 text-indigo-600",
}, },
].map((feature) => ( ].map((feature) => (
@@ -103,11 +103,11 @@ export default function Home() {
{/* How it works */} {/* How it works */}
<section className="container py-16"> <section className="container py-16">
<h2 className="text-2xl font-bold text-center mb-12">使</h2> <h2 className="text-2xl font-bold text-center mb-12">使</h2>
<div className="grid grid-cols-1 md:grid-cols-4 gap-6 max-w-4xl mx-auto"> <div className="grid grid-cols-1 md:grid-cols-4 gap-6 max-w-4xl mx-auto">
{[ {[
{ step: "1", title: "输入用户名", desc: "用户名登录即可" }, { step: "1", title: "输入用户名", desc: "用户名登录即可" },
{ step: "2", title: "生成计划", desc: "AI个性化训练方案" }, { step: "2", title: "生成计划", desc: "生成训练安排" },
{ step: "3", title: "上传视频", desc: "录制挥拍并分析" }, { step: "3", title: "上传视频", desc: "录制挥拍并分析" },
{ step: "4", title: "获取反馈", desc: "评分与矫正建议" }, { step: "4", title: "获取反馈", desc: "评分与矫正建议" },
].map((item) => ( ].map((item) => (
@@ -125,10 +125,10 @@ export default function Home() {
{/* CTA */} {/* CTA */}
<section className="container py-16"> <section className="container py-16">
<div className="max-w-2xl mx-auto text-center p-8 rounded-2xl bg-primary/5"> <div className="max-w-2xl mx-auto text-center p-8 rounded-2xl bg-primary/5">
<h2 className="text-2xl font-bold mb-3"></h2> <h2 className="text-2xl font-bold mb-3"></h2>
<p className="text-muted-foreground mb-6">使</p> <p className="text-muted-foreground mb-6"></p>
<Button onClick={() => setLocation("/login")} size="lg" className="gap-2"> <Button onClick={() => setLocation("/login")} size="lg" className="gap-2">
<ChevronRight className="h-4 w-4" /> <ChevronRight className="h-4 w-4" />
</Button> </Button>
</div> </div>
@@ -141,7 +141,7 @@ export default function Home() {
<Target className="h-4 w-4" /> <Target className="h-4 w-4" />
<span>Tennis Hub</span> <span>Tennis Hub</span>
</div> </div>
<span>AI网球训练助手</span> <span></span>
</div> </div>
</footer> </footer>
</div> </div>

查看文件

@@ -285,7 +285,7 @@ export default function LiveCamera() {
</DialogTitle> </DialogTitle>
<DialogDescription> <DialogDescription>
</DialogDescription> </DialogDescription>
</DialogHeader> </DialogHeader>
<div className="space-y-4 py-2"> <div className="space-y-4 py-2">
@@ -313,7 +313,7 @@ export default function LiveCamera() {
<Button onClick={() => setSetupStep(s => s + 1)}></Button> <Button onClick={() => setSetupStep(s => s + 1)}></Button>
) : ( ) : (
<Button onClick={handleSetupComplete} className="gap-2"> <Button onClick={handleSetupComplete} className="gap-2">
<Camera className="h-4 w-4" />使 <Camera className="h-4 w-4" />
</Button> </Button>
)} )}
</DialogFooter> </DialogFooter>

查看文件

@@ -37,13 +37,13 @@ export default function Login() {
<Target className="w-8 h-8 text-primary" /> <Target className="w-8 h-8 text-primary" />
</div> </div>
<h1 className="text-3xl font-bold tracking-tight">Tennis Hub</h1> <h1 className="text-3xl font-bold tracking-tight">Tennis Hub</h1>
<p className="text-muted-foreground mt-2">AI网球训练助手</p> <p className="text-muted-foreground mt-2"></p>
</div> </div>
<Card className="border-0 shadow-xl"> <Card className="border-0 shadow-xl">
<CardHeader className="text-center pb-2"> <CardHeader className="text-center pb-2">
<CardTitle className="text-xl" data-testid="login-title"></CardTitle> <CardTitle className="text-xl" data-testid="login-title"></CardTitle>
<CardDescription>使</CardDescription> <CardDescription></CardDescription>
</CardHeader> </CardHeader>
<CardContent> <CardContent>
<form onSubmit={handleLogin} className="space-y-4"> <form onSubmit={handleLogin} className="space-y-4">
@@ -96,7 +96,7 @@ export default function Login() {
</Card> </Card>
<p className="text-center text-xs text-muted-foreground mt-6"> <p className="text-center text-xs text-muted-foreground mt-6">
使
</p> </p>
</div> </div>
</div> </div>

查看文件

@@ -96,7 +96,7 @@ export default function Training() {
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div> <div>
<h1 className="text-2xl font-bold tracking-tight" data-testid="training-title"></h1> <h1 className="text-2xl font-bold tracking-tight" data-testid="training-title"></h1>
<p className="text-muted-foreground text-sm mt-1">AI个性化训练方案</p> <p className="text-muted-foreground text-sm mt-1"></p>
</div> </div>
</div> </div>
@@ -109,7 +109,7 @@ export default function Training() {
</CardTitle> </CardTitle>
<CardDescription> <CardDescription>
AI生成个性化训练方案
</CardDescription> </CardDescription>
</CardHeader> </CardHeader>
<CardContent className="space-y-4"> <CardContent className="space-y-4">
@@ -149,7 +149,7 @@ export default function Training() {
className="w-full sm:w-auto gap-2" className="w-full sm:w-auto gap-2"
> >
{generateMutation.isPending ? ( {generateMutation.isPending ? (
<><Loader2 className="h-4 w-4 animate-spin" />AI生成...</> <><Loader2 className="h-4 w-4 animate-spin" />...</>
) : ( ) : (
<><Sparkles className="h-4 w-4" /></> <><Sparkles className="h-4 w-4" /></>
)} )}
@@ -186,7 +186,7 @@ export default function Training() {
) : ( ) : (
<RefreshCw className="h-3 w-3" /> <RefreshCw className="h-3 w-3" />
)} )}
</Button> </Button>
</div> </div>
{activePlan.adjustmentNotes && ( {activePlan.adjustmentNotes && (

查看文件

@@ -98,7 +98,7 @@ export default function Tutorials() {
<BookOpen className="w-6 h-6 text-primary" /> <BookOpen className="w-6 h-6 text-primary" />
</h1> </h1>
<p className="text-muted-foreground mt-1"></p> <p className="text-muted-foreground mt-1"></p>
</div> </div>
{/* Progress Overview */} {/* Progress Overview */}

查看文件

@@ -9,7 +9,7 @@
- **训练提醒通知**:支持训练/打卡/分析多类型提醒,自定义时间和重复日期 - **训练提醒通知**:支持训练/打卡/分析多类型提醒,自定义时间和重复日期
- **浏览器通知推送**Notification API集成,权限管理和状态提示 - **浏览器通知推送**Notification API集成,权限管理和状态提示
- **通知记录管理**:未读计数、全部标记已读、历史记录浏览 - **通知记录管理**:未读计数、全部标记已读、历史记录浏览
- **文案优化**:去除“在家”等冗余描述,简化为直接信息反馈 - **文案调整**:去除冗余描述,简化为直接信息反馈
### 数据库变更 ### 数据库变更
@@ -33,11 +33,11 @@
- **每日打卡系统**:日历视图展示打卡记录,自动计算连续打卡天数 - **每日打卡系统**:日历视图展示打卡记录,自动计算连续打卡天数
- **成就徽章系统**24种成就徽章,涵盖里程碑、训练、连续打卡、视频、分析、评分6个类别 - **成就徽章系统**24种成就徽章,涵盖里程碑、训练、连续打卡、视频、分析、评分6个类别
- **实时摄像头分析**:支持手机/电脑摄像头实时捕捉和MediaPipe姿势分析 - **实时摄像头分析**:支持手机/电脑摄像头实时捕捉和MediaPipe姿势分析
- **摄像头位置确认提示**:引导用户调整摄像头到最佳位置 - **摄像头位置确认提示**:引导用户调整摄像头位置
- **在线录制系统**稳定压缩流录制,自适应码率1-2.5Mbps - **在线录制系统**稳定压缩流录制,自适应码率1-2.5Mbps
- **断线自动重连**:摄像头意外断开时自动检测并重新连接 - **断线自动重连**:摄像头意外断开时自动检测并重新连接
- **自动剪辑功能**:基于运动检测自动标记关键时刻 - **自动剪辑功能**:基于运动检测自动标记关键时刻
- **移动端全面适配**:安全区域、触摸优化、横屏支持 - **移动端适配**:安全区域、触摸优化、横屏支持
- **手机摄像头优化**:前后摄像头切换、自适应分辨率 - **手机摄像头优化**:前后摄像头切换、自适应分辨率
### 数据库变更 ### 数据库变更
@@ -53,7 +53,7 @@
### 文档 ### 文档
- 新增完整README.md - 新增 README.md
- 新增API接口文档 - 新增API接口文档
- 新增数据库设计文档 - 新增数据库设计文档
- 新增功能列表清单 - 新增功能列表清单

查看文件

@@ -10,7 +10,7 @@
### 用户与训练 ### 用户与训练
- 用户名登录:无需注册,输入用户名即可进入训练工作台 - 用户名登录:无需注册,输入用户名即可进入训练工作台
- AI 训练计划:按技能等级和训练周期生成个性化训练计划 - 训练计划:按技能等级和训练周期生成训练计划
- 训练进度:展示训练次数、时长、评分趋势、最近分析结果 - 训练进度:展示训练次数、时长、评分趋势、最近分析结果
- 每日打卡与提醒:支持训练打卡、提醒、通知记录 - 每日打卡与提醒:支持训练打卡、提醒、通知记录
@@ -31,7 +31,7 @@
- 归档回放worker 合并片段并生成 WebM,FFmpeg 可用时额外生成 MP4 - 归档回放worker 合并片段并生成 WebM,FFmpeg 可用时额外生成 MP4
- 视频库登记:归档完成后自动写回现有视频库 - 视频库登记:归档完成后自动写回现有视频库
## 前端体验能力 ## 前端能力
### 移动端 ### 移动端

查看文件

@@ -72,9 +72,9 @@ export const appRouter = router({
footworkScore: a.footworkScore, footworkScore: a.footworkScore,
})); }));
const prompt = `你是一位专业网球教练。请为一位${ const prompt = `你是一位网球教练。请为一位${
input.skillLevel === "beginner" ? "初级" : input.skillLevel === "intermediate" ? "中级" : "高级" input.skillLevel === "beginner" ? "初级" : input.skillLevel === "intermediate" ? "中级" : "高级"
}水平的网球学员生成一个${input.durationDays}天的在家训练计划。 }水平的网球学员生成一个${input.durationDays}天的训练计划。
要求: 要求:
- 只需要球拍,不需要球场和球网 - 只需要球拍,不需要球场和球网
@@ -87,7 +87,7 @@ ${recentScores.length > 0 ? `- 用户最近的分析数据: ${JSON.stringify(rec
const response = await invokeLLM({ const response = await invokeLLM({
messages: [ messages: [
{ role: "system", content: "你是专业网球教练AI助手。返回严格的JSON格式。" }, { role: "system", content: "你是网球训练计划生成器。返回严格的JSON格式。" },
{ role: "user", content: prompt }, { role: "user", content: prompt },
], ],
response_format: { response_format: {
@@ -175,7 +175,7 @@ ${recentScores.length > 0 ? `- 用户最近的分析数据: ${JSON.stringify(rec
const response = await invokeLLM({ const response = await invokeLLM({
messages: [ messages: [
{ role: "system", content: "你是专业网球教练AI助手。返回严格的JSON格式。" }, { role: "system", content: "你是网球评分生成器。返回严格的JSON格式。" },
{ role: "user", content: prompt }, { role: "user", content: prompt },
], ],
response_format: { response_format: {
@@ -361,7 +361,7 @@ ${recentScores.length > 0 ? `- 用户最近的分析数据: ${JSON.stringify(rec
messages: [ messages: [
{ {
role: "system", role: "system",
content: "你是一位专业网球教练。根据MediaPipe姿势分析数据,给出具体的姿势矫正建议。用中文回答。", content: "你是一位网球动作分析员。根据MediaPipe姿势分析数据,给出具体的姿势矫正建议。用中文回答。",
}, },
{ {
role: "user", role: "user",

查看文件

@@ -63,4 +63,4 @@
- [x] 更新导航添加新页面入口 - [x] 更新导航添加新页面入口
- [x] 编写新功能测试 - [x] 编写新功能测试
- [x] 推送更新到Gitea仓库 - [x] 推送更新到Gitea仓库
- [x] 去除“在家”等冗余说明文字,简化为直接信息反馈 - [x] 去除冗余说明文字,简化为直接信息反馈