congzc преди 1 година
родител
ревизия
6479365c39
променени са 9 файла, в които са добавени 235 реда и са изтрити 103 реда
  1. +2
    -1
      README.md
  2. +38
    -2
      src/apis/createVideo.js
  3. +1
    -5
      src/layout/components/Sidebar/Logo.vue
  4. +12
    -0
      src/store/modules/userInfo.js
  5. +12
    -11
      src/styles/sidebar.scss
  6. +1
    -1
      src/utils/request.js
  7. +161
    -80
      src/views/createVideo/index.vue
  8. +4
    -2
      src/views/login/index.vue
  9. +4
    -1
      src/views/myAccount/index.vue

+ 2
- 1
README.md Целия файл

@@ -150,4 +150,5 @@ proxy_pass http://vapi.youlai.tech/;
git commit -m "feat: "
git commit --no-verify -m "提交时的注释" 跳过检验
git commit --no-verify -m "提交时的注释" 跳过检验
git merge origin/dev_

+ 38
- 2
src/apis/createVideo.js Целия файл

@@ -5,9 +5,9 @@ import request from "@/utils/request.js";
* @params pageNum, pageSize
* @returns data
*/
export function personPhotoList(pageNum, pageSize) {
export function personPhotoList(pageNum, pageSize, sendType) {
return request({
url: `api/personPhoto/list?pageNum=${pageNum}&pageSize=${pageSize}`,
url: `api/personPhoto/list?pageNum=${pageNum}&pageSize=${pageSize}&sendType=${sendType}`,
method: 'get'
})
}
@@ -48,4 +48,40 @@ export function videoUpload(data) {
method: 'post',
data
})
}

/**
* @description 获取视频上传进度
* @params videoId
* @returns data
*/
export function uploadProgressApi(videoId) {
return request({
url: `api/video/uploadProgress?videoId=${videoId}`,
method: 'GET',
})
}

/**
* @description 获取语言
* @params
* @returns data
*/
export function voiceTotalApi() {
return request({
url: `api/voiceMould/voiceTotal`,
method: 'GET',
})
}

/**
* @description 获取声音列表
* @params id
* @returns data
*/
export function chooseTypeApi(id) {
return request({
url: `api/voiceMould/chooseType/?id=${id}`,
method: 'GET',
})
}

+ 1
- 5
src/layout/components/Sidebar/Logo.vue Целия файл

@@ -22,14 +22,10 @@ const logo = ref(new URL(`../../../assets/logo.png`, import.meta.url).href);
class="h-full w-full flex items-center justify-center left-top-box"
to="/"
>
<!-- <img
v-if="settingsStore.sidebarLogo"
src="../../../assets/img/left-top-logo.png"
class="w-5 h-5"
/> -->
<img
v-if="settingsStore.sidebarLogo"
src="../../../assets/img/left-top-logo.png"
class="left-top-logo"
/>
<span v-else class="ml-3 text-white text-sm font-bold"></span>
</router-link>


+ 12
- 0
src/store/modules/userInfo.js Целия файл

@@ -0,0 +1,12 @@
import { defineStore } from "pinia";
import { store } from "@/store";
export const userInfoPinia = defineStore("userInfo", () => {
// state
const email = ref("");
function setEmail(params) {
email.value = params
}
return { email, setEmail };
});

+ 12
- 11
src/styles/sidebar.scss Целия файл

@@ -70,11 +70,11 @@
}
}

