feat: mobile端同步更新上报模式、语言设置功能

This commit is contained in:
zhuoqinglian
2026-03-06 09:40:04 +08:00
parent a1d37b61e4
commit d51c2a241c
13 changed files with 581 additions and 28 deletions
@@ -206,3 +206,19 @@ export function updateAgentTags(agentId: string, data) {
},
})
}
// 获取所有语言
export function getAllLanguage(modelId, voiceName) {
const queryParams = new URLSearchParams({
voiceName: voiceName || '',
}).toString()
return http.Get(`/models/${modelId}/voices?${queryParams}`, {
meta: {
ignoreAuth: false,
toast: false,
},
cacheFor: {
expire: 0,
},
})
}
+5 -1
View File
@@ -11,7 +11,7 @@ export interface Agent {
summaryMemory: string | null
lastConnectedAt: string | null
deviceCount: number
tags: { id: string, tagName: string }[]
tags: Record<string, string>[]
}
// 智能体创建数据类型
@@ -43,6 +43,10 @@ export interface AgentDetail {
createdAt: string
updater: string
updatedAt: string
ttsLanguage: string
ttsVolume: number
ttsRate: number
ttsPitch: number
functions: AgentFunction[]
}
@@ -192,7 +192,7 @@ watch(() => props.visible, (val) => {
{{ t('contextProviderDialog.noHeaders') }}
</text>
<wd-button type="text" size="small" @click="addHeader(pIndex, 0)">
{{ t('contextProviderDialog.addHeaders') }}
{{ t('contextProviderDialog.addHeader') }}
</wd-button>
</view>
</view>
@@ -0,0 +1,270 @@
<script lang="ts" setup>
import { ref, watch } from 'vue'
import { t } from '@/i18n'
interface Props {
visible?: boolean
settings?: {
volume: number
speed: number
pitch: number
}
}
const props = withDefaults(defineProps<Props>(), {
visible: false,
settings: () => ({
volume: 0,
speed: 0,
pitch: 0,
}),
})
const emit = defineEmits<{
'update:visible': [value: boolean]
'confirm': [value: {
volume: number
speed: number
pitch: number
}]
}>()
const localSettings = ref({
volume: 0,
speed: 0,
pitch: 0,
})
function initLocalData() {
localSettings.value = {
volume: props.settings.volume || 0,
speed: props.settings.speed || 0,
pitch: props.settings.pitch || 0,
}
}
function handleConfirm() {
emit('confirm', localSettings.value)
emit('update:visible', false)
}
function handleClose() {
emit('update:visible', false)
}
watch(() => props.visible, (val) => {
if (val) {
initLocalData()
}
})
</script>
<template>
<view v-if="visible" class="voice-settings-dialog-mask" @click="handleClose">
<view class="voice-settings-dialog" @click.stop>
<view class="dialog-header">
<text class="dialog-title">
{{ t('agent.languageConfig') }}
</text>
</view>
<view class="dialog-content">
<!-- 音量调节 -->
<view class="setting-item">
<text class="setting-label">
{{ t('agent.ttsVolume') }}
</text>
<view class="slider-container">
<wd-slider
v-model="localSettings.volume"
:min="-100"
:max="100"
:step="1"
:show-value="false"
custom-class="voice-slider"
/>
<text class="slider-value">
{{ localSettings.volume }}
</text>
</view>
<text class="setting-desc">
{{ t('agent.volumeHint') }}
</text>
</view>
<!-- 语速调节 -->
<view class="setting-item">
<text class="setting-label">
{{ t('agent.ttsRate') }}
</text>
<view class="slider-container">
<wd-slider
v-model="localSettings.speed"
:min="-100"
:max="100"
:step="1"
:show-value="false"
custom-class="voice-slider"
/>
<text class="slider-value">
{{ localSettings.speed }}
</text>
</view>
<text class="setting-desc">
{{ t('agent.speedHint') }}
</text>
</view>
<!-- 音调调节 -->
<view class="setting-item">
<text class="setting-label">
{{ t('agent.ttsPitch') }}
</text>
<view class="slider-container">
<wd-slider
v-model="localSettings.pitch"
:min="-100"
:max="100"
:step="1"
:show-value="false"
custom-class="voice-slider"
/>
<text class="slider-value">
{{ localSettings.pitch }}
</text>
</view>
<text class="setting-desc">
{{ t('agent.pitchHint') }}
</text>
</view>
</view>
<view class="dialog-footer">
<wd-button class="cancel-btn" @click="handleClose">
{{ t('agent.cancel') }}
</wd-button>
<wd-button type="primary" class="confirm-btn" @click="handleConfirm">
{{ t('agent.save') }}
</wd-button>
</view>
</view>
</view>
</template>
<style scoped lang="scss">
.voice-settings-dialog-mask {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
z-index: 9999;
display: flex;
align-items: center;
justify-content: center;
padding: 0;
}
.voice-settings-dialog {
background: #fff;
width: 100%;
height: 100vh;
display: flex;
flex-direction: column;
overflow: hidden;
}
.dialog-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 30rpx;
border-bottom: 1px solid #eee;
}
.dialog-title {
font-size: 32rpx;
font-weight: 600;
color: #232338;
}
.close-icon {
font-size: 40rpx;
color: #9d9ea3;
cursor: pointer;
}
.dialog-content {
flex: 1;
overflow-y: auto;
padding: 40rpx;
display: flex;
flex-direction: column;
gap: 50rpx;
}
.setting-item {
display: flex;
flex-direction: column;
gap: 20rpx;
}
.setting-label {
font-size: 30rpx;
font-weight: 600;
color: #232338;
}
.slider-container {
display: flex;
align-items: center;
gap: 20rpx;
}
.voice-slider {
flex: 1;
}
.slider-value {
font-size: 28rpx;
color: #336cff;
font-weight: 500;
min-width: 80rpx;
text-align: right;
}
.setting-desc {
font-size: 24rpx;
color: #9d9ea3;
margin-top: 10rpx;
}
.dialog-footer {
display: flex;
gap: 20rpx;
padding: 30rpx;
border-top: 1px solid #eee;
}
.cancel-btn,
.confirm-btn {
flex: 1;
height: 80rpx;
border-radius: 12rpx;
font-size: 28rpx;
}
.confirm-btn {
background-color: #336cff !important;
}
/* 自定义滑块样式 */
:deep(.wd-slider) {
--wd-slider-bar-background: #e6ebff;
--wd-slider-bar-active-background: #336cff;
--wd-slider-thumb-border-color: #336cff;
--wd-slider-thumb-background: #336cff;
--wd-slider-thumb-size: 32rpx;
}
</style>
@@ -110,6 +110,7 @@ const alovaInstance = createAlova({
if (config.meta?.isExposeError) {
return Promise.reject(msg)
}
if (config.meta?.toast !== false) {
toast.warning(msg)
}
+11
View File
@@ -124,6 +124,17 @@ export default {
'agent.pleaseInputAgentName': 'Bitte Agenten-Namen eingeben',
'agent.pleaseInputRoleDescription': 'Bitte Rollenbeschreibung eingeben',
'agent.pleaseSelect': 'Bitte auswählen',
'agent.reportText': 'Text melden',
'agent.reportTextVoice': 'Text + Sprache melden',
'agent.reportMode': 'Meldemodus',
'agent.language': 'Unterhaltungssprache',
'agent.languageConfig': 'Sprachgeschwindigkeit & Tonhöhe',
'agent.ttsVolume': 'Lautstärke',
'agent.ttsRate': 'Sprechtempo',
'agent.ttsPitch': 'Tonhöhe',
'agent.volumeHint': '-100=min, 0=Standard, 100=max',
'agent.speedHint': '-100=langsamst, 0=Standard, 100=schnellst',
'agent.pitchHint': '-100=tiefst, 0=Standard, 100=höchst',
// Context provider dialog related
'contextProviderDialog.title': 'Quelle bearbeiten',
+11
View File
@@ -124,6 +124,17 @@ export default {
'agent.pleaseInputAgentName': 'Please input agent name',
'agent.pleaseInputRoleDescription': 'Please input role description',
'agent.pleaseSelect': 'Please select',
'agent.reportText': 'Report Text',
'agent.reportTextVoice': 'Report Text+Voice',
'agent.reportMode': 'Report Mode',
'agent.language': 'Conversation Language',
'agent.languageConfig': 'Speech Rate&Tone',
'agent.ttsVolume': 'Volume',
'agent.ttsRate': 'Speech Rate',
'agent.ttsPitch': 'Tone',
'agent.volumeHint': '-100=Minimum, 0=Standard, 100=Maximum',
'agent.speedHint': '-100=Slower, 0=Standard, 100=Faster',
'agent.pitchHint': '-100=Lowest, 0=Standard, 100=Highest',
// Context provider dialog related
'contextProviderDialog.title': 'Edit Source',
+11
View File
@@ -124,6 +124,17 @@ export default {
'agent.pleaseInputAgentName': 'Por favor, insira o nome do agente',
'agent.pleaseInputRoleDescription': 'Por favor, insira a descrição do papel',
'agent.pleaseSelect': 'Por favor, selecione',
'agent.reportText': 'Enviar Texto',
'agent.reportTextVoice': 'Enviar Texto+Voz',
'agent.reportMode': 'Modo de Envio',
'agent.language': 'Idioma de Conversa',
'agent.languageConfig': 'Configuração de Voz',
'agent.ttsVolume': 'Volume',
'agent.ttsRate': 'Taxa de Fala',
'agent.ttsPitch': 'Tonalidade',
'agent.volumeHint': '-100=Mínimo, 0=Padrão, 100=Máximo',
'agent.speedHint': '-100=Slower, 0=Standard, 100=Faster',
'agent.pitchHint': '-100=Lowest, 0=Standard, 100=Highest',
// Diálogo de provedor de contexto
'contextProviderDialog.title': 'Editar Fonte',
+11
View File
@@ -124,6 +124,17 @@ export default {
'agent.pleaseInputAgentName': 'Vui lòng nhập tên đại lý',
'agent.pleaseInputRoleDescription': 'Vui lòng nhập mô tả vai trò',
'agent.pleaseSelect': 'Vui lòng chọn',
'agent.reportText': 'Gửi văn bản',
'agent.reportTextVoice': 'Gửi văn bản+giọng nói',
'agent.reportMode': 'Chế độ gửi',
'agent.language': 'Ngôn ngữ',
'agent.languageConfig': 'Giọng nói',
'agent.ttsVolume': 'Âm lượng',
'agent.ttsRate': 'Tốc độ',
'agent.ttsPitch': 'Tone',
'agent.volumeHint': '-100=Minimum, 0=Standard, 100=Maximum',
'agent.speedHint': '-100=Slowest, 0=Standard, 100=Fastest',
'agent.pitchHint': '-100=Lowest, 0=Standard, 100=Highest',
// Context provider dialog related
'contextProviderDialog.title': 'Chỉnh sửa nguồn',
+11
View File
@@ -124,6 +124,17 @@ export default {
'agent.pleaseInputAgentName': '请输入智能体名称',
'agent.pleaseInputRoleDescription': '请输入角色介绍',
'agent.pleaseSelect': '请选择',
'agent.reportText': '上报文字',
'agent.reportTextVoice': '上报文字+语音',
'agent.reportMode': '上报模式',
'agent.language': '对话语言',
'agent.languageConfig': '语速音调',
'agent.ttsVolume': '音量',
'agent.ttsRate': '语速',
'agent.ttsPitch': '音调',
'agent.volumeHint': '-100=最小, 0=标准, 100=最大',
'agent.speedHint': '-100=最慢, 0=标准, 100=最快',
'agent.pitchHint': '-100=最低, 0=标准, 100=最高',
// 上下文源对话框相关
'contextProviderDialog.title': '编辑源',
+11
View File
@@ -145,6 +145,17 @@ export default {
'agent.pleaseInputAgentName': '請輸入助手暱稱',
'agent.pleaseInputRoleDescription': '請輸入角色介紹',
'agent.pleaseSelect': '請選擇',
'agent.reportText': '上报文字',
'agent.reportTextVoice': '上报文字+语音',
'agent.reportMode': '上報模式',
'agent.language': '對話語言',
'agent.languageConfig': '語速音調',
'agent.ttsVolume': '音量',
'agent.ttsRate': '語速',
'agent.ttsPitch': '音調',
'agent.volumeHint': '-100=最小, 0=標準, 100=最大',
'agent.speedHint': '-100=最慢, 0=標準, 100=最快',
'agent.pitchHint': '-100=最低, 0=標準, 100=最高',
// 上下文源对话框相关
'contextProviderDialog.title': '編輯源',
+221 -25
View File
@@ -1,8 +1,9 @@
<script lang="ts" setup>
import type { AgentDetail, ModelOption, PluginDefinition, RoleTemplate } from '@/api/agent/types'
import { computed, nextTick, onMounted, ref, watch } from 'vue'
import { getAgentDetail, getAgentTags, getModelOptions, getPluginFunctions, getRoleTemplates, getTTSVoices, updateAgent, updateAgentTags } from '@/api/agent/agent'
import { getAgentDetail, getAgentTags, getAllLanguage, getModelOptions, getPluginFunctions, getRoleTemplates, updateAgent, updateAgentTags } from '@/api/agent/agent'
import ContextProviderDialog from '@/components/ContextProviderDialog.vue'
import VoiceSettingsDialog from '@/components/VoiceSettingsDialog.vue'
import { t } from '@/i18n'
import { usePluginStore } from '@/store'
import { toast } from '@/utils/toast'
@@ -35,6 +36,10 @@ const formData = ref<Partial<AgentDetail>>({
memModelId: '',
ttsModelId: '',
ttsVoiceId: '',
ttsLanguage: '',
ttsVolume: 0,
ttsRate: 0,
ttsPitch: 0,
})
// 显示名称数据
@@ -48,6 +53,8 @@ const displayNames = ref({
memory: t('agent.pleaseSelect'),
tts: t('agent.pleaseSelect'),
voiceprint: t('agent.pleaseSelect'),
report: t('agent.pleaseSelect'),
language: t('agent.pleaseSelect'),
})
// 角色模板数据
@@ -72,7 +79,15 @@ const modelOptions = ref<{
})
// 音色选项数据
const voiceOptions = ref<{ id: string, name: string }[]>([])
const voiceOptions = ref([])
// 保存完整的音色信息
const voiceDetails = ref({})
// 上报模式选项数据
const reportOptions = [
{ name: t('agent.reportText'), value: 1 },
{ name: t('agent.reportTextVoice'), value: 2 },
]
// 选择器显示状态
const pickerShow = ref<{
@@ -86,6 +101,14 @@ const pickerShow = ref<{
memory: false,
tts: false,
voiceprint: false,
language: false,
report: false,
})
const ttsSettings = ref({
volume: 0,
speed: 0,
pitch: 0,
})
const allFunctions = ref<PluginDefinition[]>([])
@@ -94,6 +117,14 @@ const inputValue = ref('')
const inputVisible = ref(false)
const showContextProviderDialog = ref(false)
const currentContextProviders = ref([])
const showVoiceSettingsDialog = ref(false)
const voiceSettings = ref({
volume: 0,
speed: 0,
pitch: 0,
})
const languageOptions = ref([])
const isVisibleReport = ref(false)
// 使用插件store
const pluginStore = usePluginStore()
@@ -151,6 +182,21 @@ function handleUpdateContext(providers: any[]) {
currentContextProviders.value = providers
}
function openVoiceSettingsDialog() {
showVoiceSettingsDialog.value = true
}
function handleUpdateVoiceSettings(settings: any) {
ttsSettings.value = settings
formData.value.ttsVolume = settings.volume
formData.value.ttsRate = settings.speed
formData.value.ttsPitch = settings.pitch
}
function handleRegulate() {
openVoiceSettingsDialog()
}
// 加载智能体详情
async function loadAgentDetail() {
if (!agentId.value)
@@ -167,10 +213,16 @@ async function loadAgentDetail() {
// 加载上下文配置
currentContextProviders.value = (detail as any).contextProviders || []
// 加载语音设置
voiceSettings.value = {
volume: (detail as any).volume || 0,
speed: (detail as any).speed || 0,
pitch: (detail as any).pitch || 0,
}
// 如果有TTS模型,加载对应的音色选项
if (detail.ttsModelId) {
await loadVoiceOptions(detail.ttsModelId)
await fetchAllLanguag(detail.ttsModelId)
}
// 等待模型选项加载完成后再更新显示名称
@@ -242,7 +294,10 @@ function updateDisplayNames() {
displayNames.value.tts = getModelDisplayName('TTS', formData.value.ttsModelId)
// 角色音色特殊处理
displayNames.value.voiceprint = getVoiceDisplayName(formData.value.ttsVoiceId || '')
displayNames.value.report = reportOptions.find(item => item.value === formData.value.chatHistoryConf)?.name
displayNames.value.language = formData.value.ttsLanguage
isVisibleReport.value = formData.value.memModelId !== 'Memory_nomem'
console.log('最终音色显示名称:', displayNames.value.voiceprint)
}
@@ -278,23 +333,110 @@ async function loadModelOptions() {
}
}
// 加载TTS音色选项
async function loadVoiceOptions(ttsModelId?: string) {
if (!ttsModelId)
return
try {
console.log(`加载音色选项: ${ttsModelId}`)
const voices = await getTTSVoices(ttsModelId)
voiceOptions.value = voices
console.log('音色选项:', voices)
}
catch (error) {
console.error('加载音色选项失败:', error)
// 根据语言筛选音色
function filterVoicesByLanguage() {
if (!voiceDetails.value || Object.keys(voiceDetails.value).length === 0) {
voiceOptions.value = []
return
}
const allVoices = Object.values(voiceDetails.value) as any[]
// 根据选中的语言筛选音色
const filteredVoices = allVoices.filter((voice) => {
if (!voice.languages) {
return false
}
const languagesArray = voice.languages.split(/[、;;,]/).map(lang => lang.trim()).filter(lang => lang)
return languagesArray.includes(formData.value.language)
})
voiceOptions.value = filteredVoices.map(voice => ({
value: voice.id,
name: voice.name,
voiceDemo: voice.voiceDemo,
voice_demo: voice.voice_demo,
isClone: Boolean(voice.isClone),
train_status: voice.trainStatus,
}))
// 检查当前选中的音色是否支持当前语言,如果不支持则选择第一个
const currentVoiceSupportsLanguage = formData.value.ttsVoiceId
&& filteredVoices.some(voice => voice.id === formData.value.ttsVoiceId)
if (!currentVoiceSupportsLanguage) {
formData.value.ttsVoiceId = filteredVoices.length > 0 ? filteredVoices[0].id : ''
displayNames.value.voiceprint = filteredVoices.length > 0 ? filteredVoices[0].name : ''
}
else {
displayNames.value.voiceprint = filteredVoices.find(item => item.id === formData.value.ttsVoiceId)?.name
}
// 同步到ttsSettings(如果值为null,使用0作为显示默认值,但不修改form中的值)
ttsSettings.value = {
volume: formData.value.ttsVolume !== null && formData.value.ttsVolume !== undefined ? formData.value.ttsVolume : 0,
speed: formData.value.ttsRate !== null && formData.value.ttsRate !== undefined ? formData.value.ttsRate : 0,
pitch: formData.value.ttsPitch !== null && formData.value.ttsPitch !== undefined ? formData.value.ttsPitch : 0,
}
}
// 根据语音合成模型加载语言
async function fetchAllLanguag(ttsModelId: string) {
try {
const res = await getAllLanguage(ttsModelId, '') as any[]
// 保存完整的音色信息
voiceDetails.value = res.reduce((acc, voice) => {
acc[voice.id] = voice
return acc
}, {})
// 提取所有语言选项并去重
const allLanguages = new Set()
res.forEach((voice) => {
if (voice.languages) {
const languagesArray = voice.languages.split(/[、;;,]/).map(lang => lang.trim()).filter(lang => lang)
languagesArray.forEach(lang => allLanguages.add(lang))
}
})
languageOptions.value = Array.from(allLanguages).map(lang => ({
value: lang,
name: lang,
}))
// 使用后端返回的用户选择的语言,如果没有则使用第一个语言选项
if (formData.value.ttsLanguage && languageOptions.value.some(option => option.value === formData.value.ttsLanguage)) {
formData.value.language = formData.value.ttsLanguage
displayNames.value.language = formData.value.ttsLanguage
}
else if (languageOptions.value.length > 0) {
formData.value.language = languageOptions.value[0].value
displayNames.value.language = languageOptions.value[0].value
}
// 根据选中的语言筛选音色
filterVoicesByLanguage()
}
catch {
languageOptions.value = []
}
}
// 加载TTS音色选项
// async function loadVoiceOptions(ttsModelId?: string) {
// if (!ttsModelId)
// return
// try {
// console.log(`加载音色选项: ${ttsModelId}`)
// const voices = await getTTSVoices(ttsModelId)
// voiceOptions.value = voices
// console.log('音色选项:', voices)
// }
// catch (error) {
// console.error('加载音色选项失败:', error)
// voiceOptions.value = []
// }
// }
// 选择角色模板
function selectRoleTemplate(templateId: string) {
if (selectedTemplateId.value === templateId) {
@@ -348,20 +490,26 @@ async function onPickerConfirm(type: string, value: any, name: string) {
break
case 'memory':
formData.value.memModelId = value
formData.value.chatHistoryConf = value === 'Memory_nomem' ? 0 : 2
displayNames.value.memory = name // 确保显示名称正确更新
displayNames.value.report = reportOptions[1].name
isVisibleReport.value = value !== 'Memory_nomem'
break
case 'tts':
formData.value.ttsModelId = value
// 当选择TTS模型时,自动加载对应的音色选项
await loadVoiceOptions(value)
// 重置音色选择
formData.value.ttsVoiceId = ''
displayNames.value.voiceprint = '请选择'
await fetchAllLanguag(value)
break
case 'language':
formData.value.language = value
filterVoicesByLanguage()
break
case 'voiceprint':
formData.value.ttsVoiceId = value
displayNames.value.voiceprint = name // 确保显示名称正确更新
break
case 'report':
formData.value.chatHistoryConf = value
break
}
pickerShow.value[type] = false
@@ -413,12 +561,14 @@ async function saveAgent() {
try {
saving.value = true
// 构建保存数据,包含上下文配置
// 构建保存数据,包含上下文配置和语音设置
const saveData = {
...formData.value,
ttsLanguage: formData.value.language,
contextProviders: currentContextProviders.value,
}
await updateAgent(agentId.value, saveData)
loadAgentDetail()
toast.success(t('agent.saveSuccess'))
}
@@ -545,7 +695,7 @@ onMounted(async () => {
<wd-tag v-for="tag in dynamicTags" :key="tag.id" class="items-center border !flex !border-[rgba(51,108,255,0.2)] !bg-[rgba(51,108,255,0.1)] !text-[#336cff]" round closable @close="handleCloseTag(tag.id)">
{{ tag.tagName }}
</wd-tag>
<wd-button class="!bg-[rgba(51,108,255,0.1)] !text-[#336cff]" v-if="!inputVisible" size="small" icon="add" @click="showInput">
<wd-button v-if="!inputVisible" class="!bg-[rgba(51,108,255,0.1)] !text-[#336cff]" size="small" icon="add" @click="showInput">
{{ t('agent.addAgentTag') }}
</wd-button>
</view>
@@ -672,6 +822,16 @@ onMounted(async () => {
</text>
<wd-icon name="arrow-right" custom-class="text-[20rpx] text-[#9d9ea3]" />
</view>
<view v-show="isVisibleReport" class="flex cursor-pointer items-center justify-between border border-[#eeeeee] rounded-[12rpx] bg-[#f5f7fb] p-[20rpx] transition-all duration-300 active:bg-[#eef3ff]" @click="openPicker('report')">
<text class="text-[28rpx] text-[#232338] font-medium">
{{ t('agent.reportMode') }}
</text>
<text class="mx-[16rpx] flex-1 text-right text-[26rpx] text-[#65686f]">
{{ displayNames.report }}
</text>
<wd-icon name="arrow-right" custom-class="text-[20rpx] text-[#9d9ea3]" />
</view>
</view>
</view>
@@ -695,6 +855,16 @@ onMounted(async () => {
<wd-icon name="arrow-right" custom-class="text-[20rpx] text-[#9d9ea3]" />
</view>
<view class="flex cursor-pointer items-center justify-between border border-[#eeeeee] rounded-[12rpx] bg-[#f5f7fb] p-[20rpx] transition-all duration-300 active:bg-[#eef3ff]" @click="openPicker('language')">
<text class="text-[28rpx] text-[#232338] font-medium">
{{ t('agent.language') }}
</text>
<text class="mx-[16rpx] flex-1 text-right text-[26rpx] text-[#65686f]">
{{ displayNames.language }}
</text>
<wd-icon name="arrow-right" custom-class="text-[20rpx] text-[#9d9ea3]" />
</view>
<view class="flex cursor-pointer items-center justify-between border border-[#eeeeee] rounded-[12rpx] bg-[#f5f7fb] p-[20rpx] transition-all duration-300 active:bg-[#eef3ff]" @click="openPicker('voiceprint')">
<text class="text-[28rpx] text-[#232338] font-medium">
{{ t('agent.voiceprint') }}
@@ -705,6 +875,15 @@ onMounted(async () => {
<wd-icon name="arrow-right" custom-class="text-[20rpx] text-[#9d9ea3]" />
</view>
<view class="flex items-center justify-between border border-[#eeeeee] rounded-[12rpx] bg-[#f5f7fb] p-[20rpx]">
<view class="text-[28rpx] text-[#232338] font-medium">
{{ t('agent.languageConfig') }}
</view>
<view class="cursor-pointer rounded-[20rpx] bg-[rgba(51,108,255,0.1)] px-[24rpx] py-[12rpx] text-[24rpx] text-[#336cff] transition-all duration-300 active:bg-[#336cff] active:text-white" @click="handleRegulate">
<text>{{ t('agent.editFunctions') }}</text>
</view>
</view>
<view class="flex items-center justify-between border border-[#eeeeee] rounded-[12rpx] bg-[#f5f7fb] p-[20rpx]">
<view class="text-[28rpx] text-[#232338] font-medium">
{{ t('agent.plugins') }}
@@ -799,15 +978,32 @@ onMounted(async () => {
<wd-action-sheet
v-model="pickerShow.voiceprint"
:actions="voiceOptions && voiceOptions.map(item => ({ name: item.name, value: item.id }))"
:actions="voiceOptions"
@close="onPickerCancel('voiceprint')"
@select="({ item }) => onPickerConfirm('voiceprint', item.value, item.name)"
/>
<wd-action-sheet
v-model="pickerShow.language"
:actions="languageOptions"
@close="onPickerCancel('language')"
@select="({ item }) => onPickerConfirm('language', item.value, item.name)"
/>
<wd-action-sheet
v-model="pickerShow.report"
:actions="reportOptions"
@close="onPickerCancel('report')"
@select="({ item }) => onPickerConfirm('report', item.value, item.name)"
/>
<ContextProviderDialog
v-model:visible="showContextProviderDialog"
:providers="currentContextProviders"
@confirm="handleUpdateContext"
/>
<VoiceSettingsDialog
v-model:visible="showVoiceSettingsDialog"
:settings="ttsSettings"
@confirm="handleUpdateVoiceSettings"
/>
</view>
</template>
@@ -242,7 +242,7 @@ onMounted(() => {
{{ t('home.lastConversation') }}{{ formatTime(agent.lastConnectedAt) }}
</text>
</view>
<text v-if="agent.tags" class="flex-1 truncate text-[22rpx] text-[#666]">
<text v-if="agent.tags" class="flex-1 truncate text-right text-[22rpx] text-[#666]">
{{ agent.tags.map(tag => tag.tagName).join(',') }}
</text>
</view>