feat(frontend): engine dropdown + emotion dual mode
- Replace engine buttons with <select> dropdown - Add ENGINE_CONFIG for centralized engine management - Add global/per-sentence emotion UI mode switching - Add #engine-select dark theme styles - Remove old .switch-btn and .engine-icon styles - Reset emotion state on engine switch
This commit is contained in:
+47
-10
@@ -3,10 +3,23 @@
|
||||
*/
|
||||
|
||||
const CONFIG = {
|
||||
API_BASE_URL: '', // 空字符串表示使用相对路径,通过Nginx代理
|
||||
API_BASE_URL: '',
|
||||
MAX_CHARS: 2000,
|
||||
};
|
||||
|
||||
const ENGINE_CONFIG = {
|
||||
minimax: {
|
||||
name: 'MiniMax',
|
||||
emotionMode: 'global',
|
||||
color: '#00d4aa'
|
||||
},
|
||||
cosyvoice: {
|
||||
name: 'CosyVoice',
|
||||
emotionMode: 'global',
|
||||
color: '#ff6b6b'
|
||||
}
|
||||
};
|
||||
|
||||
// 情绪按钮与 API emotion 值的映射
|
||||
const EMOTION_MAP = {
|
||||
'happy': { emoji: '😊', label: '开心', apiValue: 'happy' },
|
||||
@@ -30,8 +43,10 @@ const elements = {
|
||||
editor: document.getElementById('editor'),
|
||||
editorPlaceholder: document.getElementById('editorPlaceholder'),
|
||||
charCount: document.getElementById('charCount'),
|
||||
switchBtns: document.querySelectorAll('.switch-btn'),
|
||||
engineSelect: document.getElementById('engine-select'),
|
||||
emotionBtns: document.querySelectorAll('.emotion-btn'),
|
||||
emotionGlobal: document.getElementById('emotion-global'),
|
||||
emotionPerSentence: document.getElementById('emotion-per-sentence'),
|
||||
speedSlider: document.getElementById('speedSlider'),
|
||||
speedValue: document.getElementById('speedValue'),
|
||||
pitchSlider: document.getElementById('pitchSlider'),
|
||||
@@ -143,12 +158,34 @@ function updateSliderDisplay() {
|
||||
|
||||
function switchEngine(engine) {
|
||||
state.currentEngine = engine;
|
||||
elements.switchBtns.forEach(btn => {
|
||||
btn.classList.toggle('active', btn.dataset.engine === engine);
|
||||
});
|
||||
const engineName = engine === 'minimax' ? 'MiniMax' : 'CosyVoice';
|
||||
document.getElementById('engineBadge').textContent = `蓝皓 · ${engineName}`;
|
||||
showToast(`已切换到${engineName}引擎`);
|
||||
const config = ENGINE_CONFIG[engine];
|
||||
if (!config) return;
|
||||
|
||||
// Update engine badge
|
||||
const badge = document.getElementById('engineBadge');
|
||||
badge.textContent = `蓝皓 · ${config.name}`;
|
||||
badge.style.background = config.color;
|
||||
|
||||
// Update engine select value
|
||||
elements.engineSelect.value = engine;
|
||||
|
||||
// Switch emotion UI mode
|
||||
updateEmotionMode(config.emotionMode);
|
||||
|
||||
// Reset emotion state
|
||||
clearEmotionSelection();
|
||||
|
||||
showToast(`已切换到${config.name}引擎`);
|
||||
}
|
||||
|
||||
function updateEmotionMode(mode) {
|
||||
if (mode === 'global') {
|
||||
elements.emotionGlobal.style.display = 'flex';
|
||||
elements.emotionPerSentence.style.display = 'none';
|
||||
} else if (mode === 'perSentence') {
|
||||
elements.emotionGlobal.style.display = 'none';
|
||||
elements.emotionPerSentence.style.display = 'block';
|
||||
}
|
||||
}
|
||||
|
||||
async function generateAudio() {
|
||||
@@ -290,8 +327,8 @@ function downloadAudio() {
|
||||
|
||||
elements.editor.addEventListener('input', updateCharCount);
|
||||
elements.editor.addEventListener('paste', () => { setTimeout(updateCharCount, 0); });
|
||||
elements.switchBtns.forEach(btn => {
|
||||
btn.addEventListener('click', () => switchEngine(btn.dataset.engine));
|
||||
elements.engineSelect.addEventListener('change', (e) => {
|
||||
switchEngine(e.target.value);
|
||||
});
|
||||
elements.emotionBtns.forEach(btn => {
|
||||
btn.addEventListener('click', () => {
|
||||
|
||||
+9
-8
@@ -37,17 +37,14 @@
|
||||
<div class="toolbar">
|
||||
<div class="toolbar-group">
|
||||
<span class="toolbar-label">引擎:</span>
|
||||
<button class="switch-btn active" data-engine="minimax">
|
||||
<span class="engine-icon minimax-icon">M</span>
|
||||
MiniMax
|
||||
</button>
|
||||
<button class="switch-btn" data-engine="cosyvoice">
|
||||
<span class="engine-icon cosyvoice-icon">C</span>
|
||||
CosyVoice
|
||||
</button>
|
||||
<select id="engine-select">
|
||||
<option value="minimax">MiniMax</option>
|
||||
<option value="cosyvoice">CosyVoice</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="toolbar-group emotion-group">
|
||||
<span class="toolbar-label">情绪:</span>
|
||||
<div id="emotion-global">
|
||||
<button class="emotion-btn" data-emotion="happy" title="开心愉快">😊</button>
|
||||
<button class="emotion-btn" data-emotion="sad" title="悲伤">😢</button>
|
||||
<button class="emotion-btn" data-emotion="angry" title="激昂">🔥</button>
|
||||
@@ -56,6 +53,10 @@
|
||||
<button class="emotion-btn" data-emotion="disgusted" title="厌恶">😒</button>
|
||||
<span class="toolbar-hint">← 选择整段语音的情绪风格</span>
|
||||
</div>
|
||||
<div id="emotion-per-sentence" style="display:none">
|
||||
逐句标注模式:选中句子后点击情绪标签(即将支持)
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Editor -->
|
||||
|
||||
+25
-17
@@ -121,30 +121,38 @@ body {
|
||||
.toolbar-label { font-size: 0.85rem; color: var(--text-secondary); }
|
||||
.toolbar-hint { font-size: 0.7rem; color: var(--text-muted); margin-left: 8px; }
|
||||
|
||||
.switch-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
padding: 4px 12px;
|
||||
background: transparent;
|
||||
border: 1px solid var(--border-color);
|
||||
#engine-select {
|
||||
padding: 6px 12px;
|
||||
background: #1a1a25;
|
||||
border: 1px solid #2a2a3a;
|
||||
border-radius: var(--radius-sm);
|
||||
color: var(--text-secondary);
|
||||
color: #ffffff;
|
||||
font-size: 0.85rem;
|
||||
cursor: pointer;
|
||||
transition: var(--transition-fast);
|
||||
outline: none;
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%2371717a' d='M2 4l4 4 4-4'/%3E%3C/svg%3E");
|
||||
background-repeat: no-repeat;
|
||||
background-position: right 8px center;
|
||||
padding-right: 28px;
|
||||
}
|
||||
.switch-btn:hover { border-color: var(--border-hover); color: var(--text-primary); }
|
||||
.switch-btn.active { background: var(--bg-card); border-color: var(--accent-primary); color: var(--text-primary); }
|
||||
#engine-select:hover { border-color: var(--border-hover); }
|
||||
#engine-select:focus { border-color: var(--accent-primary); }
|
||||
#engine-select option { background: #1a1a25; color: #ffffff; }
|
||||
|
||||
.engine-icon {
|
||||
width: 18px; height: 18px;
|
||||
border-radius: var(--radius-sm);
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
font-size: 0.65rem; font-weight: 700;
|
||||
#emotion-global {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
#emotion-per-sentence {
|
||||
color: #71717a;
|
||||
padding: 8px;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
.minimax-icon { background: var(--minimax-color); color: #0a0a0f; }
|
||||
.cosyvoice-icon { background: var(--cosyvoice-color); color: white; }
|
||||
|
||||
.emotion-btn {
|
||||
width: 32px; height: 32px;
|
||||
|
||||
Reference in New Issue
Block a user