mirror of
https://github.com/xinnan-tech/xiaozhi-esp32-server.git
synced 2026-07-28 18:43:52 +08:00
@@ -4,13 +4,15 @@ import agent from './module/agent.js'
|
|||||||
import device from './module/device.js'
|
import device from './module/device.js'
|
||||||
import model from './module/model.js'
|
import model from './module/model.js'
|
||||||
import user from './module/user.js'
|
import user from './module/user.js'
|
||||||
|
import timbre from "./module/timbre.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 接口地址
|
* 接口地址
|
||||||
* 开发时自动读取使用.env.development文件
|
* 开发时自动读取使用.env.development文件
|
||||||
* 编译时自动读取使用.env.production文件
|
* 编译时自动读取使用.env.production文件
|
||||||
*/
|
*/
|
||||||
const DEV_API_SERVICE = process.env.VUE_APP_API_BASE_URL
|
// const DEV_API_SERVICE = process.env.VUE_APP_API_BASE_URL
|
||||||
|
const DEV_API_SERVICE = 'https://2662r3426b.vicp.fun/xiaozhi'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据开发环境返回接口url
|
* 根据开发环境返回接口url
|
||||||
@@ -29,4 +31,5 @@ export default {
|
|||||||
agent,
|
agent,
|
||||||
device,
|
device,
|
||||||
model,
|
model,
|
||||||
|
timbre,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,102 @@
|
|||||||
|
// timbre.js
|
||||||
|
import {getServiceUrl} from '../api';
|
||||||
|
import RequestService from '../httpRequest';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
/**
|
||||||
|
* 获取音色数据
|
||||||
|
*/
|
||||||
|
getVoiceList(params, callback) {
|
||||||
|
const queryParams = new URLSearchParams({
|
||||||
|
ttsModelId: params.ttsModelId,
|
||||||
|
page: params.page || 1,
|
||||||
|
limit: params.limit || 10,
|
||||||
|
name: params.name || ''
|
||||||
|
}).toString();
|
||||||
|
|
||||||
|
RequestService.sendRequest()
|
||||||
|
.url(`${getServiceUrl()}/ttsVoice?${queryParams}`)
|
||||||
|
.method('GET')
|
||||||
|
.success((res) => {
|
||||||
|
RequestService.clearRequestTime();
|
||||||
|
callback(res.data || []);
|
||||||
|
})
|
||||||
|
.fail((err) => {
|
||||||
|
console.error('获取音色列表失败:', err);
|
||||||
|
RequestService.reAjaxFun(() => {
|
||||||
|
this.getVoiceList(params, callback);
|
||||||
|
});
|
||||||
|
}).send();
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 音色保存
|
||||||
|
*/
|
||||||
|
saveVoice(params, callback) {
|
||||||
|
RequestService.sendRequest()
|
||||||
|
.url(`${getServiceUrl()}/ttsVoice`)
|
||||||
|
.method('POST')
|
||||||
|
.data(JSON.stringify({
|
||||||
|
languages: params.languageType,
|
||||||
|
name: params.voiceName,
|
||||||
|
remark: params.remark,
|
||||||
|
sort: params.sort,
|
||||||
|
ttsModelId: params.ttsModelId,
|
||||||
|
ttsVoice: params.voiceCode,
|
||||||
|
voiceDemo: params.voiceDemo || ''
|
||||||
|
}))
|
||||||
|
.success((res) => {
|
||||||
|
callback(res.data);
|
||||||
|
})
|
||||||
|
.fail((err) => {
|
||||||
|
console.error('保存音色失败:', err);
|
||||||
|
RequestService.reAjaxFun(() => {
|
||||||
|
this.saveVoice(params, callback);
|
||||||
|
});
|
||||||
|
}).send();
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 音色删除
|
||||||
|
*/
|
||||||
|
deleteVoice(id, callback) {
|
||||||
|
console.log('尝试删除音色,ID:', id);
|
||||||
|
RequestService.sendRequest()
|
||||||
|
.url(`${getServiceUrl()}/ttsVoice`)
|
||||||
|
.method('DELETE')
|
||||||
|
.data({ ids: [id] })
|
||||||
|
.success((res) => {
|
||||||
|
RequestService.clearRequestTime()
|
||||||
|
callback(res);
|
||||||
|
})
|
||||||
|
.fail((err) => {
|
||||||
|
console.error('删除音色失败:', err);
|
||||||
|
RequestService.reAjaxFun(() => {
|
||||||
|
this.deleteVoice(id, callback);
|
||||||
|
});
|
||||||
|
}).send();
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 音色修改
|
||||||
|
*/
|
||||||
|
updateVoice(params, callback) {
|
||||||
|
RequestService.sendRequest()
|
||||||
|
.url(`${getServiceUrl()}/ttsVoice/${params.id}`)
|
||||||
|
.method('PUT')
|
||||||
|
.data(JSON.stringify({
|
||||||
|
languages: params.languageType,
|
||||||
|
name: params.voiceName,
|
||||||
|
remark: params.remark,
|
||||||
|
ttsModelId: params.ttsModelId,
|
||||||
|
ttsVoice: params.voiceCode,
|
||||||
|
voiceDemo: params.voiceDemo || ''
|
||||||
|
}))
|
||||||
|
.success((res) => {
|
||||||
|
callback(res.data);
|
||||||
|
})
|
||||||
|
.fail((err) => {
|
||||||
|
console.error('修改音色失败:', err);
|
||||||
|
RequestService.reAjaxFun(() => {
|
||||||
|
this.updateVoice(params, callback);
|
||||||
|
});
|
||||||
|
}).send();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -360,8 +360,8 @@ onUnmounted(() => {
|
|||||||
left: -14px;
|
left: -14px;
|
||||||
width: 30px;
|
width: 30px;
|
||||||
height: 60px;
|
height: 60px;
|
||||||
-webkit-appearance: slider-vertical;
|
writing-mode: vertical-lr;
|
||||||
writing-mode: bt-lr;
|
direction: rtl;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
|
|||||||
@@ -4,7 +4,8 @@
|
|||||||
width="75%"
|
width="75%"
|
||||||
@close="handleClose"
|
@close="handleClose"
|
||||||
:show-close="false"
|
:show-close="false"
|
||||||
:append-to-body="true">
|
:append-to-body="true"
|
||||||
|
:close-on-click-modal="true">
|
||||||
<button class="custom-close-btn" @click="handleClose">
|
<button class="custom-close-btn" @click="handleClose">
|
||||||
×
|
×
|
||||||
</button>
|
</button>
|
||||||
@@ -15,6 +16,7 @@
|
|||||||
@scroll="handleScroll"
|
@scroll="handleScroll"
|
||||||
>
|
>
|
||||||
<el-table
|
<el-table
|
||||||
|
v-loading="loading"
|
||||||
:data="filteredTtsModels"
|
:data="filteredTtsModels"
|
||||||
style="width: 100%;"
|
style="width: 100%;"
|
||||||
class="data-table"
|
class="data-table"
|
||||||
@@ -46,7 +48,14 @@
|
|||||||
<el-table-column label="试听" align="center" min-width="225" class-name="audio-column">
|
<el-table-column label="试听" align="center" min-width="225" class-name="audio-column">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<div class="custom-audio-container">
|
<div class="custom-audio-container">
|
||||||
<AudioPlayer :audioUrl="getAudioUrl(scope.row.voiceCode)"/>
|
<el-input
|
||||||
|
v-if="scope.row.editing"
|
||||||
|
v-model="scope.row.voiceDemo"
|
||||||
|
placeholder="请输入MP3地址"
|
||||||
|
size="mini"
|
||||||
|
class="audio-input"
|
||||||
|
></el-input>
|
||||||
|
<AudioPlayer v-else-if="isValidAudioUrl(scope.row.voiceDemo)" :audioUrl="scope.row.voiceDemo"/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
@@ -96,7 +105,9 @@
|
|||||||
<el-button type="primary" size="mini" @click="toggleSelectAll" style="background: #606ff3;border: None">
|
<el-button type="primary" size="mini" @click="toggleSelectAll" style="background: #606ff3;border: None">
|
||||||
{{ selectAll ? '取消全选' : '全选' }}
|
{{ selectAll ? '取消全选' : '全选' }}
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button type="primary" size="mini" @click="addNew" style="background: #f6cf79;border: None; color: #000012">新增</el-button>
|
<el-button type="primary" size="mini" @click="addNew" style="background: #f6cf79;border: None; color: #000012">
|
||||||
|
新增
|
||||||
|
</el-button>
|
||||||
<el-button type="primary" size="mini" @click="batchDelete" style="background: red;border:None">删除</el-button>
|
<el-button type="primary" size="mini" @click="batchDelete" style="background: red;border:None">删除</el-button>
|
||||||
</div>
|
</div>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
@@ -104,6 +115,7 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import AudioPlayer from './AudioPlayer.vue'
|
import AudioPlayer from './AudioPlayer.vue'
|
||||||
|
import Api from "@/apis/api";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {AudioPlayer},
|
components: {AudioPlayer},
|
||||||
@@ -111,49 +123,36 @@ export default {
|
|||||||
visible: {
|
visible: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false
|
default: false
|
||||||
|
},
|
||||||
|
ttsModelId: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
localVisible: this.visible,
|
localVisible: this.visible,
|
||||||
activeModel: 'EdgeTTS',
|
|
||||||
searchQuery: '',
|
searchQuery: '',
|
||||||
editDialogVisible: false,
|
editDialogVisible: false,
|
||||||
editVoiceData: {},
|
editVoiceData: {},
|
||||||
ttsModels: [
|
ttsModels: [],
|
||||||
{voiceCode: 'wawaxiaohe', voiceName: '湾湾小何', languageType: '中文', remark: 'AAAA国少', selected: false},
|
|
||||||
{voiceCode: 'wawaxiaohe', voiceName: '湾湾小何', languageType: '中文', remark: '', selected: false},
|
|
||||||
{voiceCode: 'wawaxiaohe', voiceName: '湾湾小何', languageType: '中文', remark: '', selected: false},
|
|
||||||
{voiceCode: 'wawaxiaohe', voiceName: '湾湾小何', languageType: '中文', remark: '', selected: false},
|
|
||||||
{voiceCode: 'wawaxiaohe', voiceName: '湾湾小何', languageType: '中文', remark: '', selected: false},
|
|
||||||
{voiceCode: 'wawaxiaohe', voiceName: '湾湾小何', languageType: '中文', remark: '', selected: false},
|
|
||||||
{voiceCode: 'wawaxiaohe', voiceName: '湾湾小何', languageType: '中文', remark: '', selected: false},
|
|
||||||
{voiceCode: 'wawaxiaohe', voiceName: '湾湾小何', languageType: '中文', remark: '', selected: false},
|
|
||||||
{voiceCode: 'wawaxiaohe', voiceName: '湾湾小何', languageType: '中文', remark: '', selected: false},
|
|
||||||
{voiceCode: 'wawaxiaohe', voiceName: '湾湾小何', languageType: '中文', remark: '', selected: false},
|
|
||||||
{voiceCode: 'wawaxiaohe', voiceName: '湾湾小何', languageType: '中文', remark: '', selected: false},
|
|
||||||
{voiceCode: 'wawaxiaohe', voiceName: '湾湾小何', languageType: '中文', remark: '', selected: false},
|
|
||||||
{voiceCode: 'wawaxiaohe', voiceName: '湾湾小何', languageType: '中文', remark: '', selected: false},
|
|
||||||
{voiceCode: 'wawaxiaohe', voiceName: '湾湾小何', languageType: '中文', remark: '', selected: false},
|
|
||||||
{voiceCode: 'wawaxiaohe', voiceName: '湾湾小何', languageType: '中文', remark: '', selected: false},
|
|
||||||
{voiceCode: 'wawaxiaohe', voiceName: '湾湾小何', languageType: '中文', remark: '', selected: false},
|
|
||||||
{voiceCode: 'wawaxiaohe', voiceName: '湾湾小何', languageType: '中文', remark: '', selected: false},
|
|
||||||
{voiceCode: 'wawaxiaohe', voiceName: '湾湾小何', languageType: '中文', remark: '', selected: false},
|
|
||||||
],
|
|
||||||
currentPage: 1,
|
currentPage: 1,
|
||||||
pageSize: 4,
|
pageSize: 10000,
|
||||||
total: 20,
|
total: 0,
|
||||||
isDragging: false,
|
isDragging: false,
|
||||||
startY: 0,
|
startY: 0,
|
||||||
scrollTop: 0,
|
scrollTop: 0,
|
||||||
selectAll: false,
|
selectAll: false,
|
||||||
selectedRows: []
|
selectedRows: [],
|
||||||
|
loading: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
visible(newVal) {
|
visible(newVal) {
|
||||||
this.localVisible = newVal;
|
this.localVisible = newVal;
|
||||||
if (newVal) {
|
if (newVal) {
|
||||||
|
this.currentPage = 1;
|
||||||
|
this.loadData(); // 对话框显示时加载数据
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
this.updateScrollbar();
|
this.updateScrollbar();
|
||||||
});
|
});
|
||||||
@@ -184,16 +183,57 @@ export default {
|
|||||||
window.removeEventListener('mousemove', this.handleDrag);
|
window.removeEventListener('mousemove', this.handleDrag);
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
handleClose() {
|
loadData() {
|
||||||
this.localVisible = false;
|
const params = {
|
||||||
this.$emit('update:visible', false);
|
ttsModelId: this.ttsModelId,
|
||||||
this.ttsModels.forEach(model => {
|
page: this.currentPage,
|
||||||
model.remark = '';
|
limit: this.pageSize,
|
||||||
|
name: this.searchQuery
|
||||||
|
};
|
||||||
|
|
||||||
|
Api.timbre.getVoiceList(params, (data) => {
|
||||||
|
if (data.code === 0) {
|
||||||
|
this.ttsModels = data.data.list
|
||||||
|
.map(item => ({
|
||||||
|
id: item.id || '',
|
||||||
|
voiceCode: item.ttsVoice || '',
|
||||||
|
voiceName: item.name || '未命名音色',
|
||||||
|
languageType: item.languages || '',
|
||||||
|
remark: item.remark || '',
|
||||||
|
voiceDemo: item.voiceDemo || '',
|
||||||
|
selected: false,
|
||||||
|
editing: false,
|
||||||
|
sort: Number(item.sort)
|
||||||
|
}))
|
||||||
|
.sort((a, b) => a.sort - b.sort);
|
||||||
|
this.total = data.total;
|
||||||
|
} else {
|
||||||
|
this.$message.error({
|
||||||
|
message: data.msg || '获取音色列表失败',
|
||||||
|
showClose: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, (err) => {
|
||||||
|
console.error('加载失败:', err);
|
||||||
|
this.$message.error({
|
||||||
|
message: '加载音色数据失败',
|
||||||
|
showClose: true
|
||||||
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
getAudioUrl(voiceCode) {
|
|
||||||
return `https://music.163.com/song/media/outer/url?id=5257138.mp3`;
|
handleClose() {
|
||||||
|
// 重置状态
|
||||||
|
this.ttsModels = [];
|
||||||
|
this.currentPage = 1;
|
||||||
|
this.total = 0;
|
||||||
|
this.selectAll = false;
|
||||||
|
this.searchQuery = '';
|
||||||
|
|
||||||
|
this.localVisible = false;
|
||||||
|
this.$emit('update:visible', false);
|
||||||
},
|
},
|
||||||
|
|
||||||
updateScrollbar() {
|
updateScrollbar() {
|
||||||
const container = this.$refs.tableContainer;
|
const container = this.$refs.tableContainer;
|
||||||
const scrollbarThumb = this.$refs.scrollbarThumb;
|
const scrollbarThumb = this.$refs.scrollbarThumb;
|
||||||
@@ -208,6 +248,7 @@ export default {
|
|||||||
scrollbarThumb.style.height = `${thumbHeight}px`;
|
scrollbarThumb.style.height = `${thumbHeight}px`;
|
||||||
this.updateThumbPosition();
|
this.updateThumbPosition();
|
||||||
},
|
},
|
||||||
|
|
||||||
updateThumbPosition() {
|
updateThumbPosition() {
|
||||||
const container = this.$refs.tableContainer;
|
const container = this.$refs.tableContainer;
|
||||||
const scrollbarThumb = this.$refs.scrollbarThumb;
|
const scrollbarThumb = this.$refs.scrollbarThumb;
|
||||||
@@ -223,18 +264,29 @@ export default {
|
|||||||
|
|
||||||
scrollbarThumb.style.top = `${Math.min(thumbTop, maxTop)}px`;
|
scrollbarThumb.style.top = `${Math.min(thumbTop, maxTop)}px`;
|
||||||
},
|
},
|
||||||
|
|
||||||
handleScroll() {
|
handleScroll() {
|
||||||
|
const container = this.$refs.tableContainer;
|
||||||
|
if (container.scrollTop + container.clientHeight >= container.scrollHeight - 50) {
|
||||||
|
if (this.currentPage * this.pageSize < this.total) {
|
||||||
|
this.currentPage++;
|
||||||
|
this.loadData();
|
||||||
|
}
|
||||||
|
}
|
||||||
this.updateThumbPosition();
|
this.updateThumbPosition();
|
||||||
},
|
},
|
||||||
|
|
||||||
startDrag(e) {
|
startDrag(e) {
|
||||||
this.isDragging = true;
|
this.isDragging = true;
|
||||||
this.startY = e.clientY;
|
this.startY = e.clientY;
|
||||||
this.scrollTop = this.$refs.tableContainer.scrollTop;
|
this.scrollTop = this.$refs.tableContainer.scrollTop;
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
},
|
},
|
||||||
|
|
||||||
stopDrag() {
|
stopDrag() {
|
||||||
this.isDragging = false;
|
this.isDragging = false;
|
||||||
},
|
},
|
||||||
|
|
||||||
handleDrag(e) {
|
handleDrag(e) {
|
||||||
if (!this.isDragging) return;
|
if (!this.isDragging) return;
|
||||||
|
|
||||||
@@ -249,6 +301,7 @@ export default {
|
|||||||
const scrollRatio = (trackHeight - thumbHeight) / maxScrollTop;
|
const scrollRatio = (trackHeight - thumbHeight) / maxScrollTop;
|
||||||
container.scrollTop = this.scrollTop + deltaY / scrollRatio;
|
container.scrollTop = this.scrollTop + deltaY / scrollRatio;
|
||||||
},
|
},
|
||||||
|
|
||||||
handleTrackClick(e) {
|
handleTrackClick(e) {
|
||||||
const container = this.$refs.tableContainer;
|
const container = this.$refs.tableContainer;
|
||||||
const scrollbarTrack = this.$refs.scrollbarTrack;
|
const scrollbarTrack = this.$refs.scrollbarTrack;
|
||||||
@@ -268,16 +321,75 @@ export default {
|
|||||||
scrollbarThumb.style.top = `${newTop}px`;
|
scrollbarThumb.style.top = `${newTop}px`;
|
||||||
container.scrollTop = (newTop / (trackHeight - thumbHeight)) * (container.scrollHeight - container.clientHeight);
|
container.scrollTop = (newTop / (trackHeight - thumbHeight)) * (container.scrollHeight - container.clientHeight);
|
||||||
},
|
},
|
||||||
// 按钮组
|
|
||||||
startEdit(row) {
|
startEdit(row) {
|
||||||
row.editing = true;
|
row.editing = true;
|
||||||
this.$set(row, 'originalData', {...row});
|
this.$set(row, 'originalData', {...row});
|
||||||
},
|
},
|
||||||
|
|
||||||
saveEdit(row) {
|
saveEdit(row) {
|
||||||
row.editing = false;
|
try {
|
||||||
delete row.originalData;
|
const params = {
|
||||||
// 这里可以添加保存到服务器的逻辑
|
id: row.id,
|
||||||
|
voiceCode: row.voiceCode,
|
||||||
|
voiceName: row.voiceName,
|
||||||
|
languageType: row.languageType,
|
||||||
|
remark: row.remark,
|
||||||
|
ttsModelId: this.ttsModelId,
|
||||||
|
voiceDemo: row.voiceDemo || '',
|
||||||
|
sort: row.sort
|
||||||
|
};
|
||||||
|
|
||||||
|
let res;
|
||||||
|
if (row.id) {
|
||||||
|
// 已有ID,执行更新操作
|
||||||
|
Api.timbre.updateVoice(params, (response) => {
|
||||||
|
res = response;
|
||||||
|
this.handleResponse(res, row);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// 没有ID,执行新增操作
|
||||||
|
Api.timbre.saveVoice(params, (response) => {
|
||||||
|
res = response;
|
||||||
|
this.handleResponse(res, row);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('操作失败:', error);
|
||||||
|
// 异常情况下也恢复原始数据
|
||||||
|
if (row.originalData) {
|
||||||
|
Object.assign(row, row.originalData);
|
||||||
|
row.editing = false;
|
||||||
|
delete row.originalData;
|
||||||
|
}
|
||||||
|
this.$message.error({
|
||||||
|
message: '操作失败,请重试',
|
||||||
|
showClose: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
handleResponse(res, row) {
|
||||||
|
if (res.code === 0) {
|
||||||
|
this.$message.success({
|
||||||
|
message: row.id ? '修改成功' : '保存成功',
|
||||||
|
showClose: true
|
||||||
|
});
|
||||||
|
row.editing = false;
|
||||||
|
delete row.originalData;
|
||||||
|
this.loadData(); // 刷新数据
|
||||||
|
} else {
|
||||||
|
// 保存失败时恢复原始数据
|
||||||
|
if (row.originalData) {
|
||||||
|
Object.assign(row, row.originalData);
|
||||||
|
row.editing = false;
|
||||||
|
delete row.originalData;
|
||||||
|
}
|
||||||
|
this.$message.error({
|
||||||
|
message: res.msg || (row.id ? '修改失败' : '保存失败'),
|
||||||
|
showClose: true
|
||||||
|
});
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
toggleSelectAll() {
|
toggleSelectAll() {
|
||||||
@@ -288,24 +400,128 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
addNew() {
|
addNew() {
|
||||||
|
const maxSort = this.ttsModels.length > 0
|
||||||
|
? Math.max(...this.ttsModels.map(item => Number(item.sort) || 0))
|
||||||
|
: 0;
|
||||||
|
|
||||||
const newRow = {
|
const newRow = {
|
||||||
voiceCode: '新编码',
|
voiceCode: '',
|
||||||
voiceName: '新音色',
|
voiceName: '',
|
||||||
languageType: '中文',
|
languageType: '中文',
|
||||||
|
voiceDemo: '',
|
||||||
remark: '',
|
remark: '',
|
||||||
selected: false,
|
selected: false,
|
||||||
editing: true
|
editing: true,
|
||||||
|
sort: maxSort + 1
|
||||||
};
|
};
|
||||||
|
|
||||||
this.ttsModels.unshift(newRow);
|
this.ttsModels.unshift(newRow);
|
||||||
},
|
},
|
||||||
|
|
||||||
deleteRow(row) {
|
deleteRow(row) {
|
||||||
const index = this.ttsModels.indexOf(row);
|
this.$confirm("确定要删除该音色吗?", "警告", {
|
||||||
this.ttsModels.splice(index, 1);
|
confirmButtonText: "确定",
|
||||||
|
cancelButtonText: "取消",
|
||||||
|
type: "warning",
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
Api.timbre.deleteVoice(row.id, (response) => {
|
||||||
|
if (response.code === 0) {
|
||||||
|
this.$message.success({
|
||||||
|
message: "删除成功",
|
||||||
|
showClose: true
|
||||||
|
});
|
||||||
|
this.loadData(); // 刷新数据
|
||||||
|
} else {
|
||||||
|
this.$message.error({
|
||||||
|
message: response.msg || "删除失败",
|
||||||
|
showClose: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
this.$message.info("已取消删除");
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
batchDelete() {
|
batchDelete() {
|
||||||
this.ttsModels = this.ttsModels.filter(row => !row.selected);
|
const selectedRows = this.filteredTtsModels.filter(row => row.selected);
|
||||||
|
if (selectedRows.length === 0) {
|
||||||
|
this.$message.warning("请先选择需要删除的音色");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$confirm(`确定要删除选中的${selectedRows.length}个音色吗?`, "警告", {
|
||||||
|
confirmButtonText: "确定",
|
||||||
|
cancelButtonText: "取消",
|
||||||
|
type: "warning",
|
||||||
|
})
|
||||||
|
.then(async () => {
|
||||||
|
const loading = this.$loading({
|
||||||
|
lock: true,
|
||||||
|
text: "正在删除中...",
|
||||||
|
spinner: "el-icon-loading",
|
||||||
|
background: "rgba(0, 0, 0, 0.7)",
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
const results = await Promise.all(
|
||||||
|
selectedRows.map((row) => {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
Api.timbre.deleteVoice(row.id, (response) => {
|
||||||
|
if (response.code === 0) {
|
||||||
|
resolve({ success: true, id: row.id });
|
||||||
|
} else {
|
||||||
|
resolve({
|
||||||
|
success: false,
|
||||||
|
id: row.id,
|
||||||
|
msg: response.msg || '删除失败'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
const successCount = results.filter(r => r.success).length;
|
||||||
|
const failCount = results.length - successCount;
|
||||||
|
|
||||||
|
if (failCount === 0) {
|
||||||
|
this.$message.success({
|
||||||
|
message: `成功删除${successCount}个音色`,
|
||||||
|
showClose: true
|
||||||
|
});
|
||||||
|
} else if (successCount === 0) {
|
||||||
|
this.$message.error({
|
||||||
|
message: '删除失败,请重试',
|
||||||
|
showClose: true
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.$message.warning({
|
||||||
|
message: `成功删除${successCount}个音色,${failCount}个删除失败`,
|
||||||
|
showClose: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.loadData(); // 刷新数据
|
||||||
|
} catch (error) {
|
||||||
|
console.error('批量删除出错:', error);
|
||||||
|
this.$message.error({
|
||||||
|
message: '删除过程中发生错误',
|
||||||
|
showClose: true
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
loading.close();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
this.$message.info("已取消删除");
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
isValidAudioUrl(url) {
|
||||||
|
return url && (url.endsWith('.mp3') || url.endsWith('.ogg') || url.endsWith('.wav'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -80,7 +80,11 @@
|
|||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column v-if="activeTab === 'tts'" label="音色管理" align="center">
|
<el-table-column v-if="activeTab === 'tts'" label="音色管理" align="center">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<el-button type="text" size="mini" @click="ttsDialogVisible = true" class="voice-management-btn">
|
<el-button
|
||||||
|
type="text"
|
||||||
|
size="mini"
|
||||||
|
@click="openTtsDialog(scope.row)"
|
||||||
|
class="voice-management-btn">
|
||||||
音色管理
|
音色管理
|
||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
@@ -124,7 +128,7 @@
|
|||||||
|
|
||||||
<ModelEditDialog :modelType="activeTab" :visible.sync="editDialogVisible" :modelData="editModelData"
|
<ModelEditDialog :modelType="activeTab" :visible.sync="editDialogVisible" :modelData="editModelData"
|
||||||
@save="handleModelSave" />
|
@save="handleModelSave" />
|
||||||
<TtsModel :visible.sync="ttsDialogVisible" />
|
<TtsModel :visible.sync="ttsDialogVisible" :ttsModelId="selectedTtsModelId"/>
|
||||||
<AddModelDialog :modelType="activeTab" :visible.sync="addDialogVisible" @confirm="handleAddConfirm" />
|
<AddModelDialog :modelType="activeTab" :visible.sync="addDialogVisible" @confirm="handleAddConfirm" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -151,6 +155,7 @@ export default {
|
|||||||
editDialogVisible: false,
|
editDialogVisible: false,
|
||||||
editModelData: {},
|
editModelData: {},
|
||||||
ttsDialogVisible: false,
|
ttsDialogVisible: false,
|
||||||
|
selectedTtsModelId: '',
|
||||||
modelList: [],
|
modelList: [],
|
||||||
currentPage: 1,
|
currentPage: 1,
|
||||||
pageSize: 5,
|
pageSize: 5,
|
||||||
@@ -200,6 +205,10 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
|
openTtsDialog(row) {
|
||||||
|
this.selectedTtsModelId = row.id;
|
||||||
|
this.ttsDialogVisible = true;
|
||||||
|
},
|
||||||
headerCellClassName({ column, columnIndex }) {
|
headerCellClassName({ column, columnIndex }) {
|
||||||
if (columnIndex === 0) {
|
if (columnIndex === 0) {
|
||||||
return 'custom-selection-header';
|
return 'custom-selection-header';
|
||||||
|
|||||||
Reference in New Issue
Block a user