feat: 优化
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { message } from 'ant-design-vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { resolveId } from '@/utils/url'
|
||||
import { exportBenchmarkDataToExcel } from '@/utils/excel'
|
||||
import { usePromptStore } from '@/stores/prompt'
|
||||
@@ -16,7 +15,6 @@ import BatchAnalyzeModal from './components/BatchAnalyzeModal.vue'
|
||||
import SavePromptModal from './components/SavePromptModal.vue'
|
||||
|
||||
// ==================== 初始化 ====================
|
||||
const router = useRouter()
|
||||
const promptStore = usePromptStore()
|
||||
|
||||
// ==================== 数据管理 ====================
|
||||
@@ -209,7 +207,7 @@ function handleCopyBatchPrompt(prompt) {
|
||||
// ==================== 创作相关函数 ====================
|
||||
function handleCreateContent(row) {
|
||||
promptStore.setPrompt(row.prompt, row)
|
||||
router.push('/content-style/copywriting')
|
||||
// 路由已移除
|
||||
}
|
||||
|
||||
function handleUseBatchPrompt(prompt) {
|
||||
@@ -219,7 +217,7 @@ function handleUseBatchPrompt(prompt) {
|
||||
}
|
||||
|
||||
promptStore.setPrompt(prompt, { batch: true })
|
||||
router.push('/content-style/copywriting')
|
||||
// 路由已移除
|
||||
}
|
||||
|
||||
// ==================== 保存提示词到服务器 ====================
|
||||
|
||||
@@ -1,146 +0,0 @@
|
||||
<script setup>
|
||||
import { ref, computed, h, watch } from 'vue'
|
||||
import { message } from 'ant-design-vue'
|
||||
import { useVoiceCopyStore } from '@/stores/voiceCopy'
|
||||
|
||||
const store = useVoiceCopyStore()
|
||||
|
||||
const selectedVoiceId = ref(store.activeId)
|
||||
const text = ref('')
|
||||
const speed = ref(1.0)
|
||||
const emotion = ref('neutral')
|
||||
|
||||
const records = ref([]) // 生成记录:{ id, voiceId, voiceName, text, status, url, createdAt }
|
||||
|
||||
const voiceOptions = computed(() => store.profiles.map(p => ({ value: p.id, label: p.name || '未命名' })))
|
||||
const selectedVoice = computed(() => store.profiles.find(p => p.id === selectedVoiceId.value) || null)
|
||||
|
||||
// 监听 store.activeId 变化,同步到 selectedVoiceId
|
||||
watch(() => store.activeId, (newId) => {
|
||||
if (newId && store.profiles.find(p => p.id === newId)) {
|
||||
selectedVoiceId.value = newId
|
||||
}
|
||||
}, { immediate: true })
|
||||
|
||||
function ensureReady() {
|
||||
if (!selectedVoiceId.value) { message.warning('请选择克隆声音'); return false }
|
||||
if (!text.value.trim()) { message.warning('请输入文案'); return false }
|
||||
return true
|
||||
}
|
||||
|
||||
function simulateGenerate() {
|
||||
// 本地模拟:新增记录 → 排队 → 处理中 → 完成
|
||||
const id = `${Date.now()}_${Math.floor(Math.random()*1e5)}`
|
||||
const now = Date.now()
|
||||
const rec = {
|
||||
id,
|
||||
voiceId: selectedVoiceId.value,
|
||||
voiceName: selectedVoice.value?.name || '未命名',
|
||||
text: text.value.trim(),
|
||||
status: 'queued',
|
||||
url: '',
|
||||
createdAt: now,
|
||||
}
|
||||
records.value = [rec, ...records.value]
|
||||
|
||||
setTimeout(() => {
|
||||
updateRecord(id, { status: 'processing' })
|
||||
}, 600)
|
||||
setTimeout(() => {
|
||||
// 模拟成功产出
|
||||
const blob = new Blob([new Uint8Array([1,2,3])], { type: 'audio/mp3' })
|
||||
const url = URL.createObjectURL(blob)
|
||||
updateRecord(id, { status: 'done', url })
|
||||
message.success('生成完成(模拟)')
|
||||
}, 2200)
|
||||
}
|
||||
|
||||
function updateRecord(id, patch) {
|
||||
const idx = records.value.findIndex(r => r.id === id)
|
||||
if (idx !== -1) records.value[idx] = { ...records.value[idx], ...patch }
|
||||
}
|
||||
|
||||
function formatTime(ts) {
|
||||
const d = new Date(ts)
|
||||
const p = (n) => String(n).padStart(2, '0')
|
||||
return `${d.getFullYear()}-${p(d.getMonth()+1)}-${p(d.getDate())} ${p(d.getHours())}:${p(d.getMinutes())}:${p(d.getSeconds())}`
|
||||
}
|
||||
|
||||
function onGenerate() {
|
||||
if (!ensureReady()) return
|
||||
simulateGenerate()
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="vg-page">
|
||||
<div class="vg-grid">
|
||||
<!-- 左侧:配置区 -->
|
||||
<section class="vg-left">
|
||||
<div class="vg-title">配音生成</div>
|
||||
<a-form layout="vertical">
|
||||
<a-form-item label="克隆声音">
|
||||
<a-select v-model:value="selectedVoiceId" :options="voiceOptions" placeholder="请选择已保存的克隆声音" />
|
||||
</a-form-item>
|
||||
<a-form-item label="文案">
|
||||
<a-textarea v-model:value="text" :rows="5" placeholder="输入要合成的文案" />
|
||||
</a-form-item>
|
||||
<div class="vg-row">
|
||||
<a-form-item label="语速" style="flex:1">
|
||||
<a-slider v-model:value="speed" :min="0.5" :max="2" :step="0.1" />
|
||||
</a-form-item>
|
||||
<a-form-item label="情感" style="flex:1">
|
||||
<a-select v-model:value="emotion" :options="[
|
||||
{ value: 'neutral', label: '中性' },
|
||||
{ value: 'happy', label: '开心' },
|
||||
{ value: 'sad', label: '悲伤' },
|
||||
{ value: 'angry', label: '愤怒' }
|
||||
]" />
|
||||
</a-form-item>
|
||||
</div>
|
||||
<a-space>
|
||||
<a-button type="primary" @click="onGenerate">生成</a-button>
|
||||
</a-space>
|
||||
</a-form>
|
||||
</section>
|
||||
|
||||
<!-- 右侧:结果/预览与记录 -->
|
||||
<section class="vg-right">
|
||||
<div class="vg-title">生成记录</div>
|
||||
<template v-if="records.length">
|
||||
<a-table :dataSource="records" :pagination="false" rowKey="id">
|
||||
<a-table-column key="createdAt" title="时间" :customRender="({ record }) => formatTime(record.createdAt)" />
|
||||
<a-table-column key="voiceName" title="声音" dataIndex="voiceName" />
|
||||
<a-table-column key="text" title="文案" :customRender="({ record }) => record.text?.slice(0, 40) + (record.text?.length>40?'...':'')" />
|
||||
<a-table-column key="status" title="状态" :customRender="({ record }) => record.status" />
|
||||
<a-table-column key="action" title="操作"
|
||||
:customRender="({ record }) => record.url ? h('a', { href: record.url, target: '_blank' }, '预览') : h('span', {}, '-')" />
|
||||
</a-table>
|
||||
</template>
|
||||
<a-empty v-else class="vg-empty">
|
||||
<template #description>
|
||||
<div>暂无生成记录,选择声音并输入文案后点击“生成”</div>
|
||||
</template>
|
||||
</a-empty>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<style scoped>
|
||||
.vg-page { color: var(--color-text); }
|
||||
.vg-grid { display: grid; grid-template-columns: 1fr; gap: 16px; }
|
||||
@media (min-width: 1024px) { .vg-grid { grid-template-columns: 1fr 1fr; } }
|
||||
|
||||
.vg-left, .vg-right {
|
||||
background: var(--color-surface);
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--radius-card);
|
||||
box-shadow: var(--shadow-inset-card);
|
||||
padding: 16px;
|
||||
}
|
||||
.vg-title { font-size: 14px; color: var(--color-text-secondary); margin-bottom: 8px; }
|
||||
.vg-row { display: flex; gap: 16px; }
|
||||
</style>
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user