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

查看文件

@@ -67,7 +67,7 @@ export default function Dashboard() {
<div className="flex gap-2">
<Button data-testid="dashboard-training-button" onClick={() => setLocation("/training")} className="gap-2">
<Target className="h-4 w-4" />
</Button>
<Button variant="outline" onClick={() => setLocation("/analysis")} className="gap-2">
<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="text-center">
<Video className="h-8 w-8 mx-auto mb-2 opacity-30" />
<p>AI分析</p>
<p></p>
</div>
</div>
)}
@@ -231,7 +231,7 @@ export default function Dashboard() {
{/* Quick actions */}
<Card className="border-0 shadow-sm">
<CardHeader className="pb-2">
<CardTitle className="text-base font-semibold"></CardTitle>
<CardTitle className="text-base font-semibold"></CardTitle>
</CardHeader>
<CardContent>
<div className="grid grid-cols-1 sm:grid-cols-3 gap-3">
@@ -244,7 +244,7 @@ export default function Dashboard() {
</div>
<div>
<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>
</button>
<button
@@ -256,7 +256,7 @@ export default function Dashboard() {
</div>
<div>
<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>
</button>
<button
@@ -268,7 +268,7 @@ export default function Dashboard() {
</div>
<div>
<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>
</button>
</div>

查看文件

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

查看文件

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

查看文件

@@ -37,13 +37,13 @@ export default function Login() {
<Target className="w-8 h-8 text-primary" />
</div>
<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>
<Card className="border-0 shadow-xl">
<CardHeader className="text-center pb-2">
<CardTitle className="text-xl" data-testid="login-title"></CardTitle>
<CardDescription>使</CardDescription>
<CardTitle className="text-xl" data-testid="login-title"></CardTitle>
<CardDescription></CardDescription>
</CardHeader>
<CardContent>
<form onSubmit={handleLogin} className="space-y-4">
@@ -96,7 +96,7 @@ export default function Login() {
</Card>
<p className="text-center text-xs text-muted-foreground mt-6">
使
</p>
</div>
</div>

查看文件

@@ -96,7 +96,7 @@ export default function Training() {
<div className="flex items-center justify-between">
<div>
<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>
@@ -109,7 +109,7 @@ export default function Training() {
</CardTitle>
<CardDescription>
AI生成个性化训练方案
</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
@@ -149,7 +149,7 @@ export default function Training() {
className="w-full sm:w-auto gap-2"
>
{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" /></>
)}
@@ -186,7 +186,7 @@ export default function Training() {
) : (
<RefreshCw className="h-3 w-3" />
)}
</Button>
</div>
{activePlan.adjustmentNotes && (

查看文件

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

查看文件

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

查看文件

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

查看文件

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

查看文件

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