/** * TalkingQ Platform - Hotword Library Page * 热词库管理:统一维护 ASR 热词组,供应用配置选择绑定。 */ (function() { function ensureHotwordGroups() { if (!window.__agentHotwordGroups) { window.__agentHotwordGroups = [ { id: 'hw-001', name: '品牌基础热词', mode: 'manual', words: [ { text: 'TalkingQ', lang: '英文' }, { text: '小Q', lang: '中文' }, { text: '故事机', lang: '中文' }, ], updatedAt: '2026-05-20 11:57:38', }, { id: 'hw-002', name: '儿童内容热词', mode: 'manual', words: [ { text: '白雪公主', lang: '中文' }, { text: '睡前故事', lang: '中文' }, ], updatedAt: '2026-05-20 12:08:16', }, ]; } return window.__agentHotwordGroups; } function escapeHtml(value) { const div = document.createElement('div'); div.textContent = value ?? ''; return div.innerHTML; } function escapeAttr(value) { return escapeHtml(value).replace(/"/g, '"').replace(/'/g, '''); } function formatHotwordTime(date = new Date()) { return date.toLocaleString('zh-CN', { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false, }).replace(/\//g, '-'); } function getHotwordPreview(group) { const preview = group.words.slice(0, 2).map(word => word.text).join('、') || '暂无热词'; return group.words.length > 2 ? `${preview} 等 ${group.words.length} 个` : `${preview} · ${group.words.length} 个`; } function renderHotwordRows(words) { if (!words.length) { return `
点击“添加热词”开始配置,最多可添加 500 个热词。
`; } return words.map((word, index) => `
`).join(''); } function renderHotwordGroupList(groups) { if (!groups.length) { return `

暂无热词组

创建热词组后,可在智能体配置里选择绑定。

`; } return groups.map(group => `
${escapeHtml(group.name)}
${escapeHtml(getHotwordPreview(group))}
${group.words.length} 个
${escapeHtml(group.updatedAt || '-')}
`).join(''); } function notifyHotwordGroupsChanged() { window.dispatchEvent(new CustomEvent('hotwordGroupsChanged', { detail: { groups: ensureHotwordGroups() }, })); } function openHotwordEditor(groupId, afterSave) { const groups = ensureHotwordGroups(); const editingGroup = groups.find(group => group.id === groupId); let draftWords = editingGroup ? editingGroup.words.map(word => ({ ...word })) : []; const modal = createModal({ title: editingGroup ? '编辑热词组' : '配置热词库', size: 'xl', cancelText: '取消', confirmText: '确定', content: `
${(editingGroup?.name || '').length}/50 字
手动添加最多支持 500 个热词,每个热词最多 15 个字。
热词列表
${draftWords.length}/500 个
${renderHotwordRows(draftWords)}
`, onConfirm: async () => { const nameInput = document.getElementById('hotwordGroupNameInput'); const groupName = nameInput?.value.trim() || ''; if (!groupName) { Toast.warning('请填写热词组名字'); nameInput?.focus(); return false; } if (groupName.length > 50) { Toast.warning('热词组名字不能超过 50 个字'); nameInput?.focus(); return false; } const words = readHotwordRows(modal.overlay); if (!words.length) { Toast.warning('请至少添加一个热词'); return false; } const invalid = words.find(word => !word.text || word.text.length > 15); if (invalid) { Toast.warning('请填写热词,且每个热词不能超过 15 个字'); return false; } const now = formatHotwordTime(); const payload = { name: groupName, mode: 'manual', words }; const api = typeof API !== 'undefined' ? API : window.API; let result = null; try { if (editingGroup && api?.agentDev?.updateHotwordGroup) { await api.agentDev.updateHotwordGroup(editingGroup.id, payload); } else if (!editingGroup && api?.agentDev?.createHotwordGroup) { result = await api.agentDev.createHotwordGroup(payload); } } catch (error) { console.error('Save hotword group failed', error); Toast.error('热词组保存失败,请稍后重试'); return false; } if (editingGroup) { editingGroup.name = groupName; editingGroup.mode = 'manual'; editingGroup.words = words; editingGroup.updatedAt = now; } else { groups.unshift({ id: result?.data?.id || 'hw-' + Date.now(), name: groupName, mode: 'manual', words, updatedAt: now, }); } notifyHotwordGroupsChanged(); Toast.success(editingGroup ? '热词组已更新' : '热词组已创建'); afterSave?.(); }, }); const overlay = modal.overlay; overlay.querySelector('.modal')?.classList.add('hotword-editor-modal'); const list = overlay.querySelector('#hotwordEntryList'); const countHint = overlay.querySelector('#hotwordCountHint'); function readHotwordRows(scope = overlay) { return Array.from(scope.querySelectorAll('.hotword-entry-row')).map(row => ({ text: row.querySelector('.hotword-text-input')?.value.trim() || '', lang: row.querySelector('.hotword-lang-select')?.value || '中文', })); } function rerenderRows() { list.innerHTML = renderHotwordRows(draftWords); countHint.textContent = `${draftWords.length}/500 个`; bindRowEvents(); lucide.createIcons({ elements: [overlay] }); } function bindRowEvents() { overlay.querySelectorAll('.hotword-row-delete').forEach(btn => { btn.addEventListener('click', () => { draftWords = readHotwordRows(); draftWords.splice(Number(btn.dataset.index), 1); rerenderRows(); }); }); overlay.querySelectorAll('.hotword-text-input').forEach(input => { input.addEventListener('input', () => { if (input.value.length > 15) input.value = input.value.slice(0, 15); }); }); } overlay.querySelector('#addHotwordRowBtn')?.addEventListener('click', () => { draftWords = readHotwordRows(); if (draftWords.length >= 500) { Toast.warning('手动添加热词最多支持 500 个'); return; } draftWords.push({ text: '', lang: '中文' }); rerenderRows(); list.scrollTop = list.scrollHeight; overlay.querySelector('.hotword-entry-row:last-child .hotword-text-input')?.focus(); }); overlay.querySelector('#hotwordGroupNameInput')?.addEventListener('input', e => { overlay.querySelector('#hotwordGroupNameHint').textContent = `${e.target.value.length}/50 字`; }); bindRowEvents(); lucide.createIcons({ elements: [overlay] }); } window.__ensureAgentHotwordGroups = ensureHotwordGroups; window.__getHotwordPreview = getHotwordPreview; registerPage('hotword-library', function(container) { function renderPage() { const groups = ensureHotwordGroups(); container.innerHTML = `
${renderHotwordGroupList(groups)}
`; lucide.createIcons({ elements: [container] }); bindEvents(); } function bindEvents() { container.querySelector('#createHotwordGroupPageBtn')?.addEventListener('click', () => { openHotwordEditor(null, renderPage); }); container.querySelectorAll('.hotword-edit-btn').forEach(btn => { btn.addEventListener('click', () => openHotwordEditor(btn.dataset.groupId, renderPage)); }); container.querySelectorAll('.hotword-delete-btn').forEach(btn => { btn.addEventListener('click', () => { const groups = ensureHotwordGroups(); const group = groups.find(item => item.id === btn.dataset.groupId); if (!group) return; createModal({ title: '删除热词组', content: `

确定删除热词组 ${escapeHtml(group.name)} 吗?删除后已绑定的智能体将不再使用该热词组。

`, confirmText: '删除', onConfirm: async () => { const api = typeof API !== 'undefined' ? API : window.API; try { if (api?.agentDev?.deleteHotwordGroup) { await api.agentDev.deleteHotwordGroup(group.id); } } catch (error) { console.error('Delete hotword group failed', error); Toast.error('热词组删除失败,请稍后重试'); return false; } const index = groups.findIndex(item => item.id === group.id); if (index >= 0) groups.splice(index, 1); if (window.__agentHotwordBindings) { Object.keys(window.__agentHotwordBindings).forEach(appId => { if (Array.isArray(window.__agentHotwordBindings[appId])) { window.__agentHotwordBindings[appId] = window.__agentHotwordBindings[appId].filter(id => id !== group.id); } }); } notifyHotwordGroupsChanged(); renderPage(); Toast.success('热词组已删除'); }, }); }); }); } renderPage(); }); })();