feat: update README and Settings page for dual backend support and Aliyun API key management
This commit is contained in:
62
README.md
62
README.md
@@ -9,6 +9,7 @@ A text-to-speech web application based on Qwen3-TTS, supporting custom voice, vo
|
|||||||
- Custom Voice: Predefined speaker voices
|
- Custom Voice: Predefined speaker voices
|
||||||
- Voice Design: Create voices from natural language descriptions
|
- Voice Design: Create voices from natural language descriptions
|
||||||
- Voice Cloning: Clone voices from uploaded audio
|
- Voice Cloning: Clone voices from uploaded audio
|
||||||
|
- Dual Backend Support: Switch between local model and Aliyun TTS API
|
||||||
- JWT auth, async tasks, voice cache, dark mode
|
- JWT auth, async tasks, voice cache, dark mode
|
||||||
|
|
||||||
## Tech Stack
|
## Tech Stack
|
||||||
@@ -26,7 +27,9 @@ python -m venv venv
|
|||||||
source venv/bin/activate
|
source venv/bin/activate
|
||||||
pip install -r requirements.txt
|
pip install -r requirements.txt
|
||||||
cp .env.example .env
|
cp .env.example .env
|
||||||
# Edit .env to configure MODEL_BASE_PATH etc.
|
# Edit .env to configure MODEL_BASE_PATH and DEFAULT_BACKEND
|
||||||
|
# For local model: Ensure MODEL_BASE_PATH points to Qwen model directory
|
||||||
|
# For Aliyun: Set DEFAULT_BACKEND=aliyun and configure API key in web settings
|
||||||
uvicorn main:app --host 0.0.0.0 --port 8000 --reload
|
uvicorn main:app --host 0.0.0.0 --port 8000 --reload
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -49,6 +52,8 @@ Visit `http://localhost:5173`
|
|||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
|
### Backend Configuration
|
||||||
|
|
||||||
Backend `.env` key settings:
|
Backend `.env` key settings:
|
||||||
|
|
||||||
```env
|
```env
|
||||||
@@ -56,26 +61,75 @@ SECRET_KEY=your-secret-key
|
|||||||
MODEL_DEVICE=cuda:0
|
MODEL_DEVICE=cuda:0
|
||||||
MODEL_BASE_PATH=../Qwen
|
MODEL_BASE_PATH=../Qwen
|
||||||
DATABASE_URL=sqlite:///./qwen_tts.db
|
DATABASE_URL=sqlite:///./qwen_tts.db
|
||||||
|
|
||||||
|
DEFAULT_BACKEND=local
|
||||||
|
|
||||||
|
ALIYUN_REGION=beijing
|
||||||
|
ALIYUN_MODEL_FLASH=qwen3-tts-flash-realtime
|
||||||
|
ALIYUN_MODEL_VC=qwen3-tts-vc-realtime-2026-01-15
|
||||||
|
ALIYUN_MODEL_VD=qwen3-tts-vd-realtime-2026-01-15
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**Backend Options:**
|
||||||
|
|
||||||
|
- `DEFAULT_BACKEND`: Default TTS backend, options: `local` or `aliyun`
|
||||||
|
- **Local Mode**: Uses local Qwen3-TTS model (requires `MODEL_BASE_PATH` configuration)
|
||||||
|
- **Aliyun Mode**: Uses Aliyun TTS API (requires users to configure their API keys in settings)
|
||||||
|
|
||||||
|
**Aliyun Configuration:**
|
||||||
|
|
||||||
|
- Users need to add their Aliyun API keys in the web interface settings page
|
||||||
|
- API keys are encrypted and stored securely in the database
|
||||||
|
- Superuser can enable/disable local model access for all users
|
||||||
|
- To obtain an Aliyun API key, visit the [Aliyun Console](https://dashscope.console.aliyun.com/)
|
||||||
|
|
||||||
|
### Frontend Configuration
|
||||||
|
|
||||||
Frontend `.env`:
|
Frontend `.env`:
|
||||||
|
|
||||||
```env
|
```env
|
||||||
VITE_API_URL=http://localhost:8000
|
VITE_API_URL=http://localhost:8000
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
### Switching Between Backends
|
||||||
|
|
||||||
|
1. Log in to the web interface
|
||||||
|
2. Navigate to Settings page
|
||||||
|
3. Configure your preferred backend:
|
||||||
|
- **Local Model**: Select "本地模型" (requires local model to be enabled by superuser)
|
||||||
|
- **Aliyun API**: Select "阿里云" and add your API key
|
||||||
|
4. The selected backend will be used for all TTS operations by default
|
||||||
|
5. You can also specify a different backend per request using the `backend` parameter in the API
|
||||||
|
|
||||||
|
### Managing Aliyun API Key
|
||||||
|
|
||||||
|
1. In Settings page, find the "阿里云 API 密钥" section
|
||||||
|
2. Enter your Aliyun API key
|
||||||
|
3. Click "更新密钥" to save and validate
|
||||||
|
4. The system will verify the key before saving
|
||||||
|
5. You can delete the key anytime using the delete button
|
||||||
|
|
||||||
## API
|
## API
|
||||||
|
|
||||||
```
|
```
|
||||||
POST /auth/register - Register
|
POST /auth/register - Register
|
||||||
POST /auth/token - Login
|
POST /auth/token - Login
|
||||||
POST /tts/custom-voice - Custom voice
|
POST /tts/custom-voice - Custom voice (supports backend parameter)
|
||||||
POST /tts/voice-design - Voice design
|
POST /tts/voice-design - Voice design (supports backend parameter)
|
||||||
POST /tts/voice-clone - Voice cloning
|
POST /tts/voice-clone - Voice cloning (supports backend parameter)
|
||||||
GET /jobs - Job list
|
GET /jobs - Job list
|
||||||
GET /jobs/{id}/download - Download result
|
GET /jobs/{id}/download - Download result
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**Backend Parameter:**
|
||||||
|
|
||||||
|
All TTS endpoints support an optional `backend` parameter to specify the TTS backend:
|
||||||
|
- `backend: "local"` - Use local Qwen3-TTS model
|
||||||
|
- `backend: "aliyun"` - Use Aliyun TTS API
|
||||||
|
- If not specified, uses the user's default backend setting
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
Apache-2.0 license
|
Apache-2.0 license
|
||||||
|
|||||||
62
README.zh.md
62
README.zh.md
@@ -9,6 +9,7 @@
|
|||||||
- 自定义语音:预定义说话人语音
|
- 自定义语音:预定义说话人语音
|
||||||
- 语音设计:自然语言描述创建语音
|
- 语音设计:自然语言描述创建语音
|
||||||
- 语音克隆:上传音频克隆语音
|
- 语音克隆:上传音频克隆语音
|
||||||
|
- 双后端支持:支持本地模型和阿里云 TTS API 切换
|
||||||
- JWT 认证、异步任务、语音缓存、暗黑模式
|
- JWT 认证、异步任务、语音缓存、暗黑模式
|
||||||
|
|
||||||
## 技术栈
|
## 技术栈
|
||||||
@@ -26,7 +27,9 @@ python -m venv venv
|
|||||||
source venv/bin/activate
|
source venv/bin/activate
|
||||||
pip install -r requirements.txt
|
pip install -r requirements.txt
|
||||||
cp .env.example .env
|
cp .env.example .env
|
||||||
# 编辑 .env 配置 MODEL_BASE_PATH 等
|
# 编辑 .env 配置 MODEL_BASE_PATH 和 DEFAULT_BACKEND
|
||||||
|
# 本地模型:确保 MODEL_BASE_PATH 指向 Qwen 模型目录
|
||||||
|
# 阿里云:设置 DEFAULT_BACKEND=aliyun 并在 Web 设置页面配置 API 密钥
|
||||||
uvicorn main:app --host 0.0.0.0 --port 8000 --reload
|
uvicorn main:app --host 0.0.0.0 --port 8000 --reload
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -49,6 +52,8 @@ npm run dev
|
|||||||
|
|
||||||
## 配置
|
## 配置
|
||||||
|
|
||||||
|
### 后端配置
|
||||||
|
|
||||||
后端 `.env` 关键配置:
|
后端 `.env` 关键配置:
|
||||||
|
|
||||||
```env
|
```env
|
||||||
@@ -56,26 +61,75 @@ SECRET_KEY=your-secret-key
|
|||||||
MODEL_DEVICE=cuda:0
|
MODEL_DEVICE=cuda:0
|
||||||
MODEL_BASE_PATH=../Qwen
|
MODEL_BASE_PATH=../Qwen
|
||||||
DATABASE_URL=sqlite:///./qwen_tts.db
|
DATABASE_URL=sqlite:///./qwen_tts.db
|
||||||
|
|
||||||
|
DEFAULT_BACKEND=local
|
||||||
|
|
||||||
|
ALIYUN_REGION=beijing
|
||||||
|
ALIYUN_MODEL_FLASH=qwen3-tts-flash-realtime
|
||||||
|
ALIYUN_MODEL_VC=qwen3-tts-vc-realtime-2026-01-15
|
||||||
|
ALIYUN_MODEL_VD=qwen3-tts-vd-realtime-2026-01-15
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**后端选项:**
|
||||||
|
|
||||||
|
- `DEFAULT_BACKEND`: 默认 TTS 后端,可选值:`local` 或 `aliyun`
|
||||||
|
- **本地模式**: 使用本地 Qwen3-TTS 模型(需要配置 `MODEL_BASE_PATH`)
|
||||||
|
- **阿里云模式**: 使用阿里云 TTS API(需要用户在设置页面配置 API 密钥)
|
||||||
|
|
||||||
|
**阿里云配置:**
|
||||||
|
|
||||||
|
- 用户需要在 Web 界面的设置页面添加阿里云 API 密钥
|
||||||
|
- API 密钥经过加密后安全存储在数据库中
|
||||||
|
- 超级管理员可以控制是否为所有用户启用本地模型
|
||||||
|
- 获取阿里云 API 密钥,请访问 [阿里云控制台](https://dashscope.console.aliyun.com/)
|
||||||
|
|
||||||
|
### 前端配置
|
||||||
|
|
||||||
前端 `.env`:
|
前端 `.env`:
|
||||||
|
|
||||||
```env
|
```env
|
||||||
VITE_API_URL=http://localhost:8000
|
VITE_API_URL=http://localhost:8000
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## 使用说明
|
||||||
|
|
||||||
|
### 切换后端
|
||||||
|
|
||||||
|
1. 登录 Web 界面
|
||||||
|
2. 进入设置页面
|
||||||
|
3. 配置您偏好的后端:
|
||||||
|
- **本地模型**:选择"本地模型"(需要超级管理员启用本地模型)
|
||||||
|
- **阿里云 API**:选择"阿里云"并添加您的 API 密钥
|
||||||
|
4. 选择的后端将默认用于所有 TTS 操作
|
||||||
|
5. 也可以通过 API 的 `backend` 参数为单次请求指定不同的后端
|
||||||
|
|
||||||
|
### 管理阿里云 API 密钥
|
||||||
|
|
||||||
|
1. 在设置页面找到"阿里云 API 密钥"部分
|
||||||
|
2. 输入您的阿里云 API 密钥
|
||||||
|
3. 点击"更新密钥"保存并验证
|
||||||
|
4. 系统会在保存前验证密钥的有效性
|
||||||
|
5. 可随时使用删除按钮删除密钥
|
||||||
|
|
||||||
## API
|
## API
|
||||||
|
|
||||||
```
|
```
|
||||||
POST /auth/register - 注册
|
POST /auth/register - 注册
|
||||||
POST /auth/token - 登录
|
POST /auth/token - 登录
|
||||||
POST /tts/custom-voice - 自定义语音
|
POST /tts/custom-voice - 自定义语音(支持 backend 参数)
|
||||||
POST /tts/voice-design - 语音设计
|
POST /tts/voice-design - 语音设计(支持 backend 参数)
|
||||||
POST /tts/voice-clone - 语音克隆
|
POST /tts/voice-clone - 语音克隆(支持 backend 参数)
|
||||||
GET /jobs - 任务列表
|
GET /jobs - 任务列表
|
||||||
GET /jobs/{id}/download - 下载结果
|
GET /jobs/{id}/download - 下载结果
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**Backend 参数:**
|
||||||
|
|
||||||
|
所有 TTS 接口都支持可选的 `backend` 参数来指定使用的 TTS 后端:
|
||||||
|
- `backend: "local"` - 使用本地 Qwen3-TTS 模型
|
||||||
|
- `backend: "aliyun"` - 使用阿里云 TTS API
|
||||||
|
- 如果不指定,则使用用户的默认后端设置
|
||||||
|
|
||||||
## 许可证
|
## 许可证
|
||||||
|
|
||||||
Apache-2.0 license
|
Apache-2.0 license
|
||||||
|
|||||||
@@ -154,24 +154,24 @@ export default function Settings() {
|
|||||||
<div className="h-screen overflow-hidden flex flex-col bg-background">
|
<div className="h-screen overflow-hidden flex flex-col bg-background">
|
||||||
<Navbar />
|
<Navbar />
|
||||||
|
|
||||||
<main className="flex-1 overflow-y-auto container mx-auto p-6 max-w-[800px]">
|
<main className="flex-1 overflow-y-auto container mx-auto p-3 sm:p-6 max-w-[800px]">
|
||||||
<div className="space-y-6">
|
<div className="space-y-3 sm:space-y-6">
|
||||||
<div>
|
<div>
|
||||||
<h1 className="text-3xl font-bold">设置</h1>
|
<h1 className="text-2xl sm:text-3xl font-bold">设置</h1>
|
||||||
<p className="text-muted-foreground mt-2">管理您的账户设置和偏好</p>
|
<p className="text-sm sm:text-base text-muted-foreground mt-1 sm:mt-2">管理您的账户设置和偏好</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Card>
|
<Card>
|
||||||
<CardHeader>
|
<CardHeader className="p-4 sm:p-6">
|
||||||
<CardTitle>后端偏好</CardTitle>
|
<CardTitle className="text-lg sm:text-xl">后端偏好</CardTitle>
|
||||||
<CardDescription>选择默认的 TTS 后端模式</CardDescription>
|
<CardDescription className="text-sm">选择默认的 TTS 后端模式</CardDescription>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent>
|
<CardContent className="p-4 sm:p-6">
|
||||||
<RadioGroup
|
<RadioGroup
|
||||||
value={preferences.default_backend}
|
value={preferences.default_backend}
|
||||||
onValueChange={handleBackendChange}
|
onValueChange={handleBackendChange}
|
||||||
>
|
>
|
||||||
<div className={`flex items-center space-x-3 border rounded-lg p-4 ${
|
<div className={`flex items-center space-x-2 sm:space-x-3 border rounded-lg p-3 sm:p-4 ${
|
||||||
!isBackendAvailable('local') ? 'opacity-50' : 'hover:bg-accent/50 cursor-pointer'
|
!isBackendAvailable('local') ? 'opacity-50' : 'hover:bg-accent/50 cursor-pointer'
|
||||||
}`}>
|
}`}>
|
||||||
<RadioGroupItem
|
<RadioGroupItem
|
||||||
@@ -180,18 +180,18 @@ export default function Settings() {
|
|||||||
disabled={!isBackendAvailable('local')}
|
disabled={!isBackendAvailable('local')}
|
||||||
/>
|
/>
|
||||||
<Label htmlFor="backend-local" className="flex-1 cursor-pointer">
|
<Label htmlFor="backend-local" className="flex-1 cursor-pointer">
|
||||||
<div className="font-medium">本地模型</div>
|
<div className="font-medium text-sm sm:text-base">本地模型</div>
|
||||||
<div className="text-sm text-muted-foreground">
|
<div className="text-xs sm:text-sm text-muted-foreground">
|
||||||
免费使用本地 Qwen3-TTS 模型
|
免费使用本地 Qwen3-TTS 模型
|
||||||
{!isBackendAvailable('local') && ' (管理员未启用)'}
|
{!isBackendAvailable('local') && ' (管理员未启用)'}
|
||||||
</div>
|
</div>
|
||||||
</Label>
|
</Label>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center space-x-3 border rounded-lg p-4 hover:bg-accent/50 cursor-pointer">
|
<div className="flex items-center space-x-2 sm:space-x-3 border rounded-lg p-3 sm:p-4 hover:bg-accent/50 cursor-pointer">
|
||||||
<RadioGroupItem value="aliyun" id="backend-aliyun" />
|
<RadioGroupItem value="aliyun" id="backend-aliyun" />
|
||||||
<Label htmlFor="backend-aliyun" className="flex-1 cursor-pointer">
|
<Label htmlFor="backend-aliyun" className="flex-1 cursor-pointer">
|
||||||
<div className="font-medium">阿里云 API</div>
|
<div className="font-medium text-sm sm:text-base">阿里云 API</div>
|
||||||
<div className="text-sm text-muted-foreground">使用阿里云 TTS 服务</div>
|
<div className="text-xs sm:text-sm text-muted-foreground">使用阿里云 TTS 服务</div>
|
||||||
</Label>
|
</Label>
|
||||||
</div>
|
</div>
|
||||||
</RadioGroup>
|
</RadioGroup>
|
||||||
@@ -199,21 +199,21 @@ export default function Settings() {
|
|||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
<Card>
|
<Card>
|
||||||
<CardHeader>
|
<CardHeader className="p-4 sm:p-6">
|
||||||
<CardTitle>阿里云 API 密钥</CardTitle>
|
<CardTitle className="text-lg sm:text-xl">阿里云 API 密钥</CardTitle>
|
||||||
<CardDescription>管理您的阿里云 API 密钥配置</CardDescription>
|
<CardDescription className="text-sm">管理您的阿里云 API 密钥配置</CardDescription>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="space-y-4">
|
<CardContent className="space-y-3 sm:space-y-4 p-4 sm:p-6">
|
||||||
<div className="flex items-center gap-2 text-sm">
|
<div className="flex items-center gap-2 text-xs sm:text-sm">
|
||||||
<span className="text-muted-foreground">当前状态:</span>
|
<span className="text-muted-foreground">当前状态:</span>
|
||||||
{hasAliyunKey ? (
|
{hasAliyunKey ? (
|
||||||
<span className="flex items-center gap-1 text-green-600">
|
<span className="flex items-center gap-1 text-green-600">
|
||||||
<Check className="h-4 w-4" />
|
<Check className="h-3 w-3 sm:h-4 sm:w-4" />
|
||||||
已配置并有效
|
已配置并有效
|
||||||
</span>
|
</span>
|
||||||
) : (
|
) : (
|
||||||
<span className="flex items-center gap-1 text-muted-foreground">
|
<span className="flex items-center gap-1 text-muted-foreground">
|
||||||
<X className="h-4 w-4" />
|
<X className="h-3 w-3 sm:h-4 sm:w-4" />
|
||||||
未配置
|
未配置
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
@@ -226,7 +226,7 @@ export default function Settings() {
|
|||||||
name="api_key"
|
name="api_key"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>API 密钥</FormLabel>
|
<FormLabel className="text-sm sm:text-base">API 密钥</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<div className="relative flex-1">
|
<div className="relative flex-1">
|
||||||
@@ -257,8 +257,8 @@ export default function Settings() {
|
|||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="flex gap-2">
|
<div className="flex flex-wrap gap-2">
|
||||||
<Button type="submit" disabled={isLoading}>
|
<Button type="submit" disabled={isLoading} className="flex-1 sm:flex-initial">
|
||||||
{isLoading ? '更新中...' : hasAliyunKey ? '更新密钥' : '添加密钥'}
|
{isLoading ? '更新中...' : hasAliyunKey ? '更新密钥' : '添加密钥'}
|
||||||
</Button>
|
</Button>
|
||||||
{hasAliyunKey && (
|
{hasAliyunKey && (
|
||||||
@@ -268,6 +268,7 @@ export default function Settings() {
|
|||||||
variant="outline"
|
variant="outline"
|
||||||
onClick={handleVerifyKey}
|
onClick={handleVerifyKey}
|
||||||
disabled={isLoading}
|
disabled={isLoading}
|
||||||
|
className="flex-1 sm:flex-initial"
|
||||||
>
|
>
|
||||||
验证密钥
|
验证密钥
|
||||||
</Button>
|
</Button>
|
||||||
@@ -276,9 +277,11 @@ export default function Settings() {
|
|||||||
variant="destructive"
|
variant="destructive"
|
||||||
onClick={handleDeleteKey}
|
onClick={handleDeleteKey}
|
||||||
disabled={isLoading}
|
disabled={isLoading}
|
||||||
|
size="icon"
|
||||||
|
className="sm:w-auto sm:px-4"
|
||||||
>
|
>
|
||||||
<Trash2 className="h-4 w-4 mr-2" />
|
<Trash2 className="h-4 w-4" />
|
||||||
删除密钥
|
<span className="hidden sm:inline sm:ml-2">删除密钥</span>
|
||||||
</Button>
|
</Button>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
@@ -290,16 +293,16 @@ export default function Settings() {
|
|||||||
|
|
||||||
{user.is_superuser && (
|
{user.is_superuser && (
|
||||||
<Card>
|
<Card>
|
||||||
<CardHeader>
|
<CardHeader className="p-4 sm:p-6">
|
||||||
<CardTitle>系统设置</CardTitle>
|
<CardTitle className="text-lg sm:text-xl">系统设置</CardTitle>
|
||||||
<CardDescription>管理全局系统设置(仅管理员可见)</CardDescription>
|
<CardDescription className="text-sm">管理全局系统设置(仅管理员可见)</CardDescription>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent>
|
<CardContent className="p-4 sm:p-6">
|
||||||
<div className="space-y-4">
|
<div className="space-y-3 sm:space-y-4">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-start sm:items-center justify-between gap-4">
|
||||||
<div className="space-y-0.5">
|
<div className="space-y-0.5 flex-1">
|
||||||
<Label htmlFor="local-model-toggle">启用本地模型</Label>
|
<Label htmlFor="local-model-toggle" className="text-sm sm:text-base">启用本地模型</Label>
|
||||||
<p className="text-sm text-muted-foreground">
|
<p className="text-xs sm:text-sm text-muted-foreground">
|
||||||
允许普通用户在设置中选择并使用本地 Qwen3-TTS 模型
|
允许普通用户在设置中选择并使用本地 Qwen3-TTS 模型
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -307,6 +310,7 @@ export default function Settings() {
|
|||||||
id="local-model-toggle"
|
id="local-model-toggle"
|
||||||
checked={localModelEnabled}
|
checked={localModelEnabled}
|
||||||
onCheckedChange={handleToggleLocalModel}
|
onCheckedChange={handleToggleLocalModel}
|
||||||
|
className="shrink-0"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -315,21 +319,21 @@ export default function Settings() {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
<Card>
|
<Card>
|
||||||
<CardHeader>
|
<CardHeader className="p-4 sm:p-6">
|
||||||
<CardTitle>账户信息</CardTitle>
|
<CardTitle className="text-lg sm:text-xl">账户信息</CardTitle>
|
||||||
<CardDescription>您的账户基本信息</CardDescription>
|
<CardDescription className="text-sm">您的账户基本信息</CardDescription>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="space-y-4">
|
<CardContent className="space-y-3 sm:space-y-4 p-4 sm:p-6">
|
||||||
<div className="grid gap-2">
|
<div className="grid gap-1.5 sm:gap-2">
|
||||||
<Label>用户名</Label>
|
<Label className="text-sm sm:text-base">用户名</Label>
|
||||||
<Input value={user.username} disabled />
|
<Input value={user.username} disabled />
|
||||||
</div>
|
</div>
|
||||||
<div className="grid gap-2">
|
<div className="grid gap-1.5 sm:gap-2">
|
||||||
<Label>邮箱</Label>
|
<Label className="text-sm sm:text-base">邮箱</Label>
|
||||||
<Input value={user.email} disabled />
|
<Input value={user.email} disabled />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<Button onClick={() => setShowPasswordDialog(true)}>修改密码</Button>
|
<Button onClick={() => setShowPasswordDialog(true)} className="w-full sm:w-auto">修改密码</Button>
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|||||||
Reference in New Issue
Block a user