<template>
<div class="app-container">
<!-- 将声音选择器移到顶部右侧 -->
<div class="header-controls">
<h1>智能PPT生成器</h1>
<div class="voice-selector">
<label for="voiceSelect" class="form-label">选择声音:</label>
<select id="voiceSelect" class="form-select">
<option value="标准">标准</option>
<option value="Trump">Trump</option>
<option value="Trump">Leap</option>
<option value="Trump">ZFQ</option>
</select>
</div>
</div>
<div class="tabs">
<button
:class="{ active: currentTab === 'text' }"
@click="currentTab = 'text'"
>
智能PPT讲解生成器
</button>
<button
:class="{ active: currentTab === 'video' }"
@click="currentTab = 'video'"
>
智能有声PPT生成器
</button>
</div>
<!-- 其余内容保持不变 -->
<!-- 文本处理模块 -->
<div v-if="currentTab === 'text'" class="upload-box">
<p>选择PPT文件 (.pptx)</p>
<input type="file" accept=".pptx" @change="uploadTextFile" ref="textFileInput" />
</div>
<!-- 视频处理模块 -->
<div v-if="currentTab === 'video'" class="upload-box">
<div class="file-group">
<label>第一步:上传PPT文件 (.pptx)</label>
<input type="file" accept=".pptx" @change="uploadPptFile" ref="pptFileInput" />
</div>
<div class="file-group">
<label>第二步:上传讲解脚本 (.txt)</label>
<input type="file" accept=".txt" @change="uploadScriptFile" ref="scriptFileInput" />
</div>
<button @click="startVideoProcessing">开始生成有声PPT</button>
</div>
<div v-if="showStatus" id="status">
<h3>处理进度</h3>
<div class="progress">
<div class="progress-bar" :style="{ width: progress + '%' }"></div>
</div>
<p>{{ statusMessage }}</p>
<a v-if="downloadLink" :href="downloadLink" target="_blank">下载生成结果</a>
</div>
</div>
</template>
<script>
export default {
name: 'PPTGenerator',
data() {
return {
currentTab: 'text',
showStatus: false,
progress: 0,
statusMessage: '',
downloadLink: null,
currentTaskId: null,
pptFile: null,
scriptFile: null
}
},
methods: {
async uploadTextFile(event) {
const file = event.target.files[0]
if (!file) return
const formData = new FormData()
formData.append('file', file)
this.showStatus = true
this.statusMessage = '开始上传文件...'
try {
const response = await fetch('http://localhost:7860/api/text/upload', {
method: 'POST',
body: formData
})
const data = await response.json()
if (data.error) {
this.updateStatus('error', data.error)
return
}
this.currentTaskId = data.task_id
this.checkTextProgress()
} catch (error) {
this.updateStatus('error', '上传失败')
}
},
async startVideoProcessing() {
if (!this.pptFile || !this.scriptFile) {
alert('请同时上传PPT文件和讲解脚本')
return
}
const formData = new FormData()
formData.append('ppt', this.pptFile)
formData.append('script', this.scriptFile)
this.showStatus = true
this.updateProgress(5, '开始上传文件...')
try {
const response = await fetch('http://localhost:7860/api/video/upload', {
method: 'POST',
body: formData
})
const data = await response.json()
if (data.error) {
this.updateStatus('error', data.error)
return
}
this.currentTaskId = data.task_id
this.checkVideoProgress()
} catch (error) {
this.updateStatus('error', error.message)
}
},
async checkTextProgress() {
try {
const response = await fetch(`http://localhost:7860/api/text/status/${this.currentTaskId}`);
const data = await response.json();
this.updateProgress(data.progress, data.message);
if (data.status === "processing") {
setTimeout(this.checkTextProgress, 2000);
} else if (data.status === "completed") {
// 修改下载链接,移除 .txt 后缀
this.downloadLink = `http://localhost:7860/api/text/download/${this.currentTaskId}`;
} else if (data.status === "failed") {
this.updateStatus("error", data.message);
}
} catch (error) {
this.updateStatus("error", "状态查询失败");
}
},
async checkVideoProgress() {
try {
const response = await fetch(`http://localhost:7860/api/video/status/${this.currentTaskId}`);
const data = await response.json();
this.updateProgress(data.progress, data.message);
if (data.status === "processing") {
setTimeout(this.checkVideoProgress, 2000);
} else if (data.status === "completed") {
// 修改下载链接,提供 PPTX 文件
this.downloadLink = `http://localhost:7860/api/video/download/${this.currentTaskId}`;
} else if (data.status === "failed") {
this.updateStatus("error", data.message);
}
} catch (error) {
this.updateStatus("error", "状态查询失败");
}
},
updateProgress(percent, message) {
this.progress = percent
this.statusMessage = message
this.updateProgressBarColor()
},
updateStatus(type, message) {
this.statusMessage = message
this.updateProgressBarColor(type)
},
updateProgressBarColor(type = 'success') {
const progressBar = this.$el.querySelector('.progress-bar')
progressBar.style.backgroundColor = type === 'error' ? '#ff4444' : '#4CAF50'
},
uploadPptFile(event) {
this.pptFile = event.target.files[0]
},
uploadScriptFile(event) {
this.scriptFile = event.target.files[0]
}
}
}
</script>
<style scoped>
.app-container {
max-width: 800px;
margin: 20px auto;
padding: 20px;
font-family: Arial, sans-serif;
}
.header-controls {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.voice-selector {
display: flex;
align-items: center;
gap: 10px;
}
.voice-selector .form-select {
width: 180px;
}
.tabs {
display: flex;
justify-content: center;
margin-bottom: 20px;
}
.tabs button {
padding: 10px 20px;
margin: 0 10px;
border: none;
background: #f8f9fa;
border-radius: 4px;
cursor: pointer;
}
.tabs button.active {
background: #4CAF50;
color: white;
}
.upload-box {
border: 2px dashed #ccc;
padding: 30px;
text-align: center;
margin: 20px 0;
display: grid;
gap: 15px;
}
.progress {
height: 20px;
background: #f0f0f0;
border-radius: 10px;
overflow: hidden;
margin: 10px 0;
}
.progress-bar {
height: 100%;
background: #4CAF50;
transition: width 0.3s ease-in-out;
}
#status {
margin: 20px 0;
padding: 15px;
background: #f8f9fa;
border-radius: 8px;
}
.file-group {
margin: 15px 0;
padding: 10px;
border: 1px solid #eee;
border-radius: 6px;
}
.file-group label {
display: block;
margin-bottom: 8px;
font-weight: bold;
color: #666;
}
a {
text-decoration: none;
color: #fff;
background: #4CAF50;
padding: 10px 20px;
border-radius: 4px;
transition: background 0.3s;
}
a:hover {
background: #45a049;
}
</style>
浙公网安备 33010602011771号