congzc 1年前
コミット
6d2c1f8699
14個のファイルの変更275行の追加128行の削除
  1. +3
    -1
      .env.development
  2. +1
    -0
      .env.production
  3. +2
    -1
      .env.test
  4. +14
    -1
      src/apis/createVideo.js
  5. +2
    -2
      src/components/myLoadingIcon.vue
  6. +8
    -0
      src/lang/package/en.ts
  7. +8
    -0
      src/lang/package/zh-cn.ts
  8. +38
    -0
      src/mixins/webSocket.js
  9. +1
    -1
      src/permission.ts
  10. +10
    -0
      src/types/components.d.ts
  11. +8
    -3
      src/utils/request.js
  12. +170
    -116
      src/views/createVideo/index.vue
  13. +9
    -2
      src/views/login/index.vue
  14. +1
    -1
      src/views/myAccount/index.vue

+ 3
- 1
.env.development ファイルの表示

@@ -5,4 +5,6 @@ NODE_ENV='development'

VITE_APP_TITLE = 'Metavatar-PC'
VITE_APP_PORT = 3000
VITE_APP_BASE_API = 'https://photo.metavatar.cc/C'
# VITE_APP_BASE_API = 'https://photo.metavatar.cc/C'
VITE_APP_BASE_API = 'https://phototest.metavatar.cc/C'


+ 1
- 0
.env.production ファイルの表示

@@ -5,4 +5,5 @@ NODE_ENV='production'

VITE_APP_TITLE = 'Metavatar-PC'
VITE_APP_PORT = 3000
# VITE_APP_BASE_API = 'https://photo.metavatar.cc/C'
VITE_APP_BASE_API = 'https://photo.metavatar.cc/C'

+ 2
- 1
.env.test ファイルの表示

@@ -5,4 +5,5 @@ 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://photo.metavatar.cc/C'
VITE_APP_BASE_API = 'https://phototest.metavatar.cc/C'

+ 14
- 1
src/apis/createVideo.js ファイルの表示

@@ -206,7 +206,7 @@ export function musicListPersonApi(pageNum, pageSize) {
}

/**
* @description 保存音频(新)
* @description 保存音
* @params videoId
* @returns data
*/
@@ -216,4 +216,17 @@ export function saveMusicApi(data) {
method: 'post',
data
})
}

/**
* @description 保存音乐
* @params videoId
* @returns data
*/
export function delMusicApi(id) {
return request({
url: `api/userPhotoVideo/delete?id=${id}`,
method: 'get',

})
}

+ 2
- 2
src/components/myLoadingIcon.vue ファイルの表示

