348 lines
9.6 KiB
Markdown
348 lines
9.6 KiB
Markdown
# Day 12 - iOS 兼容与移动端 UI 优化
|
||
|
||
**日期**:2026-01-28
|
||
|
||
---
|
||
|
||
## 🔐 Axios 全局拦截器优化
|
||
|
||
### 背景
|
||
统一处理 API 请求的认证失败场景,避免各页面重复处理 401/403 错误。
|
||
|
||
### 实现 (`frontend/src/shared/api/axios.ts`)
|
||
|
||
```typescript
|
||
import axios from 'axios';
|
||
|
||
// 动态获取 API 地址:服务端使用 localhost,客户端使用当前域名
|
||
const API_BASE = typeof window === 'undefined'
|
||
? 'http://localhost:8006'
|
||
: '';
|
||
|
||
// 防止重复跳转
|
||
let isRedirecting = false;
|
||
|
||
const api = axios.create({
|
||
baseURL: API_BASE,
|
||
withCredentials: true, // 自动携带 HttpOnly cookie
|
||
headers: { 'Content-Type': 'application/json' },
|
||
});
|
||
|
||
// 响应拦截器 - 全局处理 401/403
|
||
api.interceptors.response.use(
|
||
(response) => response,
|
||
async (error) => {
|
||
const status = error.response?.status;
|
||
|
||
if ((status === 401 || status === 403) && !isRedirecting) {
|
||
isRedirecting = true;
|
||
|
||
// 调用 logout API 清除 HttpOnly cookie
|
||
try {
|
||
await fetch('/api/auth/logout', { method: 'POST' });
|
||
} catch (e) { /* 忽略 */ }
|
||
|
||
// 跳转登录页
|
||
if (typeof window !== 'undefined') {
|
||
window.location.replace('/login');
|
||
}
|
||
}
|
||
return Promise.reject(error);
|
||
}
|
||
);
|
||
|
||
export default api;
|
||
```
|
||
|
||
### 关键特性
|
||
- ✅ **自动携带 Cookie**: `withCredentials: true` 确保 HttpOnly JWT cookie 被发送
|
||
- ✅ **401/403 自动跳转**: 认证失败时自动清理并跳转登录页
|
||
- ✅ **防重复跳转**: `isRedirecting` 标志避免多个请求同时触发跳转
|
||
- ✅ **SSR 兼容**: 服务端渲染时使用 `localhost`,客户端使用相对路径
|
||
|
||
---
|
||
|
||
## 🔧 iOS Safari 安全区域白边修复
|
||
|
||
### 问题描述
|
||
iPhone Safari 浏览器底部和顶部显示白色区域,安卓正常。原因是 iOS Safari 有安全区域 (Safe Area),页面背景没有延伸到该区域。
|
||
|
||
### 根本原因
|
||
1. 缺少 `viewport-fit=cover` 配置
|
||
2. `min-h-screen` (100vh) 在 iOS Safari 中不包含安全区域
|
||
3. 背景渐变在页面 div 上,而非 body 上,导致安全区域显示纯色
|
||
|
||
### 解决方案
|
||
|
||
#### 1. 添加 viewport 配置 (`layout.tsx`)
|
||
```typescript
|
||
export const viewport: Viewport = {
|
||
width: 'device-width',
|
||
initialScale: 1,
|
||
viewportFit: 'cover', // 允许内容延伸到安全区域
|
||
themeColor: '#0f172a', // 顶部状态栏颜色
|
||
};
|
||
```
|
||
|
||
#### 2. 统一渐变背景到 body (`layout.tsx`)
|
||
```tsx
|
||
<html lang="en" style={{ backgroundColor: '#0f172a' }}>
|
||
<body
|
||
style={{
|
||
margin: 0,
|
||
minHeight: '100dvh',
|
||
background: 'linear-gradient(to bottom, #0f172a 0%, #0f172a 5%, #581c87 50%, #0f172a 95%, #0f172a 100%)',
|
||
}}
|
||
>
|
||
{children}
|
||
</body>
|
||
</html>
|
||
```
|
||
|
||
#### 3. CSS 安全区域支持 (`globals.css`)
|
||
```css
|
||
html {
|
||
background-color: #0f172a !important;
|
||
min-height: 100%;
|
||
}
|
||
|
||
body {
|
||
margin: 0 !important;
|
||
min-height: 100dvh;
|
||
padding-top: env(safe-area-inset-top);
|
||
padding-bottom: env(safe-area-inset-bottom);
|
||
}
|
||
```
|
||
|
||
#### 4. 移除页面独立渐变背景
|
||
各页面的根 div 移除 `bg-gradient-to-br` 类,统一使用 body 渐变:
|
||
- `page.tsx`
|
||
- `login/page.tsx`
|
||
- `publish/page.tsx`
|
||
- `admin/page.tsx`
|
||
- `register/page.tsx`
|
||
|
||
### 结果
|
||
- ✅ 顶部状态栏颜色与页面一致 (themeColor)
|
||
- ✅ 底部安全区域颜色与渐变边缘一致
|
||
- ✅ 消除分层感,背景统一
|
||
|
||
---
|
||
|
||
## 📱 移动端 Header 响应式优化
|
||
|
||
### 问题描述
|
||
移动端顶部导航按钮(视频生成、发布管理、退出)过于拥挤,文字换行显示。
|
||
|
||
### 解决方案
|
||
|
||
#### 首页 Header (`page.tsx`)
|
||
```tsx
|
||
<header className="border-b border-white/10 bg-black/20 backdrop-blur-sm">
|
||
<div className="max-w-6xl mx-auto px-4 sm:px-6 py-3 sm:py-4 flex items-center justify-between">
|
||
<Link href="/" className="text-xl sm:text-2xl font-bold ...">
|
||
<span className="text-3xl sm:text-4xl">🎬</span>
|
||
ViGent
|
||
</Link>
|
||
<div className="flex items-center gap-1 sm:gap-4">
|
||
<span className="px-2 sm:px-4 py-1 sm:py-2 text-sm sm:text-base ...">
|
||
视频生成
|
||
</span>
|
||
<!-- 其他按钮同样处理 -->
|
||
</div>
|
||
</div>
|
||
</header>
|
||
```
|
||
|
||
#### 发布管理页 Header (`publish/page.tsx`)
|
||
同步应用相同的响应式类名。
|
||
|
||
### 关键改动
|
||
| 属性 | 移动端 | 桌面端 |
|
||
|------|--------|--------|
|
||
| 容器内边距 | `px-4 py-3` | `px-6 py-4` |
|
||
| 按钮间距 | `gap-1` | `gap-4` |
|
||
| 按钮内边距 | `px-2 py-1` | `px-4 py-2` |
|
||
| 字体大小 | `text-sm` | `text-base` |
|
||
| Logo 大小 | `text-xl` + `text-3xl` | `text-2xl` + `text-4xl` |
|
||
|
||
### 结果
|
||
- ✅ 移动端按钮紧凑排列,不再换行
|
||
- ✅ 桌面端保持原有宽松布局
|
||
|
||
---
|
||
|
||
## 🚀 发布页面 UI 重构
|
||
|
||
### 问题描述
|
||
原有设计将"发布时间"选项放在表单中,用户可能误选"定时发布"但忘记设置时间。
|
||
|
||
### 解决方案
|
||
将"一键发布"按钮改为两个独立按钮:
|
||
- **立即发布** (绿色,占 3/4 宽度) - 主要操作
|
||
- **定时** (占 1/4 宽度) - 点击展开时间选择器
|
||
|
||
#### 新布局 (`publish/page.tsx`)
|
||
```tsx
|
||
{/* 发布按钮区域 */}
|
||
<div className="space-y-3">
|
||
<div className="flex gap-3">
|
||
{/* 立即发布 - 占 3/4 */}
|
||
<button
|
||
onClick={() => { setScheduleMode("now"); handlePublish(); }}
|
||
className="flex-[3] py-4 rounded-xl font-bold text-lg bg-gradient-to-r from-green-600 to-teal-600 ..."
|
||
>
|
||
🚀 立即发布
|
||
</button>
|
||
|
||
{/* 定时发布 - 占 1/4 */}
|
||
<button
|
||
onClick={() => setScheduleMode(scheduleMode === "scheduled" ? "now" : "scheduled")}
|
||
className="flex-1 py-4 rounded-xl font-bold text-base ..."
|
||
>
|
||
⏰ 定时
|
||
</button>
|
||
</div>
|
||
|
||
{/* 定时发布时间选择器 (展开时显示) */}
|
||
{scheduleMode === "scheduled" && (
|
||
<div className="flex gap-3 items-center">
|
||
<input type="datetime-local" ... />
|
||
<button>确认定时</button>
|
||
</div>
|
||
)}
|
||
</div>
|
||
```
|
||
|
||
### 结果
|
||
- ✅ 主操作(立即发布)更醒目
|
||
- ✅ 定时发布需要二次确认,防止误触
|
||
- ✅ 从表单区域移除发布时间选项,界面更简洁
|
||
|
||
---
|
||
|
||
## 🛤️ 后续优化项
|
||
|
||
### 后端定时发布 (待实现)
|
||
**当前状态**:定时发布使用平台端定时(在各平台设置发布时间)
|
||
|
||
**优化方向**:改为后端定时任务
|
||
- 使用 APScheduler 实现任务调度
|
||
- 存储定时任务到数据库
|
||
- 到时间后端自动触发发布 API
|
||
- 支持查看/取消定时任务
|
||
|
||
**优势**:
|
||
- 统一逻辑,不依赖平台定时 UI
|
||
- 更灵活,可管理定时任务
|
||
- 平台页面更新不影响功能
|
||
|
||
---
|
||
|
||
## 🤖 Qwen3-TTS 0.6B 声音克隆部署
|
||
|
||
### 背景
|
||
为实现用户自定义声音克隆功能,部署 Qwen3-TTS 0.6B-Base 模型,支持 3 秒参考音频快速克隆。
|
||
|
||
### GPU 分配
|
||
| GPU | 服务 | 模型 |
|
||
|-----|------|------|
|
||
| GPU0 | Qwen3-TTS | 0.6B-Base (声音克隆) |
|
||
| GPU1 | LatentSync | 1.6 (唇形同步) |
|
||
|
||
### 部署步骤
|
||
|
||
#### 1. 克隆仓库
|
||
```bash
|
||
cd /home/rongye/ProgramFiles/ViGent2/models
|
||
git clone https://github.com/QwenLM/Qwen3-TTS.git
|
||
```
|
||
|
||
#### 2. 创建 conda 环境
|
||
```bash
|
||
conda create -n qwen-tts python=3.10 -y
|
||
conda activate qwen-tts
|
||
```
|
||
|
||
#### 3. 安装依赖
|
||
```bash
|
||
cd Qwen3-TTS
|
||
pip install -e .
|
||
conda install -y -c conda-forge sox # 音频处理依赖
|
||
```
|
||
|
||
#### 4. 下载模型权重 (使用 ModelScope,国内更快)
|
||
```bash
|
||
pip install modelscope
|
||
# Tokenizer (651MB)
|
||
modelscope download --model Qwen/Qwen3-TTS-Tokenizer-12Hz --local_dir ./checkpoints/Tokenizer
|
||
# 0.6B-Base 模型 (2.4GB)
|
||
modelscope download --model Qwen/Qwen3-TTS-12Hz-0.6B-Base --local_dir ./checkpoints/0.6B-Base
|
||
```
|
||
|
||
#### 5. 测试推理
|
||
```python
|
||
# test_inference.py
|
||
import torch
|
||
import soundfile as sf
|
||
from qwen_tts import Qwen3TTSModel
|
||
|
||
model = Qwen3TTSModel.from_pretrained(
|
||
"./checkpoints/0.6B-Base",
|
||
device_map="cuda:0",
|
||
dtype=torch.bfloat16,
|
||
)
|
||
|
||
wavs, sr = model.generate_voice_clone(
|
||
text="测试文本",
|
||
language="Chinese",
|
||
ref_audio="./examples/myvoice.wav",
|
||
ref_text="参考音频的文字内容",
|
||
)
|
||
sf.write("output.wav", wavs[0], sr)
|
||
```
|
||
|
||
### 测试结果
|
||
- ✅ 模型加载成功 (GPU0)
|
||
- ✅ 声音克隆推理成功
|
||
- ✅ 输出音频 24000Hz,质量良好
|
||
|
||
### 目录结构
|
||
```
|
||
models/Qwen3-TTS/
|
||
├── checkpoints/
|
||
│ ├── Tokenizer/ # 651MB
|
||
│ └── 0.6B-Base/ # 2.4GB
|
||
├── qwen_tts/ # 源码
|
||
├── examples/
|
||
│ └── myvoice.wav # 参考音频
|
||
└── test_inference.py # 测试脚本
|
||
```
|
||
|
||
---
|
||
|
||
## 📁 今日修改文件清单
|
||
|
||
| 文件 | 变更类型 | 说明 |
|
||
|------|----------|------|
|
||
| `frontend/src/shared/api/axios.ts` | 修改 | Axios 全局拦截器 (401/403 自动跳转) |
|
||
| `frontend/src/app/layout.tsx` | 修改 | viewport 配置 + body 渐变背景 |
|
||
| `frontend/src/app/globals.css` | 修改 | 安全区域 CSS 支持 |
|
||
| `frontend/src/app/page.tsx` | 修改 | 移除独立渐变 + Header 响应式 |
|
||
| `frontend/src/app/login/page.tsx` | 修改 | 移除独立渐变 |
|
||
| `frontend/src/app/publish/page.tsx` | 修改 | Header 响应式 + 发布按钮重构 |
|
||
| `frontend/src/app/admin/page.tsx` | 修改 | 移除独立渐变 |
|
||
| `frontend/src/app/register/page.tsx` | 修改 | 移除独立渐变 |
|
||
| `README.md` | 修改 | 添加 "iOS/Android 移动端适配" 功能说明 |
|
||
| `Docs/FRONTEND_DEV.md` | 修改 | iOS Safari 安全区域兼容规范 + 移动端响应式规则 |
|
||
| `models/Qwen3-TTS/` | 新增 | Qwen3-TTS 声音克隆模型部署 |
|
||
| `Docs/QWEN3_TTS_DEPLOY.md` | 新增 | Qwen3-TTS 部署指南 |
|
||
|
||
---
|
||
|
||
## 🔗 相关文档
|
||
|
||
- [TASK_COMPLETE.md](../TASK_COMPLETE.md) - 任务总览
|
||
- [Day11.md](./Day11.md) - 上传架构重构
|
||
- [QWEN3_TTS_DEPLOY.md](../QWEN3_TTS_DEPLOY.md) - Qwen3-TTS 部署指南
|