소스 검색

可以生成视频

master
congzc 2 년 전
부모
커밋
5f02a1cc92
11개의 변경된 파일9228개의 추가작업 그리고 9266개의 파일을 삭제
  1. BIN
      dist.zip
  2. +1
    -0
      package.json
  3. +14
    -1
      src/api/editModel.js
  4. +2
    -2
      src/api/myPage.js
  5. +6
    -5
      src/components/editPosition.vue
  6. +10
    -1
      src/styles/index.scss
  7. +5
    -2
      src/views/model/chooseModel.vue
  8. +93
    -23
      src/views/model/editModel.vue
  9. +108
    -54
      src/views/myPage/myPage.vue
  10. +45
    -16
      src/views/videoDownload/videoDownload.vue
  11. +8944
    -9162
      yarn.lock

BIN
dist.zip 파일 보기


+ 1
- 0
package.json 파일 보기

@@ -9,6 +9,7 @@
"dependencies": {
"axios": "0.18.1",
"element-ui": "^2.15.12",
"file-saver": "^2.0.5",
"franc": "^6.1.0",
"install": "^0.13.0",
"lib-flexible": "^0.3.2",


+ 14
- 1
src/api/editModel.js 파일 보기

@@ -3,7 +3,7 @@ import request from '@/utils/request'
// https://smapitest.malls.iformall.com/C/api/

/**
* @description 获取语音列表
* @description 获取语音列表1级
* @returns personMouldId,paperwork
*/
export function voiceList(data) {
@@ -13,6 +13,19 @@ export function voiceList(data) {
data
})
}

/**
* @description 获取语音列表2级
* @returns personMouldId,paperwork
*/
export function voiceList2Api(id) {
return request({
url: `/api/voiceMould/findById?id=${id}`,
method: 'GET',
})
}