@@ -32,8 +32,8 @@ const props = defineProps({
.loadingIcon {
position: relative;
width: 100%;
height: 80px;
line-height: 80px;
height: 60px;
line-height: 60px;
text-align: center;
transform: scale(2);
> div {


+ 8
- 0
src/lang/package/en.ts ファイルの表示

@@ -73,6 +73,10 @@ export default {
searchMusic:'Search Music',
comingSoon:'coming soon',
hotMusic:'Hit song',
inputRequirement:'Please enter your requirements',
GeneratedText:'Generated Text',
SmartText:'Smart Text',
copy:'Copy to text',
},

// 我的视频
@@ -162,4 +166,8 @@ export default {
goLogin: "Login Now",
reSend: "Resend",
},
tips:{
tokenExpired:'Login expired, please login again',
systemException:'The system is abnormal. Please try again later'
}
};

+ 8
- 0
src/lang/package/zh-cn.ts ファイルの表示

@@ -73,6 +73,10 @@ export default {
searchMusic:'搜索音乐',
comingSoon:'敬请期待',
hotMusic:'热门歌曲',
inputRequirement:'请输入您的需求',
GeneratedText:'生成文本',
SmartText:'智能文本',
copy:'复制到文案',
},

// 我的视频
@@ -161,4 +165,8 @@ export default {
goLogin: "即将登录",
reSend: "重新发送",
},
tips:{
tokenExpired:'登录过期,请重新登录',
systemException:'系统异常,请稍后再试'
}
};

+ 38
- 0
src/mixins/webSocket.js ファイルの表示

@@ -0,0 +1,38 @@
export default {
data() {
return {
socket: null
};
},
methods: {
connectWebSocket() {
this.socket = new WebSocket('wss://gptsocket.malls.iformall.com:8010');
this.socket.onopen = () => {
console.log('WebSocket 连接成功');
};
this.socket.onmessage = (event) => {
console.log('接收到 WebSocket 消息:', event.data);
};
this.socket.onclose = () => {
console.log('WebSocket 连接关闭');
};
this.socket.onerror = (error) => {
console.error('WebSocket 错误:', error);
};
},
sendWebSocketMessage(message) {
if (this.socket && this.socket.readyState === WebSocket.OPEN) {
this.socket.send(message);
}
},
closeWebSocket() {
if (this.socket) {
this.socket.close();
this.socket = null;
}
}
}
};

+ 1
- 1
src/permission.ts ファイルの表示

@@ -20,7 +20,7 @@ router.beforeEach(async (to, from, next) => {
// if ((to.params.type as string) == "4") {
// next();
// }
if (to.params.type ) {
if (to.query.type ) {
next();
}
// 邮件重置密码 忘记密码


+ 10
- 0
src/types/components.d.ts ファイルの表示

@@ -11,6 +11,8 @@ declare module '@vue/runtime-core' {
ElBreadcrumb: typeof import('element-plus/es')['ElBreadcrumb']
ElBreadcrumbItem: typeof import('element-plus/es')['ElBreadcrumbItem']
ElButton: typeof import('element-plus/es')['ElButton']
ElCol: typeof import('element-plus/es')['ElCol']
ElDialog: typeof import('element-plus/es')['ElDialog']
ElDivider: typeof import('element-plus/es')['ElDivider']
ElDropdown: typeof import('element-plus/es')['ElDropdown']
ElDropdownItem: typeof import('element-plus/es')['ElDropdownItem']
@@ -32,14 +34,20 @@ declare module '@vue/runtime-core' {
Hamburger: typeof import('./../components/Hamburger/index.vue')['default']
IconSelect: typeof import('./../components/IconSelect/index.vue')['default']
IEpClose: typeof import('~icons/ep/close')['default']
IEpCloseBold: typeof import('~icons/ep/close-bold')['default']
IEpDelete: typeof import('~icons/ep/delete')['default']
IEpDocumentAdd: typeof import('~icons/ep/document-add')['default']
IEpDownload: typeof import('~icons/ep/download')['default']
IEpEdit: typeof import('~icons/ep/edit')['default']
IEpGoods: typeof import('~icons/ep/goods')['default']
IEpHeadset: typeof import('~icons/ep/headset')['default']
IEpLoading: typeof import('~icons/ep/loading')['default']
IEpLock: typeof import('~icons/ep/lock')['default']
IEpMenu: typeof import('~icons/ep/menu')['default']
IEpMic: typeof import('~icons/ep/mic')['default']
IEpSearch: typeof import('~icons/ep/search')['default']
IEpSetting: typeof import('~icons/ep/setting')['default']
IEpVideoPlay: typeof import('~icons/ep/video-play')['default']
LangSelect: typeof import('./../components/LangSelect/index.vue')['default']
MultiUpload: typeof import('./../components/Upload/MultiUpload.vue')['default']
MyLoading: typeof import('./../components/myLoading.vue')['default']
@@ -52,6 +60,8 @@ declare module '@vue/runtime-core' {
SizeSelect: typeof import('./../components/SizeSelect/index.vue')['default']
SvgIcon: typeof import('./../components/SvgIcon/index.vue')['default']
WangEditor: typeof import('./../components/WangEditor/index.vue')['default']
Websocket: typeof import('./../components/websocket.vue')['default']
Websoket: typeof import('./../components/websoket.vue')['default']
}
export interface ComponentCustomProperties {
vInfiniteScroll: typeof import('element-plus/es')['ElInfiniteScroll']


+ 8
- 3
src/utils/request.js ファイルの表示

@@ -38,18 +38,23 @@ request.interceptors.request.use(
// 响应拦截器
request.interceptors.response.use(
function (response) {
console.log(response.data);
if (!response.data) {
ElMessage.error($t('tips.systemException'));
}
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'));
return
// 弹出错误
} else if (code != 200) {
if (code == 500) {
ElMessage.error("系统异常,请稍后再试");
// ElMessage.error("系统异常,请稍后再试");
ElMessage.error($t('tips.systemException'));
// 其他错误
} else {
return Promise.reject(response.data)


+ 170
- 116
src/views/createVideo/index.vue ファイルの表示

@@ -483,6 +483,12 @@
>
<span class="musicNmae"> 🎵{{ item.name }}</span>
<span class="musicTime"> {{ handleTime(item.time) }}</span>
<span
v-if="musicItemAction == item.id"
class="delMusic"
@click="deleteMusic(item.id)"
><el-icon><i-ep-Delete /></el-icon>
</span>
</div>
</div>
<!-- 系统分页 -->
@@ -542,104 +548,40 @@
<!-- ----------------------- 4 -->
<el-tab-pane v-if="AccessToken" :label="$t('createVideo.aiScript')">
<div class="card4">
<!-- 语言 -->
<div class="text">
<!-- 文本域 -->
<el-input
maxlength="3000"
maxlength="300"
id="textInputId"
v-model="textareaContent"
:rows="15"
v-model="problemContent"
:rows="5"
type="textarea"
:placeholder="$t('createVideo.aiText')"
:placeholder="$t('createVideo.inputRequirement')"
/>
<span
class="pointer"
@click="getPreviewAudio"
style="margin-right: 10px; font-size: 20px"
>🔊</span
>
<span class="pointer" @click="insertIcon">🕗</span>
</div>
<p>{{ $t("createVideo.languages") }}</p>
<div class="card4Btn" @click="sendRequirement">
<my-Loading-Icon v-if="sendLoading"></my-Loading-Icon>
<span v-if="!sendLoading">
{{ $t("createVideo.GeneratedText") }}
</span>
</div>

<p>{{ $t("createVideo.SmartText") }}</p>
<!-- 语言 -->
<el-select
@change="chooseType"
v-model="language"
:placeholder="$t('createVideo.select')"
style="width: 100%"
>
<el-option
v-for="item in languageOptions"
:key="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
@change="chooseStyle"
:disabled="language ? false : true"
v-model="sound"
:placeholder="$t('createVideo.select')"
style="width: 100%"
>
<el-option
v-for="item in soundOptions"
:key="item.value"
:label="item.displayName"
:value="item.id"
/>
</el-select>
<p>{{ $t("createVideo.styles") }}</p>
<!-- 风格 -->
<el-select
:disabled="soundStyleOptions ? false : true"
v-model="soundStyle"
:placeholder="$t('createVideo.select')"
style="width: 100%"
>
<el-option
v-for="item in soundStyleOptions"
:key="item"
:label="item"
:value="item"
<div class="text">
<!-- 文本域 -->
<el-input
id="textInputId"
v-model="answerContent"
:rows="15"
type="textarea"
disabled=""
/>
</el-select>
<!-- 保存视频 -->
<el-button
@click="saveOrUpdate(1, 'saveVideo')"
color="#000"
type="primary"
>
<my-loading :isLoading="createLoading"></my-loading>
<span v-if="!createLoading">{{
$t("createVideo.saveVideo")
}}</span></el-button
>
<!-- 生成视频 -->
<el-button
style="margin-left: 0"
@click="saveOrUpdate(1)"
color="#000"
type="primary"
>
<my-loading :isLoading="createLoading"></my-loading>
<span v-if="!createLoading">{{
$t("createVideo.generateVideo")
}}</span></el-button
>
</div>
<div class="card4Btn" @click="copyAnswerContent">
{{ $t("createVideo.copy") }}
</div>
</div>
</el-tab-pane>
<!-- -------------------------脚标 -->
@@ -653,6 +595,7 @@
</template>

<script setup>
//#region 导入
import { getCurrentInstance } from "vue";
import {
personPhotoList,
@@ -672,11 +615,15 @@ import {
musicListSystemApi,
musicListPersonApi,
saveMusicApi,
delMusicApi,
} from "@/apis/createVideo";
import { useI18n } from "vue-i18n";
import Recorder from "js-audio-recorder"; //mp3插件
import imageCompression from "browser-image-compression"; //压缩图片插件
import { useRoute, useRouter } from "vue-router";

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

const route = useRoute();
const router = useRouter();
const { locale } = useI18n();
@@ -784,7 +731,9 @@ async function deleteFaceImg(id) {
pageNumUser.value = 1;
overLoadUser.value = false;
} catch (error) {
ElMessage.error(errpr);
ElMessage.error(
lanChange.value == "zh-cn" ? `删除失败!` : `Deletion failure`
);
}
});
}
@@ -1218,7 +1167,7 @@ async function saveOrUpdate(index, isSaveVideo) {
createLoading.value = false;
return;
}
if (!saveOrUpdateData.voiceMaterialId) {
if (!audioData.value) {
ElMessage.error(
lanChange.value == "zh-cn"
? `请选择音频文件`
@@ -1311,9 +1260,9 @@ const musicListPerson = ref(null); //展示音乐列表
const audioMusicUrl = ref(null); //选中音乐Url回显
const musicItemAction = ref(null); //选中高亮
const musicListPageNumSystem = ref(1); //系统音乐页数
const musicListTotalSystem = ref(10); //系统音乐总条数
const musicListTotalSystem = ref(null); //系统音乐总条数
const musicListPageNumPerson = ref(1); //个人音乐页数
const musicListTotalPerson = ref(10); //个人音乐总条数
const musicListTotalPerson = ref(null); //个人音乐总条数
const musicListPageSize = ref(8); //
const musicTabActive = ref(1); //热门歌曲和个人仓库高亮
const musicData = ref(null); //上传歌曲的数据
@@ -1366,7 +1315,6 @@ function handleTime(time) {
async function uploadMusic() {
const file = fileMusicInput.value.files[0];
if (file) {
const videoUrl = URL.createObjectURL(file);
musicData.value = file;
let formData = new FormData();
formData.append("file", musicData.value, file.name + ".mp3");
@@ -1390,37 +1338,127 @@ async function uploadMusic() {
try {
const res3 = await saveMusicApi({ voiceId: res.data.videoId });
afterSaveAudioId.value = res2.data.id; // 得到id
musicListPageNumPerson.value = 1;
const res4 = await musicListPersonApi(
musicListPageNumPerson.value,
musicListPageSize.value
);
musicListPerson.value = res4.data.list;
musicListTotalPerson.value = Number(res4.data.total);
ElMessage.success(
lanChange.value == "zh-cn" ? `上传成功!` : `Upload successfully`
);
} catch (error) {
ElMessage.error(error);
}
}
}, 1000);
musicListPageNumPerson.value = 1;
const res4 = await musicListPersonApi(
musicListPageNumPerson.value,
musicListPageSize.value
} catch (error) {
ElMessage.error(
lanChange.value == "zh-cn" ? `上传失败!` : `Upload failure`
);
musicListPerson.value = res4.data.list;
musicListTotalPerson.value = Number(res4.data.total);
}
}
}
// 删除音乐
async function deleteMusic(id) {
const msg =
lanChange.value == "zh-cn"
? `确定要删除这首音乐吗? `
: `Are you sure to delete this music ? `;
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 () => {
try {
const res = await delMusicApi(id);
getmusicListPerson(musicListPageNumPerson.value, musicListPageSize.value);
ElMessage.success(
lanChange.value == "zh-cn" ? `上传成功!` : `Upload successfully`
lanChange.value == "zh-cn" ? `删除成功!` : `Successfully deleted `
);
} catch (error) {
ElMessage.error(error);
ElMessage.error(
lanChange.value == "zh-cn" ? `删除失败!` : `Deletion failure `
);
}
}
});
}
//#endregion ----------------------------

//#region
//#region card4的逻辑
const problemContent = ref(null);
const answerContent = ref("");
let mySocket = null;
const sendLoading = ref(false);
function createWS() {
mySocket = new WebSocket("wss://gptsocket.malls.iformall.com:8010");
mySocket.onopen = () => {
console.log("WebSocket 连接成功1");
sendRequirement();
};

mySocket.onmessage = (event) => {
console.log("接收到 WebSocket 消息2:", event.data);
if (!event.data) {
return;
}
if (event.data == "___talk_end___") {
sendLoading.value = false;
return;
}
answerContent.value = answerContent.value + event.data;
answerContent.value.replace("null", "");
};

mySocket.onclose = () => {
console.log(
"WebSocket 连接关闭3,关闭代码:",
event.code,
"关闭原因:",
event.reason
);
console.log(mySocket.readyState, "链接状态3");
};

mySocket.onerror = (error) => {
console.error("WebSocket 错误4:", error);
};
}
// 点击发送需求
async function sendRequirement() {
if (!problemContent.value) {
return;
}
const sendMessage = [
{
role: "user",
content: problemContent.value,
},
];

if (mySocket.readyState != WebSocket.OPEN) {
createWS();
} else {
answerContent.value = "";
sendLoading.value = true;
mySocket.send(JSON.stringify(sendMessage));
}
}
// 复制到文案
function copyAnswerContent() {
textareaContent.value = answerContent.value;
document.getElementById("tab-0").click();
}
//#endregion ----------------------------

//#region

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

// $t在script中的使用
//#region $t在script中的使用
const {
appContext: {
config: { globalProperties },
@@ -1429,7 +1467,9 @@ const {

const string = "createVideo.typeScript";
const t = globalProperties.$t(string);
//#endregion ----------------------------

//#region 页面初始化
onMounted(async () => {
getAiFaceList(1, 10, 1);
if (AccessToken) {
@@ -1458,6 +1498,14 @@ onMounted(async () => {
audioMusicUrl.value = item.url;
}
});
createWS();
});
//#endregion ----------------------------

onUnmounted(() => {
if (mySocket) {
mySocket.close();
}
});
</script>

@@ -2111,7 +2159,7 @@ onMounted(async () => {
cursor: pointer;
.musicNmae {
display: inline-block;
width: 280px;
width: 260px;
margin-right: 20px;
overflow: hidden;
text-overflow: ellipsis;
@@ -2124,6 +2172,13 @@ onMounted(async () => {
text-overflow: ellipsis;
white-space: nowrap;
}
.delMusic {
color: #fff;
padding-left: 15px;
padding-top: -15px;
line-height: 30px;
vertical-align: top;
}
}
.musicItemAvtive {
background-color: #000;
@@ -2184,7 +2239,7 @@ onMounted(async () => {
.card4 {
.text {
padding: 10px;
border: 1px solid #000;
border: 1px solid #c0c4cc;
.el-button {
float: right;
margin-top: 10px;
@@ -2199,16 +2254,15 @@ onMounted(async () => {
p {
margin: 7px 0;
}

> .el-button {
width: 100%;
height: 50px;
.card4Btn {
margin-top: 10px;
}

:deep(.el-input .is-focus) {
// box-shadow: none !important;
// box-shadow: 0 0 0 1px #000 !important;
border-radius: 5px;
height: 50px;
width: 100%;
color: #fff;
background-color: #000;
text-align: center;
line-height: 50px;
}
}
:deep(.el-tabs__item.is-active) {


+ 9
- 2
src/views/login/index.vue ファイルの表示

@@ -996,8 +996,15 @@ onMounted(() => {
getCode();
getInfo();
// 重置密码
if (router.currentRoute.value.params.type) {
// isLogin.value = Number(router.currentRoute.value.params.type);
// if (router.currentRoute.value.params.type) {
// // isLogin.value = Number(router.currentRoute.value.params.type);
// isLogin.value = 4;
// }
// if (router.currentRoute.value.params.type) {
// // isLogin.value = Number(router.currentRoute.value.params.type);
// isLogin.value = 4;
// }
if (route.query.type) {
isLogin.value = 4;
}
//有resetPwd则说明是重置密码邮件来的


+ 1
- 1
src/views/myAccount/index.vue ファイルの表示

@@ -233,7 +233,7 @@ const dialogAvatarData = ref(null); //图片上传数据
const infoDialogLoad = ref(false); //load效果
// 去重置密码
function resetPassWord() {
router.push({ name: "login", params: { type: 4 } });
router.push({ name: "login", query: { type: 4 } });
}
// 选择图片
async function chooseImg(e) {


読み込み中…
キャンセル
保存