コミットを比較

...

101 コミット

作成者 SHA1 メッセージ 日付
  winter c7b28528a7 去掉合作商接入菜单 1年前
  winter 19a6a00e96 fix 1年前
  chutingting 6ab79212fe edit 1年前
  chutingting fd520ba2dc edit 1年前
  chutingting 2a1699a22b edit 1年前
  chutingting 8200692e68 edit 1年前
  chutingting 46aa2178be Merge branch 'ctt_dev' into dev 1年前
  chutingting 51d3cca8ad 生成文本 1年前
  chutingting 2e04075a55 Merge branch 'ctt_dev' into dev 1年前
  chutingting 7b52f5489e edit title 1年前
  chutingting f11a1b9266 Merge branch 'ctt_dev' into dev 1年前
  chutingting 32efebeb00 edit 1年前
  chutingting fa7462c376 Merge branch 'ctt_dev' into dev 1年前
  chutingting d398ae67b0 edit 1年前
  chutingting 402b7fb583 Merge branch 'ctt_dev' into dev 1年前
  chutingting 59e1dc524b edit title 1年前
  chutingting 49e818dd1b Merge branch 'ctt_dev' into dev 1年前
  chutingting 839d0187b5 edit api 1年前
  chutingting c57b72a200 Merge branch 'ctt_dev' into dev 1年前
  chutingting 160a196f97 edit time 1年前
  chutingting 35d2f84af5 Merge branch 'ctt_dev' into dev 1年前
  chutingting 6b83764334 0.2-2.0 1年前
  chutingting 663c23daec Merge branch 'ctt_dev' into dev 1年前
  chutingting dfbf58c9cf edit zindex 1年前
  chutingting e6f0f6a1d3 Merge branch 'ctt_dev' into dev 1年前
  chutingting 7edfa614f0 选中 1年前
  chutingting bba94a60d3 Merge branch 'ctt_dev' into dev 1年前
  chutingting 6069139fbc edit z 1年前
  chutingting e3819ec876 edit bug 1年前
  chutingting 1d86a399b2 del <<< 1年前
  chutingting 033edd82c1 edit 1年前
  chutingting b20b31fb89 Merge branch 'ctt_dev' into dev 1年前
  chutingting 58addd5730 scroll 1年前
  chutingting c74bc464f4 Merge branch 'ctt_dev' into dev 1年前
  chutingting 3ad78b3403 edit store 1年前
  chutingting 950b7df682 Merge branch 'ctt_dev' into dev 1年前
  chutingting e7a19528a5 edit css 1年前
  chutingting 554da99452 edit brn 1年前
  chutingting 3e27cd327f Merge branch 'ctt_dev' into dev 1年前
  chutingting 7e81f3d01f edit api 1年前
  chutingting b5f14fede7 Merge branch 'ctt_dev' into dev 1年前
  chutingting 8141254955 edit 1年前
  chutingting cb50dc5d39 Merge branch 'ctt_dev' into dev 1年前
  chutingting 989b30db37 add api 1年前
  chutingting 8feb90eb08 Merge branch 'ctt_dev' into dev 1年前
  chutingting 7f22868ba4 edit 1年前
  chutingting d84265284b Merge branch 'ctt_dev' into dev 1年前
  chutingting f6c2c026e7 edit text 1年前
  chutingting fbf81777a9 Merge branch 'ctt_dev' into dev 1年前
  chutingting 6ed3529c76 扣币 1年前
  chutingting ed308e3b5a Merge branch 'ctt_dev' into dev 1年前
  chutingting f707fb32e8 gpt 1年前
  chutingting 6a44a097b0 Merge branch 'ctt_dev' into dev 1年前
  chutingting 7151d06fc5 url 1年前
  chutingting d117cdedcf Merge branch 'ctt_dev' into dev 1年前
  chutingting 211f69236b 图片检验 1年前
  chutingting a243eaa438 Merge branch 'ctt_dev' into dev 1年前
  chutingting 4ca7040149 lang 1年前
  chutingting 0626bbf500 Merge branch 'ctt_dev' into dev 1年前
  chutingting 8e55ff3bad edit 1年前
  chutingting 952e2d5086 私人定制 1年前
  chutingting bf8c02b7d9 Merge branch 'ctt_dev' into dev 1年前
  chutingting 8ef05dfe4c 校验视频 1年前
  chutingting 3d8f2222cb Merge branch 'ctt_dev' into dev 1年前
  chutingting 0a714db591 edit qrcode 1年前
  chutingting aff24e3fe8 Merge branch 'ctt_dev' into dev 1年前
  chutingting 4d35512fa0 rem 1年前
  chutingting 1fa971c933 Merge branch 'ctt_dev' into dev 1年前
  chutingting 7958f5ab55 edit css 1年前
  chutingting 2559a9de1f Merge branch 'ctt_dev' into dev 1年前
  chutingting 6709710325 edit font 1年前
  chutingting 0c3d8db5b2 Merge branch 'ctt_dev' into dev 1年前
  chutingting 46199d670d del rem 1年前
  chutingting bd7fb0d798 Merge branch 'ctt_dev' into dev 1年前
  chutingting 9c23f77397 订单详情 1年前
  chutingting a948491b70 Merge branch 'ctt_dev' into dev 1年前
  chutingting b37aa42c96 订单 1年前
  chutingting a9a5602285 edit 1年前
  chutingting a607c1b042 ts 1年前
  chutingting 73063a7d6d 订单 1年前
  chutingting b1f302eaec edit 1年前
  chutingting 68c3e279cb edit 1年前
  chutingting 37d6d4f994 username 1年前
  chutingting e5c1449d5f 支付 1年前
  chutingting 93903fef14 支付 1年前
  chutingting b72534ac83 支付 1年前
  chutingting 39f9567d79 商店 1年前
  chutingting 7357522f0d 增加我的 1年前
  chutingting d09946669c 增加我的列表 1年前
  chutingting ac2822f97d test 1年前
  congzc bf1dbdeef7 1 1年前
  congzc af8a52f71a 1 1年前
  congzc 3c02f7889b 1 1年前
  congzc e010b8687d 1 1年前
  congzc 3ea71a8495 1 1年前
  congzc ae78019597 1 1年前
  congzc 6f62765b56 1 1年前
  congzc 837db768cd 1 1年前
  congzc 63b20451f0 AI文案 1年前
  congzc b2d3b0ad3e Merge branch 'dev' of https://git.malls.iformall.com/suimang/Home_wap_pc into dev 1年前
  congzc a75a03d1b0 1 1年前
43個のファイルの変更4962行の追加1016行の削除
分割表示
  1. +3
    -1
      .env.development
  2. +1
    -1
      .env.test
  3. バイナリ
      dist.zip
  4. +1
    -1
      index.html
  5. +1
    -0
      package.json
  6. バイナリ
      public/favicon.ico
  7. +39
    -0
      src/apis/api.js
  8. +65
    -4
      src/apis/createVideo.js
  9. +81
    -0
      src/apis/my.js
  10. +2
    -1
      src/apis/myCreating.js
  11. +40
    -1
      src/apis/myStore.js
  12. +39
    -0
      src/apis/pay.js
  13. バイナリ
      src/assets/img/alipay.png
  14. バイナリ
      src/assets/img/gpt.png
  15. バイナリ
      src/assets/img/weChat.png
  16. +17
    -12
      src/components/EditPosition.vue
  17. +42
    -14
      src/components/cutImg.vue
  18. +51
    -0
      src/components/myLoadingIcon2.vue
  19. +59
    -0
      src/lang/package/en.ts
  20. +59
    -0
      src/lang/package/zh-cn.ts
  21. +70
    -13
      src/layout/index.vue
  22. +108
    -8
      src/router/index.ts
  23. +1
    -0
      src/store/modules/userInfo.js
  24. +4
    -0
      src/types/auto-imports.d.ts
  25. +11
    -0
      src/types/components.d.ts
  26. +2
    -2
      src/utils/request.js
  27. +141
    -0
      src/views/api/demo.html
  28. +125
    -0
      src/views/api/index.vue
  29. +755
    -148
      src/views/createVideo/index.vue
  30. +17
    -8
      src/views/login/index.vue
  31. +207
    -0
      src/views/my/coindeduc.vue
  32. +193
    -0
      src/views/my/recharge.vue
  33. +0
    -22
      src/views/myAPI/index.vue
  34. +2
    -1
      src/views/myCreating/index.vue
  35. +1152
    -0
      src/views/myStore/index copy.vue
  36. +303
    -445
      src/views/myStore/index.vue
  37. +7
    -0
      src/views/privatization/index.vue
  38. +6
    -0
      src/views/separation/index.vue
  39. +944
    -0
      src/views/userModel/index.vue
  40. +16
    -0
      src/views/userModel/videoStatus.js
  41. +7
    -0
      src/views/voiceModel/index.vue
  42. +6
    -0
      src/views/voiceprint/index.vue
  43. +385
    -334
      yarn.lock

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

@@ -6,5 +6,7 @@ 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://test.metavatar.cc/C'
# VITE_APP_BASE_API = 'https://test.metavatar.cc/C'
# VITE_APP_BASE_API = 'https://phototest.metavatar.cc/C'
VITE_APP_BASE_API = 'https://m.metavatar.cc/C'


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

@@ -6,4 +6,4 @@ 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://test.metavatar.cc/C'
VITE_APP_BASE_API = 'https://mtest.metavatar.cc/C'

バイナリ
dist.zip ファイルの表示


+ 1
- 1
index.html ファイルの表示

@@ -5,7 +5,7 @@
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Metavatar-慧影</title>
<title>Metavatar</title>
</head>

<body>


+ 1
- 0
package.json ファイルの表示

