feat: improve agent snapshot history

This commit is contained in:
Tyke Chen
2026-07-10 11:44:35 +08:00
parent 8ec5851027
commit a5aee109fe
23 changed files with 2833 additions and 130 deletions
@@ -8,6 +8,13 @@
@open="open"
@close="close"
>
<template slot="title">
<div class="snapshot-dialog-title">
<i class="el-icon-time snapshot-title-icon" aria-hidden="true"></i>
<span>{{ $t('agentSnapshot.title') }}</span>
</div>
</template>
<el-table
v-loading="loading"
:data="snapshots"
@@ -116,6 +123,13 @@
width="860px"
class="snapshot-detail-dialog"
>
<template slot="title">
<div class="snapshot-dialog-title">
<i class="el-icon-time snapshot-title-icon" aria-hidden="true"></i>
<span>{{ detailDialogTitle }}</span>
</div>
</template>
<div v-loading="detailLoading" class="snapshot-diff">
<template v-if="currentSnapshot && snapshotDiffs.length">
<div class="detail-summary">
@@ -289,6 +303,13 @@
width="860px"
class="snapshot-detail-dialog"
>
<template slot="title">
<div class="snapshot-dialog-title">
<i class="el-icon-time snapshot-title-icon" aria-hidden="true"></i>
<span>{{ restorePreviewTitle }}</span>
</div>
</template>
<div v-loading="restorePreviewLoading" class="snapshot-diff">
<template v-if="restorePreviewSnapshot && restorePreviewDiffs.length">
<div class="detail-summary">
@@ -418,17 +439,23 @@
:title="$t('agentSnapshot.restoreMemoryWarning')"
/>
</div>
<span slot="footer" class="dialog-footer">
<el-button @click="restorePreviewVisible = false">
<span slot="footer" class="snapshot-dialog-footer">
<el-button
class="snapshot-footer-button snapshot-footer-cancel"
@click="restorePreviewVisible = false"
>
{{ $t('button.cancel') }}
</el-button>
<el-button
type="primary"
class="snapshot-footer-button snapshot-footer-confirm"
:loading="restoring"
:disabled="restorePreviewLoading || !restorePreviewSnapshot || restorePreviewDiffs.length === 0"
@click="confirmRestoreSnapshot"
>
{{ $t('agentSnapshot.confirmRestore') }}
<span class="confirm-inner">
<i class="el-icon-refresh-left confirm-icon" aria-hidden="true"></i>
{{ $t('agentSnapshot.confirmRestore') }}
</span>
</el-button>
</span>
</el-dialog>
@@ -437,6 +464,7 @@
<script>
import Api from "@/apis/api";
import correctWord from "@/apis/module/correctWord";
import { formatDate } from "@/utils/date";
const FALLBACK_PLUGIN_NAME_KEYS = {
@@ -545,6 +573,9 @@ export default {
voiceNameMap: {},
voiceMetadataLoading: {},
loadedVoiceModelIds: {},
correctWordNameMap: {},
correctWordMetadataLoaded: false,
correctWordMetadataLoading: null,
snapshotFetchSeq: 0,
detailFetchSeq: 0,
restorePreviewFetchSeq: 0,
@@ -598,12 +629,13 @@ export default {
const beforeData = this.currentSnapshot.snapshotData || {};
const afterData = this.currentSnapshot.afterSnapshotData || {};
return this.buildDiffs(beforeData, afterData, this.currentSnapshot.changedFields || [], {
return this.buildDiffs(beforeData, afterData, this.currentSnapshot.changedFields, {
beforeLabel: this.$t("agentSnapshot.beforeChange"),
afterLabel: this.$t("agentSnapshot.afterChange"),
beforeVersionNo: this.currentSnapshot.beforeVersionNo,
afterVersionNo: this.currentSnapshot.afterVersionNo || this.currentSnapshot.versionNo,
fieldOrder: this.currentSnapshot.fieldOrder
fieldOrder: this.currentSnapshot.fieldOrder,
forceCompare: Boolean(this.currentSnapshot.forceCompare)
});
},
restorePreviewDiffs() {
@@ -620,7 +652,8 @@ export default {
afterLabel: this.$t("agentSnapshot.afterRestore"),
beforeVersionNo: this.restorePreviewSnapshot.beforeVersionNo || this.resolveCurrentVersionNo(),
afterVersionNo: this.restorePreviewSnapshot.afterVersionNo || this.restorePreviewSnapshot.versionNo,
fieldOrder: this.restorePreviewSnapshot.fieldOrder
fieldOrder: this.restorePreviewSnapshot.fieldOrder,
forceCompare: true
}
);
},
@@ -668,7 +701,13 @@ export default {
});
},
buildDiffs(beforeData, afterData, changedFields, options = {}) {
const fields = this.resolveDiffFields(beforeData, afterData, changedFields, options.fieldOrder);
const fields = this.resolveDiffFields(
beforeData,
afterData,
changedFields,
options.fieldOrder,
options.forceCompare
);
return fields.map((field) => {
const beforeValue = this.getFieldValue(beforeData, field);
const afterValue = this.getFieldValue(afterData, field);
@@ -1023,6 +1062,7 @@ export default {
...selectedSnapshot,
versionNo: displayVersionNo,
changedFields: adjacentVersion ? (selectedSnapshot.changedFields || row.changedFields || []) : [],
forceCompare: !adjacentVersion,
snapshotData: previousSnapshot.snapshotData || {},
afterSnapshotData: selectedSnapshot.snapshotData || {},
beforeVersionNo: previousSnapshot.versionNo,
@@ -1210,9 +1250,44 @@ export default {
const afterData = snapshot.afterSnapshotData || {};
return Promise.all([
this.ensureModelMetadata(),
this.ensureVoiceMetadataForData(beforeData, afterData)
this.ensureVoiceMetadataForData(beforeData, afterData),
this.ensureCorrectWordMetadataForData(beforeData, afterData)
]);
},
ensureCorrectWordMetadataForData(...dataList) {
const hasCorrectWordIds = dataList.some((data) => {
return Array.isArray(data?.correctWordFileIds) && data.correctWordFileIds.length > 0;
});
return hasCorrectWordIds ? this.ensureCorrectWordMetadata() : Promise.resolve();
},
ensureCorrectWordMetadata() {
if (this.correctWordMetadataLoaded) {
return Promise.resolve();
}
if (this.correctWordMetadataLoading) {
return this.correctWordMetadataLoading;
}
this.correctWordMetadataLoading = new Promise((resolve) => {
correctWord.selectAll(({ data }) => {
if (data.code === 0) {
const metadata = {};
(data.data || []).forEach((item) => {
if (item.id) {
metadata[item.id] = item.fileName || item.id;
}
});
this.correctWordNameMap = metadata;
this.correctWordMetadataLoaded = true;
}
resolve();
});
}).finally(() => {
this.correctWordMetadataLoading = null;
});
return this.correctWordMetadataLoading;
},
ensureVoiceMetadataForData(...dataList) {
const modelIds = Array.from(new Set(
dataList
@@ -1661,17 +1736,21 @@ export default {
? `${this.$t("agentSnapshot.restoreFailedRollback")}: ${data.msg}`
: this.$t("agentSnapshot.restoreFailedRollback");
},
resolveDiffFields(beforeData, afterData, changedFields = [], fieldOrder = []) {
const safeChangedFields = Array.isArray(changedFields) ? changedFields : [];
resolveDiffFields(beforeData, afterData, changedFields = null, fieldOrder = [], forceCompare = false) {
const hasBackendChangedFields = Array.isArray(changedFields);
const safeChangedFields = hasBackendChangedFields ? changedFields : [];
const directFields = safeChangedFields
.filter((field) => field && field !== "restore")
.map((field) => this.canonicalField(field));
const candidates = directFields.length > 0 && !safeChangedFields.includes("restore")
? directFields
: this.snapshotFieldOrder(fieldOrder, beforeData, afterData);
if (!forceCompare && hasBackendChangedFields) {
return Array.from(new Set(directFields));
}
const candidates = this.snapshotFieldOrder(fieldOrder, beforeData, afterData);
return Array.from(new Set(candidates)).filter((field) => {
return !this.isSameValue(
return !this.isSameFieldValue(
field,
this.getFieldValue(beforeData, field),
this.getFieldValue(afterData, field)
);
@@ -1706,6 +1785,52 @@ export default {
isSameValue(left, right) {
return this.isEquivalentValue(this.normalizeValue(left), this.normalizeValue(right));
},
isSameFieldValue(field, left, right) {
return this.isEquivalentValue(
this.normalizeValueForField(field, left),
this.normalizeValueForField(field, right)
);
},
normalizeValueForField(field, value) {
if (field === "functions") {
return this.normalizeFunctionMap(value);
}
if (field === "contextProviders") {
return this.normalizeSortedJsonList(value);
}
if (["ttsVolume", "ttsRate", "ttsPitch"].includes(field)) {
return this.normalizeDefaultTtsNumber(value);
}
if (field === "summaryMemory") {
return value === null || value === undefined || String(value).trim() === "" ? "" : String(value);
}
if (field === "correctWordFileIds" || field === "tagNames") {
return Array.isArray(value)
? value.filter((item) => item !== null && item !== undefined && String(item).trim() !== "")
.map((item) => String(item))
.sort()
: [];
}
return this.normalizeValue(value);
},
normalizeSortedJsonList(value) {
if (!Array.isArray(value)) {
return [];
}
return value
.map((item) => this.normalizeValue(item))
.sort((left, right) => JSON.stringify(left).localeCompare(JSON.stringify(right)));
},
normalizeDefaultTtsNumber(value) {
if (value === null || value === undefined || String(value).trim() === "") {
return 0;
}
if (typeof value === "number") {
return Math.trunc(value);
}
const text = String(value).trim();
return /^[+-]?\d+$/.test(text) ? Number.parseInt(text, 10) : text;
},
isEquivalentValue(left, right) {
if (left === SNAPSHOT_SECRET_REDACTED || right === SNAPSHOT_SECRET_REDACTED) {
return true;
@@ -1754,6 +1879,9 @@ export default {
return this.translateKey(CHAT_HISTORY_CONF_LABEL_KEYS[value] || CHAT_HISTORY_CONF_LABEL_KEYS[Number(value)])
|| this.formatValue(value);
}
if (field === "correctWordFileIds") {
return this.correctWordDisplayNames(value);
}
return this.formatValue(value);
},
modelDisplayName(value) {
@@ -1769,6 +1897,14 @@ export default {
return this.voiceNameMap[`${modelId}:${value}`] || this.voiceNameMap[value]
|| this.translateKey(FALLBACK_VOICE_NAME_KEYS[value], String(value)) || String(value);
},
correctWordDisplayNames(value) {
if (!Array.isArray(value) || value.length === 0) {
return this.$t("agentSnapshot.emptyValue");
}
return value
.map((id) => this.correctWordNameMap[id] || id)
.join(", ");
},
formatValue(value) {
if (value === null || value === undefined || value === "") {
return this.$t("agentSnapshot.emptyValue");
@@ -1846,6 +1982,59 @@ export default {
font-weight: 500;
}
.snapshot-dialog-title {
display: inline-flex;
align-items: center;
font-size: 18px;
> span {
line-height: 18px;
font-weight: 500;
}
}
.snapshot-title-icon {
display: inline-flex;
align-items: center;
justify-content: center;
width: 24px;
height: 24px;
margin-right: 8px;
color: #4a7cfd;
font-size: 24px;
line-height: 1;
}
::v-deep .el-dialog__headerbtn {
top: 12px;
right: 16px;
display: flex;
align-items: center;
justify-content: center;
width: 32px;
height: 32px;
border: none;
border-radius: 50%;
background: #fff;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.12);
.el-dialog__close {
position: static;
color: #666;
font-size: 18px;
transform: none;
}
&:hover {
background: #fff;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.18);
.el-dialog__close {
color: #333;
}
}
}
::v-deep .el-dialog__body {
padding: 20px;
}
@@ -1854,6 +2043,81 @@ export default {
padding: 12px 20px 16px;
}
.snapshot-dialog-footer {
display: flex;
justify-content: flex-end;
gap: 10px;
}
.snapshot-footer-button {
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 96px;
height: 36px;
padding: 10px 20px;
border-radius: 4px;
font-size: 14px;
transition: all 0.3s ease;
& + .snapshot-footer-button {
margin-left: 0;
}
}
.snapshot-footer-cancel {
border: 1px solid #d7e1ff;
background: #f7f9ff;
color: #4a7cfd;
&:hover,
&:focus {
border-color: #4a7cfd;
background: #eef4ff;
color: #4a7cfd;
box-shadow: 0 2px 8px rgba(74, 124, 253, 0.14);
transform: translateY(-2px);
}
}
.snapshot-footer-confirm {
border: none;
background: linear-gradient(to right, #4a7cfd, #8154fc);
color: #fff;
&:hover,
&:focus {
border-color: transparent;
background: linear-gradient(to right, #4a7cfd, #8154fc);
color: #fff;
box-shadow: 0 2px 8px rgba(74, 124, 253, 0.3);
transform: translateY(-2px);
opacity: 0.88;
}
&.is-disabled,
&.is-disabled:hover,
&.is-disabled:focus {
border-color: transparent;
background: linear-gradient(to right, #a0b4fd, #b8a4fd);
color: rgba(255, 255, 255, 0.6);
box-shadow: none;
transform: none;
cursor: not-allowed;
opacity: 1;
}
}
.confirm-inner {
display: inline-flex;
align-items: center;
}
.confirm-icon {
margin-right: 5px;
font-size: 16px;
}
.snapshot-table {
width: 100%;
}
@@ -25,6 +25,7 @@
:step="1"
:format-tooltip="formatTooltip"
class="tts-slider"
@change="markTtsSettingChanged('volume')"
/>
<span class="slider-hint">{{ $t('roleConfig.volumeHint') }}</span>
</div>
@@ -40,6 +41,7 @@
:step="1"
:format-tooltip="formatTooltip"
class="tts-slider"
@change="markTtsSettingChanged('speed')"
/>
<span class="slider-hint">{{ $t('roleConfig.speedHint') }}</span>
</div>
@@ -55,6 +57,7 @@
:step="1"
:format-tooltip="formatTooltip"
class="tts-slider"
@change="markTtsSettingChanged('pitch')"
/>
<span class="slider-hint">{{ $t('roleConfig.pitchHint') }}</span>
</div>
@@ -122,6 +125,11 @@ export default {
speed: 0,
pitch: 0,
},
changedTtsFields: {
volume: false,
speed: false,
pitch: false,
},
replacementWordIds: [],
replacementWordList: []
};
@@ -141,6 +149,11 @@ export default {
if (newVal) {
// 当抽屉打开时,复制当前设置到本地
this.localSettings = { ...this.settings };
this.changedTtsFields = {
volume: false,
speed: false,
pitch: false,
};
this.replacementWordIds = [...this.checkedReplacementWordIds];
this.fetchReplacementWordList();
}
@@ -156,9 +169,18 @@ export default {
},
handleSave() {
// 保存设置并关闭
this.$emit('save', { ...this.localSettings, replacementWordIds: this.replacementWordIds });
const changedTtsFields = Object.keys(this.changedTtsFields)
.filter((field) => this.changedTtsFields[field]);
this.$emit('save', {
...this.localSettings,
changedTtsFields,
replacementWordIds: this.replacementWordIds
});
this.handleClose();
},
markTtsSettingChanged(field) {
this.$set(this.changedTtsFields, field, true);
},
formatTooltip(val) {
return `${val}%`;
},
+137 -35
View File
@@ -51,7 +51,12 @@
<el-button class="history-btn" @click="showSnapshotDialog = true">
{{ $t("roleConfig.snapshotHistory") }}
</el-button>
<el-button type="primary" class="save-btn" @click="saveConfig">
<el-button
type="primary"
class="save-btn"
:disabled="voiceOptionsLoading"
@click="saveConfig"
>
{{ $t("roleConfig.saveConfig") }}
</el-button>
<el-button class="reset-btn" @click="resetConfig">{{
@@ -367,7 +372,7 @@
v-model="selectedLanguage"
:placeholder="$t('roleConfig.selectLanguage')"
class="form-select language-select"
@change="filterVoicesByLanguage"
@change="handleLanguageChange"
>
<el-option
v-for="(lang, index) in languageOptions"
@@ -392,6 +397,7 @@
filterable
:placeholder="$t('roleConfig.pleaseSelect')"
class="form-select"
@change="handleVoiceChange"
>
<el-option
v-for="(item, index) in voiceOptions"
@@ -556,6 +562,10 @@ export default {
// 语言筛选相关状态
languageOptions: [], // 语言选项列表
selectedLanguage: '', // 当前选中的语言
ttsLanguageTouched: false,
ttsVoiceTouched: false,
voiceFetchSeq: 0,
voiceOptionsLoading: false,
// 功能状态
featureStatus: {
vad: false, // 语言检测活动功能状态
@@ -592,6 +602,9 @@ export default {
return { ...fallback };
},
async saveConfig() {
if (this.voiceOptionsLoading) {
return;
}
const configData = {
agentCode: this.form.agentCode,
agentName: this.form.agentName,
@@ -601,8 +614,6 @@ export default {
slmModelId: this.form.model.slmModelId,
vllmModelId: this.form.model.vllmModelId,
ttsModelId: this.form.model.ttsModelId,
ttsVoiceId: this.form.ttsVoiceId,
ttsLanguage: this.selectedLanguage,
chatHistoryConf: this.form.chatHistoryConf,
memModelId: this.form.model.memModelId,
intentModelId: this.form.model.intentModelId,
@@ -625,6 +636,17 @@ export default {
if (tagsChanged) {
configData.tagNames = tagNames;
}
if (this.shouldSubmitTtsLanguage()) {
configData.ttsLanguage = this.selectedLanguage;
}
if (this.ttsVoiceTouched && this.form.ttsVoiceId !== null && this.form.ttsVoiceId !== undefined) {
configData.ttsVoiceId = this.form.ttsVoiceId;
}
const submittedTtsLanguageTouched = this.ttsLanguageTouched;
const submittedTtsVoiceTouched = this.ttsVoiceTouched;
const submittedTtsLanguage = configData.ttsLanguage;
const submittedTtsVoiceId = configData.ttsVoiceId;
const submittedVoiceFetchSeq = this.voiceFetchSeq;
// 只在用户设置了TTS参数时才传递(不为null/undefined
if (this.form.ttsVolume !== null && this.form.ttsVolume !== undefined) {
@@ -643,6 +665,17 @@ export default {
if (tagsChanged) {
this.originalTagNames = [...tagNames];
}
if (submittedVoiceFetchSeq === this.voiceFetchSeq
&& submittedTtsLanguageTouched
&& this.selectedLanguage === submittedTtsLanguage) {
this.form.ttsLanguage = submittedTtsLanguage;
this.ttsLanguageTouched = false;
}
if (submittedVoiceFetchSeq === this.voiceFetchSeq
&& submittedTtsVoiceTouched
&& this.form.ttsVoiceId === submittedTtsVoiceId) {
this.ttsVoiceTouched = false;
}
this.$message.success({
message: i18n.t("roleConfig.saveSuccess"),
showClose: true,
@@ -686,10 +719,14 @@ export default {
type: "warning",
})
.then(() => {
this.selectedLanguage = "";
this.ttsLanguageTouched = true;
this.ttsVoiceTouched = true;
this.form = {
agentCode: "",
agentName: "",
ttsVoiceId: "",
ttsLanguage: "",
chatHistoryConf: 0,
systemPrompt: "",
summaryMemory: "",
@@ -707,6 +744,7 @@ export default {
intentModelId: "",
},
};
this.fetchVoiceOptions("");
this.dynamicTags = [];
this.currentFunctions = [];
this.$message.success({
@@ -745,6 +783,7 @@ export default {
}
},
applyTemplateData(templateData) {
const currentLanguage = this.selectedLanguage;
this.form = {
...this.form,
agentName: templateData.agentName || this.form.agentName,
@@ -764,11 +803,28 @@ export default {
intentModelId: templateData.intentModelId || this.form.model.intentModelId,
},
};
if (templateData.ttsLanguage) {
this.selectedLanguage = templateData.ttsLanguage;
this.ttsLanguageTouched = true;
}
if (templateData.ttsModelId || templateData.ttsVoiceId || templateData.ttsLanguage) {
if (templateData.ttsModelId || templateData.ttsVoiceId) {
this.ttsVoiceTouched = true;
}
this.ttsLanguageTouched = true;
this.fetchVoiceOptions(this.form.model.ttsModelId, {
autoSelectVoice: true,
preferredLanguage: templateData.ttsLanguage || (templateData.ttsVoiceId ? "" : currentLanguage),
preferredVoiceId: templateData.ttsVoiceId || ""
});
}
},
fetchAgentConfig(agentId) {
Api.agent.getDeviceConfig(agentId, ({ data }) => {
if (data.code === 0) {
this.tempSummaryMemory = "";
this.ttsLanguageTouched = false;
this.ttsVoiceTouched = false;
this.form = {
...this.form,
...data.data,
@@ -783,6 +839,10 @@ export default {
intentModelId: data.data.intentModelId,
},
};
this.fetchVoiceOptions(data.data.ttsModelId, {
preferredLanguage: data.data.ttsLanguage,
preferredVoiceId: data.data.ttsVoiceId
});
// 同步TTS设置到ttsSettings
this.ttsSettings = {
@@ -874,15 +934,22 @@ export default {
}
});
},
fetchVoiceOptions(modelId) {
fetchVoiceOptions(modelId, options = {}) {
const requestSeq = ++this.voiceFetchSeq;
if (!modelId) {
this.voiceOptionsLoading = false;
this.voiceOptions = [];
this.voiceDetails = {};
this.languageOptions = [];
this.selectedLanguage = '';
return;
}
this.voiceOptionsLoading = true;
Api.model.getModelVoices(modelId, "", ({ data }) => {
if (requestSeq !== this.voiceFetchSeq) {
return;
}
this.voiceOptionsLoading = false;
if (data.code === 0 && data.data) {
// 保存完整的音色信息
this.voiceDetails = data.data.reduce((acc, voice) => {
@@ -904,15 +971,27 @@ export default {
label: lang
}));
// 使用后端返回的用户选择的语言,如果没有则使用第一个语言选项
if (this.form.ttsLanguage && this.languageOptions.some(option => option.value === this.form.ttsLanguage)) {
// 优先保留调用方指定或后端保存的语言,其次使用当前音色的默认语言
const requestedLanguage = options.preferredLanguage;
const preferredVoiceLanguage = options.preferredVoiceId
? this.getVoiceDefaultLanguage(options.preferredVoiceId)
: "";
if (requestedLanguage && this.languageOptions.some(option => option.value === requestedLanguage)) {
this.selectedLanguage = requestedLanguage;
} else if (preferredVoiceLanguage && this.languageOptions.some(option => option.value === preferredVoiceLanguage)) {
this.selectedLanguage = preferredVoiceLanguage;
} else if (this.form.ttsLanguage && this.languageOptions.some(option => option.value === this.form.ttsLanguage)) {
this.selectedLanguage = this.form.ttsLanguage;
} else if (this.getVoiceDefaultLanguage(this.form.ttsVoiceId)) {
this.selectedLanguage = this.getVoiceDefaultLanguage(this.form.ttsVoiceId);
} else if (this.languageOptions.length > 0) {
this.selectedLanguage = this.languageOptions[0].value;
} else {
this.selectedLanguage = "";
}
// 根据选中的语言筛选音色
this.filterVoicesByLanguage();
this.filterVoicesByLanguage(options);
} else {
this.voiceOptions = [];
this.voiceDetails = {};
@@ -921,9 +1000,19 @@ export default {
}
});
},
getVoiceDefaultLanguage(voiceId) {
if (!voiceId || !this.voiceDetails || !this.voiceDetails[voiceId]?.languages) {
return "";
}
const languages = this.voiceDetails[voiceId].languages
.split(/[、;;,]/)
.map(lang => lang.trim())
.filter(Boolean);
return languages[0] || "";
},
// 根据语言筛选音色
filterVoicesByLanguage() {
filterVoicesByLanguage(options = {}) {
if (!this.voiceDetails || Object.keys(this.voiceDetails).length === 0) {
this.voiceOptions = [];
return;
@@ -954,8 +1043,9 @@ export default {
const currentVoiceSupportsLanguage = this.form.ttsVoiceId &&
filteredVoices.some(voice => voice.id === this.form.ttsVoiceId);
if (!currentVoiceSupportsLanguage) {
if (!currentVoiceSupportsLanguage && options.autoSelectVoice) {
this.form.ttsVoiceId = filteredVoices.length > 0 ? filteredVoices[0].id : '';
this.ttsVoiceTouched = true;
}
// 同步到ttsSettings(如果值为null,使用0作为显示默认值,但不修改form中的值)
@@ -965,6 +1055,21 @@ export default {
pitch: this.form.ttsPitch !== null && this.form.ttsPitch !== undefined ? this.form.ttsPitch : 0
};
},
handleLanguageChange() {
this.ttsLanguageTouched = true;
this.form.ttsLanguage = this.selectedLanguage;
this.filterVoicesByLanguage({ autoSelectVoice: true });
},
handleVoiceChange() {
this.ttsVoiceTouched = true;
if (this.selectedLanguage) {
this.form.ttsLanguage = this.selectedLanguage;
this.ttsLanguageTouched = true;
}
},
shouldSubmitTtsLanguage() {
return this.ttsLanguageTouched;
},
getFunctionDisplayChar(name) {
if (!name || name.length === 0) return "";
@@ -1006,6 +1111,17 @@ export default {
// 当LLM类型改变时,更新意图识别选项的可见性
this.updateIntentOptionsVisibility();
}
if (type === "TTS") {
const preferredLanguage = this.selectedLanguage;
this.ttsLanguageTouched = true;
this.ttsVoiceTouched = true;
this.form.ttsVoiceId = "";
this.form.ttsLanguage = "";
this.fetchVoiceOptions(value, {
autoSelectVoice: true,
preferredLanguage
});
}
},
fetchAllFunctions() {
return new Promise((resolve, reject) => {
@@ -1042,13 +1158,20 @@ export default {
this.showTtsAdvancedDialog = true;
},
handleTtsSettingsSave(settings) {
const { replacementWordIds, ...ttsSettings } = settings;
const { replacementWordIds, changedTtsFields = [], ...ttsSettings } = settings;
this.checkedReplacementWordIds = replacementWordIds;
// 保存TTS设置
this.ttsSettings = ttsSettings;
this.form.ttsVolume = ttsSettings.volume;
this.form.ttsRate = ttsSettings.speed;
this.form.ttsPitch = ttsSettings.pitch;
const changedFields = new Set(changedTtsFields);
if (changedFields.has("volume")) {
this.form.ttsVolume = ttsSettings.volume;
}
if (changedFields.has("speed")) {
this.form.ttsRate = ttsSettings.speed;
}
if (changedFields.has("pitch")) {
this.form.ttsPitch = ttsSettings.pitch;
}
},
handleUpdateContext(providers) {
this.currentContextProviders = providers;
@@ -1424,27 +1547,6 @@ export default {
});
}
},
watch: {
"form.model.ttsModelId": {
handler(newVal, oldVal) {
if (oldVal && newVal !== oldVal) {
this.form.ttsVoiceId = "";
this.fetchVoiceOptions(newVal);
} else {
this.fetchVoiceOptions(newVal);
}
},
immediate: true,
},
voiceOptions: {
handler(newVal) {
if (newVal && newVal.length > 0 && !this.form.ttsVoiceId) {
this.form.ttsVoiceId = newVal[0].value;
}
},
immediate: true,
},
},
async mounted() {
const agentId = this.$route.query.agentId;
if (agentId) {