@@ -176,3 +176,15 @@ export function createVideoApi(id) { | |||
data: { id } | |||
}) | |||
} | |||
/** | |||
* @description:根据id获取用户作品详情 | |||
* @param {number} id | |||
* @return data | |||
*/ | |||
export function getModelDetailByIdApi(id) { | |||
return request({ | |||
url: `/api/userVideo/findById?id=${id}`, | |||
method: 'get' | |||
}) | |||
} |
@@ -2,7 +2,7 @@ import request from "@/utils/request.js"; | |||
/** | |||
* @description 获取我的视频作品 | |||
* @description 获取我的视频作品(已改) | |||
* @params pageNum, pageSize | |||
* @returns data | |||
*/ | |||
@@ -12,35 +12,47 @@ import request from "@/utils/request.js"; | |||
// method: 'get' | |||
// }) | |||
// } | |||
export function userPhotoVideoList(pageNum, pageSize, title) { | |||
export function userPhotoVideoListApi(pageNum, pageSize, title) { | |||
let videoStatuss = [1, 2, 3, 4, 5, 6] | |||
return request({ | |||
url: `/api/userVideo/list?pageNum=${pageNum}&pageSize=${pageSize}&title=${title}&videoType=-1&videoStatuss=${videoStatuss}`, | |||
url: `/api/userVideo/list?pageNum=${pageNum}&pageSize=${pageSize}&title=${title}&videoType=-1`, | |||
method: 'get' | |||
}) | |||
} | |||
/** | |||
* @description 删除视频作品 | |||
* @description 删除视频作品(旧) | |||
* @params videoID | |||
* @returns data | |||
*/ | |||
export function delUserPhotoVideoByID(videoID) { | |||
export function delUserPhotoVideoByIDApi(videoID) { | |||
return request({ | |||
url: `/api/userPhotoVideo/del?id=${videoID}`, | |||
url: `/api/userVideo/del?id=${videoID}`, | |||
method: 'get' | |||
}) | |||
} | |||
/** | |||
* @description 提升画质(旧) | |||
* @params id | |||
* @returns data | |||
*/ | |||
export function videoHyApi(id) { | |||
return request({ | |||
url: `/api/userPhotoVideo/videoHy?id=${id}`, | |||
method: 'get' | |||
}) | |||
} | |||
/** | |||
* @description 获取作品列表 | |||
* @param {number} pageParams (pageNum, pageSize) | |||
* @description 查询是否有视频更新(旧) | |||
* @params id | |||
* @returns data | |||
*/ | |||
export function getVideoList(pageNum, pageSize, videoType, videoStatuss) { | |||
export function checkVideoStatusApi(list) { | |||
return request({ | |||
url: `/api/userVideo/list?pageNum=${pageNum}&pageSize=${pageSize}&videoType=${videoType}&videoStatuss=${videoStatuss}`, | |||
url: `/api/userPhotoVideo/checkVideoStatus?list=${list}`, | |||
method: 'get' | |||
}) | |||
} | |||
} |
@@ -1,5 +1,5 @@ | |||
<template> | |||
<div class="loadingIcon"> | |||
<div v-if="isLoading" class="loadingIcon"> | |||
<div> | |||
<el-icon class="is-loading"> | |||
<svg | |||
@@ -32,8 +32,8 @@ const props = defineProps({ | |||
.loadingIcon { | |||
position: relative; | |||
width: 100%; | |||
height: 80px; | |||
line-height: 80px; | |||
height: 30px; | |||
line-height: 35px; | |||
text-align: center; | |||
transform: scale(2); | |||
> div { | |||
@@ -92,6 +92,10 @@ export default { | |||
personBGI:'Person background diagram', | |||
systemMaterial:'System Material', | |||
personMaterial:'Person Material', | |||
abscissa:'abscissa', | |||
ordinate:'ordinate', | |||
hierarchy:'hierarchy', | |||
ratio:'ratio', | |||
}, | |||
// 我的视频 | |||
@@ -114,7 +118,12 @@ export default { | |||
manuscriptText: "Manuscript", | |||
generatingText: "The video is generating, Please wait a moment", | |||
generationFailedText: "The video generate is failed, Reason:", | |||
edit:'Edit' | |||
edit:'Edit', | |||
upSharpness:'Upgrade image quality', | |||
upSharpnessTips:'If you are satisfied with the preview, you can click here to improve the video sharpness ~', | |||
freeUserTip:'Free user-generated videos are limited to 1 minute', | |||
hasNewVideo:'You have a new video status!', | |||
reset:"reset", | |||
}, | |||
// 个人账户 | |||
@@ -94,6 +94,10 @@ export default { | |||
personBGI:'个人背景', | |||
systemMaterial:'系统素材', | |||
personMaterial:'个人素材', | |||
abscissa:'横坐标', | |||
ordinate:'纵坐标', | |||
hierarchy:'层级', | |||
ratio:'缩放倍率', | |||
}, | |||
// 我的视频 | |||
@@ -116,7 +120,12 @@ export default { | |||
manuscriptText: "草稿", | |||
generatingText: "视频正在生成中,请稍等哦", | |||
generationFailedText: "视频生成失败。原因:", | |||
edit:'编辑' | |||
edit:'编辑', | |||
upSharpness:'升级画质', | |||
upSharpnessTips:'如果对预览效果满意,您可以点击此处来提升视频清晰度~', | |||
freeUserTip:'免费用户生成视频时长限制为1分钟', | |||
hasNewVideo:'您有新的视频状态!', | |||
reset:"重置", | |||
}, | |||
// 个人账户 | |||
@@ -17,12 +17,12 @@ | |||
</div> | |||
{{ $t("createVideo.title") }} | |||
</div> | |||
<!-- <div class="myRouter-item" @click="router.push('/myCreating')"> | |||
<div class="myRouter-item" @click="router.push('/myCreating')"> | |||
<div class="icon"> | |||
<el-icon><i-ep-edit /></el-icon> | |||
<el-icon><i-ep-menu /></el-icon> | |||
</div> | |||
{{ $t("myCreating.title") }} | |||
</div> --> | |||
</div> | |||
</div> | |||
</div> | |||
<!-- 右边显示部分 --> | |||
@@ -25,6 +25,7 @@ div { | |||
text-align: center; | |||
border: 1px solid #000; | |||
border-radius: 5px; | |||
user-select: none; | |||
} | |||
p { | |||
@@ -41,6 +41,7 @@ declare module '@vue/runtime-core' { | |||
IEpDownload: typeof import('~icons/ep/download')['default'] | |||
IEpEdit: typeof import('~icons/ep/edit')['default'] | |||
IEpLoading: typeof import('~icons/ep/loading')['default'] | |||
IEpMenu: typeof import('~icons/ep/menu')['default'] | |||
IEpSearch: typeof import('~icons/ep/search')['default'] | |||
IEpVideoPlay: typeof import('~icons/ep/video-play')['default'] | |||
LangSelect: typeof import('./../components/LangSelect/index.vue')['default'] | |||
@@ -0,0 +1,67 @@ | |||
import axios from 'axios' | |||
import router from '@/router/index' | |||
import { ElMessage } from 'element-plus' | |||
// ElMessage | |||
import { userInfoModules } from "@/store/modules/userInfo"; | |||
// import stores from '@/store/index' | |||
import { store } from '@/store/index'; // 因为不是在setup环境而是js中,必须重新初始化加载store | |||
const userInfoPinia = userInfoModules(store); //pinia | |||
// 开发环境需以 "/api" 拼接,其他环境需以 "/C" 拼接 | |||
const env = process.env.NODE_ENV == 'development' ? "/api" : "/C" | |||
// console.log(process.env.NODE_ENV, 'env') | |||
/** | |||
* @description:发送Axios请求 | |||
*/ | |||
const request = axios.create({ | |||
baseURL: env, | |||
timeout: 15000 | |||
}) | |||
// 请求拦截器 | |||
request.interceptors.request.use( | |||
function (config) { | |||
// 给所有授权的请求提供token | |||
const AccessToken = localStorage.getItem("AccessToken") | |||
if (AccessToken) { | |||
config.headers.token = AccessToken | |||
} | |||
return config | |||
}, | |||
function (error) { | |||
// 操作 | |||
return Promise.reject(error) | |||
} | |||
) | |||
// 响应拦截器 | |||
request.interceptors.response.use( | |||
function (response) { | |||
let code = response.data.code | |||
// 当token无效(过期或损坏)时 1053为未登录 | |||
if (code == 1052 || code == 1053) { | |||
localStorage.removeItem("AccessToken"); | |||
router.push("/login"); | |||
ElMessage.error("登录过期,请重新登录!"); | |||
return | |||
// 弹出错误 | |||
} else if (code != 200) { | |||
if (code == 500) { | |||
ElMessage.error("系统异常,请稍后再试"); | |||
// 其他错误 | |||
} else { | |||
return Promise.reject(response.data) | |||
} | |||
} else { | |||
// 正常返回 | |||
return response.data | |||
} | |||
}, | |||
function (error) { | |||
} | |||
) | |||
export default request |
@@ -1,15 +1,19 @@ | |||
import axios from 'axios' | |||
import router from '@/router/index' | |||
import { ElMessage } from 'element-plus' | |||
// ElMessage | |||
import { useAppStore } from '@/store/modules/app'; | |||
import { userInfoModules } from "@/store/modules/userInfo"; | |||
// import stores from '@/store/index' | |||
import { store } from '@/store/index'; // 因为不是在setup环境而是js中,必须重新初始化加载store | |||
const appStore = useAppStore(store); //pinia | |||
const userInfoPinia = userInfoModules(store); //pinia | |||
// const userInfoPinia = userInfoModules(store); //pinia | |||
// 开发环境需以 "/api" 拼接,其他环境需以 "/C" 拼接 | |||
const env = process.env.NODE_ENV == 'development' ? "/api" : "/C" | |||
// console.log(process.env.NODE_ENV, 'env') | |||
// const env = process.env.NODE_ENV == 'development' ? "" : "" | |||
console.log(env) | |||
/** | |||
* @description:发送Axios请求 | |||
@@ -21,6 +25,7 @@ const request = axios.create({ | |||
// 请求拦截器 | |||
request.interceptors.request.use( | |||
function (config) { | |||
// 给所有授权的请求提供token | |||
const AccessToken = localStorage.getItem("AccessToken") | |||
@@ -30,6 +35,14 @@ request.interceptors.request.use( | |||
return config | |||
}, | |||
function (error) { | |||
if (axios.isCancel(error)) { | |||
// 请求被取消处理 | |||
ElMessage.error( | |||
appStore.language == "zh-cn" ? `请求超时,请重试!` : `Request timeout, please try again` | |||
); | |||
} | |||
// 操作 | |||
return Promise.reject(error) | |||
} | |||
@@ -38,18 +51,31 @@ request.interceptors.request.use( | |||
// 响应拦截器 | |||
request.interceptors.response.use( | |||
function (response) { | |||
if (!response.data) { | |||
// ElMessage.error($t('tips.systemException')); | |||
ElMessage.error( | |||
appStore.language == "zh-cn" ? `服务器繁忙,请重试!` : `The server is busy, please try again` | |||
); | |||
} | |||
let code = response.data.code | |||
// 当token无效(过期或损坏)时 1053为未登录 | |||
if (code == 1052 || code == 1053) { | |||
localStorage.removeItem("AccessToken"); | |||
router.push("/login"); | |||
ElMessage.error("登录过期,请重新登录!"); | |||
// ElMessage.error("登录过期,请重新登录!"); | |||
// ElMessage.error($t('tips.tokenExpired')); | |||
ElMessage.error( | |||
appStore.language == "zh-cn" ? `登录过期,请重新登录!` : `Login expired, please login again` | |||
); | |||
return | |||
// 弹出错误 | |||
} else if (code != 200) { | |||
if (code == 500) { | |||
ElMessage.error("系统异常,请稍后再试"); | |||
// ElMessage.error("系统异常,请稍后再试"); | |||
// ElMessage.error($t('tips.systemException')); | |||
ElMessage.error( | |||
appStore.language == "zh-cn" ? `服务器繁忙,请重试!` : `The server is busy, please try again` | |||
); | |||
// 其他错误 | |||
} else { | |||
return Promise.reject(response.data) | |||
@@ -109,7 +109,7 @@ | |||
<div v-if="showImgInfo.data" class="showImgInfoBox"> | |||
<!-- <div class="infoItem"> | |||
</div> --> | |||
<div class="ItemName">横坐标</div> | |||
<div class="ItemName">{{ $t("createVideo.abscissa") }}</div> | |||
<div class="ItemValue"> | |||
<el-input-number | |||
v-model="showImgInfo.data.x" | |||
@@ -117,7 +117,7 @@ | |||
controls-position="right" | |||
/> | |||
</div> | |||
<div class="ItemName">纵坐标</div> | |||
<div class="ItemName">{{ $t("createVideo.ordinate") }}</div> | |||
<div class="ItemValue"> | |||
<el-input-number | |||
v-model="showImgInfo.data.y" | |||
@@ -125,7 +125,7 @@ | |||
controls-position="right" | |||
/> | |||
</div> | |||
<div class="ItemName">层级</div> | |||
<div class="ItemName">{{ $t("createVideo.hierarchy") }}</div> | |||
<div class="ItemValue"> | |||
<el-input-number | |||
v-model="showImgInfo.data.z" | |||
@@ -135,7 +135,7 @@ | |||
controls-position="right" | |||
/> | |||
</div> | |||
<div class="ItemName">缩放倍率</div> | |||
<div class="ItemName">{{ $t("createVideo.ratio") }}</div> | |||
<div class="ItemValue"> | |||
<el-input-number | |||
v-model="showImgInfo.data.w" | |||
@@ -148,11 +148,21 @@ | |||
</div> | |||
<!-- 保存视频和生成视频 --> | |||
<div class="btnBox" v-show="!showChooseModel"> | |||
<div class="customBtn" style="margin-right: 15px"> | |||
{{ $t("createVideo.saveVideo") }} | |||
<div | |||
@click="saveOrUpdate('isSave')" | |||
class="customBtn" | |||
style="margin-right: 15px" | |||
> | |||
<MyLoadingIcon :isLoading="saveLoading"></MyLoadingIcon> | |||
<div v-if="!saveLoading"> | |||
{{ $t("createVideo.saveVideo") }} | |||
</div> | |||
</div> | |||
<div class="customBtn"> | |||
{{ $t("createVideo.generateVideo") }} | |||
<div @click="createVideo" class="customBtn"> | |||
<MyLoadingIcon :isLoading="createLoading"></MyLoadingIcon> | |||
<div v-if="!createLoading"> | |||
{{ $t("createVideo.generateVideo") }} | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
@@ -298,14 +308,14 @@ | |||
<div | |||
class="tabItem" | |||
:class="bjTabItemActive == 1 ? 'tabItemActive' : ''" | |||
@click="bjTabItemActive = 1" | |||
@click="changeBjTabItemActive(1)" | |||
> | |||
{{ $t("createVideo.systemBGI") }} | |||
</div> | |||
<div | |||
class="tabItem" | |||
:class="bjTabItemActive == 2 ? 'tabItemActive' : ''" | |||
@click="bjTabItemActive = 2" | |||
@click="changeBjTabItemActive(2)" | |||
> | |||
{{ $t("createVideo.personBGI") }} | |||
</div> | |||
@@ -372,14 +382,14 @@ | |||
<div | |||
class="tabItem" | |||
:class="scTabItemActive == 1 ? 'tabItemActive' : ''" | |||
@click="scTabItemActive = 1" | |||
@click="changeScTabItemActive(1)" | |||
> | |||
{{ $t("createVideo.systemMaterial") }} | |||
</div> | |||
<div | |||
class="tabItem" | |||
:class="scTabItemActive == 2 ? 'tabItemActive' : ''" | |||
@click="scTabItemActive = 2" | |||
@click="changeScTabItemActive(2)" | |||
> | |||
{{ $t("createVideo.personMaterial") }} | |||
</div> | |||
@@ -466,6 +476,7 @@ import { | |||
personPatchApi, | |||
saveOrUpdateApi, | |||
createVideoApi, | |||
getModelDetailByIdApi, | |||
} from "@/apis/createVideo"; | |||
import { useI18n } from "vue-i18n"; | |||
import Recorder from "js-audio-recorder"; //mp3插件 | |||
@@ -478,14 +489,14 @@ const route = useRoute(); | |||
const router = useRouter(); | |||
const { locale } = useI18n(); | |||
const lanChange = ref(locale); | |||
const AccessToken = localStorage.getItem("AccessToken"); // 判断有没有登录 | |||
// ElMessage.error( | |||
// lanChange.value == "zh-cn" | |||
// ? `您还没有输入文案` | |||
// : `You have not entered the copy yet` | |||
// ); | |||
const AccessToken = localStorage.getItem("AccessToken"); // 判断有没有登录 | |||
//#region 选择模版弹出框 | |||
const videoType = ref(null); //判断横屏2还是竖屏1,初始为没有,控制按钮显隐 | |||
const showChooseModel = ref(true); // 控制弹窗显示隐藏 | |||
@@ -720,6 +731,18 @@ const bjTabItemActive = ref(1); //系统或个人tab高亮 | |||
const systemBGIList = ref([]); //系统背景列表 | |||
const systemBGPageNum = ref(1); //系统背景页数 | |||
const overLoadSystemBG = ref(true); //禁止系统背景触底刷新 | |||
// tab栏切换 | |||
function changeBjTabItemActive(type) { | |||
bjTabItemActive.value = type; | |||
if (type == 2 && !AccessToken) { | |||
ElMessage.error( | |||
lanChange.value == "zh-cn" | |||
? `登录过期,请重新登录!` | |||
: `Login expired, please login again` | |||
); | |||
router.push("login"); | |||
} | |||
} | |||
async function getSystemBGIList() { | |||
const res = await systemBGIListApi( | |||
systemBGPageNum.value, | |||
@@ -780,7 +803,9 @@ watch( | |||
overLoadSystemBG.value = true; | |||
overLoadpersonBG.value = true; | |||
await getSystemBGIList(); | |||
await getpersonBGIList(); | |||
if (AccessToken) { | |||
await getpersonBGIList(); | |||
} | |||
}, | |||
{ deep: true } | |||
); | |||
@@ -804,6 +829,18 @@ const scTabItemActive = ref(1); //系统或个人tab高亮 | |||
const systemSCIList = ref([]); //系统素材列表 | |||
const systemSCPageNum = ref(1); //系统素材页数 | |||
const overLoadSystemSC = ref(true); //禁止系统素材触底刷新 | |||
// tab栏切换 | |||
function changeScTabItemActive(type) { | |||
scTabItemActive.value = type; | |||
if (type == 2 && !AccessToken) { | |||
ElMessage.error( | |||
lanChange.value == "zh-cn" | |||
? `登录过期,请重新登录!` | |||
: `Login expired, please login again` | |||
); | |||
router.push("login"); | |||
} | |||
} | |||
async function getSystemSCIList() { | |||
const res = await systemSCIListApi( | |||
systemSCPageNum.value, | |||
@@ -970,14 +1007,176 @@ watch( | |||
//#region 保存视频和生成草稿 | |||
const workId = ref(null); //保存作品完成后会有一个id,用于生成视频 | |||
async function saveOrUpdate() { | |||
const saveLoading = ref(false); | |||
const createLoading = ref(false); | |||
async function saveOrUpdate(isCreate) { | |||
// 单纯的保存视频 | |||
if (isCreate == "isSave") { | |||
if (saveLoading.value == true) { | |||
ElMessage.error(lanChange.value == "zh-cn" ? `正在保存` : `Be saving`); | |||
return; | |||
} | |||
if (createLoading.value == true) { | |||
ElMessage.error( | |||
lanChange.value == "zh-cn" ? `正在生成` : `Be generating` | |||
); | |||
return; | |||
} | |||
try { | |||
saveLoading.value = true; | |||
if (!textareaContent.value) { | |||
ElMessage.error( | |||
lanChange.value == "zh-cn" | |||
? `您还没有输入文案` | |||
: `You have not entered the copy yet` | |||
); | |||
saveLoading.value = false; | |||
return; | |||
} | |||
if (!sound.value && !soundStyle.value) { | |||
ElMessage.error( | |||
lanChange.value == "zh-cn" | |||
? `您还没有选择声音` | |||
: `You have not selected a sound` | |||
); | |||
saveLoading.value = false; | |||
return; | |||
} | |||
// 处理数字人 | |||
const shuziren = { | |||
id: materialList.virtual.id, | |||
type: 1, | |||
material: materialList.virtual.material, | |||
x: materialList.virtual.x, | |||
y: materialList.virtual.y, | |||
z: materialList.virtual.z, | |||
w: materialList.virtual.w, | |||
h: materialList.virtual.h, | |||
}; | |||
// 处理素材 | |||
const scList = materialList.scList.map((item) => { | |||
return { | |||
id: item.id, | |||
type: 5, | |||
material: item.material, | |||
x: item.x, | |||
y: item.y, | |||
z: item.z, | |||
w: item.w, | |||
h: item.h, | |||
}; | |||
}); | |||
// 处理上传数据 | |||
const data = { | |||
id: workId.value, | |||
videoType: videoType.value, //横屏竖屏 | |||
personMouldId: materialList.virtual.id, //数字人id | |||
voiceMouldIds: [soundStyle.value ? soundStyle.value : sound.value], //声音id | |||
title: title.value, //标题 | |||
paperwork: textareaContent.value, //语音内容 | |||
backgroundId: materialList.BGI.id + "", //背景id | |||
personJson: JSON.stringify(shuziren), // 数字人 | |||
materialAllJson: JSON.stringify([...scList]), // 素材 | |||
}; | |||
const res = await saveOrUpdateApi(data); | |||
workId.value = res.data.id; | |||
saveLoading.value = false; | |||
ElMessage.success( | |||
lanChange.value == "zh-cn" | |||
? `保存草稿成功!` | |||
: `Save draft successfully!` | |||
); | |||
} catch (error) { | |||
ElMessage.error(error); | |||
saveLoading.value = false; | |||
} | |||
} | |||
// 生成视频时触发的保存 | |||
else if (isCreate == "isCreate") { | |||
try { | |||
if (!textareaContent.value) { | |||
ElMessage.error( | |||
lanChange.value == "zh-cn" | |||
? `您还没有输入文案` | |||
: `You have not entered the copy yet` | |||
); | |||
saveLoading.value = false; | |||
return; | |||
} | |||
if (!sound.value && !soundStyle.value) { | |||
ElMessage.error( | |||
lanChange.value == "zh-cn" | |||
? `您还没有选择声音` | |||
: `You have not selected a sound` | |||
); | |||
saveLoading.value = false; | |||
return; | |||
} | |||
// 处理数字人 | |||
const shuziren = { | |||
id: materialList.virtual.id, | |||
type: 1, | |||
material: materialList.virtual.material, | |||
x: materialList.virtual.x, | |||
y: materialList.virtual.y, | |||
z: materialList.virtual.z, | |||
w: materialList.virtual.w, | |||
h: materialList.virtual.h, | |||
}; | |||
// 处理素材 | |||
const scList = materialList.scList.map((item) => { | |||
return { | |||
id: item.id, | |||
type: 5, | |||
material: item.material, | |||
x: item.x, | |||
y: item.y, | |||
z: item.z, | |||
w: item.w, | |||
h: item.h, | |||
}; | |||
}); | |||
// 处理上传数据 | |||
const data = { | |||
id: workId.value, | |||
videoType: videoType.value, //横屏竖屏 | |||
personMouldId: materialList.virtual.id, //数字人id | |||
voiceMouldIds: [soundStyle.value ? soundStyle.value : sound.value], //声音id | |||
title: title.value, //标题 | |||
paperwork: textareaContent.value, //语音内容 | |||
backgroundId: materialList.BGI.id + "", //背景id | |||
personJson: JSON.stringify(shuziren), // 数字人 | |||
materialAllJson: JSON.stringify([...scList]), // 素材 | |||
}; | |||
const res = await saveOrUpdateApi(data); | |||
workId.value = res.data.id; | |||
saveLoading.value = false; | |||
} catch (error) { | |||
ElMessage.error(error); | |||
saveLoading.value = false; | |||
} | |||
} | |||
} | |||
async function createVideo() { | |||
if (saveLoading.value == true) { | |||
ElMessage.error(lanChange.value == "zh-cn" ? `正在保存` : `Be saving`); | |||
return; | |||
} | |||
if (createLoading.value == true) { | |||
ElMessage.error(lanChange.value == "zh-cn" ? `正在生成` : `Be generating`); | |||
return; | |||
} | |||
try { | |||
createLoading.value = true; | |||
if (!textareaContent.value) { | |||
ElMessage.error( | |||
lanChange.value == "zh-cn" | |||
? `您还没有输入文案` | |||
: `You have not entered the copy yet` | |||
); | |||
createLoading.value = false; | |||
return; | |||
} | |||
if (!sound.value && !soundStyle.value) { | |||
@@ -986,62 +1185,42 @@ async function saveOrUpdate() { | |||
? `您还没有选择声音` | |||
: `You have not selected a sound` | |||
); | |||
createLoading.value = false; | |||
return; | |||
} | |||
// 处理数字人 | |||
const shuziren = { | |||
id: materialList.virtual.id, | |||
type: 1, | |||
material: materialList.virtual.material, | |||
x: materialList.virtual.x, | |||
y: materialList.virtual.y, | |||
z: materialList.virtual.z, | |||
w: materialList.virtual.w, | |||
h: materialList.virtual.h, | |||
}; | |||
// 处理素材 | |||
const scList = materialList.scList.map((item) => { | |||
return { | |||
id: item.id, | |||
type: 5, | |||
material: item.material, | |||
x: item.x, | |||
y: item.y, | |||
z: item.z, | |||
w: item.w, | |||
h: item.h, | |||
}; | |||
}); | |||
// 处理上传数据 | |||
const data = { | |||
id: workId.value, | |||
videoType: videoType.value, //横屏竖屏 | |||
personMouldId: materialList.virtual.id, //数字人id | |||
voiceMouldIds: [soundStyle.value ? soundStyle.value : sound.value], //声音id | |||
title: title.value, //标题 | |||
paperwork: textareaContent.value, //语音内容 | |||
backgroundId: materialList.BGI.id + "", //背景id | |||
personJson: JSON.stringify(shuziren), // 数字人 | |||
materialAllJson: JSON.stringify([...scList]), // 素材 | |||
}; | |||
const res = await saveOrUpdateApi(data); | |||
workId.value = res.data.id; | |||
} catch (error) { | |||
ElMessage.error(error); | |||
} | |||
} | |||
async function createVideo() { | |||
try { | |||
await saveOrUpdate(); | |||
await saveOrUpdate("isCreate"); | |||
const res2 = await createVideoApi(workId.value); | |||
ElMessage.success( | |||
lanChange.value == "zh-cn" | |||
? `视频生成成功!` | |||
: `Video generation is successful!` | |||
); | |||
createLoading.value = false; | |||
router.push("/myCreating"); | |||
} catch (error) { | |||
ElMessage.error(error); | |||
createLoading.value = false; | |||
} | |||
} | |||
//#endregion ---------------------------- | |||
//#region | |||
async function getModelDetailById() { | |||
const res = await getModelDetailByIdApi(route.query.id); | |||
workId.value = res.data.id; | |||
videoType.value = res.data.videoType; | |||
title.value = res.data.title; //标题 | |||
textareaContent.value = res.data.paperwork; //语音内容 | |||
materialList.BGI.id = res.data.backgroundId + ""; //背景id | |||
materialList.BGI.material = JSON.parse(res.data.backgroundSm).material; | |||
materialList.virtual = JSON.parse(res.data.personJson); | |||
materialList.virtual.id = res.data.personMouldId; //数字人id | |||
materialList.scList = res.data.materialAllJson | |||
? JSON.parse(res.data.materialAllJson) | |||
: []; | |||
} | |||
//#endregion ---------------------------- | |||
//#region | |||
//#endregion ---------------------------- | |||
@@ -1064,8 +1243,13 @@ onMounted(async () => { | |||
// getSystemSCIList(); | |||
// getpersonBGIList(); | |||
await getModeList(tabSex.value, 1, 20, tabVideoType.value); //获取模版列表 | |||
getpersonSCIList(); | |||
if (AccessToken) { | |||
getpersonSCIList(); | |||
} | |||
getSystemSCIList(); | |||
if (route.query.id) { | |||
// getModelDetailById(); | |||
} | |||
}); | |||
</script> | |||
@@ -1169,6 +1353,7 @@ onMounted(async () => { | |||
// margin: 0 10px; | |||
// padding: 0 5px; | |||
background-color: #000; | |||
user-select: none; | |||
} | |||
.tabItemActive { | |||
background-color: #fff; | |||
@@ -1287,7 +1472,17 @@ onMounted(async () => { | |||
right: 10px; | |||
width: 250px; | |||
.customBtn { | |||
display: flex; | |||
justify-content: space-between; | |||
align-items: center; | |||
width: 45%; | |||
height: 50px; | |||
user-select: none; | |||
> div { | |||
width: 100%; | |||
text-align: center; | |||
cursor: pointer; | |||
} | |||
} | |||
} | |||
} | |||
@@ -1314,6 +1509,7 @@ onMounted(async () => { | |||
border-radius: 25px; | |||
transition: all 0.3s; | |||
cursor: pointer; | |||
user-select: none; | |||
} | |||
.tabItemActive { | |||
background-color: #000; | |||
@@ -0,0 +1,695 @@ | |||
<template> | |||
<div class="page"> | |||
<!-- 搜索框 --> | |||
<div class="top"> | |||
<div class="flex justify-between" style="width: 300px"> | |||
<el-input | |||
v-model="title" | |||
:placeholder="$t('myCreating.search')" | |||
@keyup.enter="searchVideoList" | |||
/> | |||
<el-icon class="icon-edit" @click="searchVideoList" | |||
><i-ep-search | |||
/></el-icon> | |||
</div> | |||
</div> | |||
<!-- 作品列表 --> | |||
<div v-if="!isLoading"> | |||
<div v-if="videoList.length > 0" class="workList flex"> | |||
<div class="workItem" v-for="item in videoList" :key="item.id"> | |||
<div class="workImg"> | |||
<img | |||
v-if="item.coverImg" | |||
class="coverImg inmiddle" | |||
:src="item.coverImg" | |||
alt="" | |||
/> | |||
<img | |||
v-else | |||
class="takePlace inmiddle" | |||
src="@/assets/img/left-top-logo.png" | |||
alt="" | |||
/> | |||
</div> | |||
<div :class="'statusName ' + item.statusName"> | |||
{{ $t("myCreating." + item.statusName) }} | |||
</div> | |||
<div class="workInfo flex"> | |||
<span>{{ item.title }}</span> | |||
<span>{{ dayjs(item.createDate).format("YYYY-MM-DD") }}</span> | |||
</div> | |||
<!-- 更多遮罩层 --> | |||
<div | |||
class="more" | |||
@mouseleave="mouseleave" | |||
@click="clickVideoItem(item)" | |||
> | |||
<el-icon | |||
v-if="[0].includes(item.videoStatus)" | |||
class="icon-videoPlay" | |||
><i-ep-edit | |||
/></el-icon> | |||
<el-icon | |||
v-if="[1, 2, 4, 6].includes(item.videoStatus)" | |||
class="icon-videoPlay" | |||
><i-ep-loading | |||
/></el-icon> | |||
<el-icon | |||
v-if="[3].includes(item.videoStatus)" | |||
class="icon-videoPlay" | |||
><i-ep-closeBold | |||
/></el-icon> | |||
<el-icon | |||
v-if="[5].includes(item.videoStatus)" | |||
class="icon-videoPlay" | |||
><i-ep-videoPlay | |||
/></el-icon> | |||
<div class="showBtn" @click.stop="mouseenter(item)">...</div> | |||
<div class="btns" v-show="showPopup"> | |||
<!-- 下载按钮 --> | |||
<div | |||
class="btns-item" | |||
@click.stop="handleDownload(item.videoPlayUrl)" | |||
> | |||
<div class="btns-item-icon"> | |||
<el-icon class="icon-delete"><i-ep-download /></el-icon> | |||
</div> | |||
<div class="text">{{ $t("myCreating.download") }}</div> | |||
</div> | |||
<!-- 删除按钮 --> | |||
<div | |||
class="btns-item" | |||
@click.stop="handelDelete(item.id, item.title)" | |||
> | |||
<div class="btns-item-icon"> | |||
<el-icon class="icon-delete"><i-ep-delete /></el-icon> | |||
</div> | |||
<div class="text">{{ $t("myCreating.delete") }}</div> | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
<div v-if="videoList.length == 0" class="noList"> | |||
<div class="title"> | |||
{{ $t("myCreating.noList") }} | |||
</div> | |||
<div class="goCreate" @click="goCreate"> | |||
{{ $t("myCreating.goCreate") }} | |||
</div> | |||
</div> | |||
</div> | |||
<div v-if="isLoading" class="loadingIcon"> | |||
<el-icon class="is-loading"> | |||
<svg | |||
viewBox="0 0 1024 1024" | |||
xmlns="http://www.w3.org/2000/svg" | |||
data-v-ea893728="" | |||
> | |||
<path | |||
fill="currentColor" | |||
d="M512 64a32 32 0 0 1 32 32v192a32 32 0 0 1-64 0V96a32 32 0 0 1 32-32zm0 640a32 32 0 0 1 32 32v192a32 32 0 1 1-64 0V736a32 32 0 0 1 32-32zm448-192a32 32 0 0 1-32 32H736a32 32 0 1 1 0-64h192a32 32 0 0 1 32 32zm-640 0a32 32 0 0 1-32 32H96a32 32 0 0 1 0-64h192a32 32 0 0 1 32 32zM195.2 195.2a32 32 0 0 1 45.248 0L376.32 331.008a32 32 0 0 1-45.248 45.248L195.2 240.448a32 32 0 0 1 0-45.248zm452.544 452.544a32 32 0 0 1 45.248 0L828.8 783.552a32 32 0 0 1-45.248 45.248L647.744 692.992a32 32 0 0 1 0-45.248zM828.8 195.264a32 32 0 0 1 0 45.184L692.992 376.32a32 32 0 0 1-45.248-45.248l135.808-135.808a32 32 0 0 1 45.248 0zm-452.544 452.48a32 32 0 0 1 0 45.248L240.448 828.8a32 32 0 0 1-45.248-45.248l135.808-135.808a32 32 0 0 1 45.248 0z" | |||
></path> | |||
</svg> | |||
</el-icon> | |||
<span class="loadingText">{{ $t("myCreating.loading") }}</span> | |||
</div> | |||
<!-- 弹出框预览作品 --> | |||
<el-dialog | |||
destroy-on-close | |||
class="elDialog" | |||
v-model="showDialog" | |||
:title="presentItem.title" | |||
width="40%" | |||
center | |||
> | |||
<div class="videoBox"> | |||
<video | |||
v-if="presentItem.videoPlayUrl" | |||
controls="true" | |||
class="" | |||
:src="presentItem.videoPlayUrl" | |||
></video> | |||
<div v-else class="insteadVideo"> | |||
{{ $t("myCreating." + presentItem.statusName + "Text") }} | |||
<span v-if="presentItem.videoStatus == 3">{{ | |||
presentItem.videoMsg | |||
}}</span> | |||
</div> | |||
<!-- 有视频路径的页脚 --> | |||
<div class="footer" v-if="presentItem.videoPlayUrl"> | |||
<span class="dialog-footer"> | |||
<!-- dialog下载按钮 --> | |||
<button | |||
class="elBtn" | |||
type="primary" | |||
@click.stop="handleDownload(presentItem.videoPlayUrl)" | |||
> | |||
<el-icon class="icon-delete"><i-ep-download /></el-icon | |||
>{{ $t("myCreating.download") }} | |||
</button> | |||
<!-- dialog删除按钮 --> | |||
<button | |||
class="elBtn" | |||
@click.stop="handelDelete(presentItem.id, presentItem.title)" | |||
> | |||
<el-icon class="icon-delete"><i-ep-delete /></el-icon | |||
>{{ $t("myCreating.delete") }} | |||
</button> | |||
</span> | |||
</div> | |||
<!-- 无视频路径的页脚 --> | |||
<div class="footer" v-if="!presentItem.videoPlayUrl"> | |||
<span class="dialog-footer"> | |||
<!-- dialog编辑按钮 --> | |||
<button | |||
class="elBtn" | |||
type="primary" | |||
@click.stop=" | |||
router.push({ | |||
path: '/createVideo', | |||
query: { id: presentItem.id }, | |||
}) | |||
" | |||
v-if="[0, 3].includes(presentItem.videoStatus)" | |||
> | |||
{{ $t("myCreating.edit") }} | |||
</button> | |||
</span> | |||
</div> | |||
</div> | |||
</el-dialog> | |||
<!-- 分页 --> | |||
<div v-if="!isLoading" class="pagination"> | |||
<el-pagination | |||
v-model:current-page="pageNum" | |||
v-model:page-size="pageSize" | |||
:page-sizes="[10, 20, 50, 100, 200]" | |||
:small="false" | |||
:disabled="false" | |||
:background="true" | |||
layout="total, sizes, prev, pager, next, jumper" | |||
:total="total" | |||
@size-change="handleSizeChange" | |||
@current-change="handleCurrentChange" | |||
/> | |||
</div> | |||
</div> | |||
</template> | |||
<script setup> | |||
import { userPhotoVideoList, delUserPhotoVideoByID } from "@/apis/myCreating"; | |||
import { timestampToTime } from "@/utils/tools"; | |||
import { useI18n } from "vue-i18n"; | |||
import { getStatus } from "./videoStatus"; | |||
import { useRouter } from "vue-router"; | |||
import dayjs from "dayjs"; | |||
const router = useRouter(); | |||
// 当前语言状态与国际化组件 | |||
const { locale } = useI18n(); | |||
const lanChange = ref(locale); | |||
const title = ref(""); | |||
const pageNum = ref(1); | |||
const pageSize = ref(10); | |||
const total = ref(0); | |||
// 弹窗显示隐藏 | |||
const showDialog = ref(false); | |||
const isLoading = ref(true); | |||
// 当前作品 | |||
const presentItem = ref(""); | |||
// 作品列表 | |||
const videoList = ref([]); | |||
const handleSizeChange = (val) => { | |||
pageSize.value = val; | |||
getPhotoVideoList(pageNum.value, pageSize.value, title.value); | |||
}; | |||
const handleCurrentChange = (val) => { | |||
pageNum.value = val; | |||
getPhotoVideoList(pageNum.value, pageSize.value, title.value); | |||
}; | |||
// 点击作品 | |||
function clickVideoItem(item) { | |||
showDialog.value = true; | |||
presentItem.value = item; | |||
} | |||
// 删除框是否显示 | |||
let showPopup = ref(false); | |||
function mouseleave() { | |||
showPopup.value = false; | |||
} | |||
function mouseenter(item) { | |||
presentItem.value = item; | |||
showPopup.value = !showPopup.value; | |||
} | |||
// 下载 | |||
function handleDownload(itemVideoPlayUrl) { | |||
if (itemVideoPlayUrl) { | |||
const link = document.createElement("a"); | |||
link.href = itemVideoPlayUrl; | |||
document.body.appendChild(link); | |||
link.click(); | |||
document.body.removeChild(link); | |||
} else { | |||
ElMessage.error( | |||
lanChange.value == "zh-cn" ? `无法下载` : `Unable to download` | |||
); | |||
} | |||
} | |||
function goCreate() { | |||
router.push("/createVideo"); | |||
} | |||
function searchVideoList() { | |||
getPhotoVideoList(pageNum.value, pageSize.value, title.value, true); | |||
} | |||
function handelDelete(id, title) { | |||
const msg = | |||
lanChange.value == "zh-cn" | |||
? `确定要删除这个作品吗? 作品标题:${title}` | |||
: `Are you sure to delete this video ? Title:${title};`; | |||
const notice = lanChange.value == "zh-cn" ? "提示" : "Notice"; | |||
const confirm = lanChange.value == "zh-cn" ? "确定" : "Confirm"; | |||
const cancel = lanChange.value == "zh-cn" ? "取消" : "Cancel"; | |||
ElMessageBox.confirm(msg, notice, { | |||
confirmButtonText: confirm, | |||
cancelButtonText: cancel, | |||
}).then(() => { | |||
delPhotoVideo(id); | |||
}); | |||
} | |||
/** | |||
* @description:获取我的视频作品 | |||
* @return: data | |||
*/ | |||
function getPhotoVideoList(pageNum, pageSize, title, searchFlag) { | |||
userPhotoVideoList(pageNum, pageSize, title) | |||
.then((res) => { | |||
console.log(res, "res"); | |||
res.data.list.forEach((item) => { | |||
// item.createDate = timestampToTime(item.createDate) || ""; | |||
// item.updateDate = timestampToTime(item.updateDate) || ""; | |||
// item.createVideoDate = timestampToTime(item.createVideoDate) || ""; | |||
item.statusName = getStatus(item.videoStatus); | |||
}); | |||
videoList.value = res.data.list; | |||
total.value = res.data.total * 1; | |||
if (searchFlag) { | |||
const total = res.data.total; | |||
const msg1 = | |||
lanChange.value == "zh-cn" | |||
? `查询成功!找到${total}条符合条件的视频` | |||
: `Found! Match ${total} videos`; | |||
const msg2 = | |||
lanChange.value == "zh-cn" | |||
? `查询成功!没有符合条件的视频` | |||
: `Found! No match videos`; | |||
const msg3 = | |||
lanChange.value == "zh-cn" | |||
? `查询成功!已加载全部视频` | |||
: `Found! Present all videos`; | |||
return title | |||
? ElMessage.success(total > 0 ? msg1 : msg2) | |||
: ElMessage.success(msg3); | |||
} | |||
isLoading.value = false; | |||
}) | |||
.catch((err) => { | |||
console.log(err, "err"); | |||
}); | |||
} | |||
/** | |||
* @description:删除视频作品 | |||
* @param: videoID | |||
* @return: data | |||
*/ | |||
function delPhotoVideo(videoID) { | |||
delUserPhotoVideoByID(videoID) | |||
.then((res) => { | |||
console.log(res, "res"); | |||
const msg = lanChange.value == "zh-cn" ? `删除成功!` : `Success !`; | |||
ElMessage.success(msg); | |||
showDialog.value = false; | |||
getPhotoVideoList(pageNum.value, pageSize.value, title.value); | |||
}) | |||
.catch((err) => { | |||
console.log(err, "err"); | |||
ElMessage.error(err.message); | |||
}); | |||
} | |||
onMounted(() => { | |||
isLoading.value = true; | |||
getPhotoVideoList(pageNum.value, pageSize.value, title.value); | |||
}); | |||
</script> | |||
<style lang="scss" scoped> | |||
.page { | |||
min-height: calc(100vh - 80px); | |||
padding: 15px 130px; | |||
background-color: #fff; | |||
border-radius: 25px 0 0; | |||
.top { | |||
margin-bottom: 35px; | |||
} | |||
.loadingIcon { | |||
height: 100px; | |||
line-height: 100px; | |||
text-align: center; | |||
transform: scale(2); | |||
.loadingText { | |||
position: relative; | |||
top: -2px; | |||
left: 4px; | |||
} | |||
} | |||
.workList { | |||
flex-wrap: wrap; | |||
justify-content: flex-start; | |||
width: 100%; | |||
// height: 200px; | |||
margin-top: 5px; | |||
padding-bottom: 50px; | |||
cursor: pointer; | |||
.workItem { | |||
position: relative; | |||
width: 300px; | |||
// height: 200px; | |||
margin: 0 20px 20px 0; | |||
border: 2px solid #c2c2c2; | |||
border-radius: 15px; | |||
cursor: pointer; | |||
overflow: hidden; | |||
.workImg { | |||
position: relative; | |||
width: 100%; | |||
height: 170px; | |||
cursor: pointer; | |||
padding: 10px; | |||
overflow: hidden; | |||
.coverImg { | |||
max-width: 90%; | |||
max-height: 170px; | |||
cursor: pointer; | |||
} | |||
.takePlace { | |||
position: absolute; | |||
top: 40%; | |||
width: 80%; | |||
} | |||
} | |||
.more { | |||
position: absolute; | |||
top: 0; | |||
left: 0; | |||
width: 100%; | |||
height: 170px; | |||
background-color: rgb(0 0 0 / 30%); | |||
cursor: pointer; | |||
opacity: 0; | |||
transition: all 0.2s; | |||
.icon-videoPlay { | |||
position: absolute; | |||
top: 50%; | |||
left: 50%; | |||
width: 50px; | |||
height: 50px; | |||
font-size: 50px; | |||
color: #fff; | |||
text-align: center; | |||
transform: translate(-50%, -50%); | |||
cursor: pointer; | |||
} | |||
.showBtn { | |||
position: absolute; | |||
top: 10px; | |||
right: 10px; | |||
width: 20px; | |||
height: 20px; | |||
font-size: 20px; | |||
line-height: 10px; | |||
color: #fff; | |||
text-align: center; | |||
transition: all 0.3s; | |||
cursor: pointer; | |||
border-radius: 2px; | |||
&:hover { | |||
background-color: #00000056; | |||
} | |||
} | |||
.btns { | |||
position: absolute; | |||
top: 45px; | |||
right: 10px; | |||
// width: 70px; | |||
// height: 60px; | |||
overflow: hidden; | |||
border-radius: 10px; | |||
cursor: pointer; | |||
&-item { | |||
// width: 70px; | |||
// height: 30px; | |||
text-align: center; | |||
background-color: #fff; | |||
padding: 0 10px; | |||
transition: all 0.3s; | |||
cursor: pointer; | |||
&-icon { | |||
position: relative; | |||
display: inline-block; | |||
width: 20px; | |||
height: 20px; | |||
cursor: pointer; | |||
.icon-delete { | |||
position: absolute; | |||
top: 4px; | |||
left: 0; | |||
width: 20px; | |||
height: 20px; | |||
font-size: 16px; | |||
color: #000; | |||
text-align: center; | |||
cursor: pointer; | |||
} | |||
} | |||
.text { | |||
display: inline-block; | |||
height: 30px; | |||
padding-left: 5px; | |||
margin-top: -5px; | |||
line-height: 20px; | |||
cursor: pointer; | |||
} | |||
&:hover { | |||
color: #ffffff; | |||
background-color: #0000001a; | |||
} | |||
} | |||
} | |||
} | |||
.workInfo { | |||
justify-content: space-between; | |||
height: 40px; | |||
padding: 0 10px; | |||
line-height: 40px; | |||
border-top: 1px solid #afafaf; | |||
white-space: nowrap; | |||
overflow: hidden; | |||
text-overflow: ellipsis; | |||
font-size: 13px; | |||
} | |||
.statusName { | |||
position: absolute; | |||
top: 10%; | |||
left: -10%; | |||
width: 120px; | |||
text-align: center; | |||
color: #ffffff; | |||
font-size: 14px; | |||
transform: rotate(-45deg); | |||
&.manuscript { | |||
/* 草稿 */ | |||
background: linear-gradient(to right, #d7d2cc 0%, #304352 100%); | |||
} | |||
&.generating { | |||
/* 生成中 */ | |||
background: linear-gradient(270deg, #4b61dc, #15d1b8); | |||
} | |||
&.generationFailed { | |||
/* 生成失败 */ | |||
background: linear-gradient(120deg, #f093fb 0%, #f5576c 100%); | |||
} | |||
&.generationSucceed { | |||
/* 生成成功 */ | |||
background: linear-gradient(120deg, #84fab0 0%, #8fd3f4 100%); | |||
} | |||
} | |||
&:hover { | |||
.more { | |||
opacity: 1; | |||
} | |||
} | |||
} | |||
} | |||
.noList { | |||
.title { | |||
font-size: 30px; | |||
text-align: center; | |||
margin-top: 100px; | |||
background-image: -webkit-linear-gradient( | |||
left, | |||
#4b61dc, | |||
#15d1b8 25%, | |||
#4b61dc 50%, | |||
#15d1b8 75%, | |||
#4b61dc | |||
) !important; | |||
-webkit-text-fill-color: transparent !important; | |||
-webkit-background-clip: text !important; | |||
background-clip: text !important; | |||
-webkit-background-size: 200% 100% !important; | |||
background-size: 200% 100% !important; | |||
} | |||
.goCreate { | |||
width: 200px; | |||
height: 40px; | |||
line-height: 40px; | |||
text-align: center; | |||
color: #ffffff; | |||
border-radius: 6px; | |||
background: linear-gradient(270deg, #4b61dc, #15d1b8); | |||
margin: 30px auto; | |||
cursor: pointer; | |||
transition: all 0.3s; | |||
&:hover { | |||
transform: scale(1.1); | |||
} | |||
} | |||
} | |||
.pagination { | |||
position: fixed; | |||
bottom: 20px; | |||
left: 50%; | |||
transform: translateX(-50%); | |||
background-color: #ffffff; | |||
padding: 10px 20px; | |||
border-radius: 8px; | |||
transition: all 0.3s; | |||
cursor: pointer; | |||
&:hover { | |||
background-color: #b8b8b873; | |||
} | |||
} | |||
} | |||
:deep(.el-input__wrapper) { | |||
border: none; | |||
border-bottom: 2px solid #000; | |||
border-radius: 0; | |||
box-shadow: none; | |||
} | |||
.el-icon.icon-edit { | |||
width: 30px; | |||
height: 30px; | |||
font-size: 30px; | |||
color: #8c939d; | |||
text-align: center; | |||
cursor: pointer; | |||
transition: all 0.3s; | |||
&:hover { | |||
color: #292929; | |||
} | |||
} | |||
.elDialog { | |||
.videoBox { | |||
// display: flex; | |||
// justify-content: space-between; | |||
width: 100%; | |||
padding: 20px; | |||
video { | |||
width: 100%; | |||
max-height: 600px; | |||
margin: auto; | |||
} | |||
.insteadVideo { | |||
font-size: 20px; | |||
font-weight: 600; | |||
margin: 20px auto; | |||
text-align: center; | |||
} | |||
} | |||
.dialog-footer { | |||
display: flex; | |||
justify-content: space-between; | |||
.elBtn { | |||
color: #fff; | |||
border-radius: 10px; | |||
padding: 5px 10px; | |||
cursor: pointer; | |||
background-color: #000; | |||
transition: all 0.3s; | |||
&:hover { | |||
background-color: #15d1b8; | |||
} | |||
} | |||
:deep(.el-icon) { | |||
margin-right: 5px; | |||
font-weight: 700; | |||
} | |||
} | |||
} | |||
:deep(.el-dialog) { | |||
overflow: hidden; | |||
border-radius: 25px; | |||
} | |||
// :deep(.el-dialog__header) { | |||
// } | |||
:deep(.el-dialog__title) { | |||
background-image: -webkit-linear-gradient( | |||
left, | |||
#4b61dc, | |||
#15d1b8 25%, | |||
#4b61dc 50%, | |||
#15d1b8 75%, | |||
#4b61dc | |||
) !important; | |||
-webkit-text-fill-color: transparent !important; | |||
-webkit-background-clip: text !important; | |||
background-clip: text !important; | |||
-webkit-background-size: 200% 100% !important; | |||
background-size: 200% 100% !important; | |||
} | |||
:deep(.el-dialog__body) { | |||
padding: 0; | |||
} | |||
:deep(.dialog__footer) { | |||
padding: 0; | |||
} | |||
</style> |
@@ -2,8 +2,9 @@ | |||
<div class="page"> | |||
<!-- 搜索框 --> | |||
<div class="top"> | |||
<div class="flex justify-between" style="width: 300px"> | |||
<div class="search"> | |||
<el-input | |||
style="width: 300px" | |||
v-model="title" | |||
:placeholder="$t('myCreating.search')" | |||
@keyup.enter="searchVideoList" | |||
@@ -11,6 +12,14 @@ | |||
<el-icon class="icon-edit" @click="searchVideoList" | |||
><i-ep-search | |||
/></el-icon> | |||
<!-- 重置按钮 --> | |||
<div v-show="title" class="resetSearch" @click="resetSearch"> | |||
{{ $t("myCreating.reset") }} | |||
</div> | |||
</div> | |||
<!-- 更新视频提示 --> | |||
<div class="hasNewVideoTips" :class="hasNewCreate ? '' : 'hasNew'"> | |||
{{ $t("myCreating.hasNewVideo") }} | |||
</div> | |||
</div> | |||
<!-- 作品列表 --> | |||
@@ -62,7 +71,7 @@ | |||
><i-ep-closeBold | |||
/></el-icon> | |||
<el-icon | |||
v-if="[5].includes(item.videoStatus)" | |||
v-if="[5, 7, 8, 9, 10, 11, 12].includes(item.videoStatus)" | |||
class="icon-videoPlay" | |||
><i-ep-videoPlay | |||
/></el-icon> | |||
@@ -71,7 +80,7 @@ | |||
<!-- 下载按钮 --> | |||
<div | |||
class="btns-item" | |||
@click.stop="handleDownload(item.videoPlayUrl)" | |||
@click.stop="handleDownload(item.videoPlayUrl, item.title)" | |||
> | |||
<div class="btns-item-icon"> | |||
<el-icon class="icon-delete"><i-ep-download /></el-icon> | |||
@@ -126,6 +135,14 @@ | |||
center | |||
> | |||
<div class="videoBox"> | |||
<!-- 免费用户1分钟 [5].includes(presentItem.videoStatus)--> | |||
<div | |||
v-if="presentItem.videoPlayUrl && presentItem.videoTime > 60" | |||
class="overtimeTips" | |||
> | |||
<img src="../../assets/img/tips.png" alt="" /> | |||
{{ $t("myCreating.freeUserTip") }} | |||
</div> | |||
<video | |||
v-if="presentItem.videoPlayUrl" | |||
controls="true" | |||
@@ -134,29 +151,68 @@ | |||
></video> | |||
<div v-else class="insteadVideo"> | |||
{{ $t("myCreating." + presentItem.statusName + "Text") }} | |||
<span v-if="presentItem.videoStatus == 3">{{ | |||
<p v-if="lanChange == 'zh-cn' && presentItem.videoStatus == 3"> | |||
错误原因: {{ presentItem.videoMsg }} | |||
</p> | |||
<p v-if="lanChange == 'en' && presentItem.videoStatus == 3"> | |||
Error cause: {{ presentItem.videoMsg }} | |||
</p> | |||
<!-- <span v-if="presentItem.videoStatus == 3">{{ | |||
presentItem.videoMsg | |||
}}</span> | |||
}}</span> --> | |||
</div> | |||
<!-- 有视频路径的页脚 --> | |||
<div class="footer" v-if="presentItem.videoPlayUrl"> | |||
<span class="dialog-footer"> | |||
<!-- dialog下载按钮 --> | |||
<!-- dialog下载按钮 upGrades--> | |||
<button | |||
class="elBtn" | |||
type="primary" | |||
@click.stop="handleDownload(presentItem.videoPlayUrl)" | |||
@click.stop=" | |||
handleDownload(presentItem.videoPlayUrl, presentItem.title) | |||
" | |||
> | |||
<el-icon class="icon-delete"><i-ep-download /></el-icon | |||
>{{ $t("myCreating.download") }} | |||
<!-- <el-icon class="icon-delete"><i-ep-download /></el-icon> --> | |||
{{ $t("myCreating.download") }} | |||
</button> | |||
<!-- 提升画质 v-if="presentItem.isHy == 0"--> | |||
<el-tooltip | |||
style="cursor: pointer" | |||
v-if="false" | |||
class="box-item" | |||
effect="light" | |||
:content="$t('myCreating.upSharpnessTips')" | |||
placement="top" | |||
> | |||
<button | |||
style="cursor: pointer" | |||
v-if="presentItem.isHy == 0" | |||
class="elBtn upSharpness" | |||
@click.stop="upGrades(presentItem.id)" | |||
> | |||
<div v-if="!upGradesLoading" style="cursor: pointer"> | |||
<img src="../../assets//img/Upgrades.png" alt="" /> | |||
{{ $t("myCreating.upSharpness") }} | |||
</div> | |||
<div v-if="upGradesLoading"> | |||
<!-- <my-Loading-Icon | |||
class="previewAudioLoadingBox" | |||
v-if="!upGradesLoading" | |||
></my-Loading-Icon> --> | |||
<el-icon class="is-loading" style="font-size: 16px" | |||
><i-ep-Loading | |||
/></el-icon> | |||
</div> | |||
</button> | |||
</el-tooltip> | |||
<!-- dialog删除按钮 --> | |||
<button | |||
class="elBtn" | |||
@click.stop="handelDelete(presentItem.id, presentItem.title)" | |||
> | |||
<el-icon class="icon-delete"><i-ep-delete /></el-icon | |||
>{{ $t("myCreating.delete") }} | |||
<!-- <el-icon class="icon-delete"><i-ep-delete /></el-icon> --> | |||
{{ $t("myCreating.delete") }} | |||
</button> | |||
</span> | |||
</div> | |||
@@ -186,7 +242,7 @@ | |||
<el-pagination | |||
v-model:current-page="pageNum" | |||
v-model:page-size="pageSize" | |||
:page-sizes="[10, 20, 50, 100, 200]" | |||
:page-sizes="[12, 24, 36]" | |||
:small="false" | |||
:disabled="false" | |||
:background="true" | |||
@@ -200,12 +256,19 @@ | |||
</template> | |||
<script setup> | |||
import { userPhotoVideoList, delUserPhotoVideoByID } from "@/apis/myCreating"; | |||
//#region 导入 | |||
import { | |||
userPhotoVideoListApi, | |||
delUserPhotoVideoByIDApi, | |||
videoHyApi, | |||
checkVideoStatusApi, | |||
} from "@/apis/myCreating"; | |||
import { timestampToTime } from "@/utils/tools"; | |||
import { useI18n } from "vue-i18n"; | |||
import { getStatus } from "./videoStatus"; | |||
import { useRouter } from "vue-router"; | |||
import dayjs from "dayjs"; | |||
//#endregion ---------------------- | |||
const router = useRouter(); | |||
// 当前语言状态与国际化组件 | |||
const { locale } = useI18n(); | |||
@@ -213,7 +276,7 @@ const lanChange = ref(locale); | |||
const title = ref(""); | |||
const pageNum = ref(1); | |||
const pageSize = ref(10); | |||
const pageSize = ref(12); | |||
const total = ref(0); | |||
// 弹窗显示隐藏 | |||
const showDialog = ref(false); | |||
@@ -250,13 +313,20 @@ function mouseenter(item) { | |||
showPopup.value = !showPopup.value; | |||
} | |||
// 下载 | |||
function handleDownload(itemVideoPlayUrl) { | |||
function handleDownload(itemVideoPlayUrl, name) { | |||
if (itemVideoPlayUrl) { | |||
const link = document.createElement("a"); | |||
link.href = itemVideoPlayUrl; | |||
document.body.appendChild(link); | |||
link.click(); | |||
document.body.removeChild(link); | |||
console.log(1); | |||
let a = document.createElement("a"); | |||
a.download = `${name}.mp4`; | |||
a.href = itemVideoPlayUrl; | |||
// 设置下载文件名 | |||
a.style.display = "none"; | |||
document.body.appendChild(a); | |||
a.click(); | |||
a.remove(); | |||
ElMessage.success( | |||
lanChange.value == "zh-cn" ? `下载成功` : `Download Successfully` | |||
); | |||
} else { | |||
ElMessage.error( | |||
lanChange.value == "zh-cn" ? `无法下载` : `Unable to download` | |||
@@ -268,10 +338,15 @@ function goCreate() { | |||
router.push("/createVideo"); | |||
} | |||
function resetSearch(params) { | |||
title.value = ""; | |||
searchVideoList(); | |||
} | |||
// 搜索 | |||
function searchVideoList() { | |||
getPhotoVideoList(pageNum.value, pageSize.value, title.value, true); | |||
} | |||
// 删除 | |||
function handelDelete(id, title) { | |||
const msg = | |||
lanChange.value == "zh-cn" | |||
@@ -287,15 +362,65 @@ function handelDelete(id, title) { | |||
delPhotoVideo(id); | |||
}); | |||
} | |||
// 升级画质 | |||
const upGradesLoading = ref(false); | |||
function upGrades(id) { | |||
if (upGradesLoading.value == true) { | |||
ElMessage.error( | |||
lanChange.value == "zh-cn" | |||
? `正在升级,请稍后~` | |||
: `Upgrading, please wait ~` | |||
); | |||
return; | |||
} | |||
const msg = | |||
lanChange.value == "zh-cn" | |||
? `确定要升级该作品吗?` | |||
: `Are you sure you want to upgrade this work?`; | |||
const notice = lanChange.value == "zh-cn" ? "提示" : "Notice"; | |||
const confirm = lanChange.value == "zh-cn" ? "确定" : "Confirm"; | |||
const cancel = lanChange.value == "zh-cn" ? "取消" : "Cancel"; | |||
ElMessageBox.confirm(msg, notice, { | |||
confirmButtonText: confirm, | |||
cancelButtonText: cancel, | |||
}).then(async () => { | |||
upGradesLoading.value = true; | |||
try { | |||
const res = await videoHyApi(id); | |||
ElMessage.success( | |||
lanChange.value == "zh-cn" | |||
? `升级中!请稍等~` | |||
: `Upgrading! Please wait a moment` | |||
); | |||
showDialog.value = false; | |||
upGradesLoading.value = false; | |||
getPhotoVideoList(pageNum.value, pageSize.value, title.value); | |||
} catch (error) { | |||
ElMessage.error( | |||
lanChange.value == "zh-cn" | |||
? `升级失败,请稍后重试` | |||
: `Upgrade failed, please try again later` | |||
); | |||
upGradesLoading.value = false; | |||
} | |||
}); | |||
} | |||
/** | |||
* @description:获取我的视频作品 | |||
* @return: data | |||
*/ | |||
function getPhotoVideoList(pageNum, pageSize, title, searchFlag) { | |||
userPhotoVideoList(pageNum, pageSize, title) | |||
userPhotoVideoListApi(pageNum, pageSize, title) | |||
.then((res) => { | |||
console.log(res, "res"); | |||
notGenerateList.value = []; | |||
// 找到数组中未生成的,放入列表,为了刷新 | |||
res.data.list.forEach((item) => { | |||
if ([1, 2, 4, 6, 7, 8, 10, 12].includes(item.videoStatus)) { | |||
notGenerateList.value.push(item.id); | |||
} | |||
}); | |||
res.data.list.forEach((item) => { | |||
// item.createDate = timestampToTime(item.createDate) || ""; | |||
// item.updateDate = timestampToTime(item.updateDate) || ""; | |||
@@ -335,7 +460,7 @@ function getPhotoVideoList(pageNum, pageSize, title, searchFlag) { | |||
* @return: data | |||
*/ | |||
function delPhotoVideo(videoID) { | |||
delUserPhotoVideoByID(videoID) | |||
delUserPhotoVideoByIDApi(videoID) | |||
.then((res) => { | |||
console.log(res, "res"); | |||
const msg = lanChange.value == "zh-cn" ? `删除成功!` : `Success !`; | |||
@@ -348,27 +473,108 @@ function delPhotoVideo(videoID) { | |||
ElMessage.error(err.message); | |||
}); | |||
} | |||
//#region 定时刷新 | |||
const hasNewCreate = ref(false); //提示框显示隐藏 | |||
const notGenerateList = ref([]); //页面上未生成的视频的id 普通视频 | |||
const notGenerateList2 = ref([]); //页面上未生成的视频的id 升级视频 | |||
const checkVideoStatus = async () => { | |||
// 如果列表中没有生成中的视频,不发送请求 | |||
const intervalId = setInterval(async () => { | |||
// && notGenerateList2.value.length == 0; | |||
if (notGenerateList.value.length == 0) { | |||
return; | |||
} | |||
try { | |||
const res = await checkVideoStatusApi(notGenerateList.value); | |||
// 如果有新的,则提示且更新列表 | |||
if (res.code != 2115) { | |||
hasNewCreate.value = true; | |||
const changeHasNewCreate = setTimeout(() => { | |||
hasNewCreate.value = false; | |||
}, 5000); | |||
notGenerateList.value = []; | |||
// notGenerateList2.value = []; | |||
getPhotoVideoList(pageNum.value, pageSize.value, title.value); | |||
} | |||
} catch (error) {} | |||
}, 3000); | |||
onUnmounted(() => { | |||
clearInterval(intervalId); | |||
}); | |||
}; | |||
//#endregion --------------------------- | |||
//#region 页面初始化 | |||
onMounted(() => { | |||
isLoading.value = true; | |||
getPhotoVideoList(pageNum.value, pageSize.value, title.value); | |||
// checkVideoStatus(); | |||
}); | |||
//#endregion --------------------------- | |||
</script> | |||
<style lang="scss" scoped> | |||
.page { | |||
min-height: calc(100vh - 80px); | |||
position: relative; | |||
height: calc(100vh - 80px); | |||
padding: 15px 130px; | |||
background-color: #fff; | |||
border-radius: 25px 0 0; | |||
width: 100%; | |||
overflow: hidden; | |||
overflow-y: auto; | |||
.top { | |||
position: relative; | |||
margin-bottom: 35px; | |||
.search { | |||
display: flex; | |||
justify-content: flex-start; | |||
.resetSearch { | |||
margin-left: 10px; | |||
padding: 0 10px; | |||
height: 30px; | |||
width: 100px; | |||
text-align: center; | |||
line-height: 30px; | |||
background-color: #000; | |||
border: 1px solid #000; | |||
border-radius: 15px; | |||
cursor: pointer; | |||
transition: all 0.3s; | |||
color: #fff; | |||
&:hover { | |||
background-color: #fff; | |||
color: #000; | |||
} | |||
} | |||
} | |||
.hasNewVideoTips { | |||
position: absolute; | |||
top: 0; | |||
left: 50%; | |||
transform: translateX(-50%); | |||
width: 400px; | |||
padding: 5px 30px; | |||
color: #fff; | |||
background-color: #000; | |||
text-align: center; | |||
font-size: 20px; | |||
transition: all 0.5s; | |||
opacity: 1; | |||
border-radius: 15px; | |||
} | |||
.hasNew { | |||
opacity: 0; | |||
transition: all 0.5s; | |||
} | |||
} | |||
.loadingIcon { | |||
height: 100px; | |||
line-height: 100px; | |||
text-align: center; | |||
transform: scale(2); | |||
.loadingText { | |||
position: relative; | |||
top: -2px; | |||
@@ -382,7 +588,6 @@ onMounted(() => { | |||
// height: 200px; | |||
margin-top: 5px; | |||
padding-bottom: 50px; | |||
cursor: pointer; | |||
.workItem { | |||
position: relative; | |||
width: 300px; | |||
@@ -495,6 +700,7 @@ onMounted(() => { | |||
padding-left: 5px; | |||
margin-top: -5px; | |||
line-height: 20px; | |||
font-size: 13px; | |||
cursor: pointer; | |||
} | |||
&:hover { | |||
@@ -542,6 +748,18 @@ onMounted(() => { | |||
/* 生成成功 */ | |||
background: linear-gradient(120deg, #84fab0 0%, #8fd3f4 100%); | |||
} | |||
&.videoUping { | |||
/* 升级中 */ | |||
background: linear-gradient(120deg, #4512c7 0%, #4fd334 100%); | |||
} | |||
&.videoUpFailed { | |||
/* HD */ | |||
background: linear-gradient(120deg, #ff0019 0%, #ff00c3 100%); | |||
} | |||
&.videoUpSucceed { | |||
/* HD */ | |||
background: linear-gradient(120deg, #fdeb71 0%, #d37e1a 100%); | |||
} | |||
} | |||
&:hover { | |||
.more { | |||
@@ -628,6 +846,23 @@ onMounted(() => { | |||
// justify-content: space-between; | |||
width: 100%; | |||
padding: 20px; | |||
.overtimeTips { | |||
// width: 60%; | |||
// display: inline-block; | |||
padding: 5px; | |||
margin-bottom: 10px; | |||
background-color: #ccc; | |||
border-radius: 5px; | |||
line-height: 20px; | |||
color: #000; | |||
text-align: center; | |||
img { | |||
width: 20px; | |||
height: 20px; | |||
vertical-align: middle; | |||
margin-right: 5px; | |||
} | |||
} | |||
video { | |||
width: 100%; | |||
max-height: 600px; | |||
@@ -635,10 +870,13 @@ onMounted(() => { | |||
} | |||
.insteadVideo { | |||
font-size: 20px; | |||
font-weight: 600; | |||
font-weight: 400; | |||
margin: 20px auto; | |||
text-align: center; | |||
} | |||
p { | |||
font-weight: 700; | |||
} | |||
} | |||
.dialog-footer { | |||
@@ -647,7 +885,7 @@ onMounted(() => { | |||
.elBtn { | |||
color: #fff; | |||
border-radius: 10px; | |||
padding: 5px 10px; | |||
padding: 10px 15px; | |||
cursor: pointer; | |||
background-color: #000; | |||
transition: all 0.3s; | |||
@@ -655,7 +893,12 @@ onMounted(() => { | |||
background-color: #15d1b8; | |||
} | |||
} | |||
.upSharpness { | |||
img { | |||
width: 14px; | |||
height: 14px; | |||
} | |||
} | |||
:deep(.el-icon) { | |||
margin-right: 5px; | |||
font-weight: 700; | |||