8413713315
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (push) Successful in 2s
CI/CD Pipeline / Check push changed paths (pull_request) Has been skipped
CI/CD Pipeline / PR Build API Image (push) Has been skipped
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 3s
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 3s
CI/CD Pipeline / Frontend Lint (push) Has been skipped
CI/CD Pipeline / Validate - Security (pull_request) Has been skipped
CI/CD Pipeline / Validate - Style (pull_request) Has been skipped
CI/CD Pipeline / PR Build Worker Image (push) Has been skipped
CI/CD Pipeline / PR Build Web Image (push) Has been skipped
CI/CD Pipeline / Check push changed paths (push) Successful in 10s
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Has been skipped
CI/CD Pipeline / Unit Tests (pull_request) Has been skipped
CI/CD Pipeline / Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / Frontend Lint (pull_request) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been skipped
CI/CD Pipeline / Build Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been skipped
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 1m32s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 3m12s
CI/CD Pipeline / Integration Tests (push) Successful in 3m12s
CI/CD Pipeline / Build Production API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Has been skipped
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Deploy Production (pull_request) Has been skipped
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been skipped
CI/CD Pipeline / Production Browser E2E (pull_request) Has been skipped
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been skipped
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been skipped
CI/CD Pipeline / Canary Release to Production (pull_request) Has been skipped
CI/CD Pipeline / Validate - Python (mypy + alembic) (push) Successful in 3m38s
CI/CD Pipeline / Validate - Style (push) Successful in 3m57s
CI/CD Pipeline / PR Build Web Image (pull_request) Successful in 3m53s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 2m33s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 4m0s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 4m0s
CI/CD Pipeline / CI Gate (pull_request) Successful in 1s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 1m6s
CI/CD Pipeline / Build Staging API Image (push) Successful in 3m50s
CI/CD Pipeline / Retag skipped Staging Web Image (push) Has been skipped
CI/CD Pipeline / Retag skipped Staging Worker Image (push) Has been skipped
CI/CD Pipeline / Retag skipped Staging API Image (push) Has been skipped
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 38s
AI Code Review / AI Code Review (pull_request) Successful in 6m50s
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 1m57s
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 2m44s
CI/CD Pipeline / Validate - Security (push) Successful in 8m7s
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 9m7s
CI/CD Pipeline / Unit Tests (push) Failing after 9m46s
CI/CD Pipeline / Build Production API Image (push) Has been skipped
CI/CD Pipeline / Build Production Web Image (push) Has been skipped
CI/CD Pipeline / Build Production Worker Image (push) Has been skipped
CI/CD Pipeline / CI Gate (push) Has been skipped
CI/CD Pipeline / Deploy Production (push) Has been skipped
CI/CD Pipeline / Canary Release to Production (push) Has been skipped
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
CI/CD Pipeline / Staging E2E Tests (push) Failing after 23m36s
feat(web): add TTS emotion style selector (#2000)
152 lines
4.3 KiB
TypeScript
152 lines
4.3 KiB
TypeScript
/**
|
|
* TTS 配音风格选择器
|
|
* - 6 种预设风格卡片(自然亲切 / 激动兴奋 / 沉稳专业 / 温柔甜美 / 新闻播报 / 直播带货)
|
|
* - 卡片单选,选中高亮紫色
|
|
* - 默认 natural
|
|
*
|
|
* 复用方式:
|
|
* <TtsStyleSelector value={style} onChange={setStyle} />
|
|
* <TtsStyleSelector value={style} onChange={setStyle} compact /> // 紧凑模式(小尺寸)
|
|
*/
|
|
import React from "react"
|
|
import { TTS_STYLE_OPTIONS, DEFAULT_TTS_STYLE, type TtsStyle } from "@/api/tts/styles"
|
|
|
|
export interface TtsStyleSelectorProps {
|
|
value?: TtsStyle | string
|
|
onChange: (style: TtsStyle) => void
|
|
/** 紧凑模式(小卡片),适合与其他参数并排 */
|
|
compact?: boolean
|
|
/** 是否显示"配音风格"标签 */
|
|
showLabel?: boolean
|
|
}
|
|
|
|
const TtsStyleSelector: React.FC<TtsStyleSelectorProps> = ({
|
|
value,
|
|
onChange,
|
|
compact = false,
|
|
showLabel = true,
|
|
}) => {
|
|
const current = value || DEFAULT_TTS_STYLE
|
|
|
|
if (compact) {
|
|
return (
|
|
<div>
|
|
{showLabel && (
|
|
<div
|
|
style={{
|
|
fontSize: 13,
|
|
color: "var(--text-secondary, #6b7280)",
|
|
marginBottom: 6,
|
|
}}
|
|
>
|
|
配音风格
|
|
</div>
|
|
)}
|
|
<div
|
|
style={{
|
|
display: "grid",
|
|
gridTemplateColumns: "repeat(3, 1fr)",
|
|
gap: 6,
|
|
}}
|
|
>
|
|
{TTS_STYLE_OPTIONS.map((opt) => {
|
|
const selected = current === opt.value
|
|
return (
|
|
<button
|
|
type="button"
|
|
key={opt.value}
|
|
onClick={() => onChange(opt.value as TtsStyle)}
|
|
title={opt.description}
|
|
style={{
|
|
padding: "6px 4px",
|
|
borderRadius: 6,
|
|
border: selected ? "2px solid #7c3aed" : "1px solid #e5e7eb",
|
|
background: selected ? "#faf5ff" : "#fff",
|
|
color: selected ? "#6d28d9" : "#374151",
|
|
cursor: "pointer",
|
|
fontSize: 12,
|
|
fontWeight: selected ? 600 : 400,
|
|
textAlign: "center",
|
|
transition: "all 0.15s",
|
|
lineHeight: 1.3,
|
|
}}
|
|
>
|
|
<span style={{ marginRight: 3 }}>{opt.emoji}</span>
|
|
{opt.label}
|
|
</button>
|
|
)
|
|
})}
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
return (
|
|
<div>
|
|
{showLabel && (
|
|
<div
|
|
style={{
|
|
fontSize: 13,
|
|
color: "var(--text-secondary, #6b7280)",
|
|
marginBottom: 8,
|
|
fontWeight: 500,
|
|
}}
|
|
>
|
|
配音风格
|
|
</div>
|
|
)}
|
|
<div
|
|
style={{
|
|
display: "grid",
|
|
gridTemplateColumns: "repeat(3, 1fr)",
|
|
gap: 8,
|
|
}}
|
|
>
|
|
{TTS_STYLE_OPTIONS.map((opt) => {
|
|
const selected = current === opt.value
|
|
return (
|
|
<button
|
|
type="button"
|
|
key={opt.value}
|
|
onClick={() => onChange(opt.value as TtsStyle)}
|
|
title={opt.description}
|
|
style={{
|
|
padding: "10px 8px",
|
|
borderRadius: 8,
|
|
border: selected ? "2px solid #7c3aed" : "1px solid #e5e7eb",
|
|
background: selected ? "#faf5ff" : "#fff",
|
|
color: selected ? "#6d28d9" : "#111",
|
|
cursor: "pointer",
|
|
textAlign: "center",
|
|
transition: "all 0.15s",
|
|
display: "flex",
|
|
flexDirection: "column",
|
|
alignItems: "center",
|
|
gap: 4,
|
|
}}
|
|
>
|
|
<span style={{ fontSize: 22, lineHeight: 1 }}>{opt.emoji}</span>
|
|
<span style={{ fontSize: 13, fontWeight: selected ? 600 : 500 }}>{opt.label}</span>
|
|
<span
|
|
style={{
|
|
fontSize: 10,
|
|
color: "#9ca3af",
|
|
lineHeight: 1.2,
|
|
maxWidth: "100%",
|
|
overflow: "hidden",
|
|
textOverflow: "ellipsis",
|
|
whiteSpace: "nowrap",
|
|
}}
|
|
>
|
|
{opt.description}
|
|
</span>
|
|
</button>
|
|
)
|
|
})}
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export default TtsStyleSelector
|