@@ -5,5 +5,13 @@ NODE_ENV='test' | |||
VITE_APP_TITLE = 'Metavatar-PC' | |||
VITE_APP_PORT = 3000 | |||
# VITE_APP_BASE_API = 'https://photo.metavatar.cc/C' | |||
VITE_APP_BASE_API = 'https://phototest.metavatar.cc/C' | |||
# NODE_ENV='development' | |||
# VITE_APP_TITLE = 'Metavatar-PC' | |||
# VITE_APP_PORT = 3000 | |||
# VITE_APP_BASE_API = 'https://phototest.metavatar.cc/C' | |||
@@ -77,4 +77,4 @@ | |||
"repository": "https://gitee.com/youlaiorg/vue3-element-admin.git", | |||
"author": "有来开源组织", | |||
"license": "MIT" | |||
} | |||
} |
@@ -66,11 +66,18 @@ export function delImgApi(id) { | |||
* @params id | |||
* @returns data | |||
*/ | |||
export function checkPhotoApi(material) { | |||
export function baiduPhotoApi(data) { | |||
const formData = new FormData(); | |||
formData.append('file', data); | |||
return request({ | |||
url: `api/personPhoto/checkPhoto?material=${material}`, | |||
method: 'get', | |||
}) | |||
url: 'api/personPhoto/baiduPhoto', | |||
method: 'post', | |||
data: formData, | |||
headers: { | |||
'Content-Type': 'application/form-data' | |||
} | |||
}); | |||
} | |||
/** | |||
@@ -79,11 +86,17 @@ export function checkPhotoApi(material) { | |||
* @returns data | |||
*/ | |||
export function checkFaceApi(data) { | |||
const formData = new FormData(); | |||
formData.append('file', data); | |||
return request({ | |||
url: `suimang/image_qualit`, | |||
url: 'api/personPhoto/checkPhoto', | |||
method: 'post', | |||
data: { img: data } | |||
}) | |||
data: formData, | |||
headers: { | |||
'Content-Type': 'application/form-data' | |||
} | |||
}); | |||
} | |||
/** | |||
@@ -35,4 +35,16 @@ export function videoHyApi(id) { | |||
url: `/api/userPhotoVideo/videoHy?id=${id}`, | |||
method: 'get' | |||
}) | |||
} | |||
/** | |||
* @description 查询是否有视频更新 | |||
* @params id | |||
* @returns data | |||
*/ | |||
export function checkVideoStatusApi(time) { | |||
return request({ | |||
url: `/api/userPhotoVideo/checkVideoStatus?time${time}`, | |||
method: 'get' | |||
}) | |||
} |
@@ -1,7 +1,11 @@ | |||
import { createI18n } from 'vue-i18n'; | |||
import { useAppStore } from '@/store/modules/app'; | |||
const appStore = useAppStore(); | |||
import { store } from '@/store/index'; // 因为不是在setup环境而是js中,必须重新初始化加载store | |||
const appStore = useAppStore(store); //pinia | |||
// const appStore = useAppStore(); | |||
// 本地语言包 | |||
import enLocale from './package/en'; | |||
import zhCnLocale from './package/zh-cn'; | |||
@@ -72,7 +72,7 @@ export default { | |||
AipictureTip:'Please customize your picture', | |||
chooseMusic:'Choose Music', | |||
searchMusic:'Search Music', | |||
comingSoon:'coming soon', | |||
comingSoon:'COMING SOON', | |||
hotMusic:'Hit song', | |||
inputRequirement:'Please enter your requirements, for example: Please generate a beverage advertisement of 500 words or less', | |||
GeneratedText:'Generated Text', | |||
@@ -81,7 +81,7 @@ export default { | |||
tooltip1:'Click to listen', | |||
tooltip2:'Add Pause', | |||
uploadImgTip1:'Click here to upload pictures', | |||
uploadImgTip2:'Please ensure that the uploaded image is an open face image', | |||
uploadImgTip2:'Please upload unobstructed frontal photo', | |||
entered:'entered' | |||
}, | |||
@@ -97,6 +97,7 @@ export default { | |||
more: "More", | |||
loading: "Loading , just a moment", | |||
loading1: "just a moment", | |||
compress:'Compressed picture', | |||
sensitiveness: "Sensitive information is being tested", | |||
chackFace: "Face detection in progress", | |||
upLoadPictures: "Uploading pictures", | |||
@@ -108,11 +109,12 @@ export default { | |||
generationSucceed: "Succeed", | |||
manuscriptText: "Manuscript", | |||
generatingText: "The video is generating, Please wait a moment", | |||
generationFailedText: "Video generation failed, you can click the button below to edit the generation again ", | |||
generationFailedText: "Video generation failed, click Edit button to retry.", | |||
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 generated!!', | |||
}, | |||
// 个人账户 | |||
@@ -150,7 +152,7 @@ export default { | |||
selectPlan: "SELECT PLAN", | |||
WeChatPay: "WeChatPay", | |||
Alipay: "Alipay", | |||
score:'score', | |||
score:'Credit', | |||
day:'day', | |||
second:'second', | |||
yes:'yes', | |||
@@ -160,7 +162,7 @@ export default { | |||
AIGeneratesPhotoPrices:'AI generates photo unit prices', | |||
UnitChargeDuration:'Unit charge duration', | |||
Language:'Language', | |||
sound:'sound', | |||
sound:'Sound', | |||
IndividualVideoDuration:'Individual video duration', | |||
AIGeneratedPortrait:'AI generated portrait', | |||
PhotoSinging:'Photo singing', | |||
@@ -97,6 +97,7 @@ export default { | |||
more: "更多", | |||
loading: "加载中,请稍等", | |||
loading1: "请稍等", | |||
compress:'正在压缩图片', | |||
sensitiveness: "正在进行敏感信息检测", | |||
chackFace: "正在进行人脸检测", | |||
upLoadPictures: "正在上传图片", | |||
@@ -113,6 +114,7 @@ export default { | |||
upSharpness:'升级画质', | |||
upSharpnessTips:'如果对预览效果满意,您可以点击此处来提升视频清晰度~', | |||
freeUserTip:'免费用户生成视频时长限制为1分钟', | |||
hasNewVideo:'您有新的视频生成了!', | |||
}, | |||
// 个人账户 | |||
@@ -2,6 +2,7 @@ | |||
export {} | |||
declare global { | |||
const EffectScope: typeof import('vue')['EffectScope'] | |||
const ElForm: typeof import('element-plus/es')['ElForm'] | |||
const ElMessage: typeof import('element-plus/es')['ElMessage'] | |||
const ElMessageBox: typeof import('element-plus/es')['ElMessageBox'] | |||
const asyncComputed: typeof import('@vueuse/core')['asyncComputed'] | |||
@@ -270,6 +271,7 @@ import { UnwrapRef } from 'vue' | |||
declare module 'vue' { | |||
interface ComponentCustomProperties { | |||
readonly EffectScope: UnwrapRef<typeof import('vue')['EffectScope']> | |||
readonly ElForm: UnwrapRef<typeof import('element-plus/es')['ElForm']> | |||
readonly ElMessage: UnwrapRef<typeof import('element-plus/es')['ElMessage']> | |||
readonly ElMessageBox: UnwrapRef<typeof import('element-plus/es')['ElMessageBox']> | |||
readonly asyncComputed: UnwrapRef<typeof import('@vueuse/core')['asyncComputed']> | |||
@@ -1,18 +1,20 @@ | |||
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" | |||
// const env = process.env.NODE_ENV == 'development' ? "" : "" | |||
// console.log(process.env.NODE_ENV, 'env') | |||
console.log(env) | |||
/** | |||
* @description:发送Axios请求 | |||
*/ | |||
@@ -42,7 +44,10 @@ request.interceptors.response.use( | |||
function (response) { | |||
if (!response.data) { | |||
ElMessage.error($t('tips.systemException')); | |||
// 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为未登录 | |||
@@ -50,13 +55,19 @@ request.interceptors.response.use( | |||
localStorage.removeItem("AccessToken"); | |||
router.push("/login"); | |||
// ElMessage.error("登录过期,请重新登录!"); | |||
ElMessage.error($t('tips.tokenExpired')); | |||
// 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($t('tips.systemException')); | |||
// ElMessage.error($t('tips.systemException')); | |||
ElMessage.error( | |||
appStore.language == "zh-cn" ? `服务器繁忙,请重试!` : `The server is busy, please try again` | |||
); | |||
// 其他错误 | |||
} else { | |||
return Promise.reject(response.data) | |||
@@ -694,7 +694,7 @@ import { | |||
personPhotoUserListList, | |||
awsImgUpload, | |||
delImgApi, | |||
checkPhotoApi, | |||
baiduPhotoApi, | |||
checkFaceApi, | |||
videoUpload, | |||
saveImgApi, | |||
@@ -850,38 +850,33 @@ async function lookImg(e) { | |||
maxSizeMB: 4, // 最大压缩大小为 4MB | |||
useWebWorker: true, // 使用 Web Worker 进行压缩,提高性能 | |||
}; | |||
imgUploadTips.value = "compress"; | |||
const compressedFile = await imageCompression(e.target.files[0], options); | |||
imgUploadTips.value = "sensitiveness"; | |||
try { | |||
await getImageBase64(compressedFile).then(async (base64) => { | |||
const res = await checkFaceApi(base64); | |||
console.log(res); | |||
// axios({ | |||
// method: "post", | |||
// url: "https://phototest.metavatar.cc/suimang/image_qualit", | |||
// // headers: { | |||
// // "content-type": "multipart/form-data", | |||
// // token: AccessToken, | |||
// // "Access-Control-Allow-Credentials": true, | |||
// // }, | |||
// data: { img: base64 }, | |||
// }).then(async (res) => { | |||
// // const res2 = await checkPhotoApi(compressedFile); | |||
// // // const res = await checkFaceApi(compressedFile); | |||
// // imgUrl.value = URL.createObjectURL(compressedFile); | |||
// // imgData.value = compressedFile; | |||
// // showUploadBtn.value = 4; | |||
// // submitImgBtn.value = true; | |||
// // compressionImgLoad.value = false; | |||
// // await changeFile(); | |||
// }); | |||
}); | |||
const res = await baiduPhotoApi(compressedFile); | |||
if (res.code != 200) { | |||
throw new Error(); | |||
} | |||
// 图片人脸信息检测通过时,继续检测敏感信息 | |||
imgUploadTips.value = "chackFace"; | |||
const res2 = await checkFaceApi(compressedFile); | |||
if (res2.code != 200) { | |||
throw new Error(); | |||
} | |||
imgUrl.value = URL.createObjectURL(compressedFile); | |||
imgData.value = compressedFile; | |||
showUploadBtn.value = 4; | |||
submitImgBtn.value = true; | |||
compressionImgLoad.value = false; | |||
await changeFile(); | |||
} catch (error) { | |||
ElMessage.error( | |||
lanChange.value == "zh-cn" | |||
? `图片涉及敏感内容,上传失败` | |||
: `Image related to sensitive content, upload failed` | |||
? `图片不符合规范,上传失败` | |||
: `Image does not meet the specifications, upload failed` | |||
); | |||
showFace.value = 1; | |||
showUploadBtn.value = 2; | |||
@@ -925,15 +920,25 @@ async function changeFile() { | |||
imgUploadTips.value = "upLoadPictures"; | |||
compressionImgLoad.value = true; | |||
showUploadBtn.value = 3; | |||
imgUrl.value = URL.createObjectURL(imgData.value); | |||
// imgUrl.value = URL.createObjectURL(imgData.value); | |||
let formData = new FormData(); | |||
const timestamp = Date.now(); | |||
formData.append("file", imgData.value, timestamp + ".png"); | |||
try { | |||
// 上传图片 | |||
const res = await awsImgUpload(formData); | |||
if (res.code != 200) { | |||
throw new Error(); | |||
} | |||
// 保存图片 | |||
const res2 = await saveImgApi({ material: res.data.url }); | |||
if (res2.code != 200) { | |||
throw new Error(); | |||
} else { | |||
ElMessage.success( | |||
lanChange.value == "zh-cn" ? `上传成功` : `Upload successfully` | |||
); | |||
} | |||
// 重新获取列表 | |||
compressionImgLoad.value = false; | |||
showUploadBtn.value = 4; | |||
@@ -941,23 +946,17 @@ async function changeFile() { | |||
getAiFaceUserList(1, 10, 3); | |||
pageNumUser.value = 1; | |||
overLoadUser.value = false; | |||
if (res2.code == 200) { | |||
ElMessage.success( | |||
lanChange.value == "zh-cn" ? `上传成功` : `Upload successfully` | |||
); | |||
} else { | |||
ElMessage.error( | |||
lanChange.value == "zh-cn" ? `上传失败` : `Upload failure` | |||
); | |||
} | |||
faceItemActive.value = res2.data.id; | |||
imgUrl.value = res2.data.material; | |||
submitImgBtn.value = false; | |||
console.log(123654); | |||
showUploadBtn.value = 1; | |||
imgUploadTips.value = null; | |||
} catch (error) { | |||
ElMessage.error(lanChange.value == "zh-cn" ? `上传失败` : `Upload failure`); | |||
imgUrl.value = null; | |||
showFace.value = 1; | |||
showUploadBtn.value = 2; | |||
compressionImgLoad.value = false; | |||
imgUploadTips.value = null; | |||
} | |||
} | |||
@@ -972,8 +971,14 @@ function chooseMyImg() { | |||
} | |||
// 点击AI头像区的+号 | |||
function clickAddBtn() { | |||
faceItemActive.value = null; | |||
// if (!imgUploadTips.value) { | |||
// showUploadBtn.value = 3; | |||
// submitImgBtn.value = false; | |||
// compressionImgLoad.value = true; | |||
// return; | |||
// } | |||
showUploadBtn.value = 2; | |||
faceItemActive.value = null; | |||
} | |||
//#endregion --------------------------- | |||
@@ -1084,6 +1089,8 @@ async function getPreviewAudio() { | |||
return; | |||
} | |||
previewAudioUrl.value = res.data.data; | |||
// previewAudioUrl.value = | |||
// "/suimang/static/tts/2023-07-04/16884538755531874.wav"; | |||
let audioA = document.getElementById("audio"); | |||
audioA.addEventListener("loadedmetadata", async () => { | |||
audioA.muted = false; // 将 muted 属性设置为 false | |||
@@ -1208,19 +1215,19 @@ const handleFileChange = async () => { | |||
if (uploadAudioTime.value > 60) { | |||
ElMessage.error( | |||
lanChange.value == "zh-cn" | |||
? `上传失败,免费用户上传音频时长最多为60秒` | |||
: `Upload failed, free users can upload audio for up to 60 seconds` | |||
? `上传音频大小或者长度超出限制,请重新上传` | |||
: `The size or length of the uploaded audio exceeds the limit. Please upload it again` | |||
); | |||
audioUrl.value = null; | |||
audioData.value = null; | |||
return; | |||
} | |||
// 限制3mb | |||
if (file.size / 1048576 > 3) { | |||
// 限制5mb | |||
if (file.size / 1048576 > 5) { | |||
ElMessage.error( | |||
lanChange.value == "zh-cn" | |||
? `上传失败,免费用户上传音频大小最大为3MB` | |||
: `Failed to upload. The maximum size of audio uploaded by free users is 3MB` | |||
? `上传音频大小或者长度超出限制,请重新上传` | |||
: `The size or length of the uploaded audio exceeds the limit. Please upload it again` | |||
); | |||
audioUrl.value = null; | |||
audioData.value = null; | |||
@@ -12,6 +12,10 @@ | |||
><i-ep-search | |||
/></el-icon> | |||
</div> | |||
<!-- 更新视频提示 --> | |||
<div class="hasNewVideoTips" :class="hasNewCreate ? '' : 'hasNew'"> | |||
{{ $t("myCreating.hasNewVideo") }} | |||
</div> | |||
</div> | |||
<!-- 作品列表 --> | |||
<div v-if="!isLoading"> | |||
@@ -127,7 +131,7 @@ | |||
> | |||
<div class="videoBox"> | |||
<!-- 免费用户1分钟 --> | |||
<div v-if="false" class="overtimeTips"> | |||
<div v-if="true" class="overtimeTips"> | |||
<img src="../../assets/img/tips.png" alt="" /> | |||
{{ $t("myCreating.freeUserTip") }} | |||
</div> | |||
@@ -243,6 +247,7 @@ import { | |||
userPhotoVideoListApi, | |||
delUserPhotoVideoByIDApi, | |||
videoHyApi, | |||
checkVideoStatusApi, | |||
} from "@/apis/myCreating"; | |||
import { timestampToTime } from "@/utils/tools"; | |||
import { useI18n } from "vue-i18n"; | |||
@@ -428,23 +433,70 @@ function delPhotoVideo(videoID) { | |||
ElMessage.error(err.message); | |||
}); | |||
} | |||
//#region 定时刷新 | |||
const hasNewCreate = ref(false); //提示框显示隐藏 | |||
const nowTime = Date.now(); | |||
const checkVideoStatus = async () => { | |||
console.log(nowTime); | |||
// const res = await checkVideoStatusApi(); | |||
// if (res.code == 2115) { | |||
// hasNewCreate.value = true; | |||
// const changeHasNewCreate = setTimeout(() => { | |||
// hasNewCreate.value = false; | |||
// }, 5000); | |||
// getPhotoVideoList(1, 10, ""); | |||
// } | |||
}; | |||
const startLogin = () => { | |||
// 每隔 3 秒执行一次登录请求 | |||
const intervalId = setInterval(checkVideoStatus, 3000); | |||
// 在组件销毁前清除定时器 | |||
onUnmounted(() => { | |||
clearInterval(intervalId); | |||
}); | |||
}; | |||
//#endregion --------------------------- | |||
//#region 页面初始化 | |||
onMounted(() => { | |||
isLoading.value = true; | |||
getPhotoVideoList(pageNum.value, pageSize.value, title.value); | |||
// startLogin(); | |||
}); | |||
//#endregion --------------------------- | |||
</script> | |||
<style lang="scss" scoped> | |||
.page { | |||
position: relative; | |||
min-height: calc(100vh - 80px); | |||
padding: 15px 130px; | |||
background-color: #fff; | |||
border-radius: 25px 0 0; | |||
width: 100%; | |||
overflow: hidden; | |||
.top { | |||
position: relative; | |||
margin-bottom: 35px; | |||
.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; | |||
@@ -465,7 +517,6 @@ onMounted(() => { | |||
// height: 200px; | |||
margin-top: 5px; | |||
padding-bottom: 50px; | |||
cursor: pointer; | |||
.workItem { | |||
position: relative; | |||
width: 300px; | |||
@@ -58,27 +58,16 @@ export default defineConfig(({ mode }: ConfigEnv): UserConfig => { | |||
changeOrigin: true, | |||
rewrite: (path) => path.replace(/^\/api/, ""), | |||
}, | |||
"/api/suimang": { | |||
// target: https://phototest.metavatar.cc/C, // 线上接口地址 | |||
target:'https://phototest.metavatar.cc/', // 线上接口地址 | |||
changeOrigin: true, | |||
rewrite: (path) => path.replace(/^\/api\/suimang/, "/suimang"), | |||
// pathRewrite: { | |||
// "^/api/suimang": "/suimang", | |||
// } | |||
}, | |||
"/suimang": { | |||
// target: https://phototest.metavatar.cc/C, // 线上接口地址 | |||
target:'https://phototest.metavatar.cc/', // 线上接口地址 | |||
changeOrigin: true, | |||
rewrite: (path) => path.replace(/^\/suimang/, '/suimang/image_qualit') | |||
// pathRewrite: { | |||
// "^/api/suimang": "/suimang", | |||
// } | |||
}, | |||
// "/C/api/suimang": { | |||
// target:'https://phototest.metavatar.cc/', // 线上接口地址 | |||
// changeOrigin: true, | |||
// rewrite: (path) => path.replace(/^\/C\/api\/suimang/, '/suimang/image_qualit') | |||
// }, | |||
// "/api/suimang": { | |||
// target:'https://phototest.metavatar.cc/', // 线上接口地址 | |||
// changeOrigin: true, | |||
// rewrite: (path) => path.replace(/^\/api\/suimang/, '/suimang/image_qualit') | |||
// }, | |||
}, | |||
}, | |||
plugins: [ | |||