feat: 语料文本
This commit is contained in:
parent
af2f9398b7
commit
a24733f6ea
@ -29,3 +29,11 @@ export function deleteCategory(id) {
|
|||||||
method: 'delete',
|
method: 'delete',
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getCorpusListApi(params) {
|
||||||
|
return request({
|
||||||
|
url: '/tts/corpus/list',
|
||||||
|
method: 'get',
|
||||||
|
params: params
|
||||||
|
})
|
||||||
|
}
|
||||||
@ -60,3 +60,10 @@ export function batchTaskApi(data) {
|
|||||||
data: data
|
data: data
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function saveTaskApi(data) {
|
||||||
|
return request({
|
||||||
|
url: `/tts/task/save-to-corpus/${data.taskId}/${data.categoryId}`,
|
||||||
|
method: 'post'
|
||||||
|
})
|
||||||
|
}
|
||||||
@ -74,13 +74,20 @@ const defaultProps = {
|
|||||||
label: 'label'
|
label: 'label'
|
||||||
}
|
}
|
||||||
|
|
||||||
// 初始树数据(含count字段用于显示,可选)
|
const emit = defineEmits(['select-category'])
|
||||||
const treeData = ref([])
|
|
||||||
|
const treeData = ref([
|
||||||
|
{
|
||||||
|
categoryName: '全部',
|
||||||
|
children: [],
|
||||||
|
id: ''
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
const getCategoryTree = async () => {
|
const getCategoryTree = async () => {
|
||||||
const res = await getCategoryTreeApi()
|
const res = await getCategoryTreeApi()
|
||||||
if (res.code === 200) {
|
if (res.code === 200) {
|
||||||
treeData.value = res.data
|
treeData.value[0].children = res.data
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -103,6 +110,7 @@ const ruleForm = ref({
|
|||||||
// 选中节点
|
// 选中节点
|
||||||
const handleNodeClick = (data) => {
|
const handleNodeClick = (data) => {
|
||||||
selectedNode.value = data
|
selectedNode.value = data
|
||||||
|
emit('select-category', data)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 新增一级分类(根节点下)
|
// 新增一级分类(根节点下)
|
||||||
|
|||||||
@ -5,47 +5,33 @@
|
|||||||
<div class="toolbar-left">
|
<div class="toolbar-left">
|
||||||
<el-button type="primary" size="default" @click="openDialog(true)">+新建语料</el-button>
|
<el-button type="primary" size="default" @click="openDialog(true)">+新建语料</el-button>
|
||||||
</div>
|
</div>
|
||||||
<div class="toolbar-right">
|
|
||||||
<el-tag v-for="tab in tabs" :key="tab.key" :type="activeTab === tab.key ? '' : 'info'"
|
|
||||||
:effect="activeTab === tab.key ? 'dark' : 'plain'" style="cursor:pointer;" @click="activeTab = tab.key">
|
|
||||||
{{ tab.label }}
|
|
||||||
<span class="tab-count">{{ tab.count }}</span>
|
|
||||||
</el-tag>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 表格 -->
|
<!-- 表格 -->
|
||||||
<el-table :data="corpusData" stripe style="width:100%;" max-height="400">
|
<el-table :data="corpusData" stripe style="width:100%;" max-height="400">
|
||||||
<el-table-column prop="id" label="语料编号" width="180" />
|
<el-table-column prop="id" label="语料编号" show-overflow-tooltip />
|
||||||
<el-table-column prop="text" label="文本内容" min-width="140" show-overflow-tooltip />
|
<el-table-column prop="text" label="文本内容" show-overflow-tooltip />
|
||||||
<el-table-column prop="category" label="功能类别" width="150" show-overflow-tooltip />
|
<el-table-column prop="categoryName" label="所属分类" show-overflow-tooltip />
|
||||||
<el-table-column prop="lang" label="语种" width="80" />
|
<el-table-column prop="language" label="语种" />
|
||||||
<el-table-column prop="dialect" label="方言" width="80" />
|
<el-table-column prop="dialect" label="方言" />
|
||||||
<el-table-column prop="emotion" label="情绪" width="70" />
|
<el-table-column prop="emotion" label="情绪" />
|
||||||
<el-table-column prop="voice" label="音色" width="100" />
|
<el-table-column prop="voice" label="音色" />
|
||||||
<el-table-column prop="status" label="审核状态" width="100">
|
<el-table-column prop="fileSize" label="文件大小" />
|
||||||
|
<el-table-column label="操作" fixed="right">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<el-tag :type="statusType(row.status)" size="small" effect="plain">
|
<el-button link @click="play(row.audioFilePath)" type="primary" icon="VideoPlay">播放</el-button>
|
||||||
{{ row.status }}
|
<el-button icon="Download" link type="primary" @click="handleDownload(row)">下载</el-button>
|
||||||
</el-tag>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column prop="updateTime" label="更新时间" width="160" />
|
|
||||||
<el-table-column label="操作" width="80" fixed="right">
|
|
||||||
<template #default="{ row }">
|
|
||||||
<el-button size="small" text type="primary" @click="handleDetail(row)">详情</el-button>
|
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
|
|
||||||
<!-- 分页 -->
|
<!-- 分页 -->
|
||||||
<div class="pagination-wrapper">
|
<div class="pagination-wrapper">
|
||||||
<span class="text-muted text-sm">共 {{ total }} 条</span>
|
|
||||||
<el-pagination background layout="prev, pager, next, jumper" :total="total" :page-size="10"
|
<el-pagination background layout="prev, pager, next, jumper" :total="total" :page-size="10"
|
||||||
v-model:current-page="page" small />
|
v-model:current-page="page" small />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<el-dialog v-model="dialogVisible" :title="'新建语料'" :append-to-body="true" width="600px" @close="resetDialog">
|
<el-dialog v-model="addCorpusVisible" :title="'新建语料'" :append-to-body="true" width="600px" @close="resetDialog">
|
||||||
<el-form ref="ruleFormRef" :model="ruleForm" label-width="auto">
|
<el-form ref="ruleFormRef" :model="ruleForm" label-width="auto">
|
||||||
<el-form-item label="模型" prop="model">
|
<el-form-item label="模型" prop="model">
|
||||||
<el-select v-model="ruleForm.model" placeholder="模型">
|
<el-select v-model="ruleForm.model" placeholder="模型">
|
||||||
@ -61,43 +47,61 @@
|
|||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="上传文件">
|
<el-form-item label="上传文件">
|
||||||
<el-upload
|
<el-upload v-model:file-list="uploadFileList" :action="`${data.baseUrl}/system/file/upload`"
|
||||||
v-model:file-list="uploadFileList"
|
:before-upload="beforeAvatarUpload" :data="{ fileType: uploadFileType }" :headers="{
|
||||||
:action="`${data.baseUrl}/system/file/upload`"
|
'authorization': getToken()
|
||||||
:before-upload="beforeAvatarUpload"
|
}" :limit="1" :on-error="uploadError" :on-success="uploadSuccess" class="file-uploader">
|
||||||
:data="{fileType:uploadFileType}"
|
<el-button type="primary">选取文件</el-button>
|
||||||
:headers="{
|
<template #tip>
|
||||||
'authorization': getToken()
|
<div class="el-upload__tip">
|
||||||
}"
|
支持格式:'wav'
|
||||||
:limit="1"
|
</div>
|
||||||
:on-error="uploadError"
|
</template>
|
||||||
:on-success="uploadSuccess"
|
</el-upload>
|
||||||
class="file-uploader"
|
|
||||||
>
|
|
||||||
<el-button type="primary">选取文件</el-button>
|
|
||||||
<template #tip>
|
|
||||||
<div class="el-upload__tip">
|
|
||||||
支持格式:'wav'
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</el-upload>
|
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
|
|
||||||
</el-form>
|
</el-form>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<el-button @click="dialogVisible = false">取消</el-button>
|
<el-button @click="addCorpusVisible = false">取消</el-button>
|
||||||
<el-button type="primary" @click="confirmDialog">确定</el-button>
|
<el-button type="primary" @click="confirmDialog">确定</el-button>
|
||||||
</template>
|
</template>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
|
||||||
|
<el-dialog v-model="dialogVisible" title="播放音频" width="600px" :destroy-on-close="true" @close="colseDialog">
|
||||||
|
<div class="wave-container">
|
||||||
|
<div class="wave">
|
||||||
|
<div v-for="(h, index) in waveform" :key="index" :style="{
|
||||||
|
width: '4px',
|
||||||
|
height: h + '%',
|
||||||
|
background: '#3b82f6',
|
||||||
|
borderRadius: '2px',
|
||||||
|
opacity: 0.6 + (h / 100) * 0.4
|
||||||
|
}"></div>
|
||||||
|
</div>
|
||||||
|
<div class="audio-container">
|
||||||
|
<AudioPlayer :src="audioUrl" @audioStatus="audioStatus" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-dialog>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, computed } from 'vue'
|
import { ref, computed, onMounted, watch } from 'vue'
|
||||||
import {getToken} from "@/utils/auth.js";
|
import { getToken } from "@/utils/auth.js";
|
||||||
|
import { getCorpusListApi } from '@/api/corpusManger/corpus.js'
|
||||||
|
import { downloadWavApi } from '@/api/corpusManger/speech_synthesis'
|
||||||
|
import AudioPlayer from '../../speech_synthesis/components/AudioPlayer.vue';
|
||||||
|
|
||||||
const dialogVisible = ref(false)
|
const props = defineProps({
|
||||||
|
categoryId: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const addCorpusVisible = ref(false)
|
||||||
|
|
||||||
const voiceOption = ref([
|
const voiceOption = ref([
|
||||||
{
|
{
|
||||||
@ -107,30 +111,30 @@ const voiceOption = ref([
|
|||||||
])
|
])
|
||||||
|
|
||||||
const langOption = ref([
|
const langOption = ref([
|
||||||
{
|
{
|
||||||
label: '中文(普通话)',
|
label: '中文(普通话)',
|
||||||
value: '普通话'
|
value: '普通话'
|
||||||
}, {
|
}, {
|
||||||
label: '英文',
|
label: '英文',
|
||||||
value: '英文'
|
value: '英文'
|
||||||
}, {
|
}, {
|
||||||
label: '粤语',
|
label: '粤语',
|
||||||
value: '粤语'
|
value: '粤语'
|
||||||
}, {
|
}, {
|
||||||
label: '四川话',
|
label: '四川话',
|
||||||
value: '四川话'
|
value: '四川话'
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
|
|
||||||
const ruleForm = ref({
|
const ruleForm = ref({
|
||||||
text: '',
|
text: '',
|
||||||
model: '科大讯飞',
|
model: '科大讯飞',
|
||||||
voice: 'xiaoyan',
|
voice: 'xiaoyan',
|
||||||
speed: 50,
|
speed: 50,
|
||||||
volume: 50,
|
volume: 50,
|
||||||
pitch: 50,
|
pitch: 50,
|
||||||
sampleRate: '16',
|
sampleRate: '16',
|
||||||
outputFormat: 'wav'
|
outputFormat: 'wav'
|
||||||
})
|
})
|
||||||
|
|
||||||
const rules = ref({
|
const rules = ref({
|
||||||
@ -145,14 +149,13 @@ const data = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const openDialog = () => {
|
const openDialog = () => {
|
||||||
dialogVisible.value = true
|
addCorpusVisible.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
const resetDialog = () => {
|
const resetDialog = () => {
|
||||||
|
addCorpusVisible.value = false
|
||||||
}
|
}
|
||||||
|
|
||||||
const activeTab = ref('all')
|
|
||||||
const page = ref(1)
|
const page = ref(1)
|
||||||
|
|
||||||
const beforeAvatarUpload = (rawFile) => {
|
const beforeAvatarUpload = (rawFile) => {
|
||||||
@ -163,7 +166,7 @@ const beforeAvatarUpload = (rawFile) => {
|
|||||||
proxy.$modal.msgError(`文件格式不正确,请上传${['png', 'jpg', 'jpeg'].join("/")}格式文件!`);
|
proxy.$modal.msgError(`文件格式不正确,请上传${['png', 'jpg', 'jpeg'].join("/")}格式文件!`);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const {label = undefined, value = undefined} = supportType?.find(item => item.label === rawFile.type)
|
const { label = undefined, value = undefined } = supportType?.find(item => item.label === rawFile.type)
|
||||||
if (label) {
|
if (label) {
|
||||||
uploadFileType.value = value
|
uploadFileType.value = value
|
||||||
}
|
}
|
||||||
@ -186,31 +189,87 @@ const uploadError = (e) => {
|
|||||||
console.log("uploadError", e)
|
console.log("uploadError", e)
|
||||||
}
|
}
|
||||||
|
|
||||||
const tabs = [
|
|
||||||
{ key: 'all', label: '全部语料', count: '1,245,678' },
|
|
||||||
{ key: 'pending', label: '待审核', count: '45,678' },
|
|
||||||
{ key: 'published', label: '已发布', count: '987,654' }
|
|
||||||
]
|
|
||||||
|
|
||||||
// 模拟数据
|
// 模拟数据
|
||||||
const corpusData = ref([
|
const corpusData = ref([])
|
||||||
{ id: 'CORP_20240517_0001', text: '打开天窗', category: '控制指令-车窗控制', lang: '中文', dialect: '普通话', emotion: '中性', voice: '男声', status: '已通过', updateTime: '2024-05-17 10:21:33' },
|
const total = ref(0)
|
||||||
{ id: 'CORP_20240517_0002', text: '导航到最近的充电站', category: '导航交互-路线规划', lang: '中文', dialect: '普通话', emotion: '中性', voice: '女声', status: '已通过', updateTime: '2024-05-17 10:19:48' },
|
|
||||||
{ id: 'CORP_20240517_0003', text: '播放周杰伦的歌', category: '媒体娱乐-音乐播放', lang: '中文', dialect: '普通话', emotion: '愉悦', voice: '女声', status: '已通过', updateTime: '2024-05-17 10:18:02' },
|
const getCorpusList = async () => {
|
||||||
{ id: 'CORP_20240517_0004', text: '把空调调到24度', category: '控制指令-空调控制', lang: '中文', dialect: '普通话', emotion: '中性', voice: '男声', status: '待审核', updateTime: '2024-05-17 10:15:26' },
|
const obj = {
|
||||||
{ id: 'CORP_20240517_0005', text: '给张三打电话', category: '电话消息-拨打电话', lang: '中文', dialect: '普通话', emotion: '中性', voice: '男声', status: '已通过', updateTime: '2024-05-17 10:12:11' },
|
pageNum: page.value,
|
||||||
{ id: 'CORP_20240517_0006', text: '今天天气怎么样', category: '场景对话-日常对话', lang: '中文', dialect: '普通话', emotion: '中性', voice: '女声', status: '已通过', updateTime: '2024-05-17 10:10:05' },
|
pageSize: 10,
|
||||||
{ id: 'CORP_20240517_0007', text: '我有点冷', category: '场景对话-出行场景', lang: '中文', dialect: '普通话', emotion: '不满', voice: '女声', status: '待审核', updateTime: '2024-05-17 10:08:47' },
|
categoryId: props.categoryId
|
||||||
{ id: 'CORP_20240517_0008', text: '关闭所有车窗', category: '控制指令-车窗控制', lang: '中文', dialect: '普通话', emotion: '中性', voice: '男声', status: '已通过', updateTime: '2024-05-17 10:06:20' }
|
}
|
||||||
])
|
const res = await getCorpusListApi(obj)
|
||||||
|
if (res.code === 0) {
|
||||||
|
corpusData.value = res.rows
|
||||||
|
total.value = res.total
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const total = computed(() => corpusData.value.length)
|
|
||||||
|
|
||||||
const statusType = (status) => {
|
const statusType = (status) => {
|
||||||
if (status === '已通过') return 'success'
|
if (status === '已通过') return 'success'
|
||||||
if (status === '待审核') return 'warning'
|
if (status === '待审核') return 'warning'
|
||||||
return 'info'
|
return 'info'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const dialogVisible = ref(false)
|
||||||
|
|
||||||
|
// 波形数据
|
||||||
|
const waveform = ref(Array.from({ length: 60 }, () => Math.floor(Math.random() * 60 + 20)))
|
||||||
|
const timer = ref(null)
|
||||||
|
|
||||||
|
// 模拟波形动态变化
|
||||||
|
const updateWaveform = () => {
|
||||||
|
waveform.value = Array.from({ length: 60 }, () => Math.floor(Math.random() * 60 + 20))
|
||||||
|
}
|
||||||
|
|
||||||
|
const audioUrl = ref('https://sf1-cdn-tos.huoshanstatic.com/obj/media-fe/xgplayer_doc_video/music/audio.mp3')
|
||||||
|
|
||||||
|
const audioStatus = (isPlaying) => {
|
||||||
|
if (isPlaying) {
|
||||||
|
timer.value = setInterval(updateWaveform, 100)
|
||||||
|
} else {
|
||||||
|
if (timer.value) clearInterval(timer.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const play = (url) => {
|
||||||
|
dialogVisible.value = true
|
||||||
|
audioUrl.value = import.meta.env.VITE_MINIO_URL + url
|
||||||
|
}
|
||||||
|
|
||||||
|
const colseDialog = () => {
|
||||||
|
dialogVisible.value = false
|
||||||
|
if (timer.value) clearInterval(timer.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleDownload = async (row) => {
|
||||||
|
const response = await fetch(import.meta.env.VITE_MINIO_URL + row.audioFilePath)
|
||||||
|
const blob = await response.blob()
|
||||||
|
const blobUrl = URL.createObjectURL(blob)
|
||||||
|
|
||||||
|
const a = document.createElement('a')
|
||||||
|
a.href = blobUrl
|
||||||
|
|
||||||
|
a.download = `${row.id}.${row.audioFilePath.split('.')[1]}`
|
||||||
|
a.click()
|
||||||
|
|
||||||
|
URL.revokeObjectURL(blobUrl)
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(() => props.categoryId,
|
||||||
|
() => {
|
||||||
|
getCorpusList()
|
||||||
|
})
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
getCorpusList()
|
||||||
|
})
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
if (timer.value) clearInterval(timer.value)
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@ -231,19 +290,6 @@ const statusType = (status) => {
|
|||||||
gap: 10px;
|
gap: 10px;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.toolbar-right {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 8px;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
|
|
||||||
.tab-count {
|
|
||||||
margin-left: 4px;
|
|
||||||
color: #94a3b8;
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.pagination-wrapper {
|
.pagination-wrapper {
|
||||||
@ -261,4 +307,25 @@ const statusType = (status) => {
|
|||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.wave-container {
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 16px;
|
||||||
|
min-height: 140px;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.wave {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-end;
|
||||||
|
height: 80px;
|
||||||
|
gap: 4px;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.audio-container {
|
||||||
|
width: 100%;
|
||||||
|
margin: 10px 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
@ -9,16 +9,23 @@
|
|||||||
<CategoryTree @select-category="onSelectCategory" />
|
<CategoryTree @select-category="onSelectCategory" />
|
||||||
</div>
|
</div>
|
||||||
<div class="right-panel">
|
<div class="right-panel">
|
||||||
<CorpusList @view-detail="onViewDetail" />
|
<CorpusList :categoryId="categoryId" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script setup>
|
<script setup>
|
||||||
|
import { ref } from 'vue'
|
||||||
import StatCards from './components/StatCards.vue'
|
import StatCards from './components/StatCards.vue'
|
||||||
import CategoryTree from './components/CategoryTree.vue'
|
import CategoryTree from './components/CategoryTree.vue'
|
||||||
import CorpusList from './components/CorpusList.vue'
|
import CorpusList from './components/CorpusList.vue'
|
||||||
|
|
||||||
|
const categoryId = ref('')
|
||||||
|
|
||||||
|
const onSelectCategory = (node) => {
|
||||||
|
categoryId.value = node.id
|
||||||
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.page-container {
|
.page-container {
|
||||||
|
|||||||
@ -46,7 +46,7 @@
|
|||||||
</el-dropdown-item>
|
</el-dropdown-item>
|
||||||
<el-dropdown-item>
|
<el-dropdown-item>
|
||||||
<el-button icon="Collection" link type="primary"
|
<el-button icon="Collection" link type="primary"
|
||||||
@click="handleSave(scope.row)">保存</el-button>
|
@click="handleSave(scope.row.id)">保存</el-button>
|
||||||
</el-dropdown-item>
|
</el-dropdown-item>
|
||||||
<el-dropdown-item>
|
<el-dropdown-item>
|
||||||
<el-button icon="Refresh" link type="primary"
|
<el-button icon="Refresh" link type="primary"
|
||||||
@ -81,28 +81,35 @@
|
|||||||
</div>
|
</div>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
|
||||||
<!-- <el-dialog v-model="corpusDialogVisible" title="保存" :append-to-body="true" width="600px" :destroy-on-close="true" @close="colseDialog">
|
<el-dialog v-model="corpusDialogVisible" title="保存到语料库" :append-to-body="true" width="400px" @close="colseDialog">
|
||||||
<div class="wave-container">
|
<el-form ref="formRef" :model="form" label-width="auto">
|
||||||
<div class="wave">
|
<el-form-item label="语料分类" :rules="[{ required: true, message: '请选择分类', trigger: 'blur' }]">
|
||||||
<div v-for="(h, index) in waveform" :key="index" :style="{
|
<el-tree-select
|
||||||
width: '4px',
|
v-model="form.categoryId"
|
||||||
height: h + '%',
|
:data="treeData"
|
||||||
background: '#3b82f6',
|
node-key="id"
|
||||||
borderRadius: '2px',
|
:props="{
|
||||||
opacity: 0.6 + (h / 100) * 0.4
|
label: 'categoryName',
|
||||||
}"></div>
|
value: 'id',
|
||||||
</div>
|
children: 'children'
|
||||||
<div class="audio-container">
|
}"
|
||||||
<AudioPlayer :src="audioUrl" @audioStatus="audioStatus" />
|
:render-after-expand="false"
|
||||||
</div>
|
style="width: 240px"
|
||||||
</div>
|
/>
|
||||||
</el-dialog> -->
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<el-button @click="corpusDialogVisible = false">取消</el-button>
|
||||||
|
<el-button type="primary" @click="confirmCorpus">确定</el-button>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, computed, onMounted } from 'vue'
|
import { ref, computed, onMounted } from 'vue'
|
||||||
import { taskListApi, deleteTask, downloadWavApi, regenerateApi } from '@/api/corpusManger/speech_synthesis'
|
import { taskListApi, deleteTask, downloadWavApi, regenerateApi, saveTaskApi } from '@/api/corpusManger/speech_synthesis'
|
||||||
|
import { getCategoryTreeApi } from '@/api/corpusManger/corpus.js'
|
||||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||||
import Download from '@/plugins/download'
|
import Download from '@/plugins/download'
|
||||||
import AudioPlayer from './AudioPlayer.vue';
|
import AudioPlayer from './AudioPlayer.vue';
|
||||||
@ -220,10 +227,6 @@ const handleDownload = async (row) => {
|
|||||||
URL.revokeObjectURL(blobUrl)
|
URL.revokeObjectURL(blobUrl)
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleSave = () => {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
const regenerate = async (id) => {
|
const regenerate = async (id) => {
|
||||||
const res = await regenerateApi(id)
|
const res = await regenerateApi(id)
|
||||||
if (res.code === 200) {
|
if (res.code === 200) {
|
||||||
@ -232,6 +235,38 @@ const regenerate = async (id) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const corpusDialogVisible = ref(false)
|
||||||
|
const formRef = ref()
|
||||||
|
const treeData = ref([])
|
||||||
|
const form = ref({
|
||||||
|
taskId: '',
|
||||||
|
categoryId: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
const handleSave = (taskId) => {
|
||||||
|
corpusDialogVisible.value = true
|
||||||
|
form.value.taskId = taskId
|
||||||
|
getCategoryTree()
|
||||||
|
}
|
||||||
|
|
||||||
|
const getCategoryTree = async () => {
|
||||||
|
const res = await getCategoryTreeApi()
|
||||||
|
if (res.code === 200) {
|
||||||
|
treeData.value = res.data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const confirmCorpus = async () => {
|
||||||
|
const result = await formRef.value.validate()
|
||||||
|
if (result) {
|
||||||
|
const res = await saveTaskApi(form.value)
|
||||||
|
if (res.code === 200) {
|
||||||
|
corpusDialogVisible.value = false
|
||||||
|
ElMessage.success(res.msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
getTaskList()
|
getTaskList()
|
||||||
})
|
})
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user