mirror of
https://github.com/xinnan-tech/xiaozhi-esp32-server.git
synced 2026-07-22 07:03:53 +08:00
Merge pull request #3132 from xinnan-tech/replacement-word
feat: 新增替换词管理功能
This commit is contained in:
@@ -10,6 +10,7 @@ import user from './module/user.js'
|
||||
import voiceClone from './module/voiceClone.js'
|
||||
import voiceResource from './module/voiceResource.js'
|
||||
import knowledgeBase from './module/knowledgeBase.js'
|
||||
import correctWord from './module/correctWord.js'
|
||||
|
||||
|
||||
|
||||
@@ -41,5 +42,6 @@ export default {
|
||||
dict,
|
||||
voiceResource,
|
||||
voiceClone,
|
||||
knowledgeBase
|
||||
knowledgeBase,
|
||||
correctWord
|
||||
}
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
import { getServiceUrl } from '../api';
|
||||
import RequestService from '../httpRequest';
|
||||
|
||||
|
||||
export default {
|
||||
// 获取替换词文件列表
|
||||
getFileList(params, callback) {
|
||||
const queryParams = new URLSearchParams({
|
||||
page: params.page,
|
||||
pageSize: params.pageSize
|
||||
}).toString();
|
||||
|
||||
RequestService.sendRequest()
|
||||
.url(`${getServiceUrl()}/correct-word/file/list?${queryParams}`)
|
||||
.method('GET')
|
||||
.success((res) => {
|
||||
RequestService.clearRequestTime()
|
||||
callback(res)
|
||||
})
|
||||
.networkFail((err) => {
|
||||
console.error('获取替换词文件列表失败:', err)
|
||||
RequestService.reAjaxFun(() => {
|
||||
this.getFileList(params, callback)
|
||||
})
|
||||
}).send()
|
||||
},
|
||||
|
||||
// 获取所有替换词文件(不分页)
|
||||
selectAll(callback) {
|
||||
RequestService.sendRequest()
|
||||
.url(`${getServiceUrl()}/correct-word/file/select`)
|
||||
.method('GET')
|
||||
.success((res) => {
|
||||
RequestService.clearRequestTime()
|
||||
callback(res)
|
||||
})
|
||||
.networkFail((err) => {
|
||||
console.error('获取所有替换词文件失败:', err)
|
||||
RequestService.reAjaxFun(() => {
|
||||
this.selectAll(callback)
|
||||
})
|
||||
}).send()
|
||||
},
|
||||
|
||||
// 下载替换词文件
|
||||
downloadFile(id, callback) {
|
||||
RequestService.sendRequest()
|
||||
.url(`${getServiceUrl()}/correct-word/file/download/${id}`)
|
||||
.method('GET')
|
||||
.success((res) => {
|
||||
RequestService.clearRequestTime()
|
||||
callback(res)
|
||||
})
|
||||
.fail((err) => {
|
||||
RequestService.clearRequestTime()
|
||||
callback(err)
|
||||
}).send()
|
||||
},
|
||||
|
||||
// 新增替换词文件
|
||||
addFile(data, callback) {
|
||||
RequestService.sendRequest()
|
||||
.url(`${getServiceUrl()}/correct-word/file`)
|
||||
.method('POST')
|
||||
.data(data)
|
||||
.success((res) => {
|
||||
RequestService.clearRequestTime()
|
||||
callback(res)
|
||||
})
|
||||
.fail((err) => {
|
||||
RequestService.clearRequestTime()
|
||||
callback(err)
|
||||
}).send()
|
||||
},
|
||||
|
||||
// 更新替换词文件
|
||||
updateFile(data, callback) {
|
||||
RequestService.sendRequest()
|
||||
.url(`${getServiceUrl()}/correct-word/file/${data.id}`)
|
||||
.method('PUT')
|
||||
.data({
|
||||
fileName: data.fileName,
|
||||
content: data.content
|
||||
})
|
||||
.success((res) => {
|
||||
RequestService.clearRequestTime()
|
||||
callback(res)
|
||||
})
|
||||
.fail((err) => {
|
||||
RequestService.clearRequestTime()
|
||||
callback(err)
|
||||
}).send()
|
||||
},
|
||||
|
||||
// 删除替换词文件
|
||||
deleteFile(id, callback) {
|
||||
RequestService.sendRequest()
|
||||
.url(`${getServiceUrl()}/correct-word/file/${id}`)
|
||||
.method('DELETE')
|
||||
.success((res) => {
|
||||
RequestService.clearRequestTime()
|
||||
callback(res)
|
||||
})
|
||||
.networkFail((err) => {
|
||||
console.error('删除替换词文件失败:', err)
|
||||
RequestService.reAjaxFun(() => {
|
||||
this.deleteFile(id, callback)
|
||||
})
|
||||
}).send()
|
||||
},
|
||||
|
||||
// 批量删除替换词文件
|
||||
batchDeleteFile(ids, callback) {
|
||||
RequestService.sendRequest()
|
||||
.url(`${getServiceUrl()}/correct-word/file/batch-delete`)
|
||||
.method('POST')
|
||||
.data(ids)
|
||||
.success((res) => {
|
||||
RequestService.clearRequestTime()
|
||||
callback(res)
|
||||
})
|
||||
.networkFail((err) => {
|
||||
console.error('批量删除替换词文件失败:', err)
|
||||
RequestService.reAjaxFun(() => {
|
||||
this.batchDeleteFile(ids, callback)
|
||||
})
|
||||
}).send()
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
<el-header class="header">
|
||||
<div class="header-container">
|
||||
<!-- 左侧元素 -->
|
||||
<div class="header-left" @click="goHome">
|
||||
<div class="header-left" @click="handleRouter('home')">
|
||||
<img loading="lazy" alt="" src="@/assets/xiaozhi-logo.png" class="logo-img" />
|
||||
<img loading="lazy" alt="" :src="xiaozhiAiIcon" class="brand-img" />
|
||||
</div>
|
||||
@@ -14,7 +14,7 @@
|
||||
$route.path === '/home' ||
|
||||
$route.path === '/role-config' ||
|
||||
$route.path === '/device-management',
|
||||
}" @click="goHome">
|
||||
}" @click="handleRouter('home')">
|
||||
<img loading="lazy" alt="" src="@/assets/header/robot.png" :style="{
|
||||
filter:
|
||||
$route.path === '/home' ||
|
||||
@@ -27,7 +27,7 @@
|
||||
</div>
|
||||
<!-- 普通用户显示音色克隆 -->
|
||||
<div v-if="!userInfo.superAdmin && featureStatus.voiceClone" class="equipment-management"
|
||||
:class="{ 'active-tab': $route.path === '/voice-clone-management' }" @click="goVoiceCloneManagement">
|
||||
:class="{ 'active-tab': $route.path === '/voice-clone-management' }" @click="handleRouter('voiceCloneManagement')">
|
||||
<img loading="lazy" alt="" src="@/assets/header/voice.png" :style="{
|
||||
filter:
|
||||
$route.path === '/voice-clone-management'
|
||||
@@ -55,17 +55,17 @@
|
||||
<i class="el-icon-arrow-down el-icon--right" :class="{ 'rotate-down': voiceCloneDropdownVisible }"></i>
|
||||
</span>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item @click.native="goVoiceCloneManagement">
|
||||
<el-dropdown-item @click.native="handleRouter('voiceCloneManagement')">
|
||||
{{ $t("header.voiceCloneManagement") }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item @click.native="goVoiceResourceManagement">
|
||||
<el-dropdown-item @click.native="handleRouter('voiceResourceManagement')">
|
||||
{{ $t("header.voiceResourceManagement") }}
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
|
||||
<div v-if="userInfo.superAdmin" class="equipment-management" :class="{ 'active-tab': $route.path === '/model-config' }"
|
||||
@click="goModelConfig">
|
||||
@click="handleRouter('modelConfig')">
|
||||
<img loading="lazy" alt="" src="@/assets/header/model_config.png" :style="{
|
||||
filter:
|
||||
$route.path === '/model-config' ? 'brightness(0) invert(1)' : 'None',
|
||||
@@ -74,7 +74,7 @@
|
||||
</div>
|
||||
<div v-if="featureStatus.knowledgeBase" class="equipment-management"
|
||||
:class="{ 'active-tab': $route.path === '/knowledge-base-management' || $route.path === '/knowledge-file-upload' }"
|
||||
@click="goKnowledgeBaseManagement">
|
||||
@click="handleRouter('knowledgeBaseManagement')">
|
||||
<img loading="lazy" alt="" src="@/assets/header/knowledge_base.png" :style="{
|
||||
filter:
|
||||
$route.path === '/knowledge-base-management' || $route.path === '/knowledge-file-upload' ? 'brightness(0) invert(1)' : 'None',
|
||||
@@ -110,28 +110,31 @@
|
||||
<i class="el-icon-arrow-down el-icon--right" :class="{ 'rotate-down': paramDropdownVisible }"></i>
|
||||
</span>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item @click.native="goParamManagement">
|
||||
<el-dropdown-item @click.native="handleRouter('paramManagement')">
|
||||
{{ $t("header.paramManagement") }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item @click.native="goUserManagement">
|
||||
<el-dropdown-item @click.native="handleRouter('userManagement')">
|
||||
{{ $t("header.userManagement") }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item @click.native="goOtaManagement">
|
||||
<el-dropdown-item @click.native="handleRouter('otaManagement')">
|
||||
{{ $t("header.otaManagement") }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item @click.native="goDictManagement">
|
||||
<el-dropdown-item @click.native="handleRouter('dictManagement')">
|
||||
{{ $t("header.dictManagement") }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item @click.native="goProviderManagement">
|
||||
<el-dropdown-item @click.native="handleRouter('providerManagement')">
|
||||
{{ $t("header.providerManagement") }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item @click.native="goAgentTemplateManagement">
|
||||
<el-dropdown-item @click.native="handleRouter('agentTemplate')">
|
||||
{{ $t("header.agentTemplate") }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item @click.native="goServerSideManagement">
|
||||
<el-dropdown-item @click.native="handleRouter('replacementWordManagement')">
|
||||
{{ $t("header.replacementWordManagement") }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item @click.native="handleRouter('serverSideManagement')">
|
||||
{{ $t("header.serverSideManagement") }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item @click.native="goFeatureManagement">
|
||||
<el-dropdown-item @click.native="handleRouter('featureManagement')">
|
||||
{{ $t("header.featureManagement") }}
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
@@ -220,6 +223,23 @@ export default {
|
||||
label: "label",
|
||||
children: "children",
|
||||
},
|
||||
// 跳转页面配置
|
||||
routerPaths: {
|
||||
home: "/home",
|
||||
modelConfig: "/model-config",
|
||||
knowledgeBaseManagement: "/knowledge-base-management",
|
||||
voiceCloneManagement: "/voice-clone-management",
|
||||
voiceResourceManagement: "/voice-resource-management",
|
||||
paramManagement: "/params-management",
|
||||
userManagement: "/user-management",
|
||||
otaManagement: "/ota-management",
|
||||
dictManagement: "/dict-management",
|
||||
providerManagement: "/provider-management",
|
||||
agentTemplate: "/agent-template-management",
|
||||
replacementWordManagement: "/replacement-word-management",
|
||||
serverSideManagement: "/server-side-management",
|
||||
featureManagement: "/feature-management",
|
||||
}
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
@@ -331,49 +351,8 @@ export default {
|
||||
window.removeEventListener("resize", this.checkScreenSize);
|
||||
},
|
||||
methods: {
|
||||
goHome() {
|
||||
// 跳转到首页
|
||||
this.$router.push("/home");
|
||||
},
|
||||
goUserManagement() {
|
||||
this.$router.push("/user-management");
|
||||
},
|
||||
goModelConfig() {
|
||||
this.$router.push("/model-config");
|
||||
},
|
||||
goKnowledgeBaseManagement() {
|
||||
this.$router.push("/knowledge-base-management");
|
||||
},
|
||||
goVoiceCloneManagement() {
|
||||
this.$router.push("/voice-clone-management");
|
||||
},
|
||||
goParamManagement() {
|
||||
this.$router.push("/params-management");
|
||||
},
|
||||
goOtaManagement() {
|
||||
this.$router.push("/ota-management");
|
||||
},
|
||||
goDictManagement() {
|
||||
this.$router.push("/dict-management");
|
||||
},
|
||||
goProviderManagement() {
|
||||
this.$router.push("/provider-management");
|
||||
},
|
||||
goServerSideManagement() {
|
||||
this.$router.push("/server-side-management");
|
||||
},
|
||||
|
||||
// 跳转到音色资源管理
|
||||
goVoiceResourceManagement() {
|
||||
this.$router.push("/voice-resource-management");
|
||||
},
|
||||
// 添加默认角色模板管理导航方法
|
||||
goAgentTemplateManagement() {
|
||||
this.$router.push("/agent-template-management");
|
||||
},
|
||||
// 跳转到功能管理
|
||||
goFeatureManagement() {
|
||||
this.$router.push("/feature-management");
|
||||
handleRouter(type) {
|
||||
this.$router.push(this.routerPaths[type]);
|
||||
},
|
||||
// 加载功能状态
|
||||
async loadFeatureStatus() {
|
||||
|
||||
@@ -0,0 +1,536 @@
|
||||
<template>
|
||||
<el-dialog :title="title" :visible.sync="visible" width="700px" class="param-dialog-wrapper" :append-to-body="true"
|
||||
:close-on-click-modal="false" :key="dialogKey" custom-class="custom-param-dialog" :show-close="false">
|
||||
<div class="dialog-container">
|
||||
<div class="dialog-header">
|
||||
<h2 class="dialog-title">{{ title }}</h2>
|
||||
<button class="custom-close-btn" @click="cancel">
|
||||
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M13 1L1 13M1 1L13 13" stroke="currentColor" stroke-width="2" stroke-linecap="round" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<el-form :model="localForm" :rules="rules" ref="form" label-width="auto" label-position="left" class="param-form">
|
||||
<el-form-item :label="$t('replacementDialog.fileName')" prop="fileName" class="form-item">
|
||||
<el-input v-model="localForm.fileName" :placeholder="$t('replacementDialog.fileNamePlaceholder')"
|
||||
class="custom-input" @input="clearFieldError('fileName')"></el-input>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item :label="$t('replacementDialog.content')" prop="content" class="form-item content-item">
|
||||
<div class="content-wrapper">
|
||||
<el-input
|
||||
type="textarea"
|
||||
v-model="localForm.content"
|
||||
:placeholder="$t('replacementDialog.contentPlaceholder')"
|
||||
:rows="8"
|
||||
class="custom-textarea"
|
||||
@input="clearFieldError('content')"
|
||||
></el-input>
|
||||
<p class="format-tip">{{ $t('replacementDialog.formatTip') }}</p>
|
||||
<div class="upload-section">
|
||||
<el-upload
|
||||
class="upload-btn"
|
||||
action=""
|
||||
:auto-upload="false"
|
||||
:show-file-list="false"
|
||||
accept=".txt"
|
||||
:on-change="handleFileChange"
|
||||
>
|
||||
<el-button size="small" type="primary" class="upload-file-btn">
|
||||
<div class="upload-file-content">
|
||||
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg" style="margin-right: 4px;">
|
||||
<path d="M7 1V13M1 7H13" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
||||
</svg>
|
||||
<div>
|
||||
<p>{{ $t('replacementDialog.clickUploadTip') }}</p>
|
||||
<p>{{ $t('replacementDialog.uploadCoverTip') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</el-button>
|
||||
</el-upload>
|
||||
<span class="word-count" :class="{ 'over-limit': isOverLimit }">
|
||||
{{ wordCountText }}{{ $t('replacementDialog.wordCountUnit') }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<div class="dialog-footer">
|
||||
<el-button type="primary" @click="submit" class="save-btn" :loading="saving" :disabled="saving">
|
||||
{{ $t('replacementDialog.save') }}
|
||||
</el-button>
|
||||
<el-button @click="cancel" class="cancel-btn">
|
||||
{{ $t('replacementDialog.cancel') }}
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
default: '新增替换词'
|
||||
},
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
form: {
|
||||
type: Object,
|
||||
default: () => ({
|
||||
id: undefined,
|
||||
fileName: '',
|
||||
content: ''
|
||||
})
|
||||
}
|
||||
},
|
||||
data() {
|
||||
const MAX_WORD_COUNT = 4000;
|
||||
|
||||
const validateContent = (rule, value, callback) => {
|
||||
if (!value || !value.trim()) {
|
||||
callback(new Error(this.$t('replacementDialog.requiredContent')));
|
||||
return;
|
||||
}
|
||||
|
||||
const lines = this.getValidLines(value);
|
||||
const lineCount = lines.length;
|
||||
|
||||
if (lineCount > MAX_WORD_COUNT) {
|
||||
callback(new Error(this.$t('replacementDialog.maxWordCountExceeded', { max: MAX_WORD_COUNT })));
|
||||
return;
|
||||
}
|
||||
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
const pipeCount = (lines[i].match(/\|/g) || []).length;
|
||||
if (pipeCount !== 1) {
|
||||
callback(new Error(this.$t('replacementDialog.invalidPipeCount', { line: i + 1 })));
|
||||
return;
|
||||
}
|
||||
const parts = lines[i].split('|');
|
||||
if (!parts[0] || !parts[0].trim()) {
|
||||
callback(new Error(this.$t('replacementDialog.emptyOriginal', { line: i + 1 })));
|
||||
return;
|
||||
}
|
||||
if (!parts[1] || !parts[1].trim()) {
|
||||
callback(new Error(this.$t('replacementDialog.emptyReplacement', { line: i + 1 })));
|
||||
return;
|
||||
}
|
||||
const specialCharRegex = /[!@#$%^&*()_+=\[\]{};':"\\<>?\/`~]/;
|
||||
if (specialCharRegex.test(parts[0])) {
|
||||
callback(new Error(this.$t('replacementDialog.invalidOriginalChar', { line: i + 1 })));
|
||||
return;
|
||||
}
|
||||
if (specialCharRegex.test(parts[1])) {
|
||||
callback(new Error(this.$t('replacementDialog.invalidReplacementChar', { line: i + 1 })));
|
||||
return;
|
||||
}
|
||||
}
|
||||
callback();
|
||||
};
|
||||
|
||||
return {
|
||||
dialogKey: Date.now(),
|
||||
saving: false,
|
||||
localForm: {
|
||||
id: undefined,
|
||||
fileName: '',
|
||||
content: ''
|
||||
},
|
||||
maxWordCount: MAX_WORD_COUNT,
|
||||
rules: {
|
||||
fileName: [
|
||||
{ required: true, message: this.$t('replacementDialog.requiredFileName'), trigger: "blur" }
|
||||
],
|
||||
content: [
|
||||
{ required: true, validator: validateContent, trigger: "blur" }
|
||||
]
|
||||
}
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
wordCount() {
|
||||
if (!this.localForm.content) return 0;
|
||||
const contentStr = Array.isArray(this.localForm.content)
|
||||
? this.localForm.content.join('\n')
|
||||
: this.localForm.content;
|
||||
if (!contentStr.trim()) {
|
||||
return 0;
|
||||
}
|
||||
const lines = this.getValidLines();
|
||||
return lines.filter(line => line.includes('|')).length;
|
||||
},
|
||||
|
||||
isOverLimit() {
|
||||
return this.wordCount > this.maxWordCount;
|
||||
},
|
||||
|
||||
wordCountText() {
|
||||
return `${this.wordCount} / ${this.maxWordCount}`;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getValidLines() {
|
||||
if (!this.localForm.content) return [];
|
||||
const contentStr = Array.isArray(this.localForm.content)
|
||||
? this.localForm.content.join('\n')
|
||||
: this.localForm.content;
|
||||
return contentStr.split(/\r?\n/).filter(line => line.trim());
|
||||
},
|
||||
|
||||
clearFieldError(field) {
|
||||
if (this.$refs.form) {
|
||||
this.$refs.form.clearValidate(field);
|
||||
}
|
||||
},
|
||||
|
||||
handleFileChange(file) {
|
||||
if (!file) return;
|
||||
|
||||
const rawFile = file.raw;
|
||||
if (!rawFile) return;
|
||||
|
||||
const reader = new FileReader();
|
||||
reader.onload = (e) => {
|
||||
const content = e.target.result;
|
||||
this.localForm.content = content;
|
||||
|
||||
const lines = this.getValidLines(content);
|
||||
if (lines.length > this.maxWordCount) {
|
||||
this.$message.warning(
|
||||
this.$t('replacementDialog.maxWordCountExceeded', { max: this.maxWordCount })
|
||||
);
|
||||
}
|
||||
|
||||
this.$nextTick(() => {
|
||||
if (this.$refs.form) {
|
||||
this.$refs.form.clearValidate('content');
|
||||
}
|
||||
});
|
||||
};
|
||||
reader.onerror = () => {
|
||||
this.$message.error(this.$t('replacementDialog.readFileError'));
|
||||
};
|
||||
reader.readAsText(rawFile);
|
||||
},
|
||||
|
||||
submit() {
|
||||
this.$refs.form.validate((valid) => {
|
||||
if (valid) {
|
||||
if (this.isOverLimit) {
|
||||
this.$message.error(
|
||||
this.$t('replacementDialog.maxWordCountExceeded', { max: this.maxWordCount })
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const submitData = {
|
||||
id: this.localForm.id,
|
||||
fileName: this.localForm.fileName,
|
||||
content: this.getValidLines()
|
||||
};
|
||||
this.saving = true;
|
||||
this.$emit('submit', submitData);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
cancel() {
|
||||
this.saving = false;
|
||||
this.dialogKey = Date.now();
|
||||
this.$emit('cancel');
|
||||
},
|
||||
|
||||
resetSaving() {
|
||||
this.saving = false;
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
visible(newVal) {
|
||||
if (newVal) {
|
||||
this.localForm.id = this.form.id;
|
||||
this.localForm.fileName = this.form.fileName || '';
|
||||
const contentData = this.form.content;
|
||||
this.localForm.content = Array.isArray(contentData) ? contentData.join('\n') : (contentData || '');
|
||||
this.$nextTick(() => {
|
||||
if (this.$refs.form) {
|
||||
this.$refs.form.clearValidate();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.saving = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.custom-param-dialog {
|
||||
border-radius: 16px !important;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.15) !important;
|
||||
border: none !important;
|
||||
|
||||
.el-dialog__header {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.el-dialog__body {
|
||||
padding: 0 !important;
|
||||
border-radius: 16px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.param-dialog-wrapper {
|
||||
.dialog-container {
|
||||
padding: 24px 32px;
|
||||
background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%);
|
||||
}
|
||||
|
||||
.dialog-header {
|
||||
position: relative;
|
||||
margin-bottom: 24px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.dialog-title {
|
||||
font-size: 20px;
|
||||
color: #1e293b;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.custom-close-btn {
|
||||
position: absolute;
|
||||
top: -8px;
|
||||
right: -8px;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
border: none;
|
||||
background: #f1f5f9;
|
||||
color: #64748b;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0;
|
||||
outline: none;
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
|
||||
&:hover {
|
||||
color: #ffffff;
|
||||
background: #ef4444;
|
||||
transform: rotate(90deg);
|
||||
box-shadow: 0 4px 6px rgba(239, 68, 68, 0.2);
|
||||
}
|
||||
|
||||
svg {
|
||||
transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
}
|
||||
|
||||
.param-form {
|
||||
.form-item {
|
||||
margin-bottom: 20px;
|
||||
|
||||
:deep(.el-form-item__label) {
|
||||
color: #475569;
|
||||
font-weight: 500;
|
||||
padding-right: 12px;
|
||||
text-align: right;
|
||||
font-size: 14px;
|
||||
letter-spacing: 0.2px;
|
||||
}
|
||||
}
|
||||
|
||||
.content-item {
|
||||
:deep(.el-form-item__content) {
|
||||
line-height: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.custom-input {
|
||||
:deep(.el-input__inner) {
|
||||
background-color: #ffffff;
|
||||
border-radius: 8px;
|
||||
border: 1px solid #e2e8f0;
|
||||
height: 42px;
|
||||
padding: 0 14px;
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
font-size: 14px;
|
||||
color: #334155;
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
|
||||
|
||||
&:focus {
|
||||
border-color: #3b82f6;
|
||||
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.2);
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
&::placeholder {
|
||||
color: #94a3b8;
|
||||
font-weight: 400;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.content-wrapper {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.custom-textarea {
|
||||
:deep(.el-textarea__inner) {
|
||||
background-color: #ffffff;
|
||||
border-radius: 8px;
|
||||
border: 1px solid #e2e8f0;
|
||||
padding: 12px 14px;
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
font-size: 14px;
|
||||
color: #334155;
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
|
||||
line-height: 1.8;
|
||||
resize: none;
|
||||
white-space: pre;
|
||||
overflow-x: auto;
|
||||
word-wrap: normal;
|
||||
|
||||
&:focus {
|
||||
border-color: #3b82f6;
|
||||
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.2);
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
&::placeholder {
|
||||
color: #94a3b8;
|
||||
font-weight: 400;
|
||||
}
|
||||
}
|
||||
}
|
||||
.format-tip {
|
||||
font-size: 12px;
|
||||
color: #5778ff;
|
||||
}
|
||||
|
||||
.upload-section {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.upload-btn {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.upload-file-btn {
|
||||
background: #3b82f6;
|
||||
border-color: #3b82f6;
|
||||
border-radius: 6px;
|
||||
font-size: 13px;
|
||||
padding: 8px 16px;
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
.upload-file-content {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
> div {
|
||||
text-align: left;
|
||||
> p {
|
||||
margin: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: #2563eb;
|
||||
border-color: #2563eb;
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
}
|
||||
|
||||
.word-count {
|
||||
font-size: 13px;
|
||||
color: #64748b;
|
||||
font-weight: 500;
|
||||
transition: color 0.3s ease;
|
||||
|
||||
&.over-limit {
|
||||
color: #ef4444;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.dialog-footer {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
padding: 16px 0 0;
|
||||
margin-top: 16px;
|
||||
|
||||
.save-btn {
|
||||
width: 120px;
|
||||
height: 42px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
border-radius: 8px;
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
background: #3b82f6;
|
||||
color: white;
|
||||
border: none;
|
||||
letter-spacing: 0.5px;
|
||||
box-shadow: 0 2px 4px rgba(59, 130, 246, 0.2);
|
||||
|
||||
&:hover {
|
||||
background: #2563eb;
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 4px 6px rgba(59, 130, 246, 0.3);
|
||||
}
|
||||
|
||||
&:active {
|
||||
transform: translateY(0);
|
||||
box-shadow: 0 2px 3px rgba(59, 130, 246, 0.2);
|
||||
}
|
||||
}
|
||||
|
||||
.cancel-btn {
|
||||
width: 120px;
|
||||
height: 42px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
border-radius: 8px;
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
background: #ffffff;
|
||||
color: #64748b;
|
||||
border: 1px solid #e2e8f0;
|
||||
margin-left: 16px;
|
||||
letter-spacing: 0.5px;
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
|
||||
|
||||
&:hover {
|
||||
background: #f8fafc;
|
||||
color: #475569;
|
||||
border-color: #cbd5e1;
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
&:active {
|
||||
transform: translateY(0);
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -57,9 +57,32 @@
|
||||
class="tts-slider"
|
||||
/>
|
||||
<span class="slider-hint">{{ $t('roleConfig.pitchHint') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</el-form-item>
|
||||
|
||||
</el-form>
|
||||
<!-- 关联替换词 -->
|
||||
<div>
|
||||
<h4 class="replacement-label">
|
||||
{{ $t('roleConfig.replacementWordLabel') }}
|
||||
<el-tooltip popper-class="tts-tooltip" :content="$t('roleConfig.replacementWordTip')" effect="light" placement="top">
|
||||
<i class="el-icon-question"></i>
|
||||
</el-tooltip>
|
||||
</h4>
|
||||
<el-select
|
||||
v-model="replacementWordIds"
|
||||
multiple
|
||||
:placeholder="$t('replacementDialog.selectPlaceholder')"
|
||||
class="replacement-word-select"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in replacementWordList"
|
||||
:key="item.id"
|
||||
:label="item.fileName"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="drawer-footer">
|
||||
@@ -70,6 +93,8 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import correctWord from '@/apis/module/correctWord';
|
||||
|
||||
export default {
|
||||
name: 'TtsAdvancedSettings',
|
||||
props: {
|
||||
@@ -84,15 +109,21 @@ export default {
|
||||
speed: 0,
|
||||
pitch: 0
|
||||
})
|
||||
}
|
||||
},
|
||||
checkedReplacementWordIds: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
localSettings: {
|
||||
volume: 0,
|
||||
speed: 0,
|
||||
pitch: 0
|
||||
}
|
||||
pitch: 0,
|
||||
},
|
||||
replacementWordIds: [],
|
||||
replacementWordList: []
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
@@ -110,6 +141,8 @@ export default {
|
||||
if (newVal) {
|
||||
// 当抽屉打开时,复制当前设置到本地
|
||||
this.localSettings = { ...this.settings };
|
||||
this.replacementWordIds = [...this.checkedReplacementWordIds];
|
||||
this.fetchReplacementWordList();
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -123,11 +156,22 @@ export default {
|
||||
},
|
||||
handleSave() {
|
||||
// 保存设置并关闭
|
||||
this.$emit('save', { ...this.localSettings });
|
||||
this.$emit('save', { ...this.localSettings, replacementWordIds: this.replacementWordIds });
|
||||
this.handleClose();
|
||||
},
|
||||
formatTooltip(val) {
|
||||
return `${val}%`;
|
||||
},
|
||||
fetchReplacementWordList() {
|
||||
correctWord.selectAll(({ data }) => {
|
||||
if (data.code === 0) {
|
||||
this.replacementWordList = data.data;
|
||||
}
|
||||
});
|
||||
},
|
||||
getTagName(id) {
|
||||
const item = this.replacementWordList.find(item => item.id === id);
|
||||
return item ? item.fileName : '';
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -224,6 +268,30 @@ export default {
|
||||
::v-deep .el-form-item {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
.replacement-label i {
|
||||
margin-left: 4px;
|
||||
color: #909399;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
}
|
||||
.replacement-label i:hover {
|
||||
color: #409eff;
|
||||
}
|
||||
.replacement-word-select ::v-deep .el-tag {
|
||||
background: #e6ebff;
|
||||
color: #5778ff;
|
||||
border-radius: 8px;
|
||||
font-size: 12px;
|
||||
font-weight: normal;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.replacement-word-select ::v-deep .el-tag__close {
|
||||
color: #5778ff;
|
||||
}
|
||||
.replacement-word-select ::v-deep .el-icon-close {
|
||||
background: #fff;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style>
|
||||
@@ -237,4 +305,45 @@ export default {
|
||||
flex-direction: column;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.replacement-word-select {
|
||||
width: 100%;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.replacement-label {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.selected-tags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.replacement-tag {
|
||||
background-color: #409eff;
|
||||
border-color: #409eff;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.tts-tooltip {
|
||||
max-height: 60vh !important;
|
||||
max-width: 400px !important;
|
||||
overflow-y: auto !important;
|
||||
scrollbar-width: thin;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.tts-tooltip .popper__arrow {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.tts-tooltip[x-placement^="top"] .popper__arrow {
|
||||
border-top-color: transparent !important;
|
||||
}
|
||||
|
||||
.tts-tooltip[x-placement^="bottom"] .popper__arrow {
|
||||
border-bottom-color: transparent !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -22,6 +22,7 @@ export default {
|
||||
'header.clearHistory': 'Verlauf löschen',
|
||||
'header.providerManagement': 'Anbieterverwaltung',
|
||||
'header.serverSideManagement': 'Serververwaltung',
|
||||
'header.replacementWordManagement': 'Ersetzungswortverwaltung',
|
||||
'header.featureManagement': 'Systemfunktionsverwaltung',
|
||||
'header.changePassword': 'Passwort ändern',
|
||||
'header.logout': 'Abmelden',
|
||||
@@ -349,6 +350,54 @@ export default {
|
||||
'paramDialog.arrayType': 'Array',
|
||||
'paramDialog.jsonType': 'JSON-Objekt',
|
||||
|
||||
// Replacement word management page related
|
||||
'replacementWordManagement.pageTitle': 'Ersetzungswortverwaltung',
|
||||
'replacementWordManagement.fileName': 'Dateiname',
|
||||
'replacementWordManagement.replacementWordCount': 'Anzahl der Ersetzungswörter',
|
||||
'replacementWordManagement.replacementWordContent': 'Ersetzungswortinhalt',
|
||||
'replacementWordManagement.operation': 'Operation',
|
||||
'replacementWordManagement.createTime': 'Erstellungszeit',
|
||||
'replacementWordManagement.updateTime': 'Aktualisierungszeit',
|
||||
'replacementWordManagement.addFile': 'Ersetzungsdatei hinzufügen',
|
||||
'replacementWordManagement.edit': 'Bearbeiten',
|
||||
'replacementWordManagement.download': 'Herunterladen',
|
||||
'replacementWordManagement.delete': 'Löschen',
|
||||
|
||||
// Replacement word dialog related
|
||||
'replacementDialog.fileName': 'Dateiname',
|
||||
'replacementDialog.fileNamePlaceholder': 'Bitte Dateiname eingeben',
|
||||
'replacementDialog.content': 'Ersetzungsinhalt',
|
||||
'replacementDialog.contentPlaceholder': 'Ersetzungsinhalt eingeben, Format: original|ersatz pro Zeile',
|
||||
'replacementDialog.formatTip': 'Format pro Zeile: original|ersatz',
|
||||
'replacementDialog.wordCountUnit': 'Ersetzungswörter',
|
||||
'replacementDialog.uploadTxt': 'txt hochladen',
|
||||
'replacementDialog.uploadReplacementFile': 'Ersetzungsdatei hochladen',
|
||||
'replacementDialog.clickUploadTip': 'Hier klicken zum Hochladen',
|
||||
'replacementDialog.uploadCoverTip': 'Upload überschreibt aktuellen Inhalt',
|
||||
'replacementDialog.save': 'Speichern',
|
||||
'replacementDialog.cancel': 'Abbrechen',
|
||||
'replacementDialog.requiredFileName': 'Bitte Dateiname eingeben',
|
||||
'replacementDialog.requiredContent': 'Bitte Ersetzungsinhalt eingeben',
|
||||
'replacementDialog.invalidFormat': 'Zeile {line} Formatfehler, jede Zeile muss | Trennzeichen enthalten',
|
||||
'replacementDialog.invalidPipeCount': 'Zeile {line} Formatfehler, jede Zeile darf nur ein | Trennzeichen enthalten',
|
||||
'replacementDialog.emptyOriginal': 'Zeile {line} Originalwort darf nicht leer sein',
|
||||
'replacementDialog.emptyReplacement': 'Zeile {line} Ersetzungswort darf nicht leer sein',
|
||||
'replacementDialog.invalidOriginalChar': 'Zeile {line} Originalwort enthält Sonderzeichen',
|
||||
'replacementDialog.invalidReplacementChar': 'Zeile {line} Ersetzungswort enthält Sonderzeichen',
|
||||
'replacementDialog.readFileError': 'Datei konnte nicht gelesen werden',
|
||||
'replacementDialog.maxWordCountExceeded': 'Die Anzahl der Ersetzungswörter darf {max} nicht überschreiten',
|
||||
|
||||
// Ersetzungswortverwaltung Seite Text
|
||||
'replacementWordManagement.getListFailed': 'Liste konnte nicht abgerufen werden',
|
||||
'replacementWordManagement.confirmDelete': 'Sind Sie sicher, dass Sie diese Datei löschen möchten?',
|
||||
'replacementWordManagement.deleteHint': 'Hinweis',
|
||||
'replacementWordManagement.saveSuccess': 'Erfolgreich gespeichert',
|
||||
'replacementWordManagement.saveFailed': 'Speichern fehlgeschlagen',
|
||||
'replacementWordManagement.addFailed': 'Hinzufügen fehlgeschlagen',
|
||||
'replacementWordManagement.batchDelete': 'Stapelweise löschen',
|
||||
'replacementWordManagement.confirmBatchDelete': 'Sind Sie sicher, dass Sie die ausgewählten {count} Dateien löschen möchten?',
|
||||
'replacementWordManagement.batchDeleteHint': 'Hinweis zur Stapelweise löschung',
|
||||
|
||||
// Login page text
|
||||
'login.title': 'Anmelden',
|
||||
'login.welcome': 'WILLKOMMEN ZUR ANMELDUNG',
|
||||
@@ -771,10 +820,13 @@ export default {
|
||||
'roleConfig.ttsRate': 'Geschwindigkeit',
|
||||
'roleConfig.ttsPitch': 'Tonhöhe',
|
||||
'roleConfig.ttsAdvanced': 'TTS-Parameter',
|
||||
'roleConfig.advancedSettings': 'Sprachgeschwindigkeit',
|
||||
'roleConfig.advancedSettings': 'Tonhöhe Aussprache-Ersetzung Wort',
|
||||
'roleConfig.volumeHint': '-100=Min, 0=Standard, 100=Max',
|
||||
'roleConfig.speedHint': '-100=Langsamste, 0=Standard, 100=Schnellste',
|
||||
'roleConfig.pitchHint': '-100=Niedrigste, 0=Standard, 100=Höchste',
|
||||
'roleConfig.replacementWordLabel': 'Ersetzter Wort',
|
||||
'roleConfig.replacementWordTip': 'Bei Sprachsynthese werden fehlerkannte Wörter, mehrsilbige Zeichen, Eigennamen und sensible Wörter zwangsweise durch Ihren angegebenen Text/die Aussprache ersetzt, um Genauigkeit, Compliance und Erlebnis zu verbessern',
|
||||
'replacementDialog.selectPlaceholder': 'Bitte auswählen',
|
||||
'roleConfig.pleaseEnterContent': 'Bitte Inhalt eingeben',
|
||||
'roleConfig.pleaseEnterLangCode': 'Bitte Sprachcode eingeben, z.B.: en_US',
|
||||
'roleConfig.pleaseEnterLangName': 'Bitte Interaktionssprache eingeben, z.B.: Englisch',
|
||||
|
||||
@@ -22,6 +22,7 @@ export default {
|
||||
'header.clearHistory': 'Clear History',
|
||||
'header.providerManagement': 'Provider Management',
|
||||
'header.serverSideManagement': 'Server Management',
|
||||
'header.replacementWordManagement': 'Replacement Word Management',
|
||||
'header.featureManagement': 'System Feature Management',
|
||||
'header.changePassword': 'Change Password',
|
||||
'header.logout': 'Logout',
|
||||
@@ -349,6 +350,54 @@ export default {
|
||||
'paramDialog.arrayType': 'Array',
|
||||
'paramDialog.jsonType': 'JSON Object',
|
||||
|
||||
// Replacement word management page related
|
||||
'replacementWordManagement.pageTitle': 'Replacement Word Management',
|
||||
'replacementWordManagement.fileName': 'File Name',
|
||||
'replacementWordManagement.replacementWordCount': 'Replacement Word Count',
|
||||
'replacementWordManagement.replacementWordContent': 'Replacement Word Content',
|
||||
'replacementWordManagement.operation': 'Operation',
|
||||
'replacementWordManagement.createTime': 'Create Time',
|
||||
'replacementWordManagement.updateTime': 'Update Time',
|
||||
'replacementWordManagement.addFile': 'Add Replacement File',
|
||||
'replacementWordManagement.edit': 'Edit',
|
||||
'replacementWordManagement.download': 'Download',
|
||||
'replacementWordManagement.delete': 'Delete',
|
||||
|
||||
// Replacement word dialog related
|
||||
'replacementDialog.fileName': 'File Name',
|
||||
'replacementDialog.fileNamePlaceholder': 'Please enter file name',
|
||||
'replacementDialog.content': 'Replacement Content',
|
||||
'replacementDialog.contentPlaceholder': 'Enter replacement content, format: original|replacement per line',
|
||||
'replacementDialog.formatTip': 'Format per line: original|replacement',
|
||||
'replacementDialog.wordCountUnit': 'replacement words',
|
||||
'replacementDialog.uploadTxt': 'Upload txt',
|
||||
'replacementDialog.uploadReplacementFile': 'Upload Replacement File',
|
||||
'replacementDialog.clickUploadTip': 'Click to upload replacement file',
|
||||
'replacementDialog.uploadCoverTip': 'Uploading overwrites current content',
|
||||
'replacementDialog.save': 'Save',
|
||||
'replacementDialog.cancel': 'Cancel',
|
||||
'replacementDialog.requiredFileName': 'Please enter file name',
|
||||
'replacementDialog.requiredContent': 'Please enter replacement content',
|
||||
'replacementDialog.invalidFormat': 'Line {line} format error, each line must contain | separator',
|
||||
'replacementDialog.invalidPipeCount': 'Line {line} format error, each line can only contain one | separator',
|
||||
'replacementDialog.emptyOriginal': 'Line {line} original word cannot be empty',
|
||||
'replacementDialog.emptyReplacement': 'Line {line} replacement word cannot be empty',
|
||||
'replacementDialog.invalidOriginalChar': 'Line {line} original word contains special characters',
|
||||
'replacementDialog.invalidReplacementChar': 'Line {line} replacement word contains special characters',
|
||||
'replacementDialog.readFileError': 'Failed to read file',
|
||||
'replacementDialog.maxWordCountExceeded': 'Replacement word count cannot exceed {max}',
|
||||
|
||||
// Replacement Word Management page text
|
||||
'replacementWordManagement.getListFailed': 'Failed to get list',
|
||||
'replacementWordManagement.confirmDelete': 'Are you sure you want to delete this file?',
|
||||
'replacementWordManagement.deleteHint': 'Hint',
|
||||
'replacementWordManagement.saveSuccess': 'Saved successfully',
|
||||
'replacementWordManagement.saveFailed': 'Save failed',
|
||||
'replacementWordManagement.addFailed': 'Add failed',
|
||||
'replacementWordManagement.batchDelete': 'Batch Delete',
|
||||
'replacementWordManagement.confirmBatchDelete': 'Are you sure you want to delete the selected {count} files?',
|
||||
'replacementWordManagement.batchDeleteHint': 'Batch Delete Hint',
|
||||
|
||||
// Login page text
|
||||
'login.title': 'Login',
|
||||
'login.welcome': 'WELCOME TO LOGIN',
|
||||
@@ -771,10 +820,13 @@ export default {
|
||||
'roleConfig.ttsRate': 'Speed',
|
||||
'roleConfig.ttsPitch': 'Pitch',
|
||||
'roleConfig.ttsAdvanced': 'TTS Parameters',
|
||||
'roleConfig.advancedSettings': 'Speech rate and pitch',
|
||||
'roleConfig.advancedSettings': 'Pitch pronunciation replacement word',
|
||||
'roleConfig.volumeHint': '-100=Min, 0=Standard, 100=Max',
|
||||
'roleConfig.speedHint': '-100=Slowest, 0=Standard, 100=Fastest',
|
||||
'roleConfig.pitchHint': '-100=Lowest, 0=Standard, 100=Highest',
|
||||
'roleConfig.replacementWordLabel': 'Associated replacement words',
|
||||
'roleConfig.replacementWordTip': 'During speech synthesis, forcefully replace misrecognized words, polyphonic characters, proper nouns, and sensitive words with your specified correct text/pronunciation to improve accuracy, compliance, and experience',
|
||||
'replacementDialog.selectPlaceholder': 'Please select',
|
||||
'roleConfig.pleaseEnterContent': 'Please enter content',
|
||||
'roleConfig.pleaseEnterLangCode': 'Please enter language code, e.g.: en_US',
|
||||
'roleConfig.pleaseEnterLangName': 'Please enter interaction language, e.g.: English',
|
||||
|
||||
@@ -22,6 +22,7 @@ export default {
|
||||
'header.clearHistory': 'Limpar Histórico',
|
||||
'header.providerManagement': 'Gerenciamento de Provedores',
|
||||
'header.serverSideManagement': 'Gerenciamento de Servidores',
|
||||
'header.replacementWordManagement': 'Gerenciamento de Palavras de Substituição',
|
||||
'header.featureManagement': 'Gerenciamento de Funcionalidades do Sistema',
|
||||
'header.changePassword': 'Alterar Senha',
|
||||
'header.logout': 'Sair',
|
||||
@@ -349,6 +350,54 @@ export default {
|
||||
'paramDialog.arrayType': 'Array',
|
||||
'paramDialog.jsonType': 'Objeto JSON',
|
||||
|
||||
// Replacement word management page related
|
||||
'replacementWordManagement.pageTitle': 'Gerenciamento de Palavras de Substituição',
|
||||
'replacementWordManagement.fileName': 'Nome do Arquivo',
|
||||
'replacementWordManagement.replacementWordCount': 'Quantidade de Palavras de Substituição',
|
||||
'replacementWordManagement.replacementWordContent': 'Conteúdo Substituto',
|
||||
'replacementWordManagement.operation': 'Operação',
|
||||
'replacementWordManagement.createTime': 'Tempo de Criação',
|
||||
'replacementWordManagement.updateTime': 'Tempo de Atualização',
|
||||
'replacementWordManagement.addFile': 'Adicionar Arquivo de Substituição',
|
||||
'replacementWordManagement.edit': 'Editar',
|
||||
'replacementWordManagement.download': 'Baixar',
|
||||
'replacementWordManagement.delete': 'Excluir',
|
||||
|
||||
// Replacement word dialog related
|
||||
'replacementDialog.fileName': 'Nome do Arquivo',
|
||||
'replacementDialog.fileNamePlaceholder': 'Por favor, insira o nome do arquivo',
|
||||
'replacementDialog.content': 'Conteúdo Substituto',
|
||||
'replacementDialog.contentPlaceholder': 'Insira conteúdo: original|substituto',
|
||||
'replacementDialog.formatTip': 'Formato por linha: original|substituição',
|
||||
'replacementDialog.wordCountUnit': 'palavras de substituição',
|
||||
'replacementDialog.uploadTxt': 'Carregar txt',
|
||||
'replacementDialog.uploadReplacementFile': 'Carregar arquivo de substituição',
|
||||
'replacementDialog.clickUploadTip': 'Carregar arquivo de substituição',
|
||||
'replacementDialog.uploadCoverTip': 'Upload substitui conteúdo atual',
|
||||
'replacementDialog.save': 'Salvar',
|
||||
'replacementDialog.cancel': 'Cancelar',
|
||||
'replacementDialog.requiredFileName': 'Por favor, insira o nome do arquivo',
|
||||
'replacementDialog.requiredContent': 'Por favor, insira o conteúdo de substituição',
|
||||
'replacementDialog.invalidFormat': 'Linha {line} com erro de formato, cada linha deve conter o separador |',
|
||||
'replacementDialog.invalidPipeCount': 'Linha {line} com erro de formato, cada linha pode conter apenas um separador |',
|
||||
'replacementDialog.emptyOriginal': 'Linha {line} palavra original não pode estar vazia',
|
||||
'replacementDialog.emptyReplacement': 'Linha {line} palavra de substituição não pode estar vazia',
|
||||
'replacementDialog.invalidOriginalChar': 'Linha {line} palavra original contém caracteres especiais',
|
||||
'replacementDialog.invalidReplacementChar': 'Linha {line} palavra de substituição contém caracteres especiais',
|
||||
'replacementDialog.readFileError': 'Falha ao ler arquivo',
|
||||
'replacementDialog.maxWordCountExceeded': 'A quantidade de palavras de substituição não pode exceder {max}',
|
||||
|
||||
// Página de gerenciamento de palavras de substituição
|
||||
'replacementWordManagement.getListFailed': 'Falha ao obter lista',
|
||||
'replacementWordManagement.confirmDelete': 'Tem certeza de que deseja excluir este arquivo?',
|
||||
'replacementWordManagement.deleteHint': 'Dica',
|
||||
'replacementWordManagement.saveSuccess': 'Salvo com sucesso',
|
||||
'replacementWordManagement.saveFailed': 'Falha ao salvar',
|
||||
'replacementWordManagement.addFailed': 'Falha ao adicionar',
|
||||
'replacementWordManagement.batchDelete': 'Exclusão em Lote',
|
||||
'replacementWordManagement.confirmBatchDelete': 'Tem certeza de que deseja excluir os {count} arquivos selecionados?',
|
||||
'replacementWordManagement.batchDeleteHint': 'Dica de Exclusão em Lote',
|
||||
|
||||
// Página de login
|
||||
'login.title': 'Entrar',
|
||||
'login.welcome': 'BEM-VINDO AO LOGIN',
|
||||
@@ -771,10 +820,13 @@ export default {
|
||||
'roleConfig.ttsRate': 'Velocidade da fala',
|
||||
'roleConfig.ttsPitch': 'Tonalidade',
|
||||
'roleConfig.ttsAdvanced': 'Parâmetros TTS',
|
||||
'roleConfig.advancedSettings': 'Velocidade do som',
|
||||
'roleConfig.advancedSettings': 'Pronúncia Tonal Substituição de Palavras',
|
||||
'roleConfig.volumeHint': '-100=mínimo, 0=padrão, 100=máximo',
|
||||
'roleConfig.speedHint': '-100=mais lento, 0=padrão, 100=mais rápido',
|
||||
'roleConfig.pitchHint': '-100=mínimo, 0=padrão, 100=máximo',
|
||||
'roleConfig.replacementWordLabel': 'Palavras de substituição associadas',
|
||||
'roleConfig.replacementWordTip': 'Durante a síntese de fala, substitua forçadamente palavras mal reconhecidas, caracteres polissílabos, substantivos próprios e palavras sensíveis pelo texto/pronúncia correto especificado por você para melhorar a precisão, conformidade e experiência',
|
||||
'replacementDialog.selectPlaceholder': 'Por favor, selecione',
|
||||
'roleConfig.pleaseEnterContent': 'Por favor, insira o conteúdo',
|
||||
'roleConfig.pleaseEnterLangCode': 'Por favor, insira o código do idioma, ex.: pt_BR',
|
||||
'roleConfig.pleaseEnterLangName': 'Por favor, insira o idioma de interação, ex.: Português',
|
||||
|
||||
@@ -22,6 +22,7 @@ export default {
|
||||
'header.clearHistory': 'Xóa lịch sử',
|
||||
'header.providerManagement': 'Quản lý nhà cung cấp',
|
||||
'header.serverSideManagement': 'Quản lý máy chủ',
|
||||
'header.replacementWordManagement': 'Quản lý từ thay thế',
|
||||
'header.featureManagement': 'Cấu hình chức năng hệ thống',
|
||||
'header.changePassword': 'Đổi mật khẩu',
|
||||
'header.logout': 'Đăng xuất',
|
||||
@@ -349,6 +350,54 @@ export default {
|
||||
'paramDialog.arrayType': 'Mảng',
|
||||
'paramDialog.jsonType': 'Đối tượng JSON',
|
||||
|
||||
// Replacement word management page related
|
||||
'replacementWordManagement.pageTitle': 'Quản lý từ thay thế',
|
||||
'replacementWordManagement.fileName': 'Tên tệp',
|
||||
'replacementWordManagement.replacementWordCount': 'Số lượng từ thay thế',
|
||||
'replacementWordManagement.replacementWordContent': 'Nội dung từ thay thế',
|
||||
'replacementWordManagement.operation': 'Thao tác',
|
||||
'replacementWordManagement.createTime': 'Thời gian tạo',
|
||||
'replacementWordManagement.updateTime': 'Thời gian cập nhật',
|
||||
'replacementWordManagement.addFile': 'Thêm tệp thay thế',
|
||||
'replacementWordManagement.edit': 'Sửa',
|
||||
'replacementWordManagement.download': 'Tải xuống',
|
||||
'replacementWordManagement.delete': 'Xóa',
|
||||
|
||||
// Replacement word dialog related
|
||||
'replacementDialog.fileName': 'Tên tệp',
|
||||
'replacementDialog.fileNamePlaceholder': 'Vui lòng nhập tên tệp',
|
||||
'replacementDialog.content': 'Nội dung thay thế',
|
||||
'replacementDialog.contentPlaceholder': 'Nhập nội dung thay thế, định dạng: gốc|thay thế mỗi dòng',
|
||||
'replacementDialog.formatTip': 'Định dạng mỗi dòng: gốc|thay thế',
|
||||
'replacementDialog.wordCountUnit': 'từ thay thế',
|
||||
'replacementDialog.uploadTxt': 'Tải lên txt',
|
||||
'replacementDialog.uploadReplacementFile': 'Tải lên tệp thay thế',
|
||||
'replacementDialog.clickUploadTip': 'Nhấp để tải lên tệp thay thế',
|
||||
'replacementDialog.uploadCoverTip': 'Tải lên sẽ ghi đè nội dung thay thế hiện tại',
|
||||
'replacementDialog.save': 'Lưu',
|
||||
'replacementDialog.cancel': 'Hủy',
|
||||
'replacementDialog.requiredFileName': 'Vui lòng nhập tên tệp',
|
||||
'replacementDialog.requiredContent': 'Vui lòng nhập nội dung thay thế',
|
||||
'replacementDialog.invalidFormat': 'Dòng {line} lỗi định dạng, mỗi dòng phải chứa dấu |',
|
||||
'replacementDialog.invalidPipeCount': 'Dòng {line} lỗi định dạng, mỗi dòng chỉ được chứa một dấu |',
|
||||
'replacementDialog.emptyOriginal': 'Dòng {line} từ gốc không được để trống',
|
||||
'replacementDialog.emptyReplacement': 'Dòng {line} từ thay thế không được để trống',
|
||||
'replacementDialog.invalidOriginalChar': 'Dòng {line} từ gốc chứa ký tự đặc biệt',
|
||||
'replacementDialog.invalidReplacementChar': 'Dòng {line} từ thay thế chứa ký tự đặc biệt',
|
||||
'replacementDialog.readFileError': 'Không thể đọc tệp',
|
||||
'replacementDialog.maxWordCountExceeded': 'Số lượng từ thay thế không thể vượt quá {max}',
|
||||
|
||||
// Trang quản lý từ thay thế
|
||||
'replacementWordManagement.getListFailed': 'Không thể lấy danh sách',
|
||||
'replacementWordManagement.confirmDelete': 'Bạn có chắc chắn muốn xóa tệp này không?',
|
||||
'replacementWordManagement.deleteHint': 'Gợi ý',
|
||||
'replacementWordManagement.saveSuccess': 'Lưu thành công',
|
||||
'replacementWordManagement.saveFailed': 'Lưu thất bại',
|
||||
'replacementWordManagement.addFailed': 'Thêm thất bại',
|
||||
'replacementWordManagement.batchDelete': 'Xóa hàng loạt',
|
||||
'replacementWordManagement.confirmBatchDelete': 'Bạn có chắc chắn muốn xóa {count} tệp đã chọn không?',
|
||||
'replacementWordManagement.batchDeleteHint': 'Gợi ý xóa hàng loạt',
|
||||
|
||||
// Login page text
|
||||
'login.title': 'DN',
|
||||
'login.welcome': 'CHÀO MỪNG ĐẾN VỚI ĐĂNG NHẬP',
|
||||
@@ -771,10 +820,13 @@ export default {
|
||||
'roleConfig.ttsRate': 'Tốc độ',
|
||||
'roleConfig.ttsPitch': 'Cao độ',
|
||||
'roleConfig.ttsAdvanced': 'Tham số TTS',
|
||||
'roleConfig.advancedSettings': 'Giọng nói',
|
||||
'roleConfig.advancedSettings': 'Thay thế phát âm ngữ điệu từ',
|
||||
'roleConfig.volumeHint': '-100=Tối thiểu, 0=Tiêu chuẩn, 100=Tối đa',
|
||||
'roleConfig.speedHint': '-100=Chậm nhất, 0=Tiêu chuẩn, 100=Nhanh nhất',
|
||||
'roleConfig.pitchHint': '-100=Thấp nhất, 0=Tiêu chuẩn, 100=Cao nhất',
|
||||
'roleConfig.replacementWordLabel': 'Liên kết từ thay thế',
|
||||
'roleConfig.replacementWordTip': 'Trong quá trình tổng hợp giọng nói, các từ bị nhận dạng sai, từ đa âm, tên riêng và từ nhạy cảm sẽ được thay thế bắt buộc bằng văn bản/phát âm chính xác mà bạn chỉ định để cải thiện độ chính xác, tuân thủ và trải nghiệm',
|
||||
'replacementDialog.selectPlaceholder': 'Vui lòng chọn',
|
||||
'roleConfig.pleaseEnterContent': 'Vui lòng nhập nội dung',
|
||||
'roleConfig.pleaseEnterLangCode': 'Vui lòng nhập mã ngôn ngữ, ví dụ: en_US',
|
||||
'roleConfig.pleaseEnterLangName': 'Vui lòng nhập ngôn ngữ tương tác, ví dụ: Tiếng Anh',
|
||||
|
||||
@@ -22,6 +22,7 @@ export default {
|
||||
'header.clearHistory': '清空历史',
|
||||
'header.providerManagement': '字段管理',
|
||||
'header.serverSideManagement': '服务端管理',
|
||||
'header.replacementWordManagement': '替换词管理',
|
||||
'header.featureManagement': '系统功能配置',
|
||||
'header.changePassword': '修改密码',
|
||||
'header.logout': '退出登录',
|
||||
@@ -349,6 +350,52 @@ export default {
|
||||
'paramDialog.arrayType': '数组(array)',
|
||||
'paramDialog.jsonType': 'JSON对象(json)',
|
||||
|
||||
// 替换词对话框相关
|
||||
'replacementWordManagement.pageTitle': '替换词管理',
|
||||
'replacementWordManagement.fileName': '文件名称',
|
||||
'replacementWordManagement.replacementWordCount': '替换词数量',
|
||||
'replacementWordManagement.replacementWordContent': '替换词内容',
|
||||
'replacementWordManagement.operation': '操作',
|
||||
'replacementWordManagement.createTime': '创建时间',
|
||||
'replacementWordManagement.updateTime': '更新时间',
|
||||
'replacementWordManagement.addFile': '新增替换词文件',
|
||||
'replacementWordManagement.edit': '编辑',
|
||||
'replacementWordManagement.download': '下载',
|
||||
'replacementWordManagement.delete': '删除',
|
||||
'replacementDialog.fileName': '文件名称',
|
||||
'replacementDialog.fileNamePlaceholder': '请输入文件名称',
|
||||
'replacementDialog.content': '替换词内容',
|
||||
'replacementDialog.contentPlaceholder': '请输入替换词内容,每行格式:原词|替换词',
|
||||
'replacementDialog.formatTip': '每行格式:原词|替换词',
|
||||
'replacementDialog.wordCountUnit': '个替换词',
|
||||
'replacementDialog.uploadTxt': '上传txt',
|
||||
'replacementDialog.uploadReplacementFile': '上传替换词文件',
|
||||
'replacementDialog.clickUploadTip': '点击上传替换词文件',
|
||||
'replacementDialog.uploadCoverTip': '上传后将覆盖当前替换词内容',
|
||||
'replacementDialog.save': '保存',
|
||||
'replacementDialog.cancel': '取消',
|
||||
'replacementDialog.requiredFileName': '请输入文件名称',
|
||||
'replacementDialog.requiredContent': '请输入替换词内容',
|
||||
'replacementDialog.invalidFormat': '第{line}行格式错误,每行必须包含|分隔符',
|
||||
'replacementDialog.invalidPipeCount': '第{line}行格式错误,每行只能包含一个|分隔符',
|
||||
'replacementDialog.emptyOriginal': '第{line}行待替换词不能为空',
|
||||
'replacementDialog.emptyReplacement': '第{line}行替换词不能为空',
|
||||
'replacementDialog.invalidOriginalChar': '第{line}行待替换词包含特殊符号',
|
||||
'replacementDialog.invalidReplacementChar': '第{line}行替换词包含特殊符号',
|
||||
'replacementDialog.readFileError': '读取文件失败',
|
||||
'replacementDialog.maxWordCountExceeded': '替换词数量不能超过 {max} 个',
|
||||
|
||||
// 替换词管理页面提示文本
|
||||
'replacementWordManagement.getListFailed': '获取列表失败',
|
||||
'replacementWordManagement.confirmDelete': '确定要删除该文件吗?',
|
||||
'replacementWordManagement.deleteHint': '提示',
|
||||
'replacementWordManagement.saveSuccess': '保存成功',
|
||||
'replacementWordManagement.saveFailed': '保存失败',
|
||||
'replacementWordManagement.addFailed': '新增失败',
|
||||
'replacementWordManagement.batchDelete': '批量删除',
|
||||
'replacementWordManagement.confirmBatchDelete': '确定要删除选中的 {count} 个文件吗?',
|
||||
'replacementWordManagement.batchDeleteHint': '批量删除提示',
|
||||
|
||||
// 登录页面文本
|
||||
'login.title': '登录',
|
||||
'login.welcome': 'WELCOME TO LOGIN',
|
||||
@@ -771,10 +818,13 @@ export default {
|
||||
'roleConfig.ttsRate': '语速',
|
||||
'roleConfig.ttsPitch': '音调',
|
||||
'roleConfig.ttsAdvanced': 'TTS参数',
|
||||
'roleConfig.advancedSettings': '语速音调',
|
||||
'roleConfig.advancedSettings': '语调发音替换词',
|
||||
'roleConfig.volumeHint': '-100=最小, 0=标准, 100=最大',
|
||||
'roleConfig.speedHint': '-100=最慢, 0=标准, 100=最快',
|
||||
'roleConfig.pitchHint': '-100=最低, 0=标准, 100=最高',
|
||||
'roleConfig.replacementWordLabel': '关联替换词',
|
||||
'roleConfig.replacementWordTip': '语音合成时,强制把识别错、多音字、专有名词、敏感词,替换成你指定的正确文本/发音,提升准确率、合规与体验',
|
||||
'replacementDialog.selectPlaceholder': '请选择',
|
||||
'roleConfig.pleaseEnterContent': '请输入内容',
|
||||
'roleConfig.pleaseEnterLangCode': '请输入语言编码,如:zh_CN',
|
||||
'roleConfig.pleaseEnterLangName': '请输入交互语种,如:中文',
|
||||
|
||||
@@ -22,6 +22,7 @@ export default {
|
||||
'header.clearHistory': '清空歷史',
|
||||
'header.providerManagement': '字段管理',
|
||||
'header.serverSideManagement': '服務端管理',
|
||||
'header.replacementWordManagement': '替換詞管理',
|
||||
'header.featureManagement': '系統功能配置',
|
||||
'header.changePassword': '修改密碼',
|
||||
'header.logout': '退出登錄',
|
||||
@@ -349,6 +350,54 @@ export default {
|
||||
'paramDialog.arrayType': '陣列(array)',
|
||||
'paramDialog.jsonType': 'JSON物件(json)',
|
||||
|
||||
// 替換詞管理頁面相關
|
||||
'replacementWordManagement.pageTitle': '替換詞管理',
|
||||
'replacementWordManagement.fileName': '檔案名稱',
|
||||
'replacementWordManagement.replacementWordCount': '替換詞數量',
|
||||
'replacementWordManagement.replacementWordContent': '替換詞內容',
|
||||
'replacementWordManagement.operation': '操作',
|
||||
'replacementWordManagement.createTime': '創建時間',
|
||||
'replacementWordManagement.updateTime': '更新時間',
|
||||
'replacementWordManagement.addFile': '新增替換詞檔案',
|
||||
'replacementWordManagement.edit': '編輯',
|
||||
'replacementWordManagement.download': '下載',
|
||||
'replacementWordManagement.delete': '刪除',
|
||||
|
||||
// 替換詞對話框相關
|
||||
'replacementDialog.fileName': '檔案名稱',
|
||||
'replacementDialog.fileNamePlaceholder': '請輸入檔案名稱',
|
||||
'replacementDialog.content': '替換詞內容',
|
||||
'replacementDialog.contentPlaceholder': '請輸入替換詞內容,每行格式:原詞|替換詞',
|
||||
'replacementDialog.formatTip': '每行格式:原詞|替換詞',
|
||||
'replacementDialog.wordCountUnit': '個替換詞',
|
||||
'replacementDialog.uploadTxt': '上傳txt',
|
||||
'replacementDialog.uploadReplacementFile': '上傳替換詞檔案',
|
||||
'replacementDialog.clickUploadTip': '點擊上傳替換詞檔案',
|
||||
'replacementDialog.uploadCoverTip': '上傳後將覆蓋當前替換詞內容',
|
||||
'replacementDialog.save': '儲存',
|
||||
'replacementDialog.cancel': '取消',
|
||||
'replacementDialog.requiredFileName': '請輸入檔案名稱',
|
||||
'replacementDialog.requiredContent': '請輸入替換詞內容',
|
||||
'replacementDialog.invalidFormat': '第{line}行格式錯誤,每行必須包含|分隔符',
|
||||
'replacementDialog.invalidPipeCount': '第{line}行格式錯誤,每行只能包含一個|分隔符',
|
||||
'replacementDialog.emptyOriginal': '第{line}行待替換詞不能為空',
|
||||
'replacementDialog.emptyReplacement': '第{line}行替換詞不能為空',
|
||||
'replacementDialog.invalidOriginalChar': '第{line}行待替換詞包含特殊符號',
|
||||
'replacementDialog.invalidReplacementChar': '第{line}行替換詞包含特殊符號',
|
||||
'replacementDialog.readFileError': '讀取檔案失敗',
|
||||
'replacementDialog.maxWordCountExceeded': '替換詞數量不能超過 {max} 個',
|
||||
|
||||
// 替換詞管理頁面提示文本
|
||||
'replacementWordManagement.getListFailed': '獲取列表失敗',
|
||||
'replacementWordManagement.confirmDelete': '確定要刪除該檔案嗎?',
|
||||
'replacementWordManagement.deleteHint': '提示',
|
||||
'replacementWordManagement.saveSuccess': '保存成功',
|
||||
'replacementWordManagement.saveFailed': '保存失敗',
|
||||
'replacementWordManagement.addFailed': '新增失敗',
|
||||
'replacementWordManagement.batchDelete': '批量刪除',
|
||||
'replacementWordManagement.confirmBatchDelete': '確定要刪除選中的 {count} 個檔案嗎?',
|
||||
'replacementWordManagement.batchDeleteHint': '批量刪除提示',
|
||||
|
||||
// 登录页面文本
|
||||
'login.title': '登錄',
|
||||
'login.welcome': 'WELCOME TO LOGIN',
|
||||
@@ -771,10 +820,13 @@ export default {
|
||||
'roleConfig.ttsRate': '語速',
|
||||
'roleConfig.ttsPitch': '音調',
|
||||
'roleConfig.ttsAdvanced': 'TTS參數',
|
||||
'roleConfig.advancedSettings': '語速音調',
|
||||
'roleConfig.advancedSettings': '語調發音替換詞',
|
||||
'roleConfig.volumeHint': '-100=最小, 0=標準, 100=最大',
|
||||
'roleConfig.speedHint': '-100=最慢, 0=標準, 100=最快',
|
||||
'roleConfig.pitchHint': '-100=最低, 0=標準, 100=最高',
|
||||
'roleConfig.replacementWordLabel': '關聯替換詞',
|
||||
'roleConfig.replacementWordTip': '語音合成時,強制把識別錯、多音字、專有名詞、敏感詞,替換成你指定的正確文本/發音,提升準確率、合規與體驗',
|
||||
'replacementDialog.selectPlaceholder': '請選擇',
|
||||
'roleConfig.pleaseEnterContent': '請輸入內容',
|
||||
'roleConfig.pleaseEnterLangCode': '請輸入語言編碼,如:zh_TW',
|
||||
'roleConfig.pleaseEnterLangName': '請輸入交互語種,如:繁體中文',
|
||||
|
||||
@@ -196,6 +196,18 @@ const routes = [
|
||||
title: '功能配置'
|
||||
}
|
||||
},
|
||||
// 替换词管理
|
||||
{
|
||||
path: '/replacement-word-management',
|
||||
name: 'ReplacementWordManagement',
|
||||
component: function () {
|
||||
return import('../views/ReplacementWordManagement.vue')
|
||||
},
|
||||
meta: {
|
||||
requiresAuth: true,
|
||||
title: '替换词管理'
|
||||
}
|
||||
},
|
||||
]
|
||||
const router = new VueRouter({
|
||||
base: process.env.VUE_APP_PUBLIC_PATH || '/',
|
||||
|
||||
@@ -0,0 +1,763 @@
|
||||
<template>
|
||||
<div class="welcome">
|
||||
<HeaderBar/>
|
||||
<div class="operation-bar">
|
||||
<h2 class="page-title">{{ $t('replacementWordManagement.pageTitle') }}</h2>
|
||||
</div>
|
||||
<div class="main-wrapper">
|
||||
<div class="content-panel">
|
||||
<div class="content-area">
|
||||
<el-card class="params-card" shadow="never">
|
||||
<el-table
|
||||
ref="paramsTable"
|
||||
:data="paramsList"
|
||||
class="transparent-table"
|
||||
v-loading="loading"
|
||||
:element-loading-text="$t('serverSideManager.loading')"
|
||||
element-loading-spinner="el-icon-loading"
|
||||
element-loading-background="rgba(255, 255, 255, 0.7)"
|
||||
>
|
||||
<el-table-column :label="$t('modelConfig.select')" align="center" width="120">
|
||||
<template slot-scope="scope">
|
||||
<el-checkbox v-model="scope.row.selected" @change="handleCheckboxChange(scope.row)"></el-checkbox>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('replacementWordManagement.fileName')" prop="fileName" align="center"/>
|
||||
<el-table-column :label="$t('replacementWordManagement.replacementWordCount')" prop="wordCount" align="center"/>
|
||||
<el-table-column :label="$t('replacementWordManagement.replacementWordContent')" prop="content" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-tooltip placement="right" effect="light" popper-class="replace-word-tooltip">
|
||||
<div slot="content" class="replace-word-content">
|
||||
<el-tag v-for="(item, index) in scope.row.content" :key="index" size="mini" class="custom-tag">{{ item }}</el-tag>
|
||||
</div>
|
||||
<span class="content-text">{{ formatContent(scope.row.content) }}</span>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('replacementWordManagement.createTime')" prop="createdAt" align="center"/>
|
||||
<el-table-column :label="$t('replacementWordManagement.updateTime')" prop="updatedAt" align="center"/>
|
||||
<el-table-column :label="$t('replacementWordManagement.operation')" prop="operator" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-button size="medium" type="text" @click="handleEdit(scope.row)">
|
||||
{{ $t('replacementWordManagement.edit') }}
|
||||
</el-button>
|
||||
<el-button size="medium" type="text" @click="handleDownload(scope.row)">
|
||||
{{ $t('replacementWordManagement.download') }}
|
||||
</el-button>
|
||||
<el-button size="medium" type="text" @click="handleDelete(scope.row)">
|
||||
{{ $t('replacementWordManagement.delete') }}
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div class="table_bottom">
|
||||
<div class="ctrl_btn">
|
||||
<el-button size="mini" type="primary" class="select-all-btn" @click="handleSelectAll">
|
||||
{{ allSelected ? $t('user.deselectAll') : $t('user.selectAll') }}
|
||||
</el-button>
|
||||
<el-button size="mini" type="success" @click="handleAdd">{{ $t('replacementWordManagement.addFile') }}</el-button>
|
||||
<el-button size="mini" type="danger" :disabled="!hasAnySelected" @click="handleBatchDelete">
|
||||
{{ $t('replacementWordManagement.batchDelete') }}
|
||||
</el-button>
|
||||
</div>
|
||||
<div class="custom-pagination">
|
||||
<el-select v-model="pageSize" @change="handlePageSizeChange" class="page-size-select">
|
||||
<el-option
|
||||
v-for="item in pageSizeOptions"
|
||||
:key="item"
|
||||
:label="`${item}${$t('paramManagement.itemsPerPage')}`"
|
||||
:value="item"
|
||||
>
|
||||
</el-option>
|
||||
</el-select>
|
||||
<button class="pagination-btn" :disabled="currentPage === 1" @click="goFirst">
|
||||
{{ $t('paramManagement.firstPage') }}
|
||||
</button>
|
||||
<button class="pagination-btn" :disabled="currentPage === 1" @click="goPrev">
|
||||
{{ $t('paramManagement.prevPage') }}
|
||||
</button>
|
||||
<button
|
||||
v-for="page in visiblePages"
|
||||
:key="page"
|
||||
class="pagination-btn"
|
||||
:class="{ active: page === currentPage }" @click="goToPage(page)"
|
||||
>
|
||||
{{ page }}
|
||||
</button>
|
||||
<button class="pagination-btn" :disabled="currentPage === pageCount" @click="goNext">
|
||||
{{ $t('paramManagement.nextPage') }}
|
||||
</button>
|
||||
<span class="total-text">{{ $t('paramManagement.totalRecords', { total }) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<el-footer><VersionFooter/></el-footer>
|
||||
<ReplacementWordDialog
|
||||
ref="paramDialog"
|
||||
:title="dialogTitle"
|
||||
:visible.sync="dialogVisible"
|
||||
:form="dialogForm"
|
||||
@submit="handleSubmit"
|
||||
@cancel="dialogVisible = false"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Api from "@/apis/api";
|
||||
import HeaderBar from "@/components/HeaderBar.vue";
|
||||
import ParamDialog from "@/components/ParamDialog.vue";
|
||||
import VersionFooter from "@/components/VersionFooter.vue";
|
||||
import ReplacementWordDialog from "@/components/ReplacementWordDialog.vue";
|
||||
|
||||
export default {
|
||||
components: { HeaderBar, ParamDialog, VersionFooter, ReplacementWordDialog },
|
||||
data() {
|
||||
return {
|
||||
paramsList: [],
|
||||
selectedRows: new Set(),
|
||||
currentPage: 1,
|
||||
loading: false,
|
||||
pageSize: 10,
|
||||
pageSizeOptions: [10, 20, 50, 100],
|
||||
total: 0,
|
||||
dialogVisible: false,
|
||||
dialogTitle: '',
|
||||
dialogForm: {},
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.fetchFileList();
|
||||
},
|
||||
mounted() {
|
||||
this.dialogTitle = this.$t('replacementWordManagement.addFile');
|
||||
},
|
||||
|
||||
computed: {
|
||||
pageCount() {
|
||||
return Math.ceil(this.total / this.pageSize);
|
||||
},
|
||||
visiblePages() {
|
||||
const pages = [];
|
||||
const maxVisible = 3;
|
||||
let start = Math.max(1, this.currentPage - 1);
|
||||
let end = Math.min(this.pageCount, start + maxVisible - 1);
|
||||
|
||||
if (end - start + 1 < maxVisible) {
|
||||
start = Math.max(1, end - maxVisible + 1);
|
||||
}
|
||||
|
||||
for (let i = start; i <= end; i++) {
|
||||
pages.push(i);
|
||||
}
|
||||
return pages;
|
||||
},
|
||||
|
||||
hasAnySelected() {
|
||||
return this.selectedRows.size > 0;
|
||||
},
|
||||
|
||||
allSelected() {
|
||||
if (this.paramsList.length === 0) {
|
||||
return false;
|
||||
}
|
||||
return this.paramsList.every(row => this.selectedRows.has(row.id));
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
formatContent(content) {
|
||||
if (!content) return '';
|
||||
if (Array.isArray(content)) {
|
||||
return content.join(',');
|
||||
}
|
||||
return content;
|
||||
},
|
||||
|
||||
handlePageSizeChange(val) {
|
||||
this.pageSize = val;
|
||||
this.currentPage = 1;
|
||||
this.fetchFileList();
|
||||
},
|
||||
goToPage(page) {
|
||||
if (page !== this.currentPage) {
|
||||
this.currentPage = page;
|
||||
this.fetchFileList();
|
||||
}
|
||||
},
|
||||
goFirst() {
|
||||
if (this.currentPage !== 1) {
|
||||
this.currentPage = 1;
|
||||
this.fetchFileList();
|
||||
}
|
||||
},
|
||||
goPrev() {
|
||||
if (this.currentPage > 1) {
|
||||
this.currentPage--;
|
||||
this.fetchFileList();
|
||||
}
|
||||
},
|
||||
goNext() {
|
||||
if (this.currentPage < this.pageCount) {
|
||||
this.currentPage++;
|
||||
this.fetchFileList();
|
||||
}
|
||||
},
|
||||
|
||||
handleCheckboxChange(row) {
|
||||
const newSet = new Set(this.selectedRows);
|
||||
if (row.selected) {
|
||||
newSet.add(row.id);
|
||||
} else {
|
||||
newSet.delete(row.id);
|
||||
}
|
||||
this.selectedRows = newSet;
|
||||
},
|
||||
|
||||
handleSelectAll() {
|
||||
if (this.allSelected) {
|
||||
this.paramsList.forEach(row => {
|
||||
this.$set(row, 'selected', false);
|
||||
});
|
||||
this.selectedRows = new Set();
|
||||
} else {
|
||||
this.paramsList.forEach(row => {
|
||||
this.$set(row, 'selected', true);
|
||||
});
|
||||
this.selectedRows = new Set(this.paramsList.map(row => row.id));
|
||||
}
|
||||
},
|
||||
|
||||
handleBatchDelete() {
|
||||
const ids = Array.from(this.paramsList)
|
||||
.filter(row => row.selected)
|
||||
.map(row => row.id);
|
||||
|
||||
if (ids.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.$confirm(
|
||||
this.$t('replacementWordManagement.confirmBatchDelete', { count: ids.length }),
|
||||
this.$t('replacementWordManagement.batchDeleteHint'),
|
||||
{
|
||||
confirmButtonText: this.$t('common.confirm'),
|
||||
cancelButtonText: this.$t('common.cancel')
|
||||
}
|
||||
).then(() => {
|
||||
Api.correctWord.batchDeleteFile(ids, ({ data }) => {
|
||||
if (data.code === 0) {
|
||||
this.$message.success(this.$t('common.deleteSuccess'));
|
||||
|
||||
const newSet = new Set(this.selectedRows);
|
||||
ids.forEach(id => {
|
||||
newSet.delete(id);
|
||||
});
|
||||
this.selectedRows = newSet;
|
||||
|
||||
this.fetchFileList();
|
||||
} else {
|
||||
this.$message.error(data.msg || this.$t('common.deleteFailure'));
|
||||
}
|
||||
});
|
||||
}).catch(() => {});
|
||||
},
|
||||
|
||||
fetchFileList() {
|
||||
this.loading = true;
|
||||
Api.correctWord.getFileList({
|
||||
page: this.currentPage,
|
||||
pageSize: this.pageSize
|
||||
}, ({ data }) => {
|
||||
this.loading = false;
|
||||
if (data.code === 0) {
|
||||
this.paramsList = data.data.list || [];
|
||||
|
||||
this.paramsList.forEach(row => {
|
||||
if (this.selectedRows.has(row.id)) {
|
||||
this.$set(row, 'selected', true);
|
||||
} else {
|
||||
this.$set(row, 'selected', false);
|
||||
}
|
||||
});
|
||||
|
||||
this.total = data.data.total || 0;
|
||||
} else {
|
||||
this.$message.error({
|
||||
message: data.msg || this.$t('replacementWordManagement.getListFailed'),
|
||||
showClose: true
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
handleAdd() {
|
||||
this.dialogForm = {
|
||||
id: undefined,
|
||||
fileName: '',
|
||||
content: ''
|
||||
};
|
||||
this.dialogTitle = this.$t('replacementWordManagement.addFile');
|
||||
this.dialogVisible = true;
|
||||
},
|
||||
|
||||
handleEdit(row) {
|
||||
this.dialogForm = {
|
||||
id: row.id,
|
||||
fileName: row.fileName,
|
||||
content: row.content || ''
|
||||
};
|
||||
this.dialogTitle = this.$t('replacementWordManagement.edit');
|
||||
this.dialogVisible = true;
|
||||
},
|
||||
|
||||
handleDownload(row) {
|
||||
Api.correctWord.downloadFile(row.id, (res) => {
|
||||
const url = window.URL.createObjectURL(new Blob([res.data]));
|
||||
const link = document.createElement('a');
|
||||
link.href = url;
|
||||
link.download = `${row.fileName}.txt`;
|
||||
link.click();
|
||||
window.URL.revokeObjectURL(url);
|
||||
});
|
||||
},
|
||||
|
||||
handleDelete(row) {
|
||||
this.$confirm(this.$t('replacementWordManagement.confirmDelete'), this.$t('replacementWordManagement.deleteHint'), {
|
||||
confirmButtonText: this.$t('common.confirm'),
|
||||
cancelButtonText: this.$t('common.cancel')
|
||||
}).then(() => {
|
||||
Api.correctWord.deleteFile(row.id, ({ data }) => {
|
||||
if (data.code === 0) {
|
||||
this.$message.success(this.$t('common.deleteSuccess'));
|
||||
const newSet = new Set(this.selectedRows);
|
||||
newSet.delete(row.id);
|
||||
this.selectedRows = newSet;
|
||||
this.fetchFileList();
|
||||
} else {
|
||||
this.$message.error(data.msg || this.$t('common.deleteFailure'));
|
||||
}
|
||||
});
|
||||
}).catch(() => {});
|
||||
},
|
||||
|
||||
handleSubmit(formData) {
|
||||
if (formData.id) {
|
||||
Api.correctWord.updateFile(formData, ({ data }) => {
|
||||
if (data.code === 0) {
|
||||
this.$message.success(this.$t('replacementWordManagement.saveSuccess'));
|
||||
this.dialogVisible = false;
|
||||
this.fetchFileList();
|
||||
} else {
|
||||
this.$message.error(data.msg || this.$t('replacementWordManagement.saveFailed'));
|
||||
}
|
||||
if (this.$refs.paramDialog) {
|
||||
this.$refs.paramDialog.resetSaving();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
Api.correctWord.addFile(formData, ({ data }) => {
|
||||
if (data.code === 0) {
|
||||
this.$message.success(this.$t('common.addSuccess'));
|
||||
this.dialogVisible = false;
|
||||
this.fetchFileList();
|
||||
} else {
|
||||
this.$message.error(data.msg || this.$t('replacementWordManagement.addFailed'));
|
||||
}
|
||||
if (this.$refs.paramDialog) {
|
||||
this.$refs.paramDialog.resetSaving();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.welcome {
|
||||
min-width: 900px;
|
||||
min-height: 506px;
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
position: relative;
|
||||
flex-direction: column;
|
||||
background-size: cover;
|
||||
background: linear-gradient(to bottom right, #dce8ff, #e4eeff, #e6cbfd) center;
|
||||
-webkit-background-size: cover;
|
||||
-o-background-size: cover;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.main-wrapper {
|
||||
height: calc(100vh - 63px - 35px - 72px);
|
||||
margin: 0 22px;
|
||||
border-radius: 15px;
|
||||
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
|
||||
position: relative;
|
||||
background: rgba(237, 242, 255, 0.5);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.operation-bar {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 22px 24px;
|
||||
}
|
||||
|
||||
.page-title {
|
||||
font-size: 24px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.right-operations {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.search-input {
|
||||
width: 240px;
|
||||
}
|
||||
|
||||
.btn-search {
|
||||
background: linear-gradient(135deg, #6b8cff, #a966ff);
|
||||
border: none;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.content-panel {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
height: 100%;
|
||||
border-radius: 15px;
|
||||
background: transparent;
|
||||
border: 1px solid #fff;
|
||||
}
|
||||
|
||||
.content-area {
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
min-width: 600px;
|
||||
overflow: auto;
|
||||
background-color: white;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.params-card {
|
||||
background: white;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
overflow: hidden;
|
||||
|
||||
::v-deep .el-card__body {
|
||||
padding: 15px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
.table_bottom {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.ctrl_btn {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
padding-left: 26px;
|
||||
|
||||
.el-button {
|
||||
min-width: 72px;
|
||||
height: 32px;
|
||||
padding: 7px 12px 7px 10px;
|
||||
font-size: 12px;
|
||||
border-radius: 4px;
|
||||
line-height: 1;
|
||||
font-weight: 500;
|
||||
border: none;
|
||||
transition: all 0.3s ease;
|
||||
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
|
||||
|
||||
&:hover {
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
}
|
||||
|
||||
.el-button--primary {
|
||||
background: #5f70f3;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.el-button--danger {
|
||||
background: #fd5b63;
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
|
||||
.custom-pagination {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
|
||||
.el-select {
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.pagination-btn:first-child,
|
||||
.pagination-btn:nth-child(2),
|
||||
.pagination-btn:nth-last-child(2),
|
||||
.pagination-btn:nth-child(3) {
|
||||
min-width: 60px;
|
||||
height: 32px;
|
||||
padding: 0 12px;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #e4e7ed;
|
||||
background: #dee7ff;
|
||||
color: #606266;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
|
||||
&:hover {
|
||||
background: #d7dce6;
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
opacity: 0.6;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
|
||||
.pagination-btn:not(:first-child):not(:nth-child(3)):not(:nth-child(2)):not(:nth-last-child(2)) {
|
||||
min-width: 28px;
|
||||
height: 32px;
|
||||
padding: 0;
|
||||
border-radius: 4px;
|
||||
border: 1px solid transparent;
|
||||
background: transparent;
|
||||
color: #606266;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
|
||||
&:hover {
|
||||
background: rgba(245, 247, 250, 0.3);
|
||||
}
|
||||
}
|
||||
|
||||
.pagination-btn.active {
|
||||
background: #5f70f3 !important;
|
||||
color: #ffffff !important;
|
||||
border-color: #5f70f3 !important;
|
||||
|
||||
&:hover {
|
||||
background: #6d7cf5 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.total-text {
|
||||
color: #909399;
|
||||
font-size: 14px;
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.transparent-table) {
|
||||
background: white;
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.el-table__body-wrapper {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
max-height: none !important;
|
||||
}
|
||||
|
||||
.el-table__header-wrapper {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.el-table__header th {
|
||||
background: white !important;
|
||||
color: black;
|
||||
}
|
||||
|
||||
&::before {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.el-table__body tr {
|
||||
background-color: white;
|
||||
|
||||
td {
|
||||
border-top: 1px solid rgba(0, 0, 0, 0.04);
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.el-checkbox__inner) {
|
||||
background-color: #ffffff !important;
|
||||
border-color: #cccccc !important;
|
||||
}
|
||||
|
||||
:deep(.el-checkbox__inner:hover) {
|
||||
border-color: #cccccc !important;
|
||||
}
|
||||
|
||||
:deep(.el-checkbox__input.is-checked .el-checkbox__inner) {
|
||||
background-color: #5f70f3 !important;
|
||||
border-color: #5f70f3 !important;
|
||||
}
|
||||
|
||||
@media (min-width: 1144px) {
|
||||
.table_bottom {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-top: 40px;
|
||||
}
|
||||
|
||||
:deep(.transparent-table) {
|
||||
.el-table__body tr {
|
||||
td {
|
||||
padding-top: 16px;
|
||||
padding-bottom: 16px;
|
||||
}
|
||||
|
||||
&+tr {
|
||||
margin-top: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.el-table .el-button--text) {
|
||||
color: #7079aa;
|
||||
}
|
||||
|
||||
:deep(.el-table .el-button--text:hover) {
|
||||
color: #5a64b5;
|
||||
}
|
||||
|
||||
.el-button--success {
|
||||
background: #5bc98c;
|
||||
color: white;
|
||||
}
|
||||
|
||||
:deep(.el-table .cell) {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
.content-text {
|
||||
display: block;
|
||||
max-width: 300px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
.page-size-select {
|
||||
width: 100px;
|
||||
margin-right: 10px;
|
||||
|
||||
:deep(.el-input__inner) {
|
||||
height: 32px;
|
||||
line-height: 32px;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #e4e7ed;
|
||||
background: #dee7ff;
|
||||
color: #606266;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
:deep(.el-input__suffix) {
|
||||
right: 6px;
|
||||
width: 15px;
|
||||
height: 20px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
top: 6px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
:deep(.el-input__suffix-inner) {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
:deep(.el-icon-arrow-up:before) {
|
||||
content: "";
|
||||
display: inline-block;
|
||||
border-left: 6px solid transparent;
|
||||
border-right: 6px solid transparent;
|
||||
border-top: 9px solid #606266;
|
||||
position: relative;
|
||||
transform: rotate(0deg);
|
||||
transition: transform 0.3s;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.el-table) {
|
||||
.el-table__body-wrapper {
|
||||
transition: height 0.3s ease;
|
||||
}
|
||||
}
|
||||
|
||||
.el-table {
|
||||
max-height: var(--table-max-height);
|
||||
|
||||
.el-table__body-wrapper {
|
||||
max-height: calc(var(--table-max-height) - 40px);
|
||||
overflow-y: auto;
|
||||
}
|
||||
}
|
||||
.custom-tag {
|
||||
background: #e6ebff;
|
||||
color: #5778ff;
|
||||
border-radius: 8px;
|
||||
font-size: 12px;
|
||||
font-weight: normal;
|
||||
border: none;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style>
|
||||
.replace-word-tooltip {
|
||||
.popper__arrow {
|
||||
border-top-color: transparent !important;
|
||||
border-right-color: #e4e7ed !important;
|
||||
}
|
||||
}
|
||||
.replace-word-content {
|
||||
max-width: 400px;
|
||||
max-height: 60vh;
|
||||
overflow-y: auto;
|
||||
scrollbar-width: thin;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 4px;
|
||||
}
|
||||
</style>
|
||||
@@ -454,6 +454,7 @@
|
||||
<tts-advanced-settings
|
||||
:visible.sync="showTtsAdvancedDialog"
|
||||
:settings="ttsSettings"
|
||||
:checked-replacement-word-ids="checkedReplacementWordIds"
|
||||
@save="handleTtsSettingsSave"
|
||||
/>
|
||||
<el-footer>
|
||||
@@ -546,7 +547,8 @@ export default {
|
||||
},
|
||||
dynamicTags: [],
|
||||
inputVisible: false,
|
||||
inputValue: ''
|
||||
inputValue: '',
|
||||
checkedReplacementWordIds: []
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
@@ -587,6 +589,7 @@ export default {
|
||||
};
|
||||
}),
|
||||
contextProviders: this.currentContextProviders,
|
||||
correctWordFileIds: this.checkedReplacementWordIds,
|
||||
};
|
||||
|
||||
// 只在用户设置了TTS参数时才传递(不为null/undefined)
|
||||
@@ -725,6 +728,8 @@ export default {
|
||||
speed: this.form.ttsRate || 0,
|
||||
pitch: this.form.ttsPitch || 0
|
||||
};
|
||||
// 同步替换词到checkedReplacementWordIds
|
||||
this.checkedReplacementWordIds = data.data.correctWordFileIds || [];
|
||||
|
||||
// 后端只给了最小映射:[{ id, agentId, pluginId }, ...]
|
||||
const savedMappings = data.data.functions || [];
|
||||
@@ -975,11 +980,13 @@ export default {
|
||||
this.showTtsAdvancedDialog = true;
|
||||
},
|
||||
handleTtsSettingsSave(settings) {
|
||||
const { replacementWordIds, ...ttsSettings } = settings;
|
||||
this.checkedReplacementWordIds = replacementWordIds;
|
||||
// 保存TTS设置
|
||||
this.ttsSettings = { ...settings };
|
||||
this.form.ttsVolume = settings.volume;
|
||||
this.form.ttsRate = settings.speed;
|
||||
this.form.ttsPitch = settings.pitch;
|
||||
this.ttsSettings = ttsSettings;
|
||||
this.form.ttsVolume = ttsSettings.volume;
|
||||
this.form.ttsRate = ttsSettings.speed;
|
||||
this.form.ttsPitch = ttsSettings.pitch;
|
||||
},
|
||||
handleUpdateContext(providers) {
|
||||
this.currentContextProviders = providers;
|
||||
|
||||
Reference in New Issue
Block a user