|
@@ -1,373 +1,610 @@
|
|
<template>
|
|
<template>
|
|
<view class="content">
|
|
<view class="content">
|
|
|
|
+ <!-- 步骤引导轮播 -->
|
|
<view class="uni-margin-wrap">
|
|
<view class="uni-margin-wrap">
|
|
- <swiper class="swiper" circular :indicator-dots="true" indicator-color="#DE868F" indicator-active-color="#fff"
|
|
|
|
- :autoplay="true" :interval="5000" :duration="1000">
|
|
|
|
|
|
+ <swiper class="swiper" circular :indicator-dots="true" indicator-color="rgba(255,255,255,0.3)"
|
|
|
|
+ indicator-active-color="#fff" :autoplay="true" :interval="5000" :duration="800">
|
|
<swiper-item>
|
|
<swiper-item>
|
|
- <view class="swiper-item"><text>创建步骤1<br>填写视频名称</text></view>
|
|
|
|
|
|
+ <view class="swiper-item">
|
|
|
|
+ <view class="step-number">1</view>
|
|
|
|
+ <text class="step-text">填写视频名称</text>
|
|
|
|
+ </view>
|
|
</swiper-item>
|
|
</swiper-item>
|
|
<swiper-item>
|
|
<swiper-item>
|
|
- <view class="swiper-item">创建步骤2<br>选择个人形象照片,最好是正面照</view>
|
|
|
|
|
|
+ <view class="swiper-item">
|
|
|
|
+ <view class="step-number">2</view>
|
|
|
|
+ <text class="step-text">选择个人形象照片,最好是正面照</text>
|
|
|
|
+ </view>
|
|
</swiper-item>
|
|
</swiper-item>
|
|
<swiper-item>
|
|
<swiper-item>
|
|
- <view class="swiper-item">创建步骤3<br>选择生成视频内容的方式:<br>若选择文字输入,则输入口播的文字内容及使用音色;<br>若选择语音录入则点击开始录音,录音结束后再点击停止录音,可在下方播放容器试听录音内容;<br>若选择音频选择,则选择本机的音频文件</view>
|
|
|
|
|
|
+ <view class="swiper-item">
|
|
|
|
+ <view class="step-number">3</view>
|
|
|
|
+ <text
|
|
|
|
+ class="step-text">选择生成视频内容的方式:\n若选择文字输入,则输入口播的文字内容及使用音色;\n若选择语音录入则点击开始录音;\n若选择音频选择则选择本机音频文件</text>
|
|
|
|
+ </view>
|
|
</swiper-item>
|
|
</swiper-item>
|
|
</swiper>
|
|
</swiper>
|
|
</view>
|
|
</view>
|
|
- <form class="form-area">
|
|
|
|
- <view class="form-area-item">
|
|
|
|
- <text class="form-area-item-text">视频名称</text>
|
|
|
|
- <input type="text" v-model="videoName" placeholder="请输入视频名称">
|
|
|
|
- </view>
|
|
|
|
- <view class="form-area-item">
|
|
|
|
- <text class="form-area-item-text">选择您的形象</text>
|
|
|
|
- <button @click="chooseImage" class="cu-btn bg-red">选择图片</button>
|
|
|
|
- <text class="none-file" v-if="!imagePath">未选择图片</text>
|
|
|
|
- <image :src="imagePath" v-else mode="aspectFit" style="width: 30px; height: 30px;margin-left:10px" />
|
|
|
|
- </view>
|
|
|
|
- <view class="form-area-item" style="align-items: baseline;">
|
|
|
|
- <text class="form-area-item-text">视频内容</text>
|
|
|
|
- <uni-data-checkbox selectedColor="#BD3124" selectedTextColor="#BD3124" v-model="current"
|
|
|
|
- :localdata="videoContent" />
|
|
|
|
- </view>
|
|
|
|
- <view class="form-area-item" v-if="current=='text'">
|
|
|
|
- <textarea v-model="videoText" placeholder="请输入视频中的文字内容" />
|
|
|
|
- </view>
|
|
|
|
- <view class="form-area-item" v-if="current=='text'">
|
|
|
|
- <text class="form-area-item-text">音色选择</text>
|
|
|
|
- <uni-data-checkbox selectedColor="#BD3124" selectedTextColor="#BD3124" v-model="timbre"
|
|
|
|
- :localdata="timbreContent" />
|
|
|
|
- </view>
|
|
|
|
- <view class="form-area-item" v-if="current=='record'">
|
|
|
|
- <button @click="toggleRecording">{{ isRecording ? '停止录音' : '开始录音' }}</button>
|
|
|
|
- </view>
|
|
|
|
- <view class="form-area-item" v-if="current=='audio'" :key="timeStamp">
|
|
|
|
- <text class="form-area-item-text">选择音频文件</text>
|
|
|
|
- <button @click="chooseFile" class="cu-btn bg-red">选择文件</button>
|
|
|
|
- <text class="file-name" v-if="fileInfo.name">{{fileInfo.name}}</text>
|
|
|
|
- <text class="none-file" v-if="!showPerson">未选择文件</text>
|
|
|
|
|
|
+
|
|
|
|
+ <!-- 表单区域 -->
|
|
|
|
+ <u--form class="form-area" labelPosition="left" :model="formData" ref="uForm" labelWidth="70">
|
|
|
|
+ <view class="form-card">
|
|
|
|
+ <u-form-item lableWidth="100rpx" label="视频名称" prop="formData.videoName" borderBottom ref="videoName">
|
|
|
|
+ <u--input type="text" v-model="formData.videoName" placeholder="请输入视频名称" border="none"></u--input>
|
|
|
|
+ </u-form-item>
|
|
|
|
+ <u-form-item lableWidth="100rpx" label="上传形象" prop="formData.videoName" borderBottom ref="videoName">
|
|
|
|
+ <upload-component accept="image" :max-count="1" @file-updated="handleFileUpdated" />
|
|
|
|
+ </u-form-item>
|
|
|
|
+ <u-form-item @click="openPicker" :label="currentType" labelPosition="top" borderBottom>
|
|
|
|
+ <u-picker :show="show" :columns="columns" keyName="label" closeOnClickOverlay="true"
|
|
|
|
+ immediateChange="true" @confirm="confirmPicker"></u-picker>
|
|
|
|
+ </u-form-item>
|
|
|
|
+ <u-form-item >
|
|
|
|
+ <u--textarea v-if="formData.current == 'text'" v-model="formData.txtCount" placeholder="请输入视频中的文字内容" class="text-area" />
|
|
|
|
+ <view class="form-area-item input-panel" v-if="formData.current == 'record'">
|
|
|
|
+ <button @click="toggleRecording" class="record-btn" :class="{ recording: isRecording }">
|
|
|
|
+ {{ isRecording ? '停止录音' : '开始录音' }}
|
|
|
|
+ </button>
|
|
|
|
+ <view class="record-status" v-if="isRecording">
|
|
|
|
+ <view class="audio-wave">
|
|
|
|
+ <view class="wave-bar" :style="{ height: '30%' }"></view>
|
|
|
|
+ <view class="wave-bar" :style="{ height: '70%' }"></view>
|
|
|
|
+ <view class="wave-bar" :style="{ height: '50%' }"></view>
|
|
|
|
+ </view>
|
|
|
|
+ <text class="status-text">录音中...</text>
|
|
|
|
+ </view>
|
|
|
|
+ </view>
|
|
|
|
+
|
|
|
|
+ <!-- 音频选择区域 -->
|
|
|
|
+ <view class="form-area-item input-panel" v-if="formData.current == 'audio'" :key="timeStamp">
|
|
|
|
+ <upload-component accept="file" :max-count="1" @file-updated="handleFileUpdatedWav" />
|
|
|
|
+ </view>
|
|
|
|
+ <!-- 音频预览 -->
|
|
|
|
+ <audio v-if="audioPath" class="audio-bar" :src="audioPath" name="语音试听"
|
|
|
|
+ poster="../static/images/xijiao/avator.png" :action="audioAction" controls>
|
|
|
|
+ </audio>
|
|
|
|
+ </u-form-item>
|
|
|
|
+ <u-form-item @click="openTimbrePicker" :label="timbreType" labelPosition="top" borderBottom>
|
|
|
|
+ <u-picker :show="timbreShow" :columns="timbreContent" keyName="text" closeOnClickOverlay="true"
|
|
|
|
+ immediateChange="true" @confirm="confirmTimbrePicker"></u-picker>
|
|
|
|
+ </u-form-item>
|
|
</view>
|
|
</view>
|
|
- <audio v-if="audioPath" class="audio-bar" :src="audioPath" name="语音试听" poster="../static/images/xijiao/avator.png"
|
|
|
|
- :action="audioAction" controls></audio>
|
|
|
|
- </form>
|
|
|
|
|
|
+ </u--form>
|
|
|
|
+
|
|
|
|
+ <!-- 生成按钮 -->
|
|
<view class="btn-area">
|
|
<view class="btn-area">
|
|
- <button @click="jumpToList" class="bg-red lg round create-project">生成数字人视频</button>
|
|
|
|
- <uni-popup ref="alertDialog" type="dialog">
|
|
|
|
- <uni-popup-dialog type="warning" cancelText="确定" title="提示" :content="missingContent"
|
|
|
|
- @close="dialogClose"></uni-popup-dialog>
|
|
|
|
- </uni-popup>
|
|
|
|
|
|
+ <u-button @click="jumpToList" class="create-project" :hover-class="button - hover">
|
|
|
|
+ 生成数字人视频
|
|
|
|
+ </u-button>
|
|
</view>
|
|
</view>
|
|
|
|
+
|
|
|
|
+ <!-- 提示弹窗 -->
|
|
|
|
+ <uni-popup ref="alertDialog" type="dialog">
|
|
|
|
+ <uni-popup-dialog type="warning" cancelText="确定" title="提示" :content="missingContent" @close="dialogClose"
|
|
|
|
+ :style="{
|
|
|
|
+ '--uni-dialog-border-radius': '12rpx',
|
|
|
|
+ '--uni-dialog-title-color': '#BD3124'
|
|
|
|
+ }"></uni-popup-dialog>
|
|
|
|
+ </uni-popup>
|
|
|
|
+
|
|
</view>
|
|
</view>
|
|
</template>
|
|
</template>
|
|
|
|
+
|
|
<script>
|
|
<script>
|
|
- import {
|
|
|
|
- uploadPerson,
|
|
|
|
- audioCreateVideo,
|
|
|
|
- textCreateVideo
|
|
|
|
- } from "@/api/system/user"
|
|
|
|
- import store from '@/store'
|
|
|
|
- const recorderManager = uni.getRecorderManager();
|
|
|
|
- const innerAudioContext = uni.createInnerAudioContext();
|
|
|
|
- innerAudioContext.autoplay = true;
|
|
|
|
- export default {
|
|
|
|
- data() {
|
|
|
|
- return {
|
|
|
|
- videoName: '',
|
|
|
|
- fileInfo: {},
|
|
|
|
- videoContent: [{
|
|
|
|
- value: 'text',
|
|
|
|
- text: '文字输入'
|
|
|
|
- },
|
|
|
|
- {
|
|
|
|
- value: 'record',
|
|
|
|
- text: '语音录入'
|
|
|
|
- },
|
|
|
|
- {
|
|
|
|
- value: 'audio',
|
|
|
|
- text: '音频选择'
|
|
|
|
- },
|
|
|
|
- ],
|
|
|
|
- current: 'text',
|
|
|
|
- imagePath: '',
|
|
|
|
- isRecording: false,
|
|
|
|
- audioPath: null,
|
|
|
|
- recorderManager: null,
|
|
|
|
- innerAudioContext: null,
|
|
|
|
- showPerson: false,
|
|
|
|
- timeStamp: null,
|
|
|
|
- audioAction: {
|
|
|
|
- method: 'pause'
|
|
|
|
- },
|
|
|
|
- videoText: '',
|
|
|
|
- missingContent: [],
|
|
|
|
- timbre: 'a',
|
|
|
|
- timbreContent: [{
|
|
|
|
- value: 'a',
|
|
|
|
- text: 'timbre1'
|
|
|
|
- },
|
|
|
|
- {
|
|
|
|
- value: 'b',
|
|
|
|
- text: 'timbre2'
|
|
|
|
- }
|
|
|
|
- ],
|
|
|
|
- personPath: '',
|
|
|
|
- createVideoPath: '',
|
|
|
|
|
|
+import UploadComponent from './common/fileupload/index.vue';
|
|
|
|
+import { uploadPerson, audioCreateVideo, textCreateVideo } from "@/api/system/user"
|
|
|
|
+import store from '@/store'
|
|
|
|
+const recorderManager = uni.getRecorderManager();
|
|
|
|
+const innerAudioContext = uni.createInnerAudioContext();
|
|
|
|
+innerAudioContext.autoplay = true;
|
|
|
|
+
|
|
|
|
+export default {
|
|
|
|
+ components: {
|
|
|
|
+ UploadComponent
|
|
|
|
+ },
|
|
|
|
+ data() {
|
|
|
|
+ return {
|
|
|
|
+ timbreType: '选择音色',
|
|
|
|
+ currentType: '选择内容',
|
|
|
|
+ timbreShow: false,
|
|
|
|
+ show: false,
|
|
|
|
+ columns: [[
|
|
|
|
+ { id: 'text', label: '文字输入' },
|
|
|
|
+ { id: 'record', label: '语音录入' },
|
|
|
|
+ { id: 'audio', label: '音频选择' }]
|
|
|
|
+ ],
|
|
|
|
+ // 表单数据
|
|
|
|
+ formData: {
|
|
|
|
+ current: '', // 生成任务类型(text/record/audio)
|
|
|
|
+ videoName: '', // 视频名称
|
|
|
|
+ imagefile: '', // 形象文件路径
|
|
|
|
+ audioFile: '', // 音频文件路径
|
|
|
|
+ txtCount: '', // 文字内容
|
|
|
|
+ timbre: '' // 音色选择
|
|
|
|
+ },
|
|
|
|
+ // 音色选项(修复原数据value重复问题)
|
|
|
|
+ timbreContent: [[
|
|
|
|
+ { value: '1', text: '青年男' },
|
|
|
|
+ { value: '2', text: '青年女' },
|
|
|
|
+ { value: '3', text: '中年男' },
|
|
|
|
+ { value: '4', text: '中年女' }
|
|
|
|
+ ]],
|
|
|
|
+ imagePath: '', // 形象图片本地路径
|
|
|
|
+ isRecording: false, // 录音状态
|
|
|
|
+ audioPath: null, // 录音/音频本地路径
|
|
|
|
+ recorderManager: null,
|
|
|
|
+ innerAudioContext: null,
|
|
|
|
+ showPerson: false, // 是否显示音频文件
|
|
|
|
+ timeStamp: null, // 音频文件时间戳(用于刷新)
|
|
|
|
+ audioAction: { method: 'pause' },
|
|
|
|
+ fileInfo: {}, // 音频文件信息
|
|
|
|
+ missingContent: '', // 缺失项提示文本
|
|
|
|
+ personPath: '', // 人物形象路径
|
|
|
|
+ createVideoPath: '' // 生成的视频路径
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+ onLoad() {
|
|
|
|
+ // 初始化录音管理器
|
|
|
|
+ this.recorderManager = uni.getRecorderManager();
|
|
|
|
+ this.innerAudioContext = uni.createInnerAudioContext();
|
|
|
|
+
|
|
|
|
+ // 监听录音停止
|
|
|
|
+ this.recorderManager.onStop((res) => {
|
|
|
|
+ console.log('录音结束,临时路径:', res.tempFilePath);
|
|
|
|
+ this.audioPath = res.tempFilePath;
|
|
|
|
+ this.isRecording = false;
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ // 监听录音错误
|
|
|
|
+ this.recorderManager.onError((err) => {
|
|
|
|
+ uni.showToast({ title: '录音出错', icon: 'none' });
|
|
|
|
+ });
|
|
|
|
+ },
|
|
|
|
+ onShow() {
|
|
|
|
+ // 页面显示时加载本地存储的音频文件
|
|
|
|
+ this.timeStamp = new Date().getTime();
|
|
|
|
+ this.fileInfo.path = uni.getStorageSync('filePath');
|
|
|
|
+ this.fileInfo.name = uni.getStorageSync('fileName');
|
|
|
|
+ if (this.fileInfo.name) {
|
|
|
|
+ this.showPerson = true;
|
|
|
|
+ this.uploadAudioPath(this.fileInfo.path);
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+ methods: {
|
|
|
|
+ handleFileUpdatedWav(data){
|
|
|
|
+ this.formData.audioFile = data.url
|
|
|
|
+ },
|
|
|
|
+ openTimbrePicker() {
|
|
|
|
+ this.timbreShow = true;
|
|
|
|
+ },
|
|
|
|
+ // 音色选择
|
|
|
|
+ confirmTimbrePicker(data) {
|
|
|
|
+ console.log('选择结果:', data.value[0]);
|
|
|
|
+ this.formData.timbre = data.value[0].value
|
|
|
|
+ this.timbreType = data.value[0].text
|
|
|
|
+ this.timbreShow = false;
|
|
|
|
+ },
|
|
|
|
+ openPicker() {
|
|
|
|
+ this.show = true;
|
|
|
|
+ },
|
|
|
|
+ confirmPicker(data) {
|
|
|
|
+ console.log('选择结果:', data.value[0]);
|
|
|
|
+ this.formData.current = data.value[0].id
|
|
|
|
+ this.currentType = data.value[0].label
|
|
|
|
+ this.show = false;
|
|
|
|
+ },
|
|
|
|
+ handleFileUpdated(data) {
|
|
|
|
+ this.formData.imagefile = data.url
|
|
|
|
+ },
|
|
|
|
+ // 选择音频文件(跳转文件列表)
|
|
|
|
+ chooseFile() {
|
|
|
|
+ if (!this.imagePath) {
|
|
|
|
+ uni.showToast({ title: '请先选择形象照片', icon: 'none' });
|
|
|
|
+ return;
|
|
}
|
|
}
|
|
|
|
+ uni.navigateTo({ url: "/pages/root-filelist/root-filelist" });
|
|
},
|
|
},
|
|
- onLoad() {
|
|
|
|
- this.recorderManager = uni.getRecorderManager();
|
|
|
|
- this.innerAudioContext = uni.createInnerAudioContext();
|
|
|
|
-
|
|
|
|
- // 监听录音停止
|
|
|
|
- this.recorderManager.onStop((res) => {
|
|
|
|
- console.log('录音结束,临时路径:', res.tempFilePath);
|
|
|
|
- this.audioPath = res.tempFilePath; // 替换为新录音路径
|
|
|
|
- this.isRecording = false;
|
|
|
|
- });
|
|
|
|
|
|
|
|
- // 监听录音错误
|
|
|
|
- this.recorderManager.onError((err) => {
|
|
|
|
- uni.showToast({
|
|
|
|
- title: '录音出错',
|
|
|
|
- icon: 'none'
|
|
|
|
|
|
+ // 录音控制(开始/停止)
|
|
|
|
+ toggleRecording() {
|
|
|
|
+ if (this.isRecording) {
|
|
|
|
+ // 停止录音并上传
|
|
|
|
+ this.recorderManager.stop();
|
|
|
|
+ this.uploadAudioPath(this.audioPath);
|
|
|
|
+ } else {
|
|
|
|
+ // 开始录音(清空已有录音)
|
|
|
|
+ if (this.audioPath) this.audioPath = null;
|
|
|
|
+ this.recorderManager.start({
|
|
|
|
+ duration: 600000, // 最大录音时间(10分钟)
|
|
|
|
+ sampleRate: 44100,
|
|
|
|
+ numberOfChannels: 1,
|
|
|
|
+ encodeBitRate: 192000,
|
|
|
|
+ format: 'wav'
|
|
});
|
|
});
|
|
- });
|
|
|
|
- },
|
|
|
|
- onShow() {
|
|
|
|
- this.timeStamp = new Date().getTime()
|
|
|
|
- this.fileInfo.path = uni.getStorageSync('filePath');
|
|
|
|
- this.fileInfo.name = uni.getStorageSync('fileName');
|
|
|
|
- if (this.fileInfo.name) {
|
|
|
|
- this.showPerson = true
|
|
|
|
- this.uploadAudioPath(this.fileInfo.path)
|
|
|
|
|
|
+ this.isRecording = true;
|
|
}
|
|
}
|
|
},
|
|
},
|
|
- methods: {
|
|
|
|
- // 选择音频文件
|
|
|
|
- chooseFile() {
|
|
|
|
- if(!this.imagePath) {
|
|
|
|
- uni.showToast({
|
|
|
|
- title: '请先选择形象照片',
|
|
|
|
- icon: 'none'
|
|
|
|
- });
|
|
|
|
- return
|
|
|
|
- }
|
|
|
|
- uni.navigateTo({
|
|
|
|
- url: "/pages/root-filelist/root-filelist",
|
|
|
|
- });
|
|
|
|
- },
|
|
|
|
- // 选择图片文件
|
|
|
|
- async chooseImage() {
|
|
|
|
- const that = this;
|
|
|
|
- uni.chooseImage({
|
|
|
|
- count: 1,
|
|
|
|
- sizeType: ['original', 'compressed'],
|
|
|
|
- sourceType: ['album', 'camera'],
|
|
|
|
- success: function(res) {
|
|
|
|
- that.imagePath = res.tempFilePaths[0];
|
|
|
|
- that.uploadImagePath()
|
|
|
|
- },
|
|
|
|
- fail: function(err) {
|
|
|
|
- uni.showToast({
|
|
|
|
- title: '图片选择失败',
|
|
|
|
- icon: 'none'
|
|
|
|
- });
|
|
|
|
- }
|
|
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ // 验证表单并提交生成视频
|
|
|
|
+ jumpToList() {
|
|
|
|
+ // 表单验证
|
|
|
|
+ const missingFields = [];
|
|
|
|
+ // if (!this.formData.videoName.trim()) missingFields.push('视频名称');
|
|
|
|
+ // if (!this.formData.imagefile) missingFields.push('人物形象');
|
|
|
|
+
|
|
|
|
+ // // 内容验证(文字/语音至少一项)
|
|
|
|
+ // const hasTextContent = this.formData.txtCount.trim() && this.formData.timbre;
|
|
|
|
+ // const hasVoiceContent = this.audioPath;
|
|
|
|
+ // if (!hasTextContent && !hasVoiceContent) missingFields.push('文字内容或语音输入');
|
|
|
|
+
|
|
|
|
+ // // 显示缺失提示
|
|
|
|
+ // if (missingFields.length > 0) {
|
|
|
|
+ // this.missingContent = `请完善以下信息:${missingFields.join('、')}`;
|
|
|
|
+ // this.$refs.alertDialog.open();
|
|
|
|
+ // return;
|
|
|
|
+ // }
|
|
|
|
+
|
|
|
|
+ // 跳转生成页面
|
|
|
|
+ uni.navigateTo({ url: 'list/creatingVideo/creatingVideo' });
|
|
|
|
+
|
|
|
|
+ // 调用生成接口
|
|
|
|
+ if (this.formData.current === 'record' || this.formData.current === 'audio') {
|
|
|
|
+ // 音频生成视频
|
|
|
|
+ audioCreateVideo(this.formData).then(response => {
|
|
|
|
+ if (response.status === 200) this.createVideoPath = response.data;
|
|
|
|
+ this.openWebsocket();
|
|
|
|
+ }).catch(() => {
|
|
|
|
+ uni.showToast({ title: '视频生成请求已提交', icon: 'none' });
|
|
});
|
|
});
|
|
- },
|
|
|
|
- // 开始/停止录音
|
|
|
|
- toggleRecording() {
|
|
|
|
- if (this.isRecording) {
|
|
|
|
- this.recorderManager.stop();
|
|
|
|
- this.uploadAudioPath(this.audioPath)
|
|
|
|
- } else {
|
|
|
|
- // 开始录音前,如果已有录音则清空
|
|
|
|
- if (this.audioPath) {
|
|
|
|
- this.audioPath = null;
|
|
|
|
- }
|
|
|
|
- // 开始录音
|
|
|
|
- this.recorderManager.start({
|
|
|
|
- duration: 600000, // 最大录音时间(毫秒)
|
|
|
|
- sampleRate: 44100,
|
|
|
|
- numberOfChannels: 1,
|
|
|
|
- encodeBitRate: 192000,
|
|
|
|
- format: 'wav',
|
|
|
|
- });
|
|
|
|
- this.isRecording = true;
|
|
|
|
- }
|
|
|
|
- },
|
|
|
|
- // 上传形象照片
|
|
|
|
- uploadImagePath() {
|
|
|
|
- let data = {
|
|
|
|
- file: this.imagePath
|
|
|
|
- }
|
|
|
|
- uploadPerson(data).then(response => {
|
|
|
|
- this.personPath = response.data.url
|
|
|
|
- })
|
|
|
|
- },
|
|
|
|
- // 上传音频文件
|
|
|
|
- uploadAudioPath(url) {
|
|
|
|
- let data = {
|
|
|
|
- file: url
|
|
|
|
- }
|
|
|
|
- uploadPerson(data).then(response => {
|
|
|
|
- this.personAudio = response.data.url
|
|
|
|
- })
|
|
|
|
- },
|
|
|
|
- // 点击生成视频,跳转生成页面
|
|
|
|
- jumpToList() {
|
|
|
|
- let missingFields = [];
|
|
|
|
- if (!this.videoName.trim()) {
|
|
|
|
- missingFields.push('视频名称');
|
|
|
|
- }
|
|
|
|
- if (!this.imagePath) {
|
|
|
|
- missingFields.push('人物形象');
|
|
|
|
- }
|
|
|
|
- const hasContent = this.videoText.trim() !== '' && this.timbre;
|
|
|
|
- const hasVoice = this.audioPath;
|
|
|
|
- if (!hasContent && !hasVoice) {
|
|
|
|
- missingFields.push('文字内容或语音输入');
|
|
|
|
- }
|
|
|
|
- if (missingFields.length > 0) {
|
|
|
|
- this.missingContent = `请确定:${missingFields.join('、')}是否填写完整!`
|
|
|
|
- this.$refs.alertDialog.open()
|
|
|
|
- return;
|
|
|
|
- }
|
|
|
|
- uni.navigateTo({
|
|
|
|
- url: 'list/creatingVideo/creatingVideo',
|
|
|
|
|
|
+ } else if (this.formData.current === 'text') {
|
|
|
|
+ // 文字生成视频
|
|
|
|
+ textCreateVideo(this.formData).then(response => {
|
|
|
|
+ if (response.status === 200) this.createVideoPath = response.data;
|
|
|
|
+ this.openWebsocket();
|
|
});
|
|
});
|
|
- if (this.current == 'record' || this.current == 'audio') {
|
|
|
|
- let data = {
|
|
|
|
- 'persona_template': this.personPath,
|
|
|
|
- 'persona_audio': this.personAudio,
|
|
|
|
- }
|
|
|
|
- audioCreateVideo(data).then(response => {
|
|
|
|
- if (response.status == 200) {
|
|
|
|
- this.createVideoPath = response.data
|
|
|
|
- }
|
|
|
|
- }).catch(e => {
|
|
|
|
- uni.showToast({
|
|
|
|
- title: '视频返回成功'
|
|
|
|
- })
|
|
|
|
- })
|
|
|
|
- } else if (this.current == 'text') {
|
|
|
|
- let data = {
|
|
|
|
- 'persona_template': this.personPath,
|
|
|
|
- 'voice_type': this.timbre,
|
|
|
|
- 'audio_text': this.videoText,
|
|
|
|
- }
|
|
|
|
- textCreateVideo(data).then(response => {
|
|
|
|
- if (response.status == 200) {
|
|
|
|
- this.createVideoPath = response.data
|
|
|
|
- }
|
|
|
|
- })
|
|
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+ openWebsocket() {
|
|
|
|
+ uni.connectSocket({
|
|
|
|
+ url: 'ws://192.168.0.2:8080/ws',
|
|
|
|
+ header: {
|
|
|
|
+ 'Content-Type': 'application/json'
|
|
|
|
+ },
|
|
|
|
+ method: 'POST',
|
|
|
|
+ data: {
|
|
|
|
+ 'videoId': this.createVideoPath
|
|
|
|
+ },
|
|
|
|
+ success: (res) => {
|
|
|
|
+ console.log('连接成功', res);
|
|
|
|
+ },
|
|
|
|
+ fail: (err) => {
|
|
|
|
+ console.error('连接失败', err);
|
|
}
|
|
}
|
|
- },
|
|
|
|
- dialogClose() {
|
|
|
|
- console.log('点击关闭')
|
|
|
|
- },
|
|
|
|
|
|
+ });
|
|
|
|
+ },
|
|
|
|
+ // 关闭提示弹窗
|
|
|
|
+ dialogClose() {
|
|
|
|
+ console.log('弹窗关闭');
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
+}
|
|
</script>
|
|
</script>
|
|
|
|
+
|
|
<style scoped lang="scss">
|
|
<style scoped lang="scss">
|
|
- .content {
|
|
|
|
- display: flex;
|
|
|
|
- flex-direction: column;
|
|
|
|
|
|
+/* 基础变量定义 */
|
|
|
|
+$primary: #BD3124; // 主色调(红色)
|
|
|
|
+$primary-light: #F8E1E3; // 主色浅色
|
|
|
|
+$text-main: #333; // 主要文本色
|
|
|
|
+$text-secondary: #666; // 次要文本色
|
|
|
|
+$border-radius: 12rpx; // 统一圆角
|
|
|
|
+$shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.05); // 基础阴影
|
|
|
|
+
|
|
|
|
+/* 页面容器 */
|
|
|
|
+.content {
|
|
|
|
+ display: flex;
|
|
|
|
+ flex-direction: column;
|
|
|
|
+ padding: 20rpx;
|
|
|
|
+ background-color: #f9f9f9;
|
|
|
|
+ min-height: 100vh;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/* 轮播步骤区 */
|
|
|
|
+.uni-margin-wrap {
|
|
|
|
+ width: 100%;
|
|
|
|
+ margin-bottom: 30rpx;
|
|
|
|
+ border-radius: $border-radius;
|
|
|
|
+ overflow: hidden;
|
|
|
|
+ box-shadow: $shadow;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.swiper {
|
|
|
|
+ height: 320rpx;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.swiper-item {
|
|
|
|
+ position: relative;
|
|
|
|
+ display: flex;
|
|
|
|
+ flex-direction: column;
|
|
|
|
+ align-items: center;
|
|
|
|
+ justify-content: center;
|
|
|
|
+ height: 100%;
|
|
|
|
+ background: linear-gradient(135deg, $primary, #E65C68); // 渐变背景
|
|
|
|
+ color: #fff;
|
|
|
|
+ padding: 40rpx;
|
|
|
|
+ text-align: center;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.step-number {
|
|
|
|
+ width: 60rpx;
|
|
|
|
+ height: 60rpx;
|
|
|
|
+ background-color: rgba(255, 255, 255, 0.2);
|
|
|
|
+ border-radius: 50%;
|
|
|
|
+ display: flex;
|
|
|
|
+ align-items: center;
|
|
|
|
+ justify-content: center;
|
|
|
|
+ font-size: 30rpx;
|
|
|
|
+ font-weight: bold;
|
|
|
|
+ margin-bottom: 20rpx;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.step-text {
|
|
|
|
+ font-size: 28rpx;
|
|
|
|
+ line-height: 1.6;
|
|
|
|
+ white-space: pre-line; // 支持换行
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/* 表单区域 */
|
|
|
|
+.form-area {
|
|
|
|
+ width: 100%;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.form-card {
|
|
|
|
+ background-color: #fff;
|
|
|
|
+ border-radius: $border-radius;
|
|
|
|
+ padding: 30rpx 20rpx;
|
|
|
|
+ box-shadow: $shadow;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.form-area-item {
|
|
|
|
+ display: flex;
|
|
|
|
+ flex-direction: row;
|
|
|
|
+ align-items: center;
|
|
|
|
+ margin-bottom: 35rpx;
|
|
|
|
+ padding: 0 10rpx;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.form-area-item-text {
|
|
|
|
+ width: 160rpx;
|
|
|
|
+ font-size: 28rpx;
|
|
|
|
+ color: $text-main;
|
|
|
|
+ font-weight: 500;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/* 输入控件样式 */
|
|
|
|
+.form-input {
|
|
|
|
+ flex: 1;
|
|
|
|
+ height: 80rpx;
|
|
|
|
+ line-height: 80rpx;
|
|
|
|
+ padding: 0 20rpx;
|
|
|
|
+ border: 1px solid #eee;
|
|
|
|
+ border-radius: 8rpx;
|
|
|
|
+ font-size: 26rpx;
|
|
|
|
+ transition: all 0.3s;
|
|
|
|
+
|
|
|
|
+ &:focus {
|
|
|
|
+ border-color: $primary;
|
|
|
|
+ box-shadow: 0 0 0 2rpx rgba(189, 49, 36, 0.2);
|
|
}
|
|
}
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.text-area {
|
|
|
|
+ flex: 1;
|
|
|
|
+ min-height: 160rpx;
|
|
|
|
+ padding: 20rpx;
|
|
|
|
+ border: 1px solid #eee;
|
|
|
|
+ border-radius: 8rpx;
|
|
|
|
+ font-size: 26rpx;
|
|
|
|
+ line-height: 1.6;
|
|
|
|
+ resize: none;
|
|
|
|
+ transition: all 0.3s;
|
|
|
|
|
|
- .uni-margin-wrap {
|
|
|
|
- width: 690rpx;
|
|
|
|
- width: 100%;
|
|
|
|
|
|
+ &:focus {
|
|
|
|
+ border-color: $primary;
|
|
|
|
+ box-shadow: 0 0 0 2rpx rgba(189, 49, 36, 0.2);
|
|
}
|
|
}
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/* 按钮样式 */
|
|
|
|
+.btn-primary {
|
|
|
|
+ height: 70rpx;
|
|
|
|
+ line-height: 70rpx;
|
|
|
|
+ padding: 0 25rpx;
|
|
|
|
+ border-radius: 8rpx;
|
|
|
|
+ font-size: 26rpx;
|
|
|
|
+ background-color: $primary;
|
|
|
|
+ color: #fff;
|
|
|
|
+ display: flex;
|
|
|
|
+ align-items: center;
|
|
|
|
+ justify-content: center;
|
|
|
|
+ transition: all 0.2s;
|
|
|
|
|
|
- .swiper {
|
|
|
|
- height: 350rpx;
|
|
|
|
|
|
+ &:hover {
|
|
|
|
+ background-color: #C94030;
|
|
|
|
+ transform: scale(0.98);
|
|
}
|
|
}
|
|
|
|
+}
|
|
|
|
|
|
- .swiper-item {
|
|
|
|
- display: block;
|
|
|
|
- height: 350rpx;
|
|
|
|
- text-align: center;
|
|
|
|
- background-color: #BD3124;
|
|
|
|
|
|
+.record-btn {
|
|
|
|
+ height: 70rpx;
|
|
|
|
+ line-height: 70rpx;
|
|
|
|
+ padding: 0 30rpx;
|
|
|
|
+ border-radius: 8rpx;
|
|
|
|
+ font-size: 26rpx;
|
|
|
|
+ background-color: #f5f5f5;
|
|
|
|
+ color: $text-main;
|
|
|
|
+ transition: all 0.2s;
|
|
|
|
+
|
|
|
|
+ &.recording {
|
|
|
|
+ background-color: $primary;
|
|
color: #fff;
|
|
color: #fff;
|
|
- padding-top:30px;
|
|
|
|
}
|
|
}
|
|
|
|
|
|
- .form-area {
|
|
|
|
- padding: 20px;
|
|
|
|
|
|
+ &:hover {
|
|
|
|
+ transform: scale(0.98);
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
|
|
- .form-area-item {
|
|
|
|
- display: flex;
|
|
|
|
- flex-direction: row;
|
|
|
|
- margin-top: 30px;
|
|
|
|
- align-items: center;
|
|
|
|
|
|
+/* 图片预览 */
|
|
|
|
+.image-preview {
|
|
|
|
+ width: 80rpx;
|
|
|
|
+ height: 80rpx;
|
|
|
|
+ margin-left: 20rpx;
|
|
|
|
+ border-radius: 8rpx;
|
|
|
|
+ overflow: hidden;
|
|
|
|
+ border: 1px solid #eee;
|
|
|
|
+}
|
|
|
|
|
|
- .form-area-item-text {
|
|
|
|
- width: 120px;
|
|
|
|
- }
|
|
|
|
|
|
+.preview-img {
|
|
|
|
+ width: 100%;
|
|
|
|
+ height: 100%;
|
|
|
|
+ object-fit: cover;
|
|
|
|
+}
|
|
|
|
|
|
- /deep/ uni-input {
|
|
|
|
- border: 1px solid #e5e5e5;
|
|
|
|
- border-radius: 4px;
|
|
|
|
- height: 30px;
|
|
|
|
- width: 200px;
|
|
|
|
- }
|
|
|
|
|
|
+/* 录音动画 */
|
|
|
|
+.record-status {
|
|
|
|
+ display: flex;
|
|
|
|
+ align-items: center;
|
|
|
|
+ margin-left: 20rpx;
|
|
|
|
+}
|
|
|
|
|
|
- /deep/ uni-picker {
|
|
|
|
- border-bottom: 1px solid #aaa;
|
|
|
|
- height: 30px;
|
|
|
|
- width: 200px;
|
|
|
|
- display: flex;
|
|
|
|
- align-items: center;
|
|
|
|
- }
|
|
|
|
|
|
+.audio-wave {
|
|
|
|
+ display: flex;
|
|
|
|
+ align-items: center;
|
|
|
|
+ justify-content: center;
|
|
|
|
+ gap: 6rpx;
|
|
|
|
+ height: 40rpx;
|
|
|
|
+}
|
|
|
|
|
|
- /deep/ uni-radio-group,
|
|
|
|
- /deep/ uni-label {
|
|
|
|
- display: flex;
|
|
|
|
- align-items: center;
|
|
|
|
- }
|
|
|
|
|
|
+.wave-bar {
|
|
|
|
+ width: 6rpx;
|
|
|
|
+ background-color: $primary;
|
|
|
|
+ border-radius: 3rpx;
|
|
|
|
+ animation: wave 1s infinite ease-in-out;
|
|
|
|
+}
|
|
|
|
|
|
- /deep/ uni-label {
|
|
|
|
- margin-right: 10px;
|
|
|
|
- }
|
|
|
|
|
|
+.wave-bar:nth-child(2) {
|
|
|
|
+ animation-delay: 0.2s;
|
|
|
|
+}
|
|
|
|
|
|
- /deep/ textarea {
|
|
|
|
- border: 1px solid #aaa;
|
|
|
|
- border-radius: 4px;
|
|
|
|
- width: 320px;
|
|
|
|
- padding: 5px;
|
|
|
|
- margin: 0 auto;
|
|
|
|
- }
|
|
|
|
|
|
+.wave-bar:nth-child(3) {
|
|
|
|
+ animation-delay: 0.4s;
|
|
|
|
+}
|
|
|
|
|
|
- .none-file {
|
|
|
|
- margin-left: 10px;
|
|
|
|
- color: #aaa;
|
|
|
|
- }
|
|
|
|
|
|
+@keyframes wave {
|
|
|
|
|
|
- .file-name {
|
|
|
|
- margin-left: 10px;
|
|
|
|
- width: 50px;
|
|
|
|
- overflow: hidden;
|
|
|
|
- white-space: nowrap;
|
|
|
|
- text-overflow: ellipsis;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
|
|
+ 0%,
|
|
|
|
+ 100% {
|
|
|
|
+ transform: scaleY(0.5);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ 50% {
|
|
|
|
+ transform: scaleY(1);
|
|
}
|
|
}
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.status-text {
|
|
|
|
+ font-size: 24rpx;
|
|
|
|
+ color: $text-secondary;
|
|
|
|
+ margin-left: 10rpx;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/* 音频文件信息 */
|
|
|
|
+.file-info {
|
|
|
|
+ display: flex;
|
|
|
|
+ flex-direction: column;
|
|
|
|
+ margin-left: 20rpx;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.file-name {
|
|
|
|
+ font-size: 24rpx;
|
|
|
|
+ color: $text-main;
|
|
|
|
+ width: 200rpx;
|
|
|
|
+ overflow: hidden;
|
|
|
|
+ white-space: nowrap;
|
|
|
|
+ text-overflow: ellipsis;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.file-type {
|
|
|
|
+ font-size: 22rpx;
|
|
|
|
+ color: #999;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/* 音频播放器 */
|
|
|
|
+.audio-bar {
|
|
|
|
+ width: 100%;
|
|
|
|
+ margin: 20rpx auto;
|
|
|
|
+}
|
|
|
|
|
|
- .btn-area {
|
|
|
|
|
|
+/* 生成按钮 */
|
|
|
|
+.btn-area {
|
|
|
|
+ display: flex;
|
|
|
|
+ justify-content: center;
|
|
|
|
+ margin: 40rpx 0;
|
|
|
|
+ padding: 0 20rpx;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.create-project {
|
|
|
|
+ width: 100%;
|
|
|
|
+ height: 90rpx;
|
|
|
|
+ line-height: 90rpx;
|
|
|
|
+ background-color: $primary;
|
|
|
|
+ color: #fff;
|
|
|
|
+ border-radius: 45rpx;
|
|
|
|
+ font-size: 30rpx;
|
|
|
|
+ font-weight: 500;
|
|
|
|
+ box-shadow: 0 6rpx 12rpx rgba(189, 49, 36, 0.2);
|
|
|
|
+ transition: all 0.3s;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.button-hover {
|
|
|
|
+ transform: translateY(2rpx);
|
|
|
|
+ box-shadow: 0 3rpx 6rpx rgba(189, 49, 36, 0.2);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/* 提示文本 */
|
|
|
|
+.none-file {
|
|
|
|
+ margin-left: 20rpx;
|
|
|
|
+ font-size: 24rpx;
|
|
|
|
+ color: #999;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/* 选项容器 */
|
|
|
|
+.content-type-wrap,
|
|
|
|
+.timbre-wrap {
|
|
|
|
+ flex: 1;
|
|
|
|
+
|
|
|
|
+ /deep/ .uni-data-checkbox {
|
|
display: flex;
|
|
display: flex;
|
|
- justify-content: center;
|
|
|
|
- margin: 20px auto;
|
|
|
|
- width: 100%;
|
|
|
|
|
|
+ flex-wrap: wrap;
|
|
|
|
+ gap: 15rpx 20rpx;
|
|
}
|
|
}
|
|
|
|
|
|
- .audio-bar {
|
|
|
|
- text-align: center;
|
|
|
|
- display: block;
|
|
|
|
- margin-top: 10px;
|
|
|
|
|
|
+ /deep/ .uni-checkbox {
|
|
|
|
+ margin-right: 5rpx;
|
|
}
|
|
}
|
|
|
|
|
|
- /deep/ .uni-border-left {
|
|
|
|
- display: none;
|
|
|
|
|
|
+ /deep/ .uni-checkbox-input.checked {
|
|
|
|
+ background-color: $primary !important;
|
|
|
|
+ border-color: $primary !important;
|
|
}
|
|
}
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/* 功能区块面板 */
|
|
|
|
+.input-panel {
|
|
|
|
+ padding: 20rpx;
|
|
|
|
+ background-color: #fcfcfc;
|
|
|
|
+ border-radius: 8rpx;
|
|
|
|
+ margin-top: 10rpx;
|
|
|
|
+}
|
|
</style>
|
|
</style>
|