/**
* @description 试听MP3
* @returns id


+ 2
- 2
src/api/myPage.js 파일 보기

@@ -5,9 +5,9 @@ import request from '@/utils/request'
* @param {number} pageParams (pageNum, pageSize)
* @returns data
*/
export function getVideoList(pageNum, pageSize,videoType) {
export function getVideoList(pageNum, pageSize,videoType,videoStatuss) {
return request({
url: `/api/userVideo/list?pageNum=${pageNum}&pageSize=${pageSize}&videoType=${videoType}`,
url: `/api/userVideo/list?pageNum=${pageNum}&pageSize=${pageSize}&videoType=${videoType}&videoStatuss=${videoStatuss}`,
method: 'get'
})
}


+ 6
- 5
src/components/editPosition.vue 파일 보기

@@ -19,10 +19,10 @@
class="material"
:style="{
transformOrigin: 'top left',
transform: 'scale(' + item.w + ') ',
transform: 'scale(' + item.w / proportion + ') ',
zIndex: item.z,
top: item.y + 'px',
left: item.x + 'px'
top: item.y / proportion + 'px',
left: item.x / proportion + 'px'
}"
>
<div>{{ item.text }}</div>
@@ -166,8 +166,8 @@ export default {
const deltaX = event.touches[0].clientX - this.startX
const deltaY = event.touches[0].clientY - this.startY
// console.log(deltaX)
this.materialList.scList[index].x += deltaX
this.materialList.scList[index].y += deltaY
this.materialList.scList[index].x += deltaX.toFixed(0) * this.proportion
this.materialList.scList[index].y += deltaY.toFixed(0) * this.proportion

this.startX = event.touches[0].clientX
this.startY = event.touches[0].clientY
@@ -219,6 +219,7 @@ export default {
// 鼠标抬起
onMouseUp1() {
this.dragging = false
this.startDistance = 0
},
// 鼠标在父盒子移动时,判断是否按下,然后再走逻辑
onMouseMove1() {


+ 10
- 1
src/styles/index.scss 파일 보기

@@ -1,5 +1,5 @@
.floatRight {
float: right;
// float: right;
}
// 淡化文字
.desalinate {
@@ -29,4 +29,13 @@ p {
}
.relative {
position: relative;
}
.wordColor {
color: #51d9ca;
}
.displayFlex {
display: flex;
justify-content: space-between;
align-items: center;
}

+ 5
- 2
src/views/model/chooseModel.vue 파일 보기

@@ -17,7 +17,7 @@
</van-tabs>

<!-- 模板列表 -->
<div class="choose">
<div class="choose" v-if="showModeList">
<waterfall class="modeList" :col="2" :data="modeList">
<div
class="model"
@@ -65,7 +65,8 @@ export default {
type: 0,
currentId: '',
modeList: [], // 模板列表
tabList: ['全部', '男性', '女性']
tabList: ['全部', '男性', '女性'],
showModeList: true
}
},
computed: {
@@ -107,7 +108,9 @@ export default {
try {
sex = sex == 0 ? '' : sex
// const res = await getModeList(sex)
this.showModeList = false
const res = await getModeList1(sex)
this.showModeList = true
console.log(res, '获取模板列表')
this.modeList = res.data.list
} catch (error) {


+ 93
- 23
src/views/model/editModel.vue 파일 보기

@@ -59,7 +59,7 @@
<div class="addText-title">
播报声音<span class="redWord">(必选)</span>
</div>
<div>英语_女性_安娜</div>
<div>{{ soundChoose.subTitle }}</div>
</div>
<!-- 合成按钮 -->
<div class="makeVideo" @click="submit">立即制作</div>
@@ -170,6 +170,13 @@
<!-- 展示页 -->
<div class="list">
<div v-if="textareaContent.length == 0">请输入文案</div>
<div
v-if="
soundItemList.length == 0 && textareaContent.length != 0
"
>
文字过少,难以识别
</div>
<template v-if="soundItemList.length != 0">
<div
@click="clickSoundItem(index, item)"
@@ -189,6 +196,30 @@
</div>
</van-tab>
</van-tabs>

<!-- 二级列表显示 -->
<van-popup
class="SoundList2"
v-model="showSoundList2"
position="bottom"
:style="{ width: '100%' }"
>
<div class="list">
<div v-if="soundItemList2.length == 0">暂无数据</div>
<template v-if="soundItemList.length != 0">
<div
@click="clickSoundItem2(index, item)"
class="listItem"
:class="{ active: soundItemListAction2 == index }"
v-for="(item, index) in soundItemList2"
:key="index"
>
<img src="../../assets/icon/icon-earphone.png" alt="" />
<div>{{ item.title }}-{{ item.speakTypeStr }}</div>
</div>
</template>
</div></van-popup
>
</div>
</div>

@@ -376,6 +407,7 @@ import {
import {
videoDetial,
voiceList,
voiceList2Api,
systemBGIListApi,
myBGIListApi,
systemScListApi,
@@ -411,15 +443,20 @@ export default {
saveID: null, // 作品id
inputTitle: '', // 作品标题
textareaContent: '', // 输入框内容
//底部
bottomTabActive: 2, // 底部导航高亮
showSubtitle: false, // 是否展示字幕
showSynthesisDialog: false, //合成进度条窗口显示隐藏
showTabSheet: false, //tab栏弹出框显隐
showSoundList2: false, //声音的二级显隐
soundTabList: ['全部'], // 声音tab栏标题
soundListAction: -1, //声音tab栏标题高亮
soundItemList: [], // 声音展示列表
soundItemList: [], // 声音展示一级列表
soundItemList2: [], // 声音展示二级列表
soundItemListAction: -1, // 声音展示列表高亮
soundItemListAction2: -1, // 声音展示列表高亮二级
soundChoose: {}, //选中的音频
//编辑
bjAction: 0, // 背景:素材库和上传高亮
bjImgAction: -1, // 选中背景图高亮
scAction: 0, // 素材:素材库和上传高亮
@@ -446,6 +483,7 @@ export default {
this.bottomTabActive = index
if (this.textareaContent.length == 0) {
this.soundItemList = [] //清空处理
this.soundItemList2 = [] //清空处理
}
console.log(this.bottomTabActive)
this.showTabSheet = !this.showTabSheet
@@ -478,16 +516,44 @@ export default {
// 点击声音列表中的某一项
async clickSoundItem(index, item) {
if (this.soundItemListAction != index) {
this.soundItemListAction = index
this.soundItemListAction = index //高亮
this.soundChoose = item

this.showSoundList2 = true
const res = await voiceList2Api(item.id)

let list = res.data.subVoiceMould ? res.data.subVoiceMould : []
console.log(list)
let obj = JSON.parse(JSON.stringify(res))
delete obj.data.subVoiceMould
let list2 = [{ ...obj.data }, ...list]

console.log(list2)

this.soundItemList2 = list2 || []

// console.log(this.soundItemList2)
// this.audioUrl = res.data.videoUrl
} else {
this.soundItemListAction = -1
this.soundChoose = {}
}
},
// 点击声音列表中的某一项2级
async clickSoundItem2(index, item) {
if (this.soundItemListAction2 != index) {
this.soundItemListAction2 = index //高亮
this.soundChoose = item
// 得到试听文件
const res = await videoDetial(item.exampleVideoId)

this.audioUrl = res.data.videoUrl
} else {
this.soundItemListAction = -1
this.soundItemListAction2 = -1
this.soundChoose = {}
}
},

// 提交合成视频
submit() {
// 组装参数
@@ -600,8 +666,10 @@ export default {
const url = res.data.videoPathUri + res.data.videoPath
console.log(url, 'url')
this.$router.push({
path: '/downloadVideo',
// path: '/downloadVideo',
path: '/videoDownload',
query: {
id: this.$route.query.id,
videoId: res.data.videoId,
videoUrl: url
}
@@ -640,8 +708,7 @@ export default {
h: 1
}
}
// console.log(JSON.parse(res.data.backgroundSm))
// console.log(JSON.parse(res.data.materialAllJson))

this.videoType = res.data.videoType
// this.imgUrl = res.data.personMould.coverImg
this.personMouldId = res.data.personMouldId
@@ -651,20 +718,6 @@ export default {
this.soundChoose.id = res.data.voiceMouldId
this.soundChoose.mouldSmId = res.data.voiceMouldSmId

// {
// "videoType": 1:竖版2:横版
// "personMouldId":选择数字人id
// "personMouldSm":数字人json(title-数字人标题,mouldSmId-数字人模板编码,material-数字人图片,backgroundId-默认背景id,backgroundMaterial-默认背景图片),
// "voiceMouldId":声音模板id
// "voiceMouldSm":声音模板json(title-声音标题,mouldSmId-声音模板编码,personId-声音风格id,speakId-声音性格ID),
// "backgroundId":背景ID
// "backgroundSm":背景json(material-背景图片)
// "materialIds":素材ids
// "materialAllJson":素材+数字人层级关系json()
// "paperwork":文案
// "title":标题
// }

await this.loadVoice() // 获取音频列表
this.soundItemListAction = this.soundItemList.findIndex((item) => {
return item.id == this.soundChoose.id
@@ -865,11 +918,12 @@ export default {

const elementWidth = document.getElementById('editPosition').clientWidth
console.log(elementWidth)
// 根据横竖屏来确定比例 1竖2横
if (this.videoType == 1) {
this.proportion = 1920 / elementWidth
this.proportion = 1080 / elementWidth
console.log(this.proportion)
} else if (this.videoType == 2) {
this.proportion = 1080 / elementWidth
this.proportion = 1920 / elementWidth
console.log(this.proportion)
}
}
@@ -1209,6 +1263,22 @@ export default {
}
}

.SoundList2 {
height: 280px;
padding: 10px 10px 10px;
.list {
height: 280px;
margin-top: 0px;
}
.list::-webkit-scrollbar {
width: 0;
display: none;
}
}
.SoundList2::-webkit-scrollbar {
width: 0;
display: none;
}
.myTab {
display: flex;
margin: 10px 0;


+ 108
- 54
src/views/myPage/myPage.vue 파일 보기

@@ -7,9 +7,11 @@
<!-- 展示列表 -->
<div class="item">
<div class="title">
<img src="../../assets/img/myVideo.png" alt="" />
我的视频作品
<span
<div>
<img src="../../assets/img/myVideo.png" alt="" />
我的视频作品
</div>
<!-- <span
v-show="listActive == 1"
class="floatRight"
@click="listActive = 2"
@@ -19,54 +21,88 @@
v-show="listActive == 2"
class="floatRight"
><img src="../../assets/icon/icon-list1.png" alt=""
/></span>
<span class="floatRight" @click="getNewList(1)">只看竖屏</span>
<span class="floatRight" @click="getNewList(2)">只看横屏</span>
/></span> -->
<div class="title-change">
<div class="displayFlex">
<span
v-show="listActive == 1"
class="floatRight"
:class="videoType == 1 ? 'wordColor' : ''"
@click="getNewList(1)"
>只看竖屏</span
>
<span
v-show="listActive == 1"
class="floatRight"
:class="videoType == 2 ? 'wordColor' : ''"
@click="getNewList(2)"
>只看横屏</span
>
<span
v-show="listActive == 1"
class="floatRight"
@click="draftChange(2)"
>草稿箱</span
><span
@click="draftChange(1)"
v-show="listActive == 2"
class="floatRight"
>已完成</span
>
</div>
</div>
</div>
<!-- 具体内容 -->
<!-- 列表样式--大图 -->
<div v-if="videoList.length == 0">暂无数据123</div>
<div v-if="videoList.length == 0">暂无数据</div>

<!-- 新 修改v-if-->
<waterfall
v-if="videoList.length != 0"
v-show="listActive == 1"
class="waterFall"
:col="2"
:data="videoList"
>
<div
v-for="(item, index) in videoList"
class="waterFall-item"
:key="item.id ? item.id : index"
@click="goDetail(item)"
<div v-if="waterfallBug">
<waterfall
v-if="videoList.length != 0"
v-show="listActive == 1"
class="waterFall"
:col="2"
:data="videoList"
>
<div class="waterFall-item-paddding">
<img :src="index == 1 ? defaultImg : defaultImg1" alt="" />
<!-- 渐变遮罩层 -->
<div class="waterFall-item-mask">
<div>{{ item.title || '暂无' }}</div>
<div>视频时长:{{ formatSeconds(item.videoTime) }}</div>
<div>视频容量:{{ bytesToSize(item.videoSize) }}</div>
<div>
创建时间:{{
changeTime(item.createDate, 'YYYY-MM-DD hh:mm:ss')
}}
<div
v-for="(item, index) in videoList"
class="waterFall-item"
:key="item.id ? item.id : index"
@click="goDetail(item)"
>
<div class="waterFall-item-paddding">
<img
:src="item.videoType == 2 ? defaultImg : defaultImg1"
alt=""
/>
<!-- 渐变遮罩层 -->
<div class="waterFall-item-mask">
<div>{{ item.title || '暂无' }}</div>
<div>视频时长:{{ formatSeconds(item.videoTime) }}</div>
<div>视频容量:{{ bytesToSize(item.videoSize) }}</div>
<div>
创建时间:{{
changeTime(item.createDate, 'YYYY-MM-DD hh:mm:ss')
}}
</div>
</div>
<!-- 视频状态 草稿还是未完成还是失败 -->
<div class="waterFall-item-state" v-if="item.videoStatus == 2">
<div v-if="item.videoStatus == 0" class="cover">草稿</div>
<div v-if="item.videoStatus == 1" class="cover">视频生成中</div>
<div v-if="item.videoStatus == 3" class="cover">
视频生成失败
</div>
</div>
<!-- 删除 -->
<div class="waterFall-item-del" @click.stop="goDelete(item.id)">
<img src="../../assets/icon/icon-trash.png" alt="" />
</div>
</div>
<!-- 视频状态 草稿还是未完成还是失败 -->
<div class="waterFall-item-state">
<div v-if="item.videoStatus == 0" class="cover">草稿</div>
<div v-if="item.videoStatus == 1" class="cover">视频生成中</div>
<div v-if="item.videoStatus == 3" class="cover">视频生成失败</div>
</div>
<!-- 删除 -->
<div class="waterFall-item-del" @click.stop="goDelete(item.id)">
<img src="../../assets/icon/icon-trash.png" alt="" />
</div>
</div>
</div>
</waterfall>
</waterfall>
</div>

<!-- 列表样式--小图 -->
<van-swipe-cell
@@ -184,6 +220,7 @@ export default {
},
data() {
return {
waterfallBug: true, // 解决瀑布流不显示bug
videoType: 1, //1竖屏2横屏
showPopup: false, // 编辑和删除显示隐藏
clickItem: -1, // 弹出层显示隐藏
@@ -209,6 +246,12 @@ export default {

methods: {
...mapActions(['setCharacters']),
// 草稿箱切换
draftChange(type) {
this.listActive = type
this.videoType = 1
this.loadVideoList(1, this.pageSize, this.videoType)
},
// 横屏竖屏列表
getNewList(videoType) {
this.videoType = videoType
@@ -269,13 +312,7 @@ export default {
this.$router.push({
path: '/editModel',
query: {
// videoType: item.videoType,
// saveID: item.id,
id: item.id
// imgUrl: item.id,
// modelId: item.id,
// mouldSmId: item.id,
// modify: true
}
})
}
@@ -285,14 +322,15 @@ export default {
return
}
// 成功
else if (status == 2) {
else if ([2, 4, 5, 6].includes(status)) {
const url = item.videoPathUri + item.videoPath
this.$router.push({
// path: '/downloadVideo',
path: '/videoDownload',
query: {
videoId: item.videoId,
videoUrl: url
videoUrl: url,
id: item.id
}
})
}
@@ -334,10 +372,19 @@ export default {
*/
async loadVideoList(pageNum, pageSize, videoType) {
try {
const res = await getVideoList(pageNum, pageSize, videoType)
this.waterfallBug = false
// 根据listActive判断是不是看的草稿箱
let videoStatuss = this.listActive == 1 ? [2, 4, 5, 6] : [0, 1, 3]
const res = await getVideoList(
pageNum,
pageSize,
videoType,
videoStatuss
)
console.log(res, '获取作品列表')

this.videoList = res.data.list
this.waterfallBug = true
this.total = res.data.total
} catch (error) {
console.log(error, 'error')
@@ -420,7 +467,7 @@ export default {
}
}
.page {
transform: translateX(30%);
// transform: translateX(30%);
opacity: 0;
transition: all 0.5s; // global-transition
padding-bottom: 25px;
@@ -465,6 +512,9 @@ export default {
width: 100%;
padding: 15px 10px;
.title {
display: flex;
justify-content: space-between;
width: 100%;
font-size: 16px;
font-weight: 500;
// margin-left: 15px;
@@ -476,6 +526,10 @@ export default {
height: 18px;
background-size: 100% 100%;
}
&-change {
display: inline-block;
font-size: 12px;
}
}
.itemList {
.videoModel {
@@ -548,8 +602,8 @@ export default {
flex-wrap: wrap;
align-items: center;
justify-content: center;
top: 70%;
left: 290px;
top: 50%;
left: 80%;
width: 80px;
height: 30px;
border-radius: 12px;


+ 45
- 16
src/views/videoDownload/videoDownload.vue 파일 보기

@@ -4,9 +4,8 @@
<HeadTop />
<div class="content">
<!-- 视频 -->
<div class="videoBox">
<div class="videoBox" v-if="videoSrc">
<video
loop
download
ref="video"
:src="videoSrc"
@@ -49,9 +48,12 @@
</div>
</div>
<!-- 下载按钮 -->
<a :href="videoSrc" download class="videoBox-download">
<!-- <a :href="videoSrc" download class="videoBox-download">
<img src="../../assets/icon/icon-download.png" alt="" />
</a>
</a> -->
<div class="videoBox-download" @click="downloadVideo">
<img src="../../assets/icon/icon-download.png" alt="" />
</div>
</div>
</div>
</div>
@@ -64,6 +66,11 @@ import { mapState, mapActions } from 'vuex'
import HeadTop from './../../components/common/head.vue'
// 工具
import { scrollToID } from '../../utils'
import { saveAs } from 'file-saver'

// 接口
import { getModelDetailById } from '../../api/getPaper'
import { doGetVideoDetialById } from '../../api/downloadVideo'
Vue.use(Toast)
export default {
components: {
@@ -72,15 +79,17 @@ export default {
data() {
return {
inPage: false,
videoSrc:
'https://wenlian.wenzhou.gov.cn:8001//upload/video/%E3%80%8A%E7%93%AF%E8%B6%8A%E4%B9%8B%E5%8D%8E%E3%80%8B%E7%89%87%E8%8A%B11.mp4',
// videoSrc:
// 'https://wenlian.wenzhou.gov.cn:8001//upload/video/%E3%80%8A%E7%93%AF%E8%B6%8A%E4%B9%8B%E5%8D%8E%E3%80%8B%E7%89%87%E8%8A%B11.mp4',
videoSrc: null,
showControls: false, // 控制台显示隐藏
playing: false, // 播放
isLandscape: false,
progress: 0,
currentTime: 0,
totalTime: 0,
progressChanging: false
progressChanging: false,
title: null
}
},
computed: {
@@ -88,9 +97,16 @@ export default {
characters: (state) => state.publicObj.characters
})
},
created() {
// console.log(this.$route)
// this.videoSrc = this.$route.query.videoUrl
},
mounted() {
this.inPage = true
scrollToID('top')
//初始化数据
this.getVideoInfo()

this.$refs.video.addEventListener('timeupdate', this.onTimeUpdate)
this.$refs.video.addEventListener('ended', this.onVideoEnded)
screen.orientation.addEventListener('change', this.onOrientationChange)
@@ -101,15 +117,28 @@ export default {
screen.orientation.removeEventListener('change', this.onOrientationChange)
},
methods: {
// 下载视频
downloadVideo() {
// 视频地址
const videoUrl = this.videoSrc
// 下载视频
saveAs(videoUrl, this.title)
},
// 获取视频信息
async getVideoInfo() {
const { data: res } = await getModelDetailById(this.$route.query.id)
if (res.videoId) {
const { data: res2 } = await doGetVideoDetialById(res.videoId)
this.videoSrc = res2.videoUrl
this.title = res2.title
return
}
this.videoSrc = res.videoPathUri + res.videoPath
this.title = res.title
},
onVideoLoaded() {
this.totalTime = this.$refs.video.duration
},
// onTimeUpdate() {
// if (!this.progressChanging) {
// this.currentTime = this.$refs.video.currentTime
// this.progress = this.currentTime
// }
// },
onVideoEnded() {
this.playing = false
},
@@ -170,8 +199,8 @@ export default {

<style lang="scss" scoped>
.page {
width: 375px;
height: 100vh;
// width: 375px;
// height: 100vh;
overflow-y: hidden;
}
.content {
@@ -233,7 +262,7 @@ export default {
}
&-download {
position: absolute;
right: 10px;
right: 15px;
top: 15px;
display: inline-block;
width: 24px;


+ 8944
- 9162
yarn.lock
파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
파일 보기


불러오는 중...
취소
저장