@@ -34,6 +34,7 @@
"vue": "^3.2.45",
"vue-cropper": "^1.0.9",
"vue-i18n": "9",
"vue-qr": "^4.0.9",
"vue-router": "^4.0.13"
},
"devDependencies": {


バイナリ
public/favicon.ico ファイルの表示

変更前 変更後
幅: 290  |  高さ: 290  |  サイズ: 12 KiB

+ 39
- 0
src/apis/api.js ファイルの表示

@@ -0,0 +1,39 @@

import request from "@/utils/request.js";


/**
* @description 服务端api签名指南
* @params
* @returns
*/
export function getAvailableApiGuideApi() {
return request({
url: `/api/apiGuide/getAvailableApiGuide`,
method: 'get'
})
}

/**
* @description 开放接口列表
* @params
* @returns
*/
export function apiMenuApi() {
return request({
url: `/api/apiMenu/list`,
method: 'get'
})
}

/**
* @description 获取接口详情
* @params
* @returns
*/
export function getContentApi(id) {
return request({
url: `/api/apiMenu/get?id=${id}`,
method: 'get'
})
}

+ 65
- 4
src/apis/createVideo.js ファイルの表示

@@ -7,8 +7,9 @@ import request from "@/utils/request.js";
* @returns data
*/
export function modeListApi(sex, pageNum, pageSize, videoType) {
let api = localStorage.getItem("AccessToken") ? '/api/personPatch/loginList' : '/api/personPatch/list'
return request({
url: `/api/personPatch/list?sex=${sex}&pageNum=${pageNum}&pageSize=${pageSize}&videoType=${videoType}`,
url: `${api}?sex=${sex}&pageNum=${pageNum}&pageSize=${pageSize}&videoType=${videoType}`,
method: 'get'
})
}
@@ -19,8 +20,9 @@ export function modeListApi(sex, pageNum, pageSize, videoType) {
* @returns data
*/
export function voiceTotalApi() {
let api = localStorage.getItem("AccessToken") ? '/api/voiceMould/loginVoiceTotal' : '/api/voiceMould/voiceTotal'
return request({
url: `api/voiceMould/voiceTotal`,
url: api,
method: 'GET',
})
}
@@ -88,12 +90,32 @@ export function personSCIListApi(pageNum, pageSize) {
* @returns data
*/
export function previewAudioApi(gen_txt, voice_id, voice_style) {
let api = localStorage.getItem("AccessToken") ? '/api/voiceMould/loginPreview' : '/api/voiceMould/hypreview'
return request({
url: `api/voiceMould/preview`,
url: api,
method: 'POST',
data: { gen_txt, voice_id, voice_style }
})
}
/**
* @description 图片检测接口
* @params id
* @returns data
*/
export function checkPhotoApi(data) {
const timestamp = Date.now();
const formData = new FormData();
formData.append('file', data, timestamp + ".png");
return request({
url: '/api/baidu/checkPhoto',
method: 'post',
data: formData,
headers: {
'Content-Type': 'application/form-data',
"Access-Control-Allow-Credentials": true,
}
});
}

/**
* @description 上传阿里云--裁剪后的个人背景图
@@ -173,7 +195,10 @@ export function createVideoApi(id) {
return request({
url: `/api/userVideo/createVideo`,
method: 'post',
data: { id }
data: {
id,
projectType: '2'
}
})
}

@@ -188,3 +213,39 @@ export function getModelDetailByIdApi(id) {
method: 'get'
})
}

/**
* @description:文字判断语种
* @param {number} id
* @return data
*/
export function getLanguageApi(data) {
return request({
url: `/api/VoiceLanguage/getLanguage`,
method: 'POST',
data
})
}

/**
* @description:视频校验
* @param {number} id
* @return data
*/
export function checkVideoApi(id) {
return request({
url: `/api/userVideo/checkVideo?id=${id}`,
method: 'get'
})
}

/**
* @description:扣币
* @return data
*/
export function reduceUserPoinsApi() {
return request({
url: `/api/user/reduceUserPoins`,
method: 'get'
})
}

+ 81
- 0
src/apis/my.js ファイルの表示

@@ -0,0 +1,81 @@
/*
* @Autor: Chutingting
* @Date: 2023-11-13 14:29:58
* @LastEditors: Chutingting
* @LastEditTime: 2023-11-14 10:48:23
* @Description:
*/
import request from "@/utils/request.js";


/**
* @description 充值订单
* @params
* @returns
*/
export function rechargeListApi(data) {
return request({
url: `/api/productOrder/myPaiedOrders?projectType=${data.projectType}&pageNum=${data.pageNum}&pageSize=${data.pageSize}`,
method: 'get'
})
}

/**
* @description 获取详情
* @params
* @returns
*/
export function getDetailApi(id) {
return request({
url: `/api/userVideo/findById?id=${id}`,
method: 'get'
})
}

/**
* @description 扣币详情
* @params
* @returns
*/
export function coindedApi(data) {
return request({
url: `/api/userVideo/list?videoStatus=5&pageNum=${data.pageNum}&pageSize=${data.pageSize}`,
method: 'get'
})
}

/**
* @description 当前币值
* @params
* @returns
*/
export function getUserPoinsApi() {
return request({
url: `/api/user/getUserPoins`,
method: 'get'
})
}

/**
* @description 充值订单详情
* @params
* @returns
*/
export function findByNumberApi(orderNumber) {
return request({
url: `/api/productOrder/findByNumber?orderNumber=${orderNumber}`,
method: 'get'
})
}

/**
* @description 扣币订单详情
* @params
* @returns
*/
export function findByIdApi(id) {
return request({
url: `/api/userVideo/findById?id=${id}`,
method: 'get'
})
}

+ 2
- 1
src/apis/myCreating.js ファイルの表示

@@ -1,3 +1,4 @@

import request from "@/utils/request.js";


@@ -52,7 +53,7 @@ export function videoHyApi(id) {
*/
export function checkVideoStatusApi(list) {
return request({
url: `/api/userPhotoVideo/checkVideoStatus?list=${list}`,
url: `/api/userVideo/checkVideoStatus?list=${list}`,
method: 'get'
})
}

+ 40
- 1
src/apis/myStore.js ファイルの表示

@@ -8,9 +8,48 @@ import request from "@/utils/request.js";
*/
export function packageInfoApi() {
return request({
url: `/api/user/getPackageInfo`,
url: `/api/product/list_v1?type=2&projectType=2`, // /api/user/getPackageInfo
method: 'get'
})
}

/**
* @description 订单详情
* @params
* @returns
*/
export function orderDetailApi(id) {
return request({
url: `/api/productOrder/findByNumber?orderNumber=${id}`,
method: 'get'
})
}



/**
* @description 创建支付
* @params
* @returns
*/
export function createPayApi(data) {
return request({
url: `/api/productOrder/createPay`,
method: 'post',
data
})
}


/**
* @description 创建支付
* @params
* @returns
*/
export function findStatusApi(data) {
return request({
url: `/api/productOrder/findStatus?orderNumber=${data.orderNumber}&payVendor=${data.payVendor}`,
method: 'get'
})
}


+ 39
- 0
src/apis/pay.js ファイルの表示

@@ -0,0 +1,39 @@
import request from "@/utils/request.js";

/**
* @description:购买金币下单
* @param productId
* @return: data
*/
export function createOrderApi(data) {
return request({
url: `api/productOrder/createOrder`,
method: 'post',
data
})
}

/**
* @description:获取订单数据
* @param data
* @return: data
*/
export function createPayApi(data) {
return request({
url: `api/productOrder/createPay`,
method: 'post',
data
})
}

/**
* @description:获取订单支付状态
* @param orderNumber
* @return: data payStatus 查询支付状态 0:待支付,2:支付中,3:已支付
*/
export function getOrderStatusApi(orderNumber, payVendor) {
return request({
url: `api/productOrder/findStatus?orderNumber=${orderNumber}&payVendor=${payVendor}`,
method: 'get'
})
}

バイナリ
src/assets/img/alipay.png ファイルの表示

変更前 変更後
幅: 200  |  高さ: 200  |  サイズ: 3.7 KiB

バイナリ
src/assets/img/gpt.png ファイルの表示

変更前 変更後
幅: 80  |  高さ: 80  |  サイズ: 9.3 KiB

バイナリ
src/assets/img/weChat.png ファイルの表示

変更前 変更後
幅: 200  |  高さ: 200  |  サイズ: 4.0 KiB

+ 17
- 12
src/components/EditPosition.vue ファイルの表示

@@ -51,7 +51,7 @@
v-for="(item, index) in materialList.scList"
:key="item.id"
class="scListItem"
:class="showBorderActive == item.id ? 'border' : ''"
:class="scBorderActive == index ? 'border' : ''"
@mousedown.stop="handleDragSc(item, index, $event)"
:style="{
top: item.y / props.proportion + 'px',
@@ -64,7 +64,7 @@
<img id="scListItemImg" draggable="false" :src="item.material" alt="" />
<div ref="box1" class="stretchBox box1"></div>
<div
v-show="showBorderActive == item.id"
v-show="scBorderActive == index"
class="stretchBox box2"
@click="delScListItem(index)"
>
@@ -77,7 +77,7 @@
</div>
<div class="stretchBox box3"></div>
<div
v-show="showBorderActive == item.id"
v-show="scBorderActive == index"
ref="box4"
@mousedown.stop="changeScaleSc(index, $event)"
class="stretchBox box4"
@@ -161,6 +161,7 @@ const virtualStyle = computed(() => {
});
// 拖动盒子
const showBorderActive = ref(-1); //判断显示边框
const scBorderActive = ref('')
watch(
() => props.materialList.virtual.id,
(newValue, oldValue) => {
@@ -223,14 +224,14 @@ function changeScale(event) {
let deltaY = touchStart.pageY - y;
let boxDistance2 = Math.sqrt(deltaX ** 2 + deltaY ** 2);
if (deltaX < 0) {
if (props.materialList.virtual.w < 0.4) {
props.materialList.virtual.w = 0.4;
if (props.materialList.virtual.w < 0.2) {
props.materialList.virtual.w = 0.2;
return;
}
props.materialList.virtual.w = (baseLineScale + boxDistance2) / baseLine;
} else {
if (props.materialList.virtual.w > 1.3) {
props.materialList.virtual.w = 1.3;
if (props.materialList.virtual.w > 2.0) {
props.materialList.virtual.w = 2.0;
return;
}
props.materialList.virtual.w = (baseLineScale - boxDistance2) / baseLine;
@@ -253,8 +254,12 @@ function changeScale(event) {
//#region 素材图逻辑
const handleDragSc = (item, index, event) => {
realImgUrl.value = item.material;
scBorderActive.value= index
showBorderActive.value = item.id;
props.materialList.scList.forEach(res => {
res.activeIndex =''
})
item.activeIndex = item.id
event.preventDefault();
const imageElement = fatherBox.value;
const startX = event.clientX; //保存初始值,避免闪烁
@@ -293,15 +298,15 @@ function changeScaleSc(index, event) {
let deltaY = touchStart.pageY - y;
let boxDistance2 = Math.sqrt(deltaX ** 2 + deltaY ** 2);
if (deltaX < 0) {
if (props.materialList.scList[index].w < 0.4) {
props.materialList.scList[index].w = 0.4;
if (props.materialList.scList[index].w < 0.2) {
props.materialList.scList[index].w = 0.2;
return;
}
props.materialList.scList[index].w =
(baseLineScale + boxDistance2) / baseLine;
} else {
if (props.materialList.scList[index].w > 1.3) {
props.materialList.scList[index].w = 1.3;
if (props.materialList.scList[index].w > 2.0) {
props.materialList.scList[index].w = 2.0;
return;
}
props.materialList.scList[index].w =


+ 42
- 14
src/components/cutImg.vue ファイルの表示

@@ -63,7 +63,7 @@
// import { VueCropper } from "vue-cropper";
import { ElMessage } from "element-plus/es";
import { ref, reactive, watch } from "vue";
import { awsImgUpload, saveImgApi } from "../apis/createVideo";
import { awsImgUpload, saveImgApi, checkPhotoApi } from "../apis/createVideo";
import { useI18n } from "vue-i18n";

const { locale } = useI18n();
@@ -92,30 +92,58 @@ function myUpload(params) {}
const cropper = ref(null);
const option = reactive({
img: "",
canMove: true,
outputSize: 0.5, //图片质量
info: false, // 裁剪框的大小信息
// outputType: 'jpeg', // 裁剪生成图片的格式
outputType: "png", // 裁剪生成图片的格式
outputType: "png",
canScale: true, // 图片是否允许滚轮缩放
autoCrop: true, // 是否默认生成截图框
fixedBox: false,
original: false,
canMoveBox: true,
autoCrop: true,
// 只有自动截图开启 宽度高度才生效
autoCropWidth: window.innerWidth - 100 + "px", // 默认生成截图框宽度
autoCropHeight: window.innerWidth - 100 + "px", // 默认生成截图框高度
high: true, // 是否按照设备的dpr 输出等比例图片
fixedBox: false, // 固定截图框大小 不允许改变
fixed: false, // 是否开启截图框宽高固定比例
// fixedNumber: [1, 1], // 截图框的宽高比例
full: true, // 是否输出原图比例的截图
canMoveBox: true, // 截图框能否拖动
original: false, // 上传图片按照原始比例渲染
centerBox: true, // 截图框是否被限制在图片里面
centerBox: true,
high: true,
cropData: {},
enlarge: 1,
mode: 'contain',
maxImgSize: 3000,
// limitMinSize: [50, 50],
fixed: false,
// fixedNumber: [2, 1],
fillColor: '',
full: true,
infoTrue: false, // true 为展示真实输出图片宽高 false 展示看到的截图框宽高
mode: "100% auto", // 图片默认渲染方式
// outputSize: 0.5, //图片质量
// info: false, // 裁剪框的大小信息
// // outputType: 'jpeg', // 裁剪生成图片的格式
// outputType: "png", // 裁剪生成图片的格式
// canScale: true, // 图片是否允许滚轮缩放
// autoCrop: true, // 是否默认生成截图框
// autoCropWidth: window.innerWidth - 100 + "px", // 默认生成截图框宽度
// autoCropHeight: window.innerWidth - 100 + "px", // 默认生成截图框高度
// high: true, // 是否按照设备的dpr 输出等比例图片
// fixedBox: false, // 固定截图框大小 不允许改变
// fixed: false, // 是否开启截图框宽高固定比例
// // fixedNumber: [1, 1], // 截图框的宽高比例
// full: true, // 是否输出原图比例的截图
// canMoveBox: true, // 截图框能否拖动
// original: false, // 上传图片按照原始比例渲染
// centerBox: true, // 截图框是否被限制在图片里面
// infoTrue: false, // true 为展示真实输出图片宽高 false 展示看到的截图框宽高
// mode: "100% auto", // 图片默认渲染方式
});
//#region 裁剪图片
const cropperOverData = ref(null);
function sendImgData() {
cropper.value.getCropBlob(async (data) => {
try {
const checkPhotoRes = await checkPhotoApi(data)
if (checkPhotoRes.code !== 200) {
ElMessage.error(checkPhotoRes.message);
return
}
const res = await awsImgUpload(data);
const res2 = await saveImgApi({
type: props.type,


+ 51
- 0
src/components/myLoadingIcon2.vue ファイルの表示

@@ -0,0 +1,51 @@
<template>
<div class="loadingIcon">
<div>
<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>
</div>
</div>
</template>

<script setup>
import { useI18n } from "vue-i18n";
const { locale } = useI18n();
const lanChange = ref(locale);

const props = defineProps({
isLoading: {
required: false, //是否必传
},
});
</script>
<style lang="scss" scoped>
.loadingIcon {
position: relative;
width: 100%;
height: 60px;
line-height: 60px;
text-align: center;
transform: scale(2);
> div {
position: absolute;
top: 0;
left: 50%;
transform: translateX(-50%);
}
.loadingText {
position: relative;
top: -2px;
left: 4px;
}
}
</style>

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

@@ -51,6 +51,7 @@ export default {
logout: "Logout",
goLogin: "Go and log in",
LanguageChange: "Language switching",
currencyValue: 'Current currency value'
},

// 创作视频
@@ -85,6 +86,7 @@ export default {
comingSoon: "COMING SOON",
material: "Material",
subtitles: "Subtitles",
AItext:"AI Script",
sex: "Sex",
videoRatio: "Video Ratio",
all: "All",
@@ -104,6 +106,11 @@ export default {
tooltip2: "Add Pause",
SubtitlesTip: "Subtitles will be shown here",
subtitlesSwitch: "Subtitles Switch",
inputRequirement:'Please enter your requirements, for example: Please generate a beverage advertisement of 500 words or less',
GeneratedText:'Generated Text',
SmartText:'Smart Text',
copy:'Copy to text',
custom: 'VIP'
},

// 我的视频
@@ -202,6 +209,18 @@ export default {
title: "API Access",
comingSoon: "Coming Soon",
},
cooperate: {
title: 'Partner Access',
api: {
title: 'API Access'
},
privatization: {
title: 'Privatization deployment'
}
},
my: {
title: 'My',
},

signSucceed: {
title: "Confirm your email",
@@ -215,4 +234,44 @@ export default {
goLogin: "Login now",
reSend: "Resend",
},
recharge: {
title: 'Recharge order',
currencyValue: 'Current currency value',
productTitle: 'name',
orderNumber: 'Order No',
payment: 'amount of money',
createTime: 'Creation time',
operate: 'operate',
view: 'view',
detailTitle: 'Recharge Details',
payVendor: 'Payment method',
weChat: 'WeChat',
alipay: 'Alipay',
paymentStr: 'Payment amount',
orderStatus: 'Payment status',
paymentTime: 'Pay time',
close: 'close'
},
coindeduc: {
title: 'Creation Record',
currencyValue: 'Current currency value',
titleList: 'name',
videoPlayUrl: 'Video address',
videoTime: 'Video duration(S)',
costPoints: 'Deduction quantity',
createTime: 'Creation time',
operate: 'operate',
view: 'view',
detailTitle: 'Deduction Details',
second: 'S'
},
store: {
priceRmb: 'original price',
glod: 'currency value',
createOrder: 'BUY NOW',
payType:'Choose payment method',
cancel: 'Cancel',
confirm: 'Confirm',
pay: 'Payment in progress...',
}
};

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

@@ -52,6 +52,7 @@ export default {
logout: "注销",
goLogin: "登录",
LanguageChange: "语言切换",
currencyValue: '当前币值'
},

// 创作视频
@@ -86,6 +87,7 @@ export default {
BackgroundImage: "背景",
material: "素材",
subtitles: "字幕",
AItext:"AI生成文本",
sex: "性别",
videoRatio: "视频比例",
all: "全部",
@@ -105,6 +107,11 @@ export default {
tooltip2: "添加停顿",
SubtitlesTip: "字幕将在此处展示",
subtitlesSwitch: "字幕开关",
inputRequirement:'请输入您的需求,例如:请生成一个500字以内的饮料广告',
GeneratedText:'生成文本',
SmartText:'智能文本',
copy:'复制到文案',
custom: '私人订制'
},

// 我的视频
@@ -202,6 +209,18 @@ export default {
title: "API 接口",
comingSoon: "敬请期待",
},
cooperate: {
title: '合作商接入',
api: {
title: 'API 接口'
},
privatization: {
title: '私有化部署'
}
},
my: {
title: '我的',
},

signSucceed: {
title: "确认邮箱",
@@ -214,4 +233,44 @@ export default {
goLogin: "即将登录",
reSend: "重新发送",
},
recharge: {
title: '充值订单',
currencyValue: '当前币值',
productTitle: '名称',
orderNumber: '订单号',
payment: '金额',
createTime: '创建时间',
operate: '操作',
view: '查看',
detailTitle: '充值详情',
payVendor: '支付方式',
weChat: '微信',
alipay: '支付宝',
paymentStr: '支付金额',
orderStatus: '支付状态',
paymentTime: '支付时间',
close: '关闭'
},
coindeduc: {
title: '创作记录',
currencyValue: '当前币值',
titleList: '名称',
videoPlayUrl: '视频地址',
videoTime: '视频时长(秒)',
costPoints: '扣币数量',
createTime: '创建时间',
operate: '操作',
view: '查看',
detailTitle: '扣币详情',
second: '秒'
},
store: {
priceRmb: '原价',
glod: '币值',
createOrder: '立即购买',
payType:'选择支付方式',
cancel: '取消',
confirm: '确定',
pay: '支付中...',
}
};

+ 70
- 13
src/layout/index.vue ファイルの表示

@@ -23,39 +23,58 @@
</div>
{{ $t("myCreating.title") }}
</div>
<div class="myRouter-item" @click="showTalkUsDialog = true">
<div class="myRouter-item" @click="router.push('/separation')">
<div class="icon">
<el-icon><i-ep-user /></el-icon>
</div>
{{ $t("userModel.title") }}
</div>
<div class="myRouter-item" @click="showTalkUsDialog = true">
<div class="myRouter-item" @click="router.push('/voiceprint')">
<div class="icon">
<el-icon><i-ep-phone /></el-icon>
</div>
{{ $t("voiceModel.title") }}
</div>

<div class="bottomBtx">
<!-- <div class="bottomBtx"> -->
<div class="myRouter-item" @click="router.push('/myStore')">
<div class="icon">
<el-icon><i-ep-Goods /></el-icon>
</div>
{{ $t("shop.title") }}
</div>
<div class="myRouter-item" @click="router.push('/myAPI')">
<!-- <div class="myRouter-item" @click="router.push('/myAPI')">
<div class="icon">
<el-icon><i-ep-DocumentAdd /></el-icon>
</div>
{{ $t("API.title") }}
</div>
</div>
</div> -->
<el-menu
default-active=""
class="el-menu-vertical-demo"
active-text-color="#ffd04b"
background-color="#000"
text-color="#fff"
>
<el-sub-menu index="1">
<template #title>
<el-icon><location /></el-icon>
<span>{{ $t("my.title") }}</span>
</template>
<el-menu-item-group>
<el-menu-item index="2-1" @click="router.push('/myRecharge')">{{ $t("recharge.title") }} </el-menu-item>
<el-menu-item index="2-2" @click="router.push('/coindeduc')">{{ $t("coindeduc.title") }} </el-menu-item>
</el-menu-item-group>
</el-sub-menu>
</el-menu>
<!-- </div> -->
</div>
</div>
<!-- 右边显示部分 -->
<div class="rightBox">
<!-- nav栏 -->
<div class="nav">
<div class="navItem">{{ $t("navbar.currencyValue") }}:{{ currentValue }}</div>
<!-- 语言 -->
<el-tooltip
style="cursor: pointer; z-index: 999"
@@ -66,6 +85,7 @@
>
<lang-select class="navItem" />
</el-tooltip>
<div class="navItem" v-if="currentPhone">{{ currentPhone }}</div>
<!-- 登录 -->
<div class="navItem" v-if="!AccessToken" @click="router.push('login')">
{{ $t("navbar.goLogin") }}
@@ -103,6 +123,14 @@ import { useRoute, useRouter } from "vue-router";
import { useI18n } from "vue-i18n";

import { userInfoModules } from "@/store/modules/userInfo";
import { getUserPoinsApi } from '@/apis/my.js'

import {
Document,
Menu as IconMenu,
Location,
Setting,
} from '@element-plus/icons-vue'

const userInfoPinia = userInfoModules();
const route = useRoute();
@@ -162,20 +190,41 @@ function handleOutsideClick() {
appStore.closeSideBar(false);
}

onMounted(() => {});
// 当前币值
const currentValue = ref('')
const currentPhone = localStorage.getItem('phone')
function currentValFn() {
getUserPoinsApi().then(res => {
currentValue.value = res.data
localStorage.setItem('userPoinsVal', res.data)
})
}

onMounted(() => {
currentValFn()
});
//#region 联系我们弹窗
const showTalkUsDialog = ref(false);
const updateShowTalkUsDialog = (newValue: any) => {
showTalkUsDialog.value = newValue;
};
//#endregion -----------------------------------

//#region 页面跳转
function toUserModel() {
// router.push("/userModel");
window.location.href = '/priMakeHtml/dingzhifenshen/index.shtml'
showTalkUsDialog.value = true;
}
//#endregion ------------------------------------
</script>

<!-- 新样式 -->
<style lang="scss" scoped>
.app-wrapper {
width: 1920px;
width: 100%;
display: flex;
overflow: hidden;
}
.Sidebar {
width: 230px;
@@ -194,9 +243,12 @@ const updateShowTalkUsDialog = (newValue: any) => {
.myRouter {
position: relative;
height: calc(100% - 80px);
padding: 130px 20px 0;
padding: 40px 20px 0;
color: #fff;
user-select: none;
// color: #868593;
overflow: auto;
overflow-x: hidden;

.bottomBtx {
position: absolute;
@@ -207,8 +259,7 @@ const updateShowTalkUsDialog = (newValue: any) => {
width: 100%;
height: 50px;
padding-left: 10px;
font-size: 20px;
font-weight: 500;
font-size: 18px;
line-height: 50px;
text-align: center;
cursor: pointer;
@@ -234,7 +285,6 @@ const updateShowTalkUsDialog = (newValue: any) => {
vertical-align: sub;
}
}

&:hover {
// background: rgba(255, 255, 255, 0.2);
color: #000;
@@ -261,7 +311,7 @@ const updateShowTalkUsDialog = (newValue: any) => {
}
}
.rightBox {
width: 1690px;
width: calc(100% - 230px);
// background-color: #000;
.nav {
display: flex;
@@ -280,4 +330,11 @@ const updateShowTalkUsDialog = (newValue: any) => {
}
}
}
.el-menu{
border: none;
:deep .el-sub-menu__title, :deep .el-menu-item{
font-size: 18px;
}

}
</style>

+ 108
- 8
src/router/index.ts ファイルの表示

@@ -1,4 +1,4 @@
import { createRouter, createWebHashHistory, RouteRecordRaw } from "vue-router";
import { createRouter, createWebHashHistory, RouteRecordRaw, useRoute } from "vue-router";
import { getCurrentInstance } from "vue";

export const Layout = () => import("@/layout/index.vue");
@@ -55,6 +55,42 @@ export const constantRoutes: RouteRecordRaw[] = [
},
},
],
}, // 定制分身
{
path: "/",
component: Layout,
redirect: "/userModel",
children: [
{
path: "userModel",
component: () => import("@/views/userModel/index.vue"),
name: "userModel",
meta: {
title: "userModel",
icon: "homepage",
affix: true,
name: "userModel.title",
},
},
],
}, // 定制声纹
{
path: "/",
component: Layout,
redirect: "/voiceModel",
children: [
{
path: "voiceModel",
component: () => import("@/views/voiceModel/index.vue"),
name: "voiceModel",
meta: {
title: "voiceModel",
icon: "homepage",
affix: true,
name: "voiceModel.title",
},
},
],
}, {
path: "/",
component: Layout,
@@ -75,17 +111,28 @@ export const constantRoutes: RouteRecordRaw[] = [
}, {
path: "/",
component: Layout,
redirect: "/myAPI",
redirect: "/myRecharge",
children: [
{
path: "myAPI",
component: () => import("@/views/myAPI/index.vue"),
name: "myAPI",
path: "/myRecharge",
component: () => import("@/views/my/recharge.vue"),
name: "myRecharge",
meta: {
title: "myAPI",
title: "myRecharge",
icon: "homepage",
affix: true,
name: "API.title",
name: "my.title",
},
},
{
path: "/coindeduc",
component: () => import("@/views/my/coindeduc.vue"),
name: "coindeduc",
meta: {
title: "coindeduc",
icon: "homepage",
affix: true,
name: "my.title",
},
},
],
@@ -108,6 +155,57 @@ export const constantRoutes: RouteRecordRaw[] = [
},
],
},
{
path: "/",
component: Layout,
redirect: "/separation",
children: [
{
path: "/separation",
component: () => import("@/views/separation/index.vue"),
name: "separation",
meta: {
title: "separation",
icon: "homepage",
affix: true,
name: "separation.title",
},
},
{
path: "/voiceprint",
component: () => import("@/views/voiceprint/index.vue"),
name: "voiceprint",
meta: {
title: "voiceprint",
icon: "homepage",
affix: true,
name: "separation.title",
},
},
{
path: "/privatization",
component: () => import("@/views/privatization/index.vue"),
name: "privatization",
meta: {
title: "privatization",
icon: "homepage",
affix: true,
name: "separation.title",
},
},
{
path: "/api",
component: () => import("@/views/api/index.vue"),
name: "api",
meta: {
title: "api",
icon: "homepage",
affix: true,
name: "separation.title",
},
},
],
},

];

@@ -121,7 +219,9 @@ const router = createRouter({
// 2023-5-23 添加平滑滚动 YWQ
scrollBehavior: () => ({ behavior: "smooth", left: 0, top: 0 }),
});

router.beforeEach((to, from) => {
document.title = '元气智驾-' + (to.query.p || from.query.p || localStorage.getItem("title") || '悟空')
})
/**
* 重置路由
*/


+ 1
- 0
src/store/modules/userInfo.js ファイルの表示

@@ -13,6 +13,7 @@ export const userInfoModules = defineStore("userInfo", () => {
async function getUserInfo() {
const res = await getUserInfoApi()
userInfo.value = res.data
localStorage.setItem('phone', res.data.phone)
}
// 退出清除用户信息
function loginOut(params) {


+ 4
- 0
src/types/auto-imports.d.ts ファイルの表示

@@ -2,6 +2,8 @@
export {}
declare global {
const EffectScope: typeof import('vue')['EffectScope']
const ElMessage: typeof import('element-plus/es')['ElMessage']
const ElMessageBox: typeof import('element-plus/es')['ElMessageBox']
const asyncComputed: typeof import('@vueuse/core')['asyncComputed']
const autoResetRef: typeof import('@vueuse/core')['autoResetRef']
const computed: typeof import('vue')['computed']
@@ -268,6 +270,8 @@ import { UnwrapRef } from 'vue'
declare module 'vue' {
interface ComponentCustomProperties {
readonly EffectScope: UnwrapRef<typeof import('vue')['EffectScope']>
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']>
readonly autoResetRef: UnwrapRef<typeof import('@vueuse/core')['autoResetRef']>
readonly computed: UnwrapRef<typeof import('vue')['computed']>


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

@@ -13,6 +13,7 @@ declare module '@vue/runtime-core' {
CutImg1: typeof import('./../components/cutImg1.vue')['default']
EditPosition: typeof import('./../components/EditPosition.vue')['default']
'EditPosition copy': typeof import('./../components/EditPosition copy.vue')['default']
ElAlert: typeof import('element-plus/es')['ElAlert']
ElButton: typeof import('element-plus/es')['ElButton']
ElDialog: typeof import('element-plus/es')['ElDialog']
ElDropdown: typeof import('element-plus/es')['ElDropdown']
@@ -23,10 +24,19 @@ declare module '@vue/runtime-core' {
ElIcon: typeof import('element-plus/es')['ElIcon']
ElInput: typeof import('element-plus/es')['ElInput']
ElInputNumber: typeof import('element-plus/es')['ElInputNumber']
ElMenu: typeof import('element-plus/es')['ElMenu']
ElMenuItem: typeof import('element-plus/es')['ElMenuItem']
ElMenuItemGroup: typeof import('element-plus/es')['ElMenuItemGroup']
ElOption: typeof import('element-plus/es')['ElOption']
ElPagination: typeof import('element-plus/es')['ElPagination']
ElRadio: typeof import('element-plus/es')['ElRadio']
ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup']
ElScrollbar: typeof import('element-plus/es')['ElScrollbar']
ElSelect: typeof import('element-plus/es')['ElSelect']
ElSubMenu: typeof import('element-plus/es')['ElSubMenu']
ElSwitch: typeof import('element-plus/es')['ElSwitch']
ElTable: typeof import('element-plus/es')['ElTable']
ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
ElTooltip: typeof import('element-plus/es')['ElTooltip']
ElUpload: typeof import('element-plus/es')['ElUpload']
GithubCorner: typeof import('./../components/GithubCorner/index.vue')['default']
@@ -49,6 +59,7 @@ declare module '@vue/runtime-core' {
MultiUpload: typeof import('./../components/Upload/MultiUpload.vue')['default']
MyLoading: typeof import('./../components/myLoading.vue')['default']
MyLoadingIcon: typeof import('./../components/myLoadingIcon.vue')['default']
MyLoadingIcon2: typeof import('./../components/myLoadingIcon2.vue')['default']
Pagination: typeof import('./../components/Pagination/index.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']


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

@@ -69,12 +69,12 @@ request.interceptors.response.use(
);
return
// 弹出错误
} else if (code != 200) {
} else if (code != 200 && code !== 64001) {
if (code == 500) {
// ElMessage.error("系统异常,请稍后再试");
// ElMessage.error($t('tips.systemException'));
ElMessage.error(
appStore.language == "zh-cn" ? `服务器繁忙,请重试!` : `The server is busy, please try again`
appStore.language == "zh-cn" ? (response.data.languageMessage && response.data.languageMessage['zh-cn']) || `服务器繁忙,请重试!` : (response.data.languageMessage && response.data.languageMessage[appStore.language]) || `The server is busy, please try again`
);
// 其他错误
} else {


+ 141
- 0
src/views/api/demo.html ファイルの表示

@@ -0,0 +1,141 @@
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
* {
padding: 0;
margin: 0;
}

.page {
padding: 50px
}

h1 {
font-size: 20px;
font-weight: 600;
margin-bottom: 0.8em;
}

p {
margin-bottom: 0.5em;
font-size: 14px;
line-height: 1.6;
}

.table {
margin-top: 50px;
}

.table .table-wrp {
margin: 1em 0 3em;
overflow-x: auto;
max-width: 100%;
}

.table table {
display: table;
width: 100%;
border-collapse: collapse;
border-spacing: 0;
overflow: auto;
font-size: 14px;
}

.table table th {
font-weight: 600;
text-align: left;
border-bottom: 1px solid #888;
padding: 10px 20px;
white-space: nowrap;
color: #888;
}

.table table td {
color: #353535;
text-align: left;
border-bottom: 1px solid #e5e5e5;
padding: 20px 20px;
height: 62px;
box-sizing: border-box;
word-break: normal;
}
.style .code{
background-color: #f9f9fa;
margin: 1em 0;
border-radius: 4px
}
</style>
</head>

<body>
<div class="page">
<!-- 文字 -->
<div class="text">
<h1>概述</h1>
<p>在小程序管理后台开启api加密后,开发者需要对原APl的请求内容 加密 与签名,同时AP的回包内容需要开发者验签 与 解密 。支持的api可参考接口调用。</p>
<p>目前支持以下几种算法,可在MP管理页配置。</p>
</div>
<!-- 表格 -->
<div class="table">
<h1>API请求处理</h1>
<div class="table-wrp">
<table>
<thead>
<tr>
<th>参数</th>
<th>类型</th>
<th>默认值</th>
<th>必填</th>
<th>备注</th>
</tr>
</thead>
<tbody>
<tr>
<td>iv</td>
<td>string</td>
<td></td>
<td>是</td>
<td>初始向量,为16字节base64字符串(解码后为12字节随机字符串)</td>
</tr>
<tr>
<td>data</td>
<td>string</td>
<td></td>
<td>是</td>
<td>加密后的密文,使用base64编码</td>
</tr>
<tr>
<td>authtag</td>
<td>string</td>
<td></td>
<td>是</td>
<td>GCM模式输出的认证信息,使用base64编码</td>
</tr>
</tbody>
</table>
</div>
</div>

<!-- 样式 -->
<div class="style">
<h1>样式</h1>
<div class="code">
<pre class="language-json">
<code>
<span>{</span>
<span>"Sn"</span><span>:</span> <span>"fa05fe1e5bcc79b81ad5ad4b58acf787"</span><span>,</span>
<span>"Key"</span><span>:</span> <span>"otUpngOjU+nVQaWJIC3D/yMLV17RKaP6t4Ot9tbnzLY="</span>
<span>}</span>
</code>
</pre>
</div>
</div>
</div>
</body>

</html>

+ 125
- 0
src/views/api/index.vue ファイルの表示

@@ -0,0 +1,125 @@
<template>
<div class="page">
<div class="">
<el-menu
default-active="1-1"
class="sidebar"
@open="handleOpen"
@close="handleClose"
>
<el-sub-menu index="1">
<template #title>
<span>开发前必读</span>
</template>
<el-menu-item index="1-1" @click="guide">服务端API签名指南</el-menu-item>
</el-sub-menu>
<el-sub-menu index="2">
<template #title>
<span>开发接口</span>
</template>
<el-menu-item v-for="item in apiMenuList" :key="item.id" :index="item.id" @click="getContent(item.id)">{{ item.name }}</el-menu-item>
</el-sub-menu>
</el-menu>
</div>
<div class="content">
<div v-html="content"></div>
</div>
</div>
</template>

<script setup>
import { getAvailableApiGuideApi, apiMenuApi, getContentApi } from '@/apis/api.js'

const handleOpen = (key, keyPath) => {
console.log(key, keyPath)
if (key === '2') {
getMenu()
}
}
const handleClose = (key, keyPath) => {
console.log(key, keyPath)
}
const content = ref('');
// 签名指南
const guide = () => {
getAvailableApiGuideApi().then(res => {
if(res.code === 200) {
content.value = res.data.content
}
})
}

// 开发接口
const apiMenuList = ref([])
const getMenu = () => {
apiMenuApi().then(res => {
if (res.code === 200) {
apiMenuList.value = res.data
}
})
}
// 详情
const getContent = (id) => {
getContentApi(id).then(res => {
if (res.code === 200) {
content.value = res.data.content || ''
}
})
}

onMounted(() => {
guide()
});
</script>
<style>
.sidebar.el-menu--vertical .el-sub-menu__title:hover {
background-color: #fff !important;
}
.sidebar.el-menu--vertical .el-menu-item:hover {
background-color: #fff !important;
}
</style>
<style lang="scss" scoped>
.page {
display: flex;
width: 100%;
overflow: hidden;
overflow-y: scroll;
background-color: #f5f5f5;
border-radius: 25px 0 0 0;
min-height: calc(100vh - 80px);
// text-align: center;
.title {
font-size: 50px;
}
.sidebar {
position: absolute;
width: 340px;
top: 0;
bottom: 0;
overflow: auto;
padding: 40px 30px;
box-sizing: border-box;
z-index: 1;
}
.content{
position: absolute;
left: 340px;
right: 0;
top: 0;
bottom:0;
background: #fff;
padding: 40px 30px;
overflow: auto;
}
}
:deep(.el-submenu .el-menu:hover) {

background: transparent;

color: #d75b5f;

}


</style>

+ 755
- 148
src/views/createVideo/index.vue
ファイル差分が大きすぎるため省略します
ファイルの表示


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

@@ -19,7 +19,7 @@
>
<div class="flex text-white items-center">
<span class="flex-1 text-center">{{
$t("login.title") + " " + $t("login.login")
title + " " + $t("login.login")
}}</span>
<el-tooltip
class="box-item"
@@ -136,7 +136,7 @@
>
<div class="flex text-white items-center">
<span class="flex-1 text-center">{{
$t("login.title") + " " + $t("login.register")
title + " " + $t("login.register")
}}</span>
<el-tooltip
class="box-item"
@@ -250,7 +250,7 @@
>
<div class="flex text-white items-center">
<span class="flex-1 text-center">{{
$t("login.title") + " " + $t("login.register")
title + " " + $t("login.register")
}}</span>
<el-tooltip
class="box-item"
@@ -318,7 +318,7 @@
>
<div class="flex text-white items-center">
<span class="flex-1 text-center">{{
$t("login.title") + " " + $t("login.forgetPwdTitle")
title + " " + $t("login.forgetPwdTitle")
}}</span>
<el-tooltip
class="box-item"
@@ -403,7 +403,7 @@
>
<div class="flex text-white items-center">
<span class="flex-1 text-center">{{
$t("login.title") + " " + $t("login.resetPassword")
title + " " + $t("login.resetPassword")
}}</span>
<el-tooltip
class="box-item"
@@ -524,7 +524,7 @@
>
<div class="flex text-white items-center">
<span class="flex-1 text-center">{{
$t("login.title") + " " + $t("login.setNewPwd")
title + " " + $t("login.setNewPwd")
}}</span>
<el-tooltip
class="box-item"
@@ -619,7 +619,7 @@
>
<div class="flex text-white items-center">
<span class="flex-1 text-center">{{
$t("login.title") + " " + $t("login.login")
title + " " + $t("login.login")
}}</span>
<el-tooltip
class="box-item"
@@ -716,7 +716,7 @@
>
<div class="flex text-white items-center">
<span class="flex-1 text-center">{{
$t("login.title") + " " + $t("login.setInvitatioCode")
title + " " + $t("login.setInvitatioCode")
}}</span>
<el-tooltip
class="box-item"
@@ -818,6 +818,15 @@ const currentLan = ref(locale);
const userInfoPinia = userInfoModules();
const route = useRoute();
const router = useRouter();

/**
* 获取页面标题
*/
const title = ref(<string>route.query.p || '慧影')
if (title) {
localStorage.setItem("title", title.value)
document.title = 'Metavatar-' + title.value
}
/**
* 按钮loading
*/


+ 207
- 0
src/views/my/coindeduc.vue ファイルの表示

@@ -0,0 +1,207 @@
<template>
<div class="page justify-around">
<el-alert :title="`${$t('coindeduc.currencyValue')}:${currentValue}`" type="success" :closable="false" />

<el-table :data="tableData" style="width: 100%; height: 90%" size="large" align="center" border>
<el-table-column
prop="title"
:label="$t('coindeduc.titleList')"
align="center"
/>

<el-table-column
prop="videoPlayUrl"
:label="$t('coindeduc.videoPlayUrl')"
align="center"
/>
<el-table-column
prop="videoTime"
:label="$t('coindeduc.videoTime')"
align="center"
/>
<el-table-column
prop="costPoints"
:label="$t('coindeduc.costPoints')"
align="center"
/>
<el-table-column
prop="createTime"
:label="$t('coindeduc.createTime')"
align="center"
>
<template #default="scope">
{{ getDate(scope.row.createDate) }}
</template>
</el-table-column>
<el-table-column
fixed="right"
:label="$t('coindeduc.operate')"
align="center"
>
<template #default="scope">
<el-button link type="primary" size="small" @click="detailView(scope.row)">{{ $t('coindeduc.view') }}</el-button>
</template>
</el-table-column>
</el-table>

<div class="pagination">
<el-pagination
v-model:current-page="pageNum"
v-model:page-size="pageSize"
:page-sizes="[10, 50, 100]"
:small="false"
:disabled="false"
:background="true"
layout="total, sizes, prev, pager, next, jumper"
:total="total"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
<el-dialog v-model="dialogVisible" :title="`${$t('coindeduc.detailTitle')}:`" width="40%" :before-close="handleClose" >
<el-form :model="form" label-width="120px">
<el-form-item label="名称">
<el-input v-model="form.title" />
</el-form-item>
<el-form-item label="视频类型">
<el-radio-group v-model="form.videoType">
<el-radio :label="1">竖版</el-radio>
<el-radio :label="2">横版</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="文案">
<el-input v-model="form.paperwork" type="textarea" />
</el-form-item>
<el-form-item label="封面图">
<img :src="form.coverImg" alt="" :style="{ width: form.videoType === 1 ? '50%' : '100%'}">
</el-form-item>
<el-form-item label="视频地址">
<el-input v-model="form.videoPlayUrl" />
<!-- <video :src="form.videoPlayUrl" style="width:100%;max-height: 300px;"></video> -->
</el-form-item>
<el-form-item label="视频时长">
<el-input v-model="form.videoTime" />
</el-form-item>
<el-form-item label="创建时间">
<el-input v-model="form.createDate" />
</el-form-item>
<el-form-item label="消耗金币">
<el-input v-model="form.costPoints" />
</el-form-item>
<el-form-item label="消耗金币明细">
<el-input v-model="form.costPointsDetail" />
</el-form-item>
<el-form-item>
<el-button @click="dialogVisible = false">关闭</el-button>
</el-form-item>
</el-form>
</el-dialog>
</div>
</template>

<!-- videoType==1为竖,==2为横 -->
<script setup>
//#region 导入
import { coindedApi, getDetailApi, getUserPoinsApi, findByIdApi } from '@/apis/my.js'
import dayjs from "dayjs";
import { useI18n } from "vue-i18n";
const { locale } = useI18n();
const lanChange = ref(locale);

const tableData = ref([]);

const pageNum = ref(1);
const pageSize = ref(10);
const total = ref(0);

const getDate = (val) => {
return dayjs(val).format("YYYY-MM-DD HH:mm:ss");
};

const handleSizeChange = (val) => {
pageSize.value = val;
getListData(pageNum.value, pageSize.value);
};

const handleCurrentChange = (val) => {
pageNum.value = val;
getListData(pageNum.value, pageSize.value);
};

const getListData = async (PageNum, PageSize) => {
try {
let data = {
projectType: 2,
pageNum: 1,
pageSize: 10
}
const res = await coindedApi(data);
tableData.value = res.data.list;
total.value = res.data.total * 1;
} catch (error) {
console.log(error, "err");
}
};

// 当前币值
const currentValue = ref('')
function currentValFn() {
getUserPoinsApi().then(res => {
currentValue.value = res.data
})
}

// 查看详情
const dialogVisible = ref(false)
const form = ref({
title: '',
videoPlayUrl: ''

})

function detailView(item) {
findByIdApi(item.id).then(res => {
res.data.createDate = getDate(res.data.createDate)
res.data.videoTime = res.data.videoTime + (lanChange.value == "zh-cn" ? '(秒)' : '(S)')
form.value = res.data
dialogVisible.value = true
})
}

onMounted(() => {
getListData(1, 10);
currentValFn()
});
</script>

<style lang="scss" scoped>
.page {
height: calc(100vh - 80px);
// height: calc(100vh - 80px);
background-color: #ffffff;
border-radius: 30px 0 0 0;
overflow: hidden;
padding: 10vh;
.addbtn {
margin-bottom: 2vh;
}
.formData {
padding: 5vh 3vh;
}

.pagination {
position: fixed;
left: 55%;
bottom: 8vh;
transform: translateX(-50%);
border-radius: 5px;
padding: 10px 20px;
z-index: 99;
transition: all 0.3s;
cursor: pointer;
&:hover {
background-color: #b8b8b873;
}
}
}
</style>

+ 193
- 0
src/views/my/recharge.vue ファイルの表示

@@ -0,0 +1,193 @@
<template>
<div class="page justify-around">
<el-alert :title="`${$t('recharge.currencyValue')}:${currentValue}`" type="success" :closable="false" />

<el-table :data="tableData" style="width: 100%; height: 90%" size="large" align="center" border>
<el-table-column
prop="productTitle"
:label="$t('recharge.productTitle')"
align="center"
/>

<el-table-column
prop="orderNumber"
:label="$t('recharge.orderNumber')"
align="center"
/>
<el-table-column
prop="paymentStr"
:label="$t('recharge.payment')"
align="center"
/>
<el-table-column
prop="createTime"
:label="$t('recharge.createTime')"
align="center"
>
<template #default="scope">
{{ getDate(scope.row.createTime) }}
</template>
</el-table-column>
<el-table-column
fixed="right"
:label="$t('recharge.operate')"
align="center"
>
<template #default="scope">
<el-button link type="primary" size="small" @click="detailView(scope.row)">{{ $t('recharge.view') }}</el-button>
</template>
</el-table-column>
</el-table>

<div class="pagination">
<el-pagination
v-model:current-page="pageNum"
v-model:page-size="pageSize"
:page-sizes="[10, 50, 100]"
:small="false"
:disabled="false"
:background="true"
layout="total, sizes, prev, pager, next, jumper"
:total="total"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
<el-dialog v-model="dialogVisible" :title="`${ $t('recharge.detailTitle')}:`" width="40%" >
<el-form :model="form" label-width="120px">
<el-form-item :label="$t('recharge.productTitle')">
<el-input v-model="form.productTitle" />
</el-form-item>
<el-form-item :label="$t('recharge.orderNumber')">
<el-input v-model="form.orderNumber" />
</el-form-item>
<el-form-item :label="$t('recharge.payVendor')">
<el-radio-group v-model="form.payVendor">
<el-radio :label="2">{{ $t('recharge.weChat') }}</el-radio>
<el-radio :label="4">{{$t('recharge.alipay')}}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="$t('recharge.paymentStr')">
<el-input v-model="form.paymentStr" />
</el-form-item>
<el-form-item :label="$t('recharge.orderStatus')">
<el-input v-model="form.orderStatus" />
</el-form-item>
<el-form-item :label="$t('recharge.paymentTime')">
<el-input v-model="form.paymentTime" />
</el-form-item>
<el-form-item>
<el-button @click="dialogVisible = false">{{ $t('recharge.close') }}</el-button>
</el-form-item>
</el-form>
</el-dialog>
</div>
</template>

<!-- videoType==1为竖,==2为横 -->
<script setup>
//#region 导入
import { rechargeListApi, getDetailApi, getUserPoinsApi, findByNumberApi } from '@/apis/my.js'
import dayjs from "dayjs";
import { useI18n } from "vue-i18n";
const { locale } = useI18n();
const lanChange = ref(locale);

const tableData = ref([]);

const pageNum = ref(1);
const pageSize = ref(10);
const total = ref(0);

const getDate = (val) => {
return dayjs(val).format("YYYY-MM-DD HH:mm:ss");
};

const handleSizeChange = (val) => {
pageSize.value = val;
getListData(pageNum.value, pageSize.value);
};

const handleCurrentChange = (val) => {
pageNum.value = val;
getListData(pageNum.value, pageSize.value);
};

const getListData = async (PageNum, PageSize) => {
try {
let data = {
projectType: 2,
pageNum: 1,
pageSize: 10
}
const res = await rechargeListApi(data);
tableData.value = res.data.list;
total.value = res.data.total * 1;
} catch (error) {
console.log(error, "err");
}
};

// 当前币值
const currentValue = ref('')
function currentValFn() {
getUserPoinsApi().then(res => {
currentValue.value = res.data
})
}

// 查看详情
const dialogVisible = ref(false)
const form = ref({
productTitle: '',
orderStatus:'',
paymentTime: '',
})

function detailView(item) {
findByNumberApi(item.id).then(res => {
res.data.paymentTime = getDate(res.data.paymentTime)
res.data.orderStatus = res.data.orderStatus === 3 ? lanChange.value == "zh-cn" ? '支付成功' : 'Payment successful': lanChange.value == "zh-cn" ? '支付失败' : 'Payment failed'
form.value = res.data
dialogVisible.value = true
})
}

onMounted(() => {
getListData(1, 10);
currentValFn()
});
</script>

<style lang="scss" scoped>
.page {
height: calc(100vh - 80px);
// height: calc(100vh - 80px);
background-color: #ffffff;
border-radius: 30px 0 0 0;
overflow: hidden;
padding: 10vh;
.addbtn {
margin-bottom: 2vh;
}
.formData {
padding: 5vh 3vh;
}

.pagination {
position: fixed;
left: 55%;
bottom: 8vh;
transform: translateX(-50%);
border-radius: 5px;
padding: 10px 20px;
z-index: 99;
transition: all 0.3s;
cursor: pointer;
&:hover {
background-color: #b8b8b873;
}
}
}
</style>

+ 0
- 22
src/views/myAPI/index.vue ファイルの表示

@@ -1,22 +0,0 @@
<template>
<div class="page">
<div class="title">{{ $t("API.comingSoon") }}</div>
</div>
</template>
<script setup>
// import { ref } from 'vue'
</script>
<style lang="scss" scoped>
.page {
padding: 30px;
width: 100%;
overflow: hidden;
overflow-y: scroll;
background-color: #fff;
min-height: calc(100vh - 80px);
text-align: center;
.title {
font-size: 50px;
}
}
</style>

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

@@ -481,6 +481,7 @@ const checkVideoStatus = async () => {
// 如果列表中没有生成中的视频,不发送请求
const intervalId = setInterval(async () => {
// && notGenerateList2.value.length == 0;
console.log("");
if (notGenerateList.value.length == 0) {
return;
}
@@ -509,7 +510,7 @@ onMounted(() => {
isLoading.value = true;
getPhotoVideoList(pageNum.value, pageSize.value, title.value);

// checkVideoStatus();
checkVideoStatus();
});
//#endregion ---------------------------
</script>


+ 1152
- 0
src/views/myStore/index copy.vue
ファイル差分が大きすぎるため省略します
ファイルの表示


+ 303
- 445
src/views/myStore/index.vue ファイルの表示

@@ -1,141 +1,48 @@
<template>
<div class="page">
<div class="commoditylIst" ref="scrollContainerRef" @wheel="handleWheel">
<div class="item" v-for="item in packageList" :key="item.typeDesc">
<!-- 遮罩层 -->
<div class="mask" v-if="item.type != 1">
<el-icon><i-ep-Lock /></el-icon>
</div>
<div @click="clickPayDialog(item)" v-if="item.typeDesc">
<div class="line" :style="`background-color:${item.color} ;`"></div>
<div class="info">
<!-- 商品信息 -->
<div class="content">
<div class="titleBox">
<div class="title">
{{ lanChange == "zh-cn" ? item.typeDesc : item.englishName }}
<div class="commoditylIst">
<el-scrollbar :always="true">
<div class="scrollbar-flex-content">
<div class="item" v-for="item in packageList" :key="item.typeDesc" :class="item.sellPriceRmb <= 0 ? 'dashed' : ''">
<!-- @click="clickPayDialog(item)" -->
<div>
<div class="line" :style="`background-color:${ item.sellPriceRmb > 0 ? item.color : '#eee'} ;`"></div>
<div class="info">
<!-- 商品信息 -->
<div class="content">
<div class="titleBox">
<!-- <p class="iconText" v-if="item.sellPriceRmb <= 0" >会员权益</p> -->
<div class="title">
{{ item.title }}
</div>
</div>
<template v-if="item.sellPriceRmb > 0">
<div class="money">
<span class="price">{{
item.sellPriceRmb / 100
}} ¥</span
>/ <span style="text-decoration:line-through;"> {{ $t("store.priceRmb") }}{{ item.priceRmb / 100 }}</span>
</div>
<div class="time">
{{ $t("store.glod") }}<span> {{ item.glod }}</span
>
</div>
<div v-if="item.sellPriceRmb > 0" class="button" @click="clickPayDialog(item)" :style="`background-color: #f3af00 ;`">
{{ $t("store.createOrder") }}
</div>
</template>
</div>
<div class="money">
<span class="price">{{
lanChange == "zh-cn" ? item.rmb + "¥" : item.dollar + "$"
}}</span
>/{{ item.validityDuration }}{{ $t("shop.day") }}
</div>
<div class="time">
<span>{{ item.credit }}</span
>{{ $t("shop.score") }}
</div>
<div
v-if="item.type != 1"
class="button"
:style="`background-color:${item.color} ;`"
>
{{ $t("createVideo.comingSoon") }}
</div>
<div
v-else
class="button"
:style="`background-color:${item.color} ;`"
>
{{ $t("shop.selectPlan") }}
</div>
</div>
<!-- 商品权益 -->
<div class="authority">
<div class="authority-item">
{{ $t("shop.InviteGetscore") }}:

<span>
<i>{{ item.inviteUserCredit }}</i
>{{ $t("shop.score") }}</span
>
</div>
<div class="authority-item">
{{ $t("shop.createVideoPrice") }}:<span
><i>{{ item.videoPrice }}</i
>{{ $t("shop.score") }}</span
>
</div>
<div class="authority-item">
{{ $t("shop.AIGeneratesPhotoPrices") }}:<span
><i>{{ item.photoPrice }}</i
>{{ $t("shop.score") }}</span
>
</div>
<div class="authority-item">
{{ $t("shop.UnitChargeDuration") }}:<span
><i>{{ item.chargeTime }}</i
>{{ $t("shop.second") }}</span
>
</div>

<div class="authority-item">
{{ $t("shop.Language") }}:<span
><i>{{ item.userPackageDetails[0].languageDesc }}</i></span
>
</div>
<div class="authority-item">
{{ $t("shop.sound") }}:<span
><i>{{ item.userPackageDetails[0].soundDesc }}</i></span
>
</div>
<div class="authority-item">
{{ $t("shop.IndividualVideoDuration") }}:<span
><i>{{ item.userPackageDetails[0].videoTime }}</i
>{{ $t("shop.second") }}</span
>
</div>
<div class="authority-item">
{{ $t("shop.Watermark") }}:<span>{{
item.userPackageDetails[0].aiPortrait == 0
? $t("shop.yes")
: $t("shop.no")
}}</span>
</div>
<div class="authority-item">
{{ $t("shop.AIGeneratedPortrait") }}:<span>{{
item.userPackageDetails[0].aiPortrait == 0
? $t("shop.no")
: $t("shop.yes")
}}</span>
</div>
<div class="authority-item">
{{ $t("shop.PhotoSinging") }}:<span>{{
item.userPackageDetails[0].photoSing == 0
? $t("shop.no")
: $t("shop.yes")
}}</span>
</div>
<div class="authority-item">
{{ $t("shop.AIGeneratedText") }}:<span
><i>{{ item.userPackageDetails[0].aiCreateTime }}</i></span
>
</div>
<div class="authority-item">
{{ $t("shop.VideoResolution") }}:<span>{{
item.userPackageDetails[0].resolutionRatioDesc
}}</span>
<!-- 商品权益 -->
<div class="authority">
<div v-html="item.detail"></div>
</div>
<!-- <div class="authority-item">
{{ $t("shop.DigitalPersonTemplate") }}:{{
item.userPackageDetails[0].humanTemplateDesc
}}
</div> -->
<!-- <div
class="authority-item"
v-for="items in item.authority"
:key="items"
>
<span>✔</span>{{ items }}
</div> -->
</div>
</div>
</div>
</div>
</el-scrollbar>
<!-- 企业版 -->
<div class="item" @click="showTalkUsDialog = true">
<!-- 遮罩层 -->
<!-- <div class="item" @click="showTalkUsDialog = true">
<div class="mask" v-if="false">
<el-icon><i-ep-Lock /></el-icon>
</div>
@@ -147,7 +54,6 @@
{{ $t("shop.Ent") }}
</div>
</div>
<!-- 描述 -->
<div class="description">
<div>
{{ lanChange == "zh-cn" ? "所有权限" : "All Pro Benefits" }}
@@ -178,64 +84,68 @@
</div>
</div>
</div>
</div>
</div> -->
</div>
<!-- 对话框 -->
<el-dialog v-if="false" v-model="showPayDialog" width="60%" center>
<!-- 对话框 v-if="false"-->
<el-dialog v-model="showPayDialog" v-if="showPayDialog"
:title="$t('store.payType')" width="300px">
<div class="payDialog">
<div class="left">
<div class="title">{{ $t(presentItme.value.title) }}</div>
<div class="price">
<span>{{ presentItme.value.price }}</span>
元/{{ presentItme.value.priceTime }}
</div>
<div
class="authority"
v-for="item in presentItme.value.authority"
:key="item"
>
{{ item }}
</div>
</div>
<div class="right">
<div
style="background-color: #07c160; cursor: pointer"
@click="showOrderCode(1)"
>
{{ $t("shop.WeChatPay") }}
</div>
<div
style="background-color: #1378fe; cursor: pointer"
@click="showOrderCode(2)"
>
{{ $t("shop.Alipay") }}
</div>
<div
style="background-color: #0070ba; cursor: pointer"
@click="showOrderCode(3)"
>
Paypal
</div>
</div>
<el-radio-group v-model="payVendor">
<el-radio label="4" border>
<img src="../../assets/img/alipay.png" alt="" />{{ $t("recharge.alipay") }}
</el-radio>
<el-radio label="2" border>
<img src="../../assets/img/weChat.png" alt="" />{{ $t("recharge.weChat") }}
</el-radio>
</el-radio-group>
</div>
<!-- 二维码弹出框 -->
<el-dialog
v-model="orderCodeDialogShow"
width="30%"
width="500px"
append-to-body
destroy-on-close
@closed="clearCode"
:close-on-click-modal="false"
>
<div class="orderCodeDialog">
<div class="orderCodeDialog" v-loading="loading"
:element-loading-text="`${$t('store.pay')}`">
<div class="codeImg">
<my-Loading-Icon v-if="!orderCodeUrl"></my-Loading-Icon>
<img v-if="orderCodeUrl" src="../../assets/img/false1.png" alt="" />
<my-Loading-Icon2
v-if="!payCodeUrl && !iframeBody"
></my-Loading-Icon2>
<!-- :callback="qrcode" -->
<vue-qr v-if="payCodeUrl" :text="payCodeUrl" :size="280"></vue-qr>
<iframe
v-show="iframeBody"
id="myIframe"
width="210PX"
height="205PX"
frameborder="0"
></iframe>
</div>
<div class="tips">扫描二维码进行付款</div>
<div class="btnBox">
<div>刷新二维码</div>
<div class="tips">
{{
lanChange == "zh-cn"
? `扫描二维码进行付款`
: `Scan the QR code for payment`
}}
</div>
<!-- <div class="btnBox">
<div @click="RefreshQR">
{{ lanChange == "zh-cn" ? `刷新二维码` : `Refresh QR code` }}
</div>
</div> -->
</div>
</el-dialog>
<template #footer>
<span class="dialog-footer">
<el-button size="small" @click="showPayDialog = false">{{ $t("store.cancel") }}</el-button>
<el-button size="small" type="primary" @click="payConfirm">
{{$t("store.confirm")}}
</el-button>
</span>
</template>
</el-dialog>

<!-- 联系我们对话框 -->
@@ -249,10 +159,14 @@
<script setup>
//#region 导入
import { getCurrentInstance, ref, onMounted } from "vue";
// import { packageInfoApi } from "@/apis/myStore";
import { packageInfoApi } from "@/apis/myStore";
import { useI18n } from "vue-i18n";
import talkUs from "@/components/talkUs.vue";
import { useRoute, useRouter } from "vue-router";
import { createOrderApi, createPayApi, getOrderStatusApi } from "@/apis/pay";
import { orderDetailApi, findStatusApi } from '@/apis/myStore.js'
import vueQr from 'vue-qr/src/packages/vue-qr.vue';

const route = useRoute();
const router = useRouter();
const { locale } = useI18n();
@@ -261,236 +175,33 @@ const lanChange = ref(locale);
const AccessToken = localStorage.getItem("AccessToken"); // 判断有没有登录
//#endregion ------------------------------------

//#region 各个套餐的逻辑
const packageList = ref([
{
id: 1,
type: 1,
typeDesc: "基础版",
englishName: "Trial",
dollar: "0.00",
rmb: "0.00",
credit: 100,
validityDuration: 30,
inviteUserCredit: 10,
videoPrice: 10,
photoPrice: 10,
talkPrice: 20,
chargeTime: 60,
createDate: 1686814258000,
updateDate: 1686814258000,
isDel: 0,
color: "#f3af00",
userPackageDetails: [
{
id: 1,
detailType: 1,
detailTypeDesc: "照片说话",
packageId: 1,
watermark: 1,
watermarkDesc: "是",
language: 1,
languageDesc: "50+",
sound: 1,
soundDesc: "400+",
videoTime: 60,
aiPortrait: 0,
aiPortraitDesc: "否",
photoSing: 0,
photoSingDesc: "否",
aiCreateTime: 0,
resolutionRatio: 1,
resolutionRatioDesc: "720p",
createDate: 1686882177000,
updateDate: 1686882177000,
isDel: 0,
},
{
id: 4,
detailType: 2,
detailTypeDesc: "口播",
packageId: 1,
watermark: 1,
watermarkDesc: "是",
language: 1,
languageDesc: "50+",
sound: 1,
soundDesc: "400+",
videoTime: 60,
aiCreateTime: 0,
resolutionRatio: 1,
resolutionRatioDesc: "720p",
humanTemplate: 1,
humanTemplateDesc: "免费",
createDate: 1686882177000,
updateDate: 1686882177000,
isDel: 0,
},
],
},
{
id: 2,
type: 2,
typeDesc: "专业版",
englishName: "Pro",
dollar: "7.99",
rmb: "49.99",
credit: 200,
validityDuration: 30,
inviteUserCredit: 10,
videoPrice: 6,
photoPrice: 6,
talkPrice: 16,
chargeTime: 60,
createDate: 1686814258000,
updateDate: 1686814258000,
isDel: 0,
color: "#c971ff",
userPackageDetails: [
{
id: 2,
detailType: 1,
detailTypeDesc: "照片说话",
packageId: 2,
watermark: 0,
watermarkDesc: "否",
language: 1,
languageDesc: "50+",
sound: 1,
soundDesc: "400+",
videoTime: 300,
aiPortrait: 1,
aiPortraitDesc: "是",
photoSing: 1,
photoSingDesc: "是",
aiCreateTime: 30,
resolutionRatio: 2,
resolutionRatioDesc: "1080p",
createDate: 1686882177000,
updateDate: 1686882177000,
isDel: 0,
},
{
id: 5,
detailType: 2,
detailTypeDesc: "口播",
packageId: 2,
watermark: 0,
watermarkDesc: "否",
language: 1,
languageDesc: "50+",
sound: 1,
soundDesc: "400+",
videoTime: 300,
aiCreateTime: 100,
resolutionRatio: 2,
resolutionRatioDesc: "1080p",
humanTemplate: 2,
humanTemplateDesc: "VIP",
createDate: 1686882177000,
updateDate: 1686882177000,
isDel: 0,
},
],
},
{
id: 3,
type: 3,
typeDesc: "加强版",
englishName: "Advanced",
dollar: "39.99",
rmb: "279.99",
credit: 1200,
validityDuration: 30,
inviteUserCredit: 10,
videoPrice: 5,
photoPrice: 5,
talkPrice: 15,
chargeTime: 60,
createDate: 1686814258000,
updateDate: 1686814258000,
isDel: 0,
color: "#7b61ff",
userPackageDetails: [
{
id: 3,
detailType: 1,
detailTypeDesc: "照片说话",
packageId: 3,
watermark: 0,
watermarkDesc: "否",
language: 1,
languageDesc: "50+",
sound: 1,
soundDesc: "400+",
videoTime: 300,
aiPortrait: 1,
aiPortraitDesc: "是",
photoSing: 1,
photoSingDesc: "是",
aiCreateTime: 200,
resolutionRatio: 2,
resolutionRatioDesc: "1080p",
createDate: 1686882177000,
updateDate: 1686882177000,
isDel: 0,
},
{
id: 6,
detailType: 2,
detailTypeDesc: "口播",
packageId: 3,
watermark: 0,
watermarkDesc: "否",
language: 1,
languageDesc: "50+",
sound: 1,
soundDesc: "400+",
videoTime: 300,
aiCreateTime: 200,
resolutionRatio: 2,
resolutionRatioDesc: "1080p",
humanTemplate: 2,
humanTemplateDesc: "VIP",
createDate: 1686882177000,
updateDate: 1686882177000,
isDel: 0,
},
],
},
]);

const packageList = ref([]);
async function getPackageList() {
const res = await packageInfoApi();
packageList.value = res.data;
}

const showPayDialog = ref(false); //套餐信息dialog显示隐藏
const presentItme = reactive({}); //点击的套餐项
// const presentItme = ref({}); //点击的套餐项
function clickPayDialog(item) {
presentItme.value = item;
createOrder(item)
showPayDialog.value = true;
}
function showOrderCode(type) {
orderCodeDialogShow.value = true;
}

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

//#region 点击套餐内的支付
function showOrderCode(type) {
orderCodeDialogShow.value = true;
myPayVendor.value = type;
createOrderFunc();
}
const orderCodeDialogShow = ref(false);
const orderCodeUrl = ref(null);
//#endregion ------------------------------------

//#region $t在script中的使用
// const {
// appContext: {
// config: { globalProperties },
// },
// } = getCurrentInstance();

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

//#region 页面滚动
const scrollContainerRef = ref(null);
function handleWheel(event) {
@@ -517,20 +228,191 @@ const updateShowTalkUsDialog = (newValue) => {

//#region 页面初始化
onMounted(async () => {
// getPackageList();
getPackageList();
// createOrderFunc();
});
//#endregion ------------------------------------

//#region
//#region 支付
const myPayVendor = ref(null); // 2微信 4支付宝
const payCodeUrl = ref(null); //付款二维码地址
const payCodeImg = ref(null); //付款二维码图片
// 支付失败处理
function payErrorHandle() {
orderCodeDialogShow.value = false;
ElMessage.error(
lanChange.value == "zh-cn"
? `获取订单失败,请重试`
: `Failed to get the order, please try again`
);
}
// 获取订单
const myOrderNumber = ref(null); //订单号
async function createOrderFunc() {
const data = {
productId: 30,
};
try {
const res = await createOrderApi(data);
if (res.code == 200) {
myOrderNumber.value = res.data;
createPayFunc(res.data);
} else {
payErrorHandle();
}
} catch (error) {
payErrorHandle();
}
}
// 获取订单详情
let iframeBody = ref(null);
const iframeDom = ref(null);
const loading = ref(false)
async function createPayFunc(orderNumber) {
const data = {
orderNumber,
payVendor: payVendor.value,
};
try {
const res = await createPayApi(data);
if (res.code == 200) {
// 支付宝获取iframe内容
if (res.data.body) {
orderCodeDialogShow.value = true;
setTimeout(() => {
iframeBody.value = res.data.body;
iframeDom.value = document.getElementById("myIframe");
iframeDom.value.srcdoc = iframeBody.value;
}, 1000)
}
// 直接获取二维码
if (res.data.codeUrl) {
payCodeUrl.value = res.data.codeUrl;
// showOrderCode(payVendor.value)
orderCodeDialogShow.value = true;
}
// findStatus()
await getStatusOfOrderFunc(orderNumber);
} else {
payErrorHandle();
}
} catch (error) {
payErrorHandle();
}
}
// 获取订单支付状态 payStatus 查询支付状态 0:待支付,2:支付中,3:已支付
async function getStatusOfOrderFunc(orderNumber) {
// if (
// route.path != "/myStore" ||
// orderCodeDialogShow.value == false ||
// orderNumber != payVendor.value
// ) {
// return;
// }
try {
// let payVendor = payVendor.value;
let data = {
orderNumber,
payVendor: payVendor.value
}
const res = await findStatusApi(data);
if (res.code == 200) {
if (res.data.orderStatus == 1) { // 页面就不动,继续定时请求
setTimeout(() => {
getStatusOfOrderFunc(orderNumber);
}, 3000);
} else if (res.data.orderStatus == 2) { // 页面就不动, loading....的效果盖在二维码之前,继续定时请求
loading.value = true
setTimeout(() => {
getStatusOfOrderFunc(orderNumber);
}, 3000);
} else if (res.data.orderStatus == 3) { // 提示支付成功,关闭当前页
orderCodeDialogShow.value = false;
showPayDialog.value = false
ElMessage.success(
lanChange.value == "zh-cn" ? `支付成功!` : `Payment is successful!`
);
} else { // 提示支付失败,关闭当前页
ElMessage.success('支付失败');
}
} else {
setTimeout(() => {
getStatusOfOrderFunc(orderNumber);
}, 3000);
}
} catch (error) {
console.log(error, "error");
ElMessage.error(
lanChange.value == "zh-cn"
? `获取订单失败,请重试`
: `Failed to get the order, please try again`
);
}
}
// 生成二维码
// function qrcode() {
// return;
// }

// 关闭二维码弹窗
function clearCode() {
myPayVendor.value = null; // 2微信 4支付宝
payCodeUrl.value = null;
iframeBody.value = null;
showPayDialog.value = false;
}
// 刷新二维码
function RefreshQR() {
payCodeUrl.value = null;
iframeBody.value = null;
// createOrderFunc();
createPayFunc(myOrderNumber.value);
}
//#endregion ------------------------------------

//#region

//#endregion ------------------------------------
</script>
const payVendor = ref('4')
const orderNumber = ref('')
// 创建订单
function createOrder(item) {
createOrderApi({productId: item.id}).then(res => {
orderNumber.value = res.data
})
}
// 创建支付
function payConfirm() {
createPayFunc(orderNumber.value);
}
// 获取订单详情
function getOrderDetail(item) {
orderDetailApi(item.id).then(res => {
})
}
//每隔一分钟运行一次保存方法
// const timer = setInterval(()=>{
// findStatus()
// },1000)

function findStatus() {
let data = {
orderNumber: '',
payVendor: ''
}
findStatusApi(data).then(res => {
if (res.data !== 2) { // 如果是2就不停的调,直到返回1、3、4
clearInterval(timer);
}
})
}
</script>
<style>
.el-dialog {
border-radius: 5px !important;
}</style>
<style lang="scss" scoped>

.page {
// min-width: calc(100vw - 210px);
height: calc(100vh - 80px);
@@ -552,18 +434,14 @@ onMounted(async () => {
overflow-x: scroll;
padding: 30px 20px 20px 10px;
max-width: 100%;
// height: 75%;
// overflow: hidden;
// overflow-x: scroll;
// scrollbar-width: none; /* 隐藏滚动条(Chrome、Firefox) */
// -ms-overflow-style: none; /* 隐藏滚动条(IE、Edge) */
// &::-webkit-scrollbar {
// display: none; /* 隐藏滚动条(Safari、Chrome) */
// }
.scrollbar-flex-content{
display: flex;
padding-bottom: 30px;
}

.item {
position: relative;
width: 480px;
width: 450px;
// height: 680px;
margin-left: 45px;
background-color: #fffefd;
@@ -573,6 +451,9 @@ onMounted(async () => {
overflow: hidden;
transition: all 0.2s;
cursor: pointer;
&.dashed {
border: 1px dashed #c0bfbd;
}
&:hover {
.mask {
display: block;
@@ -606,10 +487,21 @@ onMounted(async () => {
padding: 20px;
border-bottom: 1px solid #c0bfbd;
cursor: pointer;
position: relative;
.titleBox {
display: flex;
justify-content: space-between;
}
.iconText{
font-size: 14px;
position: absolute;
left:-20px;
top:0;
transform: rotate(-45deg);
background: rgb(243, 175, 0);
padding: 3px 10px;
color: #fff;
}
.title {
font-size: 30px;
font-weight: 700;
@@ -636,7 +528,6 @@ onMounted(async () => {
text-align: end;
.price {
font-size: 30px;
font-weight: 700;
color: #242625;
cursor: pointer;
}
@@ -649,10 +540,10 @@ onMounted(async () => {
cursor: pointer;
margin-bottom: 20px;
span {
font-weight: 700;
color: #242625;
cursor: pointer;
margin-right: 5px;
padding-left: 10px;
}
}

@@ -729,62 +620,29 @@ onMounted(async () => {
}
}
// 支付弹窗
.payDialog {
display: flex;
position: relative;
min-height: 500px;
.left {
width: 35%;
border-right: 1px solid #7c7c7a;
.title {
color: #242625;
font-weight: 700;
font-size: 30px;
text-align: center;
}
.price {
span {
font-size: 30px;
font-weight: 700;
color: #242625;
}
}
.authority {
padding: 10px 20px;
}
}
.right {
width: 65%;
position: absolute;
top: 20%;
right: 0%;
padding-left: 20%;
> div {
width: 200px;
height: 40px;
// border: 1px solid #242625;
border-radius: 10px;
text-align: center;
line-height: 40px;
margin: 30px;
color: #fff;
}
.el-radio{
width: 100px;
img {
vertical-align: middle;
margin-right: 5px;
width: 16px;
}
}

// 二维码弹窗
.orderCodeDialog {
width: 100%;
.codeImg {
display: flex;
margin: auto;
justify-content: space-evenly;
// justify-content: space-evenly;
align-items: center;
width: 300px;
height: 300px;
width: 220PX;
height: 220PX;
border: 1px solid #ccc;
border-radius: 15px;
overflow: hidden;
padding: 10px;
padding: 13px;
img {
width: 100%;
height: 100%;


+ 7
- 0
src/views/privatization/index.vue ファイルの表示

@@ -0,0 +1,7 @@

<!--
* @Description: 定制分身
-->
<template>
<iframe src="/priMakeHtml/siyouhuabushu/index.shtml" style="width: 100%;height:86vh"></iframe>
</template>

+ 6
- 0
src/views/separation/index.vue ファイルの表示

@@ -0,0 +1,6 @@
<!--
* @Description: 定制声纹
-->
<template>
<iframe src="/priMakeHtml/dingzhifenshen/index.shtml" style="width: 100%;height:86vh"></iframe>
</template>

+ 944
- 0
src/views/userModel/index.vue ファイルの表示

@@ -0,0 +1,944 @@
<template>
<div class="page">
<!-- 搜索框 -->
<div class="top">
<div class="search">
<el-input
style="width: 300px"
v-model="title"
:placeholder="$t('myCreating.search')"
@keyup.enter="searchVideoList"
/>
<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>
<!-- 作品列表 -->
<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, 7, 8, 9, 10, 11, 12].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, item.title)"
>
<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">
<!-- 免费用户1分钟 [5].includes(presentItem.videoStatus)-->
<div
v-if="presentItem.videoPlayUrl && presentItem.videoTime > 300"
class="overtimeTips"
>
<img src="../../assets/img/tips.png" alt="" />
{{ $t("myCreating.freeUserTip") }}
</div>
<video
v-if="presentItem.videoPlayUrl"
controls="true"
class=""
:src="presentItem.videoPlayUrl"
></video>
<div v-else class="insteadVideo">
{{ $t("myCreating." + presentItem.statusName + "Text") }}

<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> -->
</div>
<!-- 有视频路径的页脚 -->
<div class="footer" v-if="presentItem.videoPlayUrl">
<span class="dialog-footer">
<!-- dialog下载按钮 upGrades-->
<button
class="elBtn"
type="primary"
@click.stop="
handleDownload(presentItem.videoPlayUrl, presentItem.title)
"
>
<!-- <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") }}
</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="[12, 24, 36]"
: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>
//#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();
const lanChange = ref(locale);

//#region 老页面逻辑
const title = ref("");
const pageNum = ref(1);
const pageSize = ref(12);
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, name) {
if (itemVideoPlayUrl) {
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`
);
}
}

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"
? `确定要删除这个作品吗? 作品标题:${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);
});
}
// 升级画质
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) {
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) || "";
// 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) {
delUserPhotoVideoByIDApi(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);
});
}
//#endregion ----------------------

//#region 定时刷新
const hasNewCreate = ref(false); //提示框显示隐藏
const notGenerateList = ref([]); //页面上未生成的视频的id 普通视频
const notGenerateList2 = ref([]); //页面上未生成的视频的id 升级视频
const checkVideoStatus = async () => {
// 如果列表中没有生成中的视频,不发送请求
const intervalId = setInterval(async () => {
// && notGenerateList2.value.length == 0;
console.log("");
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 {
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;
left: 4px;
}
}
.workList {
flex-wrap: wrap;
justify-content: flex-start;
width: 100%;
// height: 200px;
margin-top: 5px;
padding-bottom: 50px;
.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;
font-size: 13px;
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%);
}
&.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 {
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;
.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;
margin: auto;
}
.insteadVideo {
font-size: 20px;
font-weight: 400;
margin: 20px auto;
text-align: center;
}
p {
font-weight: 700;
}
}

.dialog-footer {
display: flex;
justify-content: space-between;
.elBtn {
color: #fff;
border-radius: 10px;
padding: 10px 15px;
cursor: pointer;
background-color: #000;
transition: all 0.3s;
&:hover {
background-color: #15d1b8;
}
}
.upSharpness {
img {
width: 14px;
height: 14px;
}
}
: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>

+ 16
- 0
src/views/userModel/videoStatus.js ファイルの表示

@@ -0,0 +1,16 @@
/**
* @description:根据状态返回状态名
* @param {number} status
* @return: statusName
*/
export const getStatus = status => {
if (status == 0) {
return "manuscript"
} else if (status == 1 || status == 2 || status == 4 || status == 6) {
return "generating"
} else if (status == 3) {
return "generationFailed"
} else if (status == 5) {
return "generationSucceed"
}
}

+ 7
- 0
src/views/voiceModel/index.vue ファイルの表示

@@ -0,0 +1,7 @@
<template>
<div></div>
</template>
<script setup>
// import { ref } from 'vue'
</script>
<style lang="scss" scoped></style>

+ 6
- 0
src/views/voiceprint/index.vue ファイルの表示

@@ -0,0 +1,6 @@
<!--
* @Description: 定制分身
-->
<template>
<iframe src="/priMakeHtml/dingzhishengwen/index.shtml" style="width: 100%;height:86vh"></iframe>
</template>

+ 385
- 334
yarn.lock
ファイル差分が大きすぎるため省略します
ファイルの表示


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