.is-active > .el-sub-menu__title {
.is-active>.el-sub-menu__title {
color: $subMenuActiveText !important;
}

& .nest-menu .el-sub-menu > .el-sub-menu__title,
& .nest-menu .el-sub-menu>.el-sub-menu__title,
& .el-sub-menu .el-menu-item {
min-width: $sideBarWidth !important;
background-color: $subMenuBg !important;
@@ -87,7 +87,7 @@

.hideSidebar {
.sidebar-container {
width: 54px !important;
// width: 54px !important;

.svg-icon {
margin-right: 0;
@@ -95,13 +95,13 @@
}

.main-container {
margin-left: 54px;
// margin-left: 54px;
}

.el-sub-menu {
overflow: hidden;

& > .el-sub-menu__title {
&>.el-sub-menu__title {
padding: 0 !important;

.svg-icon {
@@ -120,8 +120,8 @@

.el-menu--collapse {
.el-sub-menu {
& > .el-sub-menu__title {
& > span {
&>.el-sub-menu__title {
&>span {
display: inline-block;
width: 0;
height: 0;
@@ -158,6 +158,7 @@
}

.withoutAnimation {

.main-container,
.sidebar-container {
transition: none;
@@ -167,7 +168,7 @@

// when menu collapsed
.el-menu--vertical {
& > .el-menu {
&>.el-menu {
.svg-icon {
margin-right: 16px;
}
@@ -178,7 +179,7 @@
}
}

.nest-menu .el-sub-menu > .el-sub-menu__title,
.nest-menu .el-sub-menu>.el-sub-menu__title,
.el-menu-item {
&:hover {
// you can use $subMenuHover
@@ -187,7 +188,7 @@
}

// the scroll bar appears when the subMenu is too long
> .el-menu--popup {
>.el-menu--popup {
max-height: 100vh;
overflow-y: auto;

@@ -204,4 +205,4 @@
border-radius: 20px;
}
}
}
}

+ 1
- 1
src/utils/request.js Целия файл

@@ -12,7 +12,7 @@ console.log(process.env.NODE_ENV, 'env')
*/
const request = axios.create({
baseURL: env,
timeout: 5000
timeout: 15000
})

// 请求拦截器


+ 161
- 80
src/views/createVideo/index.vue Целия файл

@@ -99,9 +99,10 @@
v-for="item in AiFaceList"
:key="item.id"
>
<!-- <div class="img"></div> -->
<img :src="item.material" alt="" />
<p>{{ item.title }}</p>
<div class="imgBox">
<img :src="item.material" alt="" />
</div>
s
</div>
</div>
</div>
@@ -131,7 +132,9 @@
<div @click="insertIcon">🕗</div>
</div>
<p>{{ $t("createVideo.languages") }}</p>
<!-- 语言 -->
<el-select
@change="chooseType"
v-model="language"
placeholder="Select"
style="width: 100%"
@@ -139,34 +142,50 @@
<el-option
v-for="item in languageOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
:label="item.name"
:value="item.id"
>
<img
:src="item.img"
alt=""
style="
width: 20px;
margin-right: 10px;
vertical-align: text-top;
"
/>
<span style="line-height: 20px">{{ item.name }}</span>
</el-option>
</el-select>
<p>{{ $t("createVideo.voices") }}</p>
<!-- 声音 -->
<el-select
v-model="language"
placeholder="Select"
@change="chooseStyle"
:disabled="language ? false : true"
v-model="sound"
:placeholder="language ? 'Select' : 'Please select language'"
style="width: 100%"
>
<el-option
v-for="item in soundOptions"
:key="item.value"
:label="item.label"
:value="item.value"
:label="item.displayName"
:value="item.id"
/>
</el-select>
<p>{{ $t("createVideo.styles") }}</p>
<!-- 风格 -->
<el-select
v-model="language"
placeholder="Select"
:disabled="soundStyleOptions ? false : true"
v-model="soundStyle"
:placeholder="soundStyleOptions ? 'Select' : '暂无数据'"
style="width: 100%"
>
<el-option
v-for="item in soundStyleOptions"
:key="item.value"
:label="item.label"
:value="item.value"
:key="item"
:label="item"
:value="item"
/>
</el-select>
<el-button color="#000" type="primary">{{
@@ -184,8 +203,14 @@
</p>
<div class="uploadVideo">
+
<input type="file" ref="fileInput" @change="handleFileChange" />
<input
type="file"
ref="fileInput"
@click="e.target.value = null"
@change="handleFileChange"
/>
<video
style="position: absolute; top: 0"
v-if="videoSrc"
:src="videoSrc"
ref="videoPlayer"
@@ -305,6 +330,9 @@ import {
awsImgUpload,
videoUpload,
saveImgApi,
uploadProgressApi,
voiceTotalApi,
chooseTypeApi,
} from "@/apis/createVideo";
import Recorder from "js-audio-recorder"; //mp3插件
import imageCompression from "browser-image-compression"; //压缩图片插件
@@ -313,9 +341,9 @@ import imageCompression from "browser-image-compression"; //压缩图片插件
// 获取AI头像
const AiFaceList = ref([]);
const feceItemLoading = ref(false);
function getAiFaceList(pageNum, pageSize) {
function getAiFaceList(pageNum, pageSize, sendType) {
feceItemLoading.value = true;
personPhotoList(pageNum, pageSize)
personPhotoList(pageNum, pageSize, sendType)
.then((res) => {
AiFaceList.value = res.data.list;
feceItemLoading.value = false;
@@ -328,6 +356,12 @@ function getAiFaceList(pageNum, pageSize) {
// 选择显示的face列表
const showFace = ref("0");
function ChangeShowFaceList(index) {
if (index == 0) {
getAiFaceList(1, 100, 1);
}
if (index == 1) {
getAiFaceList(1, 100, 3);
}
showFace.value = index;
}
const faceItemActive = ref(); //高亮处理
@@ -409,40 +443,39 @@ function insertIcon() {
textarea.setSelectionRange(newCursorPos, newCursorPos);
textarea.focus();
}
const language = ref("");
const sound = ref("");
const soundStyle = ref("");
const languageOptions = reactive([
const language = ref(""); //选择的语言
const sound = ref(""); // 选择的声音
const soundStyle = ref(""); //选择的风格
// 语言下拉框内容
const languageOptions = ref([
{
value: "0",
label: "中文",
id: "0",
name: "中文",
},
{
value: "1",
label: "English",
},
]);
const soundOptions = reactive([
{
value: "0",
label: "老人",
},
{
value: "1",
label: "小孩",
},
]);
const soundStyleOptions = reactive([
{
value: "0",
label: "生气",
},
{
value: "1",
label: "抑郁",
id: "1",
name: "English",
},
]);
async function voiceTotal() {
const res = await voiceTotalApi();
languageOptions.value = res.data;
}
// 声音下拉框内容
const soundOptions = ref([]);
async function chooseType() {
const res = await chooseTypeApi(language.value);
soundOptions.value = res.data;
sound.value = null;
}
// 风格下拉框内容
const soundStyleOptions = ref(null);
async function chooseStyle() {
const item = soundOptions.value.find((element) => element.id == sound.value);

soundStyle.value = null;
soundStyleOptions.value = item.style;
}
//#endregion

//#region 录制MP3上传语音
@@ -499,15 +532,48 @@ async function uploadAudio(params) {
//#region 上传视频功能
const fileInput = ref(null);
const videoSrc = ref(null);
const handleFileChange = () => {
// const videoId = ref(null);
let intervalId = null; // 用于存储 setInterval 的返回值
const handleFileChange = async () => {
const file = fileInput.value.files[0];
if (file) {
const videoUrl = URL.createObjectURL(file);
videoSrc.value = videoUrl;

let formData = new FormData();
const timestamp = Date.now();
formData.append("file", file, timestamp + ".mp3");
try {
const res = await videoUpload(formData);
// videoId.value = res.videoId;
if (res) {
ElMessage.success("上传成功!");
} else {
ElMessage.error("上传失败!");
return;
}
// 查询进度
intervalId = setInterval(() => {
uploadProgress(res.data.videoId);
}, 1000);
} catch (error) {
ElMessage.error(error);
}
}
};
// 获取视频上传进度
async function uploadProgress(videoId) {
const res = await uploadProgressApi(videoId);
if (res.data == "complete") {
clearInterval(intervalId);
}
}
//#endregion --------------------------------------

//#region 获取语言列表

//#endregion ----------------------------

// $t在script中的使用
const {
appContext: {
@@ -519,7 +585,9 @@ const string = "createVideo.typeScript";
const t = globalProperties.$t(string);

onMounted(() => {
getAiFaceList(1, 100);
getAiFaceList(1, 100, 1);
voiceTotal();
chooseType();
});
</script>

@@ -555,7 +623,7 @@ onMounted(() => {
position: relative;
width: 100%;
// height: 65%;
height: 600px;
height: 580px;

.uploadBtn {
position: relative;
@@ -683,34 +751,43 @@ onMounted(() => {
}

.faceList {
padding: 3px;
padding: 8px;
display: flex;
flex-wrap: wrap;
justify-content: flex-start;
width: 100%;
height: 160px;
height: 180px;
overflow-y: scroll;
.faceItem {
position: relative;
margin-right: 13px;
margin-bottom: 10px;
margin-right: 35px;
margin-bottom: 35px;
width: 150px;
height: 150px;
padding: 0 25px;
// padding: 0 25px;
line-height: 150px;
text-align: center;
border-radius: 50%;
background-color: #fbf8f8;
border: 2px solid #fff;
box-shadow: rgba(0, 0, 0, 0.3) 0px 0px 3px 3px;
overflow: hidden;
input {
box-shadow: rgba(0, 0, 0, 0.3) 0px 0px 5px 5px;
// overflow: hidden;
.imgBox {
cursor: pointer;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
opacity: 0;
right: 0;
bottom: 0;
z-index: 3;
width: 150px;
height: 150px;
border-radius: 50%;
background-color: #fbf8f8;
overflow: hidden;
img {
width: 100%;
}
}
.uploadImg {
line-height: 145px;
@@ -718,26 +795,25 @@ onMounted(() => {
font-weight: 700;
text-align: center;
}
p {
height: 30px;
}
img {
width: 100%;
}
.img {
width: 100px;
height: 100px;
border-radius: 50%;
}
}
.faceItemActive {
background: linear-gradient(
270deg,
rgb(201, 113, 255) 24.97%,
rgb(92, 121, 252) 100%
);
// border: 2px solid #fff;
// box-shadow: rgba(0, 0, 0, 0.3) 0px 0px 3px 3px;
position: relative;
transition: all 0.3s;
&::before {
content: "";
position: absolute;
top: -3px;
right: -7px;
left: -3px;
bottom: -7px;
z-index: 2;
border-radius: 50%;
background: linear-gradient(
225deg,
rgb(81 217 202 / 100%),
rgb(114 100 245 / 100%)
);
}
}
}
}
@@ -838,7 +914,6 @@ onMounted(() => {
}
.uploadVideo {
text-align: center;

margin-bottom: 20px;
height: 100px;
font-size: 50px;
@@ -846,11 +921,17 @@ onMounted(() => {
line-height: 100px;
input {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
opacity: 0;
}
video {
position: absolute;
top: 0;
left: 0;
z-index: -1;
width: 100%;
height: 100%;
}


+ 4
- 2
src/views/login/index.vue Целия файл

@@ -501,7 +501,8 @@ import {
} from "@/apis/login";
import axios from "axios";
// 状态管理依赖
import { useUserStore } from "@/store/modules/user";

import { userInfoPinia } from "@/store/modules/userInfo";

// API依赖
import {
@@ -514,7 +515,7 @@ import { useI18n } from "vue-i18n";

const { locale } = useI18n();
const currentLan = ref(locale);
const userStore = useUserStore();
const userInfo = userInfoPinia();
const route = useRoute();
const router = useRouter();
/**
@@ -956,6 +957,7 @@ async function doLogin(data: any) {
// 注册成功将账号密码保存到本地
saveInfo(data);
isLogining.value = false;
userInfo.setEmail(data.email); //保存邮箱到pinia
})
.catch((err) => {
console.log(err, "err");


+ 4
- 1
src/views/myAccount/index.vue Целия файл

@@ -9,7 +9,7 @@
</div>
<div class="info">
<div class="name">OLO</div>
<span>weqrewerwer@mail.com</span>
<span>{{ userInfo.email }}</span>
<span
class="password pointer"
@click="router.push({ name: 'login', params: { type: 4 } })"
@@ -115,6 +115,9 @@ import { useRouter } from "vue-router";
const { locale } = useI18n();
const currentLan = ref(locale);

import { userInfoPinia } from "@/store/modules/userInfo";

const userInfo = userInfoPinia();
//#region 二维码区域
// 点击复制邀请码
function copyInviteCode() {


Зареждане…
Отказ
Запис