refactor: Improve TTSSettings component structure and code readability

- Organized imports for better clarity.
- Enhanced the formatting of the TTSSettings component for improved readability.
- Updated various function calls and state management to ensure consistency.
- Refactored the handling of voice and model additions/removals for better maintainability.
- Cleaned up unnecessary comments and improved the overall structure of the code.
This commit is contained in:
kangfenmao 2025-04-09 18:04:55 +08:00
parent e5dbf47b9b
commit 7337c44053

View File

@ -1,5 +1,6 @@
import { PlusOutlined, ReloadOutlined, SoundOutlined } from '@ant-design/icons' import { PlusOutlined, ReloadOutlined, SoundOutlined } from '@ant-design/icons'
import { useTheme } from '@renderer/context/ThemeProvider' import { useTheme } from '@renderer/context/ThemeProvider'
import TTSService from '@renderer/services/TTSService'
import store, { useAppDispatch } from '@renderer/store' import store, { useAppDispatch } from '@renderer/store'
import { import {
addTtsCustomModel, addTtsCustomModel,
@ -16,14 +17,21 @@ import {
setTtsServiceType, setTtsServiceType,
setTtsVoice setTtsVoice
} from '@renderer/store/settings' } from '@renderer/store/settings'
import { Button, Form, Input, Select, Space, Switch, Tag, message } from 'antd' import { Button, Form, Input, message, Select, Space, Switch, Tag } from 'antd'
import { FC, useState, useEffect } from 'react' import { FC, useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { useSelector } from 'react-redux' import { useSelector } from 'react-redux'
import styled from 'styled-components' import styled from 'styled-components'
import { SettingContainer, SettingDivider, SettingGroup, SettingHelpText, SettingRow, SettingRowTitle, SettingTitle } from '..' import {
import TTSService from '@renderer/services/TTSService' SettingContainer,
SettingDivider,
SettingGroup,
SettingHelpText,
SettingRow,
SettingRowTitle,
SettingTitle
} from '..'
const CustomVoiceInput = styled.div` const CustomVoiceInput = styled.div`
margin-top: 16px; margin-top: 16px;
@ -90,13 +98,16 @@ const TTSSettings: FC = () => {
const ttsEdgeVoice = useSelector((state: any) => state.settings.ttsEdgeVoice || 'zh-CN-XiaoxiaoNeural') const ttsEdgeVoice = useSelector((state: any) => state.settings.ttsEdgeVoice || 'zh-CN-XiaoxiaoNeural')
const ttsCustomVoices = useSelector((state: any) => state.settings.ttsCustomVoices || []) const ttsCustomVoices = useSelector((state: any) => state.settings.ttsCustomVoices || [])
const ttsCustomModels = useSelector((state: any) => state.settings.ttsCustomModels || []) const ttsCustomModels = useSelector((state: any) => state.settings.ttsCustomModels || [])
const ttsFilterOptions = useSelector((state: any) => state.settings.ttsFilterOptions || { const ttsFilterOptions = useSelector(
filterThinkingProcess: true, (state: any) =>
filterMarkdown: true, state.settings.ttsFilterOptions || {
filterCodeBlocks: true, filterThinkingProcess: true,
filterHtmlTags: true, filterMarkdown: true,
maxTextLength: 4000 filterCodeBlocks: true,
}) filterHtmlTags: true,
maxTextLength: 4000
}
)
// 新增自定义音色和模型的状态 // 新增自定义音色和模型的状态
const [newVoice, setNewVoice] = useState('') const [newVoice, setNewVoice] = useState('')
@ -147,7 +158,7 @@ const TTSSettings: FC = () => {
console.log('语音列表长度:', voices.length) console.log('语音列表长度:', voices.length)
// 转换浏览器语音列表为选项格式 // 转换浏览器语音列表为选项格式
const browserVoices = voices.map(voice => ({ const browserVoices = voices.map((voice) => ({
label: `${voice.name} (${voice.lang})${voice.default ? ' - 默认' : ''}`, label: `${voice.name} (${voice.lang})${voice.default ? ' - 默认' : ''}`,
value: voice.name, value: voice.name,
lang: voice.lang, lang: voice.lang,
@ -155,7 +166,7 @@ const TTSSettings: FC = () => {
})) }))
// 添加语言信息到预定义语音 // 添加语言信息到预定义语音
const enhancedPredefinedVoices = predefinedVoices.map(voice => ({ const enhancedPredefinedVoices = predefinedVoices.map((voice) => ({
...voice, ...voice,
lang: voice.value.split('-').slice(0, 2).join('-'), lang: voice.value.split('-').slice(0, 2).join('-'),
isNative: false // 标记为非浏览器原生语音 isNative: false // 标记为非浏览器原生语音
@ -171,7 +182,7 @@ const TTSSettings: FC = () => {
// 去除重复项,优先保留浏览器原生语音 // 去除重复项,优先保留浏览器原生语音
const uniqueVoices = allVoices.filter((voice, index, self) => { const uniqueVoices = allVoices.filter((voice, index, self) => {
const firstIndex = self.findIndex(v => v.value === voice.value) const firstIndex = self.findIndex((v) => v.value === voice.value)
// 如果是原生语音或者是第一次出现,则保留 // 如果是原生语音或者是第一次出现,则保留
return voice.isNative || firstIndex === index return voice.isNative || firstIndex === index
}) })
@ -202,7 +213,10 @@ const TTSSettings: FC = () => {
// 刷新语音列表 // 刷新语音列表
const refreshVoices = () => { const refreshVoices = () => {
console.log('手动刷新语音列表') console.log('手动刷新语音列表')
message.loading({ content: t('settings.tts.edge_voice.refreshing', { defaultValue: '正在刷新语音列表...' }), key: 'refresh-voices' }) message.loading({
content: t('settings.tts.edge_voice.refreshing', { defaultValue: '正在刷新语音列表...' }),
key: 'refresh-voices'
})
// 先清空当前列表 // 先清空当前列表
setAvailableVoices([]) setAvailableVoices([])
@ -216,13 +230,19 @@ const TTSSettings: FC = () => {
getVoices() getVoices()
setTimeout(() => { setTimeout(() => {
getVoices() getVoices()
message.success({ content: t('settings.tts.edge_voice.refreshed', { defaultValue: '语音列表已刷新' }), key: 'refresh-voices' }) message.success({
content: t('settings.tts.edge_voice.refreshed', { defaultValue: '语音列表已刷新' }),
key: 'refresh-voices'
})
}, 1000) }, 1000)
}, 500) }, 500)
} else { } else {
// 如果浏览器不支持Web Speech API使用预定义的语音列表 // 如果浏览器不支持Web Speech API使用预定义的语音列表
setAvailableVoices(predefinedVoices) setAvailableVoices(predefinedVoices)
message.success({ content: t('settings.tts.edge_voice.refreshed', { defaultValue: '语音列表已刷新' }), key: 'refresh-voices' }) message.success({
content: t('settings.tts.edge_voice.refreshed', { defaultValue: '语音列表已刷新' }),
key: 'refresh-voices'
})
} }
} }
@ -255,7 +275,7 @@ const TTSSettings: FC = () => {
return () => { return () => {
// 清理事件监听器和定时器 // 清理事件监听器和定时器
window.speechSynthesis.onvoiceschanged = null window.speechSynthesis.onvoiceschanged = null
timers.forEach(timer => clearTimeout(timer)) timers.forEach((timer) => clearTimeout(timer))
} }
} else { } else {
// 如果浏览器不支持Web Speech API使用预定义的语音列表 // 如果浏览器不支持Web Speech API使用预定义的语音列表
@ -318,7 +338,7 @@ const TTSSettings: FC = () => {
} }
// 确保添加的是字符串 // 确保添加的是字符串
const voiceStr = typeof newVoice === 'string' ? newVoice : String(newVoice); const voiceStr = typeof newVoice === 'string' ? newVoice : String(newVoice)
dispatch(addTtsCustomVoice(voiceStr)) dispatch(addTtsCustomVoice(voiceStr))
setNewVoice('') setNewVoice('')
} }
@ -331,7 +351,7 @@ const TTSSettings: FC = () => {
} }
// 确保添加的是字符串 // 确保添加的是字符串
const modelStr = typeof newModel === 'string' ? newModel : String(newModel); const modelStr = typeof newModel === 'string' ? newModel : String(newModel)
dispatch(addTtsCustomModel(modelStr)) dispatch(addTtsCustomModel(modelStr))
setNewModel('') setNewModel('')
} }
@ -339,14 +359,14 @@ const TTSSettings: FC = () => {
// 删除自定义音色 // 删除自定义音色
const handleRemoveVoice = (voice: string) => { const handleRemoveVoice = (voice: string) => {
// 确保删除的是字符串 // 确保删除的是字符串
const voiceStr = typeof voice === 'string' ? voice : String(voice); const voiceStr = typeof voice === 'string' ? voice : String(voice)
dispatch(removeTtsCustomVoice(voiceStr)) dispatch(removeTtsCustomVoice(voiceStr))
} }
// 删除自定义模型 // 删除自定义模型
const handleRemoveModel = (model: string) => { const handleRemoveModel = (model: string) => {
// 确保删除的是字符串 // 确保删除的是字符串
const modelStr = typeof model === 'string' ? model : String(model); const modelStr = typeof model === 'string' ? model : String(model)
dispatch(removeTtsCustomModel(modelStr)) dispatch(removeTtsCustomModel(modelStr))
} }
@ -377,11 +397,10 @@ const TTSSettings: FC = () => {
danger danger
onClick={() => { onClick={() => {
if (window.confirm(t('settings.tts.reset_confirm'))) { if (window.confirm(t('settings.tts.reset_confirm'))) {
dispatch(resetTtsCustomValues()); dispatch(resetTtsCustomValues())
window.message.success({ content: t('settings.tts.reset_success'), key: 'reset-tts' }); window.message.success({ content: t('settings.tts.reset_success'), key: 'reset-tts' })
} }
}} }}>
>
{t('settings.tts.reset')} {t('settings.tts.reset')}
</Button> </Button>
</SettingRow> </SettingRow>
@ -430,7 +449,10 @@ const TTSSettings: FC = () => {
const currentType = store.getState().settings.ttsServiceType const currentType = store.getState().settings.ttsServiceType
console.log('强制刷新TTS服务类型:', currentType) console.log('强制刷新TTS服务类型:', currentType)
dispatch(setTtsServiceType(currentType)) dispatch(setTtsServiceType(currentType))
window.message.success({ content: t('settings.tts.service_type.refreshed', { defaultValue: '已刷新TTS服务类型设置' }), key: 'tts-refresh' }) window.message.success({
content: t('settings.tts.service_type.refreshed', { defaultValue: '已刷新TTS服务类型设置' }),
key: 'tts-refresh'
})
}} }}
disabled={!ttsEnabled} disabled={!ttsEnabled}
title={t('settings.tts.service_type.refresh', { defaultValue: '刷新TTS服务类型设置' })} title={t('settings.tts.service_type.refresh', { defaultValue: '刷新TTS服务类型设置' })}
@ -467,15 +489,25 @@ const TTSSettings: FC = () => {
<Select <Select
value={ttsEdgeVoice} value={ttsEdgeVoice}
onChange={(value) => dispatch(setTtsEdgeVoice(value))} onChange={(value) => dispatch(setTtsEdgeVoice(value))}
options={availableVoices.length > 0 ? availableVoices : [ options={
{ label: t('settings.tts.edge_voice.loading'), value: '' } availableVoices.length > 0
]} ? availableVoices
: [{ label: t('settings.tts.edge_voice.loading'), value: '' }]
}
disabled={!ttsEnabled} disabled={!ttsEnabled}
style={{ flex: 1 }} style={{ flex: 1 }}
showSearch showSearch
optionFilterProp="label" optionFilterProp="label"
placeholder={availableVoices.length === 0 ? t('settings.tts.edge_voice.loading') : t('settings.tts.voice.placeholder')} placeholder={
notFoundContent={availableVoices.length === 0 ? t('settings.tts.edge_voice.loading') : t('settings.tts.edge_voice.not_found')} availableVoices.length === 0
? t('settings.tts.edge_voice.loading')
: t('settings.tts.voice.placeholder')
}
notFoundContent={
availableVoices.length === 0
? t('settings.tts.edge_voice.loading')
: t('settings.tts.edge_voice.not_found')
}
/> />
<Button <Button
icon={<ReloadOutlined />} icon={<ReloadOutlined />}
@ -484,11 +516,7 @@ const TTSSettings: FC = () => {
title={t('settings.tts.edge_voice.refresh')} title={t('settings.tts.edge_voice.refresh')}
/> />
</VoiceSelectContainer> </VoiceSelectContainer>
{availableVoices.length === 0 && ( {availableVoices.length === 0 && <LoadingText>{t('settings.tts.edge_voice.loading')}</LoadingText>}
<LoadingText>
{t('settings.tts.edge_voice.loading')}
</LoadingText>
)}
</Form.Item> </Form.Item>
)} )}
@ -502,8 +530,8 @@ const TTSSettings: FC = () => {
onChange={(value) => dispatch(setTtsVoice(value))} onChange={(value) => dispatch(setTtsVoice(value))}
options={ttsCustomVoices.map((voice: any) => { options={ttsCustomVoices.map((voice: any) => {
// 确保voice是字符串 // 确保voice是字符串
const voiceStr = typeof voice === 'string' ? voice : String(voice); const voiceStr = typeof voice === 'string' ? voice : String(voice)
return { label: voiceStr, value: voiceStr }; return { label: voiceStr, value: voiceStr }
})} })}
disabled={!ttsEnabled} disabled={!ttsEnabled}
style={{ width: '100%' }} style={{ width: '100%' }}
@ -519,22 +547,19 @@ const TTSSettings: FC = () => {
{ttsCustomVoices && ttsCustomVoices.length > 0 ? ( {ttsCustomVoices && ttsCustomVoices.length > 0 ? (
ttsCustomVoices.map((voice: any, index: number) => { ttsCustomVoices.map((voice: any, index: number) => {
// 确保voice是字符串 // 确保voice是字符串
const voiceStr = typeof voice === 'string' ? voice : String(voice); const voiceStr = typeof voice === 'string' ? voice : String(voice)
return ( return (
<Tag <Tag
key={`${voiceStr}-${index}`} key={`${voiceStr}-${index}`}
closable closable
onClose={() => handleRemoveVoice(voiceStr)} onClose={() => handleRemoveVoice(voiceStr)}
style={{ padding: '4px 8px' }} style={{ padding: '4px 8px' }}>
>
{voiceStr} {voiceStr}
</Tag> </Tag>
); )
}) })
) : ( ) : (
<EmptyText> <EmptyText>{t('settings.tts.voice_empty')}</EmptyText>
{t('settings.tts.voice_empty')}
</EmptyText>
)} )}
</TagsContainer> </TagsContainer>
@ -552,8 +577,7 @@ const TTSSettings: FC = () => {
type="primary" type="primary"
icon={<PlusOutlined />} icon={<PlusOutlined />}
onClick={handleAddVoice} onClick={handleAddVoice}
disabled={!ttsEnabled || !newVoice} disabled={!ttsEnabled || !newVoice}>
>
{t('settings.tts.voice_add')} {t('settings.tts.voice_add')}
</Button> </Button>
</InputGroup> </InputGroup>
@ -566,8 +590,8 @@ const TTSSettings: FC = () => {
onChange={(value) => dispatch(setTtsModel(value))} onChange={(value) => dispatch(setTtsModel(value))}
options={ttsCustomModels.map((model: any) => { options={ttsCustomModels.map((model: any) => {
// 确保model是字符串 // 确保model是字符串
const modelStr = typeof model === 'string' ? model : String(model); const modelStr = typeof model === 'string' ? model : String(model)
return { label: modelStr, value: modelStr }; return { label: modelStr, value: modelStr }
})} })}
disabled={!ttsEnabled} disabled={!ttsEnabled}
style={{ width: '100%' }} style={{ width: '100%' }}
@ -583,22 +607,19 @@ const TTSSettings: FC = () => {
{ttsCustomModels && ttsCustomModels.length > 0 ? ( {ttsCustomModels && ttsCustomModels.length > 0 ? (
ttsCustomModels.map((model: any, index: number) => { ttsCustomModels.map((model: any, index: number) => {
// 确保model是字符串 // 确保model是字符串
const modelStr = typeof model === 'string' ? model : String(model); const modelStr = typeof model === 'string' ? model : String(model)
return ( return (
<Tag <Tag
key={`${modelStr}-${index}`} key={`${modelStr}-${index}`}
closable closable
onClose={() => handleRemoveModel(modelStr)} onClose={() => handleRemoveModel(modelStr)}
style={{ padding: '4px 8px' }} style={{ padding: '4px 8px' }}>
>
{modelStr} {modelStr}
</Tag> </Tag>
); )
}) })
) : ( ) : (
<EmptyText> <EmptyText>{t('settings.tts.model_empty')}</EmptyText>
{t('settings.tts.model_empty')}
</EmptyText>
)} )}
</TagsContainer> </TagsContainer>
@ -616,8 +637,7 @@ const TTSSettings: FC = () => {
type="primary" type="primary"
icon={<PlusOutlined />} icon={<PlusOutlined />}
onClick={handleAddModel} onClick={handleAddModel}
disabled={!ttsEnabled || !newModel} disabled={!ttsEnabled || !newModel}>
>
{t('settings.tts.model_add')} {t('settings.tts.model_add')}
</Button> </Button>
</InputGroup> </InputGroup>
@ -632,28 +652,32 @@ const TTSSettings: FC = () => {
checked={ttsFilterOptions.filterThinkingProcess} checked={ttsFilterOptions.filterThinkingProcess}
onChange={(checked) => dispatch(setTtsFilterOptions({ filterThinkingProcess: checked }))} onChange={(checked) => dispatch(setTtsFilterOptions({ filterThinkingProcess: checked }))}
disabled={!ttsEnabled} disabled={!ttsEnabled}
/> {t('settings.tts.filter.thinking_process')} />{' '}
{t('settings.tts.filter.thinking_process')}
</FilterOptionItem> </FilterOptionItem>
<FilterOptionItem> <FilterOptionItem>
<Switch <Switch
checked={ttsFilterOptions.filterMarkdown} checked={ttsFilterOptions.filterMarkdown}
onChange={(checked) => dispatch(setTtsFilterOptions({ filterMarkdown: checked }))} onChange={(checked) => dispatch(setTtsFilterOptions({ filterMarkdown: checked }))}
disabled={!ttsEnabled} disabled={!ttsEnabled}
/> {t('settings.tts.filter.markdown')} />{' '}
{t('settings.tts.filter.markdown')}
</FilterOptionItem> </FilterOptionItem>
<FilterOptionItem> <FilterOptionItem>
<Switch <Switch
checked={ttsFilterOptions.filterCodeBlocks} checked={ttsFilterOptions.filterCodeBlocks}
onChange={(checked) => dispatch(setTtsFilterOptions({ filterCodeBlocks: checked }))} onChange={(checked) => dispatch(setTtsFilterOptions({ filterCodeBlocks: checked }))}
disabled={!ttsEnabled} disabled={!ttsEnabled}
/> {t('settings.tts.filter.code_blocks')} />{' '}
{t('settings.tts.filter.code_blocks')}
</FilterOptionItem> </FilterOptionItem>
<FilterOptionItem> <FilterOptionItem>
<Switch <Switch
checked={ttsFilterOptions.filterHtmlTags} checked={ttsFilterOptions.filterHtmlTags}
onChange={(checked) => dispatch(setTtsFilterOptions({ filterHtmlTags: checked }))} onChange={(checked) => dispatch(setTtsFilterOptions({ filterHtmlTags: checked }))}
disabled={!ttsEnabled} disabled={!ttsEnabled}
/> {t('settings.tts.filter.html_tags')} />{' '}
{t('settings.tts.filter.html_tags')}
</FilterOptionItem> </FilterOptionItem>
<FilterOptionItem> <FilterOptionItem>
<LengthLabel>{t('settings.tts.max_text_length')}:</LengthLabel> <LengthLabel>{t('settings.tts.max_text_length')}:</LengthLabel>
@ -667,7 +691,7 @@ const TTSSettings: FC = () => {
{ label: '2000', value: 2000 }, { label: '2000', value: 2000 },
{ label: '4000', value: 4000 }, { label: '4000', value: 4000 },
{ label: '8000', value: 8000 }, { label: '8000', value: 8000 },
{ label: '16000', value: 16000 }, { label: '16000', value: 16000 }
]} ]}
/> />
</FilterOptionItem> </FilterOptionItem>
@ -681,8 +705,7 @@ const TTSSettings: FC = () => {
!ttsEnabled || !ttsEnabled ||
(ttsServiceType === 'openai' && (!ttsApiKey || !ttsVoice || !ttsModel)) || (ttsServiceType === 'openai' && (!ttsApiKey || !ttsVoice || !ttsModel)) ||
(ttsServiceType === 'edge' && !ttsEdgeVoice) (ttsServiceType === 'edge' && !ttsEdgeVoice)
} }>
>
{t('settings.tts.test')} {t('settings.tts.test')}
</Button> </Button>
</Form.Item> </Form.Item>