congzc 1 yıl önce
ebeveyn
işleme
fb5b50a5e2
20 değiştirilmiş dosya ile 1036 ekleme ve 1435 silme
  1. +4
    -0
      package.json
  2. BIN
      src/assets/img/16776641647875906_FBIpSdzR_matting.png
  3. BIN
      src/assets/img/Snipaste_2023-04-18_11-44-37.png
  4. BIN
      src/assets/img/Snipaste_2023-04-18_11-44-45.png
  5. BIN
      src/assets/img/lake.jpg
  6. +1
    -1
      src/layout/components/Navbar.vue
  7. +6
    -12
      src/layout/components/Sidebar/Logo.vue
  8. +3
    -15
      src/layout/components/Sidebar/index.vue
  9. +119
    -0
      src/layout/index copy 2.vue
  10. +131
    -0
      src/layout/index copy.vue
  11. +1
    -1
      src/main.ts
  12. +1
    -1
      src/permission.ts
  13. +7
    -112
      src/router/index.ts
  14. +0
    -2
      src/types/auto-imports.d.ts
  15. +95
    -0
      src/views/moveImg/index.vue
  16. +16
    -0
      src/views/moveImg/videoStatus.js
  17. +0
    -762
      src/views/myAccount/index.vue
  18. +0
    -393
      src/views/myStore/index.vue
  19. +10
    -1
      vite.config.ts
  20. +642
    -135
      yarn.lock

+ 4
- 0
package.json Dosyayı Görüntüle

@@ -17,17 +17,20 @@
"@vueuse/core": "^9.1.1",
"@wangeditor/editor": "^5.0.0",
"@wangeditor/editor-for-vue": "^5.1.10",
"amfe-flexible": "^2.2.1",
"axios": "^1.3.4",
"browser-image-compression": "^2.0.2",
"dayjs": "^1.11.8",
"echarts": "^5.2.2",
"element-plus": "^2.3.1",
"fabric": "^5.3.0",
"js-audio-recorder": "^1.0.7",
"nprogress": "^0.2.0",
"path-browserify": "^1.0.1",
"path-to-regexp": "^6.2.0",
"pinia": "^2.0.33",
"pinia-plugin-persistedstate": "^3.1.0",
"postcss-pxtorem": "^6.0.0",
"screenfull": "^6.0.0",
"vue": "^3.2.45",
"vue-i18n": "9",
@@ -37,6 +40,7 @@
"@commitlint/cli": "^17.5.1",
"@commitlint/config-conventional": "^17.4.4",
"@iconify-json/ep": "^1.1.8",
"@types/fabric": "^5.3.3",
"@types/nprogress": "^0.2.0",
"@types/path-browserify": "^1.0.0",
"@typescript-eslint/eslint-plugin": "^5.51.0",


BIN
src/assets/img/16776641647875906_FBIpSdzR_matting.png Dosyayı Görüntüle

Önce Sonra
Genişlik: 900  |  Yükseklik: 1600  |  Boyut: 514 KiB

BIN
src/assets/img/Snipaste_2023-04-18_11-44-37.png Dosyayı Görüntüle

Önce Sonra
Genişlik: 76  |  Yükseklik: 72  |  Boyut: 4.0 KiB

BIN
src/assets/img/Snipaste_2023-04-18_11-44-45.png Dosyayı Görüntüle

Önce Sonra
Genişlik: 76  |  Yükseklik: 72  |  Boyut: 6.3 KiB

BIN
src/assets/img/lake.jpg Dosyayı Görüntüle

Önce Sonra
Genişlik: 3840  |  Yükseklik: 2160  |  Boyut: 6.8 MiB

+ 1
- 1
src/layout/components/Navbar.vue Dosyayı Görüntüle

@@ -143,7 +143,7 @@ watch(
display: flex;
align-items: center;
justify-content: flex-end;
height: 80px;
height: 100%;
padding: 0 20px;
color: #fff;
background-color: #000;


+ 6
- 12
src/layout/components/Sidebar/Logo.vue Dosyayı Görüntüle

@@ -1,8 +1,4 @@
<script lang="ts" setup>
import { useSettingsStore } from "@/store/modules/settings";

const settingsStore = useSettingsStore();

defineProps({
collapse: {
type: Boolean,
@@ -17,31 +13,29 @@ const logo = ref(new URL(`../../../assets/logo.png`, import.meta.url).href);
<div class="w-full h-[50px] bg-gray-800 dark:bg-[var(--el-bg-color-overlay)]">
<transition name="sidebarLogoFade">
<router-link
style="position: relative"
v-if="collapse"
key="collapse"
class="h-full w-full flex items-center justify-center left-top-box"
class="h-full w-full flex items-center left-top-box"
to="/"
>
<img
v-if="settingsStore.sidebarLogo"
src="../../../assets/img/left-top-logo.png"
class="left-top-logo"
class="left-top-logo boxCentreImg"
/>
<span v-else class="ml-3 text-white text-sm font-bold"></span>
</router-link>

<router-link
v-else
style="position: relative"
key="expand"
class="h-full w-full flex items-center justify-center left-top-box"
class="h-full w-full flex items-center left-top-box"
to="/"
>
<img
v-if="settingsStore.sidebarLogo"
src="../../../assets/img/left-top-logo.png"
class="left-top-logo"
class="left-top-logo boxCentreImg"
/>
<span class="ml-3 text-white text-sm font-bold"></span>
</router-link>
</transition>
</div>


+ 3
- 15
src/layout/components/Sidebar/index.vue Dosyayı Görüntüle

@@ -9,24 +9,12 @@
</div>
{{ $t("createVideo.title") }}
</div>
<!-- <div class="myRouter-item" @click="routerTo('/myCreating')">
<div class="myRouter-item" @click="routerTo('/moveImg')">
<div class="icon">
<el-icon><i-ep-menu /></el-icon>
</div>
{{ $t("myCreating.title") }}
</div>
<div class="myRouter-item pricing" @click="routerTo('/myStore')">
<div class="icon">
<el-icon><i-ep-Goods /></el-icon>
<el-icon><i-ep-edit /></el-icon>
</div>
{{ $t("shop.title") }}
{{ $t("createVideo.title") }}123
</div>
<div class="myRouter-item API" @click="routerTo('/myStore')">
<div class="icon">
<el-icon><i-ep-DocumentAdd /></el-icon>
</div>
API
</div> -->
</div>
</div>
</template>


+ 119
- 0
src/layout/index copy 2.vue Dosyayı Görüntüle

@@ -0,0 +1,119 @@
<template>
<div>
<!-- 侧边栏 -->
<div><Sidebar class="sidebar-container" /></div>
<!-- 页面主体 -->
<div>
<!-- nav -->
<div><navbar /></div>
<!-- 页面主体 -->
<div><app-main /></div>
</div>
</div>
</template>

<script setup lang="ts">
import { computed, watchEffect } from "vue";
import { useWindowSize } from "@vueuse/core";
import { AppMain, Navbar } from "./components/index";
import SignSucceed from "../views/signSucceed/index.vue";
import Sidebar from "./components/Sidebar/index.vue";

import { useAppStore } from "@/store/modules/app";
import { useSettingsStore } from "@/store/modules/settings";
import { useRoute, useRouter } from "vue-router";
const route = useRoute();
const { params } = route;
const { width } = useWindowSize();

/**
* 响应式布局容器固定宽度
*
* 大屏(>=1200px)
* 中屏(>=992px)
* 小屏(>=768px)
*/
const WIDTH = 992;

const appStore = useAppStore();
const settingsStore = useSettingsStore();

const fixedHeader = computed(() => settingsStore.fixedHeader);
const showTagsView = computed(() => settingsStore.tagsView);
const showSettings = computed(() => settingsStore.showSettings);

const classObj = computed(() => ({
hideSidebar: !appStore.sidebar.opened,
openSidebar: appStore.sidebar.opened,
withoutAnimation: appStore.sidebar.withoutAnimation,
mobile: appStore.device === "mobile",
}));

watchEffect(() => {
if (width.value < WIDTH) {
appStore.toggleDevice("mobile");
appStore.closeSideBar(true);
} else {
appStore.toggleDevice("desktop");

if (width.value >= 1200) {
//大屏
appStore.openSideBar(true);
} else {
appStore.closeSideBar(true);
}
}
});

function handleOutsideClick() {
appStore.closeSideBar(false);
}

onMounted(() => {});
</script>

<style lang="scss" scoped>
.app-wrapper {
&::after {
display: table;
clear: both;
content: "";
}

position: relative;
width: 100%;
height: 100%;
e &.mobile.openSidebar {
position: fixed;
top: 0;
}
}

.fixed-header {
position: fixed;
top: 0;
right: 0;
z-index: 9;
width: calc(100% - #{$sideBarWidth});
transition: width 0.28s;
}

.hideSidebar .fixed-header {
width: calc(100% - 54px);
}

.mobile .fixed-header {
width: 100%;
}

.drawer-bg {
position: absolute;
top: 0;
z-index: 999;
width: 100%;
height: 100%;
// background: #1b213a;
background: #000;
opacity: 0.3;
}
</style>

+ 131
- 0
src/layout/index copy.vue Dosyayı Görüntüle

@@ -0,0 +1,131 @@
<template>
<div :class="classObj" class="app-wrapper">
<!-- 手机设备侧边栏打开遮罩层 -->
<div
v-if="classObj.mobile && classObj.openSidebar"
class="drawer-bg"
@click="handleOutsideClick"
></div>

<Sidebar class="sidebar-container" />

<div :class="{ hasTagsView: showTagsView }" class="main-container">
<div
:class="{ 'fixed-header': fixedHeader }"
style="box-shadow: rgba(0, 0, 0, 0.05) 0px 4px 4px; height: 80px"
>
<navbar />
<tags-view v-if="showTagsView" />
</div>

<!-- 主页面 -->
<app-main />
</div>
</div>
</template>

<script setup lang="ts">
import { computed, watchEffect } from "vue";
import { useWindowSize } from "@vueuse/core";
import { AppMain, Navbar } from "./components/index";
import SignSucceed from "../views/signSucceed/index.vue";
import Sidebar from "./components/Sidebar/index.vue";

import { useAppStore } from "@/store/modules/app";
import { useSettingsStore } from "@/store/modules/settings";
import { useRoute, useRouter } from "vue-router";
const route = useRoute();
const { params } = route;
const { width } = useWindowSize();

/**
* 响应式布局容器固定宽度
*
* 大屏(>=1200px)
* 中屏(>=992px)
* 小屏(>=768px)
*/
const WIDTH = 992;

const appStore = useAppStore();
const settingsStore = useSettingsStore();

const fixedHeader = computed(() => settingsStore.fixedHeader);
const showTagsView = computed(() => settingsStore.tagsView);
const showSettings = computed(() => settingsStore.showSettings);

const classObj = computed(() => ({
hideSidebar: !appStore.sidebar.opened,
openSidebar: appStore.sidebar.opened,
withoutAnimation: appStore.sidebar.withoutAnimation,
mobile: appStore.device === "mobile",
}));

watchEffect(() => {
if (width.value < WIDTH) {
appStore.toggleDevice("mobile");
appStore.closeSideBar(true);
} else {
appStore.toggleDevice("desktop");

if (width.value >= 1200) {
//大屏
appStore.openSideBar(true);
} else {
appStore.closeSideBar(true);
}
}
});

function handleOutsideClick() {
appStore.closeSideBar(false);
}

onMounted(() => {});
</script>

<style lang="scss" scoped>
.app-wrapper {
&::after {
display: table;
clear: both;
content: "";
}

position: relative;
width: 100%;
height: 100%;
e &.mobile.openSidebar {
position: fixed;
top: 0;
}
}

.fixed-header {
position: fixed;
top: 0;
right: 0;
z-index: 9;
width: calc(100% - #{$sideBarWidth});
transition: width 0.28s;
}

.hideSidebar .fixed-header {
width: calc(100% - 54px);
}

.mobile .fixed-header {
width: 100%;
}

.drawer-bg {
position: absolute;
top: 0;
z-index: 999;
width: 100%;
height: 100%;
// background: #1b213a;
background: #000;
opacity: 0.3;
}
</style>

+ 1
- 1
src/main.ts Dosyayı Görüntüle

@@ -17,11 +17,11 @@ import "element-plus/theme-chalk/dark/css-vars.css";
import "@/styles/index.scss";
import "uno.css";
import "@/styles/my.scss"; // 引入自己定义的样式
import 'amfe-flexible';
// 引入组件
import myLoading from "@/components/myLoading.vue";
import myLoadingIcon from "@/components/myLoadingIcon.vue";


const app = createApp(App);
// 全局注册 自定义指令(directive)
setupDirective(app);


+ 1
- 1
src/permission.ts Dosyayı Görüntüle

@@ -9,7 +9,7 @@ NProgress.configure({ showSpinner: false }); // 进度条
const permissionStore = usePermissionStoreHook();

// 白名单路由
const whiteList = ["/login", "/signSucceed",'/createVideo','/myStore'];
const whiteList = ["/login", "/signSucceed",'/createVideo','/moveImg'];

router.beforeEach(async (to, from, next) => {
NProgress.start();


+ 7
- 112
src/router/index.ts Dosyayı Görüntüle

@@ -5,17 +5,7 @@ export const Layout = () => import("@/layout/index.vue");

// 静态路由
export const constantRoutes: RouteRecordRaw[] = [
// {
// path: "/redirect",
// component: Layout,
// meta: { hidden: true },
// children: [
// {
// path: "/redirect/:path(.*)",
// component: () => import("@/views/redirect/index.vue"),
// },
// ],
// },

// 登录
{
path: "/login",
@@ -66,120 +56,25 @@ export const constantRoutes: RouteRecordRaw[] = [
},
],
},
// 个人账户
{
path: "/",
component: Layout,
redirect: "/myAccount",
redirect: "/moveImg",
children: [
{
path: "myAccount",
component: () => import("@/views/myAccount/index.vue"),
name: "myAccount",
path: "moveImg",
component: () => import("@/views/moveImg/index.vue"),
name: "moveImg",
meta: {
title: "myAccount",
title: "moveImg",
icon: "homepage",
affix: true,
name: "account.title",
},
},
],
},
// 商店
{
path: "/",
component: Layout,
redirect: "/myStore",
children: [
{
path: "myStore",
component: () => import("@/views/myStore/index.vue"),
name: "myStore",
meta: {
title: "myStore",
icon: "homepage",
affix: true,
name: "shop.title",
name: "moveImg.title",
},
},
],
},

// {
// path: "/dashboard",
// component: Layout,
// redirect: "/dashboard",
// children: [
// {
// path: "dashboard",
// component: () => import("@/views/dashboard/index.vue"),
// name: "Dashboard",
// meta: { title: "dashboard", icon: "homepage", affix: true },
// },
// {
// path: "401",
// component: () => import("@/views/error-page/401.vue"),
// meta: { hidden: true },
// },
// {
// path: "404",
// component: () => import("@/views/error-page/404.vue"),
// meta: { hidden: true },
// },
// ],
// },

// 外部链接
/*{
path: '/external-link',
component: Layout,
children: [
{
path: 'https://www.cnblogs.com/haoxianrui/',
meta: { title: '外部链接', icon: 'link' }
}
]
}*/
// 多级嵌套路由
/* {
path: '/nested',
component: Layout,
redirect: '/nested/level1/level2',
name: 'Nested',
meta: {title: '多级菜单', icon: 'nested'},
children: [
{
path: 'level1',
component: () => import('@/views/nested/level1/index.vue'),
name: 'Level1',
meta: {title: '菜单一级'},
redirect: '/nested/level1/level2',
children: [
{
path: 'level2',
component: () => import('@/views/nested/level1/level2/index.vue'),
name: 'Level2',
meta: {title: '菜单二级'},
redirect: '/nested/level1/level2/level3',
children: [
{
path: 'level3-1',
component: () => import('@/views/nested/level1/level2/level3/index1.vue'),
name: 'Level3-1',
meta: {title: '菜单三级-1'}
},
{
path: 'level3-2',
component: () => import('@/views/nested/level1/level2/level3/index2.vue'),
name: 'Level3-2',
meta: {title: '菜单三级-2'}
}
]
}
]
},
]
}*/
];

/**


+ 0
- 2
src/types/auto-imports.d.ts Dosyayı Görüntüle

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


+ 95
- 0
src/views/moveImg/index.vue Dosyayı Görüntüle

@@ -0,0 +1,95 @@
<template>
<div class="page">
<div class="canvasBox">
<canvas class="canvas" id="canvas" width="960" height="540">
<!-- 背景图 -->
<!-- <div class="bjImg">
<img src="../../assets/img/lake.jpg" alt="" />
</div> -->
<!-- 数字人 -->
<!-- <div class="people"></div> -->
<!-- 素材 -->
<!-- <div class="other"></div> -->
</canvas>
<img
style="display: none; width: 200px; height: 100px"
id="bjImg"
src="../../assets/img/lake.jpg"
alt=""
/>
</div>
</div>
</template>

<script setup>
//#region 导入
import { fabric } from "fabric";
//#endregion ---------------------------

//#region 画布交互
let canvas = new fabric.Canvas("canvas");

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

onMounted(() => {
let canvas = new fabric.Canvas("canvas");
// let rect = new fabric.Rect({
// left: 0, //距离画布左侧的距离,单位是像素
// top: 0, //距离画布上边的距离
// fill: "red", //填充的颜色
// width: 50, //方形的宽度
// height: 50, //方形的高度
// });
// canvas.add(rect);
let imgElement = document.getElementById("bjImg"); //声明我们的图片
let imgInstance = new fabric.Image(imgElement, {
//设置图片位置和样子
left: 100,
top: 100,
width: 200,
height: 100,
angle: 30, //设置图形顺时针旋转角度
});
canvas.add(imgInstance);
// canvas.on("mouse:up", function (options) {
// console.log(options.e.clientX, options.e.clientY);
// });
});
</script>

<style lang="scss" scoped>
.page {
position: relative;
padding: 10px;
}
.canvasBox {
// position: absolute;
// top: 50%;
// left: 50%;
// transform: translate(-50%);
width: 100%;
height: 100%;
.canvas-container {
}
background-color: green;
.canvas {
background-color: skyblue;
.bjImg {
position: absolute;
top: 0;
left: 0;
width: 100px;
height: 100px;
z-index: 999;
img {
width: 100px;
height: 100px;
}
}
.people {
}
.other {
}
}
}
</style>

+ 16
- 0
src/views/moveImg/videoStatus.js Dosyayı Görüntüle

@@ -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"
}
}

+ 0
- 762
src/views/myAccount/index.vue Dosyayı Görüntüle

@@ -1,762 +0,0 @@
<template>
<div class="page flex">
<!-- 左侧内容 -->
<div class="left">
<!-- 头像 -->
<div class="left-one border flex justify-between flex-wrap">
<div class="avatar">
<img
:src="
userInfoPinia.userInfo.avatarUrl
? userInfoPinia.userInfo.avatarUrl
: defaultAvater
"
alt=""
/>
</div>
<div class="info">
<div class="name" v-if="userInfoPinia.userInfo.name">
{{ userInfoPinia.userInfo.name }}
</div>
<span>{{ userInfoPinia.userInfo.email }}</span>
<div>
<span class="password pointer" @click="showEditInfoDialog = true">{{
$t("account.editInfo")
}}</span>
<span
class="password pointer"
style="margin-right: 10px"
@click="router.push({ name: 'login', params: { type: 4 } })"
>{{ $t("account.reSetPwd") }}</span
>
</div>
</div>
</div>
<!-- 剩余次数 -->
<div class="left-two border">
<div v-if="lanChange == 'en'" class="number">
3 Videos Generation Left
</div>
<div v-if="lanChange == 'zh-cn'" class="number">
剩余生成视频次数:3条
</div>
<div class="time">{{ $t("account.until") }} 2023/12/31</div>
<div class="getMore pointer" @click="routerTo('/myStore')">
{{ $t("account.getMore") }}
</div>
</div>
<!-- 我的邀请码 -->
<div class="left-three border flex justify-between">
<div class="codeImg">
<img ref="codeImgRef" :src="defaultAvater" alt="" />
</div>
<div class="other">
<div v-if="lanChange == 'en'">
<span>Invite friends to get</span>
<span class="bigWord">free</span>
<span>video</span>
<span class="bigWord">Generation!</span>
<p class="myInviteCode">
your Invitation code:
<span id="InviteCodeId">{{ myInviteCode }}</span>
</p>
</div>

<div v-if="lanChange == 'zh-cn'">
<span>邀请更多好友</span>
<span class="bigWord">免费</span>
<span>生成视频!</span>
<p class="myInviteCode">
邀请码:
<span id="InviteCodeId">{{ myInviteCode }}</span>
</p>
</div>

<div class="btns">
<span class="myBtn" @click="copyInviteCode">{{
$t("account.copyCode")
}}</span>
<span class="myBtn" @click="DownloadCodeImg">{{
$t("account.saveCode")
}}</span>
</div>
</div>
</div>
<!-- 输入邀请码 -->
<div class="left-four border" v-if="!isUseState">
<el-input
class="w-50 m-2"
v-model="inputCode"
:placeholder="$t('account.inputCode')"
/>
<span class="submitCodeBtn" @click="submitUseCode">{{
$t("account.submit")
}}</span>
</div>
</div>
<!-- 右侧内容 -->
<div class="right">
<el-tabs type="border-card" class="tabs">
<!-- ----------------------------- 1 -->
<el-tab-pane :label="$t('account.buyHistory')">
<div class="card scrolly">
<div class="orderItem" v-for="item in 9" :key="item">
<p>{{ $t("account.orderID") }}:123456</p>
<div class="orderItem-content">
<div class="orderCode"><img :src="defaultAvater" alt="" /></div>
<div class="info">
<h5>疯狂星期{{ item }}</h5>
<p>{{ $t("account.price") }}:88$</p>
<p>{{ $t("account.date") }}:2023/5/2</p>
</div>
<div class="state">{{ $t("account.successed") }}</div>
</div>
</div>
</div>
</el-tab-pane>
<!-- ------------------------ 2 -->
<el-tab-pane :label="$t('account.inviteHistory')">
<div class="card scrolly card2">
<div v-if="InviteList.length != 0">
<div class="orderItem" v-for="item in InviteList" :key="item">
<div class="orderItem-content">
<div class="orderCode">
<img
:src="item.avatarUrl ? item.avatarUrl : defaultAvater"
alt=""
/>
</div>
<div class="info">
<h5 v-if="item.name">{{ item.name }}</h5>
<p>E-mail:{{ item.email }}</p>
<p>
{{ $t("account.date") }}:{{
dayjs(item.createDate).format("YYYY-MM-DD HH:mm:ss")
}}
</p>
</div>
<div class="state">{{ $t("account.msg1") }}</div>
</div>
</div>
</div>
<!-- 无数据时 -->
<div v-if="InviteList.length == 0" class="noList">
<div class="title">
{{ $t("account.noListInvite") }}
</div>
</div>
</div>
</el-tab-pane>
</el-tabs>
</div>
<!-- 修改个人信息弹窗 -->
<el-dialog
width="30%"
class="editInfoDialog"
v-model="showEditInfoDialog"
:title="$t('account.editInfo')"
destroy-on-close
@close="closeInfoDialog"
>
<el-row>
<el-col :span="4"> {{ $t("public.avatar") }}: </el-col>
<el-col :span="20">
<div class="dialogAvatar">
<input accept="image/*" type="file" @change="chooseImg" />
<div class="add">+</div>
<img v-if="dialogAvatarUrl" :src="dialogAvatarUrl" alt="" />
</div>
</el-col>
</el-row>
<el-row>
<el-col :span="4"> {{ $t("public.username") }}: </el-col>
<el-col :span="20">
<el-input v-model="userNameinput" />
</el-col>
</el-row>
<el-row>
<div class="customBtn" @click="showEditInfoDialog = false">
{{ $t("public.cancel") }}
</div>
<div class="customBtn" @click="submitUserInfo">
{{ $t("public.confirm") }}
</div>
</el-row>
</el-dialog>
</div>
</template>

<script setup>
//#region 导入文件
import { useI18n } from "vue-i18n";
import { useRouter } from "vue-router";
import { userInfoModules } from "@/store/modules/userInfo";
import dayjs from "dayjs";
import imageCompression from "browser-image-compression"; //压缩图片插件
import {
inviteCodeApi,
useCodeApi,
isUseApi,
useCodeUserApi,
updUserInfoApi,
} from "../../apis/myAccount";
import { awsImgUpload } from "@/apis/createVideo";
import defaultAvater from "../../assets/img/left-top-logo.png";

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

const { locale } = useI18n();
const lanChange = ref(locale); //国际化

const userInfoPinia = userInfoModules(); //pinia

//#region 编辑个人信息
const showEditInfoDialog = ref(false); //控制弹窗显隐
const userNameinput = ref(null); //用户名输入框绑定
const dialogAvatarUrl = ref(null); //图片回显地址
const dialogAvatarData = ref(null); //图片上传数据
const infoDialogLoad = ref(false); //load效果
// 选择图片
async function chooseImg(e) {
if (e.target.files[0]) {
dialogAvatarUrl.value = null;
dialogAvatarData.value = null;
// 压缩配置
const options = {
maxSizeMB: 2, // 最大压缩大小为 1MB
useWebWorker: true, // 使用 Web Worker 进行压缩,提高性能
};
const compressedFile = await imageCompression(e.target.files[0], options);
dialogAvatarUrl.value = URL.createObjectURL(compressedFile);
dialogAvatarData.value = compressedFile;
} else {
return;
}
}
// 上传编辑信息
async function submitUserInfo() {
let formData = new FormData();
const timestamp = Date.now();
formData.append("file", dialogAvatarData.value, timestamp + ".png");
const res1 = await awsImgUpload(formData);
const data = {
name: userNameinput.value,
avatarUrl: res1.data.url,
};
try {
const res2 = await updUserInfoApi(data);
ElMessage.success(
lanChange.value == "zh-cn" ? `修改成功` : `Modified successfully`
);
userInfoPinia.getUserInfo();
showEditInfoDialog.value = false;
} catch (error) {
ElMessage.error(
lanChange.value == "zh-cn"
? `修改失败,请稍后重试`
: `The modification failed. Please try again later`
);
}
}
// 关闭弹窗清空
function closeInfoDialog() {
userNameinput.value = userInfoPinia.userInfo.name;
dialogAvatarUrl.value = userInfoPinia.userInfo.avatarUrl;
dialogAvatarData.value = null;
}
//#endregion ---------------------------

//#region 二维码邀请码区域
const myInviteCode = ref(null); //我的邀请码
async function getMyInviteCode() {
const res = await inviteCodeApi();
myInviteCode.value = res.data;
}
// 点击复制邀请码
function copyInviteCode() {
const inviteCode = document.getElementById("InviteCodeId").innerText;
navigator.clipboard
.writeText(inviteCode)
.then(() => {
ElMessage.success(
lanChange.value == "zh-cn"
? "邀请码已复制到剪贴板"
: `The invitation code has been copied to the clipboard`
);
})
.catch((error) => {
ElMessage.error(
lanChange.value == "zh-cn"
? "无法复制邀请码:"
: `Unable to copy invitation code`
);
});
}
// 下载二维码图片
const codeImgRef = ref();
function DownloadCodeImg() {
const link = document.createElement("a");
link.href = codeImgRef.value.src;
link.download = "image.jpg";
link.click();
}
//#endregion

//#region 输入邀请码区
const inputCode = ref(""); // 邀请码输入框内容
const isUseState = ref(false); //用户是否填写过过邀请码
// 提交邀请码
async function submitUseCode() {
console.log(inputCode.value);
const res = await useCodeApi(inputCode.value);
if (res.code == 200) {
ElMessage.success(
lanChange.value == "zh-cn"
? `提交邀请码成功`
: `The invitation code was submitted successfully`
);
} else {
ElMessage.error(
lanChange.value == "zh-cn"
? `提交邀请码成失败`
: `Failed to submit the invitation code`
);
}
getIsUseState();
}
// 查询是否使用过邀请码
async function getIsUseState() {
const res = await isUseApi();
isUseState.value = res.data;
}
//#endregion ---------------------------

//#region 右侧列表
const InviteList = ref([]); //邀请列表

async function getInviteList() {
const res = await useCodeUserApi();
InviteList.value = res.data;
}

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

//#region 页面初始化
onMounted(() => {
getMyInviteCode(); // 查询用户的邀请码
getIsUseState(); //查询是否使用过邀请码
getInviteList();
userNameinput.value = userInfoPinia.userInfo.name;
dialogAvatarUrl.value = userInfoPinia.userInfo.avatarUrl;
});
//#endregion ---------------------------

//#region

//#endregion ---------------------------
const router = useRouter();
function routerTo(url) {
router.push(url);
}
</script>

<style lang="scss" scoped>
.page {
height: calc(100vh - 80px);
// height: calc(100vh - 84px);
background-color: #fff;
border-radius: 25px 0 0;
}

.left {
width: 39%;
height: 100%;

padding: 30px;
.border {
padding: 10px;
margin-bottom: 10px;
border: 1px solid #000;
border-radius: 15px;
}

&-one {
.avatar {
position: relative;
width: 80px;
height: 80px;
margin-right: 10px;
border-radius: 50%;
border: 1px solid #ccc;
overflow: hidden;
img {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
max-width: 100%;
max-height: 100%;
}
}

.info {
flex: 1;

.name {
height: 30px;
margin-top: 10px;
font-size: 20px;
}

.password {
display: inline-block;
float: right;
padding: 2px 8px;
border: 1px solid #000;
border-radius: 5px;
cursor: pointer;
transition: all 0.3s;
&:hover {
color: #fff;
background-color: #000;
}
}
}
}

&-two {
position: relative;
height: 80px;
overflow: hidden;

.number {
font-size: 30px;
font-weight: 700;
}

.time {
position: absolute;
right: 90px;
}

.getMore {
position: absolute;
top: -1px;
right: -1px;
padding: 5px;
width: 80px;
height: 80px;
line-height: 70px;
text-align: center;
background-color: #000;
color: #fff;
font-size: 14px;
transition: all 0.3s;
&:hover {
background-color: #15d1b8;
color: #fff;
}
}
}

&-three {
height: 200px;
.codeImg {
position: relative;
padding: 10px;
width: 120px;
height: 120px;
margin-right: 10px;
border: 1px solid #ccc;
border-radius: 10px;
// background-color: red;
img {
position: absolute;
top: 50%;
transform: translateY(-50%);
width: 100px;
max-height: 100px;
}
}

.other {
position: relative;
flex: 1;

.btns {
position: absolute;
bottom: 10px;
// padding: 0 80px;
display: flex;
justify-content: space-evenly;
width: 100%;
.myBtn {
margin-right: 10px;
cursor: pointer;
transition: all 0.3s;
&:hover {
color: #fff;
background-color: #000;
}
}
}
.myInviteCode {
margin-top: 20px;
}
}
}
&-four {
.el-input {
width: 80%;
}
:deep(.el-input__wrapper.is-focus) {
box-shadow: 0 0 0 1px #000 !important;
}

// padding: 30px 50px !important;
div {
margin-top: 5px;
height: 40px;
}
.submitCodeBtn {
display: inline-block;

width: 15%;
padding: 10px 15px;
color: #000;
border-radius: 10px;
text-align: center;
cursor: pointer;
transition: all 0.3s;
&:hover {
color: #fff;
background-color: #000;
}
}
}
}

.right {
background-color: #f1f2f6;
padding: 30px;
width: 61%;
height: 100%;
// height: 95%;
border-radius: 30px 0 0 0;
overflow: hidden;
// element组件样式
:deep(.el-tabs--border-card) {
font-size: 20px;
border: none;
}

.tabs {
// height: 760px;
height: 100%;
padding: 20px;
border-radius: 20px;
overflow: hidden;
.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;
}
}
.card {
// height: 100%;
height: 740px;
padding: 10px;
// height: 79vh;
.orderItem {
width: 100%;
height: 110px;
padding: 8px;
margin-bottom: 10px;
border: 1px solid #000;
border-radius: 5px;

p {
font-size: 12px;
}

&-content {
display: flex;
width: 100%;
// justify-content: space-between;
.orderCode {
position: relative;
width: 70px;
height: 70px;
padding: 5px;
border: 1px solid #ccc;
border-radius: 5px;
overflow: hidden;

img {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
max-width: 60px;
max-height: 60px;
}
}

.info {
// width: 50%;
flex: 1;
margin-left: 10px;

h5 {
margin: 0;
}

p {
margin-top: 5px;
}
}

.state {
margin-right: 30px;
}
}
}
}

.card2 {
.orderItem {
padding: 20px;

&-content {
.orderCode {
position: relative;
border: 1px solid #ccc;
border-radius: 50%;
overflow: hidden;
img {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
max-width: 100%;
max-height: 100%;
}
}

.state {
font-size: 14px;
line-height: 60px;
}
}
}
}

:deep(.el-tabs__item.is-active) {
color: #fff;
background-color: #000;
}

:deep(.el-tabs__nav) {
width: 100%;
}

:deep(.el-tabs__item) {
width: 34%;
}

:deep(.el-tabs__item:hover) {
color: #909393;
}

:deep(.el-tabs__content) {
height: 760px;
// height: calc(100% - 60px);
padding: 0;
}
}
}

.editInfoDialog {
.el-row {
display: flex;
justify-content: space-evenly;
}
.el-col {
margin-bottom: 10px;
text-align: right;
line-height: 32px;
padding-right: 20px;
}
.dialogAvatar {
position: relative;
width: 200px;
height: 200px;
background-color: rgba(204, 204, 204, 0.2);
border-radius: 30px;
overflow: hidden;
&:hover {
img {
z-index: 0;
}
.add {
background-color: rgba(0, 0, 0, 0.2);
}
}
input {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
opacity: 0;
z-index: 3;
cursor: pointer;
}
.add {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 1;
text-align: center;
line-height: 200px;
font-size: 50px;
font-weight: 700;
color: #000;
transition: all 0.3s;
}
img {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
padding: 10px;
max-width: 100%;
max-height: 100%;
z-index: 2;
transition: all 0.3s;
}
}
}
// 公共类名
.bigWord {
font-size: 30px;
font-weight: 700;
margin: 0 10px;
}
</style>

+ 0
- 393
src/views/myStore/index.vue Dosyayı Görüntüle

@@ -1,393 +0,0 @@
<template>
<div class="page">
<div class="commoditylIst">
<div class="item" v-for="item in list" :key="item.title">
<div @click="clickPayDialog(item)">
<div class="line" :style="`background-color:${item.color} ;`"></div>
<div class="info">
<!-- 商品信息 -->
<div class="content">
<div class="title">
<el-icon><i-ep-edit /></el-icon>
{{ $t(item.title) }}
</div>
<div class="money">
<span class="price">{{ item.price }}元</span>/{{
item.priceTime
}}
</div>
<div class="time">
<span>{{ item.time }}</span
>分钟/月
</div>
<div class="button" :style="`background-color:${item.color} ;`">
{{ $t("shop.selectPlan") }}
</div>
</div>
<!-- 商品权益 -->
<div class="authority">
<div
class="authority-item"
v-for="items in item.authority"
:key="items"
>
<span>✔</span>{{ items }}
</div>
</div>
</div>
</div>
</div>
</div>

<!-- 对话框 -->
<el-dialog v-model="showPayDialog" width="60%" center>
<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>
</div>
<!-- 二维码弹出框 -->
<el-dialog
v-model="orderCodeDialogShow"
width="30%"
append-to-body
destroy-on-close
>
<div class="orderCodeDialog">
<div class="codeImg">
<my-Loading-Icon v-if="!orderCodeUrl"></my-Loading-Icon>
<img v-if="orderCodeUrl" src="../../assets/img/false1.png" alt="" />
</div>
<div class="tips">扫描二维码进行付款</div>
<div class="btnBox">
<div>刷新二维码</div>
</div>
</div>
</el-dialog>
</el-dialog>
</div>
</template>

<script setup>
const title = ref("");
//#region 各个套餐的逻辑
const list = reactive([
{
color: "#f3af00",
title: "shop.Lite",
icon: "",
price: "0",
priceTime: "14天",
time: "5",
authority: [
"Metavatar 水印",
"高级和标准演示器",
"15 个 AI 演示提示",
"AI 脚本世代",
],
},
{
color: "#c971ff",
title: "shop.Pro",
icon: "",
price: "10",
priceTime: "30天",
time: "10",
authority: [
"Metavatar 水印",
"高级和标准演示器",
"15 个 AI 演示提示",
"AI 脚本世代",
"API访问",
],
},
{
color: "#7b61ff",
title: "shop.Adv",
icon: "",
price: "20",
priceTime: "30天",
time: "15",
authority: [
"Metavatar 水印",
"高级和标准演示器",
"15 个 AI 演示提示",
"AI 脚本世代",
"API访问",
"商业用途",
],
},
{
color: "#000",
title: "shop.Ent",
icon: "",
price: "50",
priceTime: "30天",
time: "40",
authority: [
"Metavatar 水印",
"高级和标准演示器",
"15 个 AI 演示提示",
"AI 脚本世代",
"API访问",
"商业用途",
"PowerPoint 插件",
],
},
]);
const showPayDialog = ref(false); //套餐信息dialog显示隐藏
const presentItme = reactive({}); //点击的套餐项
function clickPayDialog(item) {
presentItme.value = item;
showPayDialog.value = true;
}
function showOrderCode(type) {
orderCodeDialogShow.value = true;
}
//#endregion ------------------------------------

//#region 点击套餐内的支付
const orderCodeDialogShow = ref(false);
const orderCodeUrl = ref(null);
//#endregion ------------------------------------

//#region

//#endregion ------------------------------------
</script>

<style lang="scss" scoped>
.page {
min-width: calc(100vw - 210px);
height: calc(100vh - 80px);
padding: 20px 20px 0 20px;
background-color: #fff;
border-radius: 25px 0 0;
padding: 20px;
}
.commoditylIst {
display: flex;
justify-content: flex-start;
// overflow-x: auto;
padding: 20px 20px 20px 50px;
// 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) */
// }
.item {
width: 350px;
height: 780px;
margin-right: 50px;
background-color: #fffefd;
flex-shrink: 0; /* 防止缩小宽度 */
border: 1px solid #c0bfbd;
border-radius: 20px;
overflow: hidden;
transition: all 0.2s;
cursor: pointer;
.line {
height: 20px;
background-color: pink;
cursor: pointer;
}
.info {
padding: 20px;
cursor: pointer;
.content {
height: 300px;
padding: 20px;
border-bottom: 1px solid #c0bfbd;
cursor: pointer;
.title {
font-size: 30px;
font-weight: 700;
line-height: 25px;
margin-bottom: 30px;
cursor: pointer;
.el-icon {
line-height: 30px;
width: 40px;
height: 40px;
font-size: 40px;
color: #000;
text-align: center;
vertical-align: sub;
cursor: pointer;
}
}
.money {
margin-bottom: 30px;
font-size: 20px;
color: #7c7c7a;
cursor: pointer;
.price {
font-size: 30px;
font-weight: 700;
color: #242625;
cursor: pointer;
}
}
.time {
margin-bottom: 30px;
font-size: 20px;
color: #7c7c7a;
cursor: pointer;
span {
font-weight: 700;
color: #242625;
cursor: pointer;
}
}
.button {
width: 100%;
height: 40px;
border-radius: 10px;
text-align: center;
line-height: 40px;
color: #fff;
font-weight: 700;
background-color: yellow;
cursor: pointer;
}
}
.authority {
cursor: pointer;
&-item {
cursor: pointer;
padding: 20px 10px;
span {
cursor: pointer;
color: #7c7c7a;
margin-right: 30px;
}
}
}
}
&:hover {
cursor: pointer;
transform: translateY(-20px);
box-shadow: #00000026 2px 2px 2px 2px;
}
}
}

.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;
}
}
}
.orderCodeDialog {
width: 100%;
.codeImg {
display: flex;
margin: auto;
justify-content: space-evenly;
align-items: center;
width: 300px;
height: 300px;
border: 1px solid #ccc;
border-radius: 15px;
overflow: hidden;
padding: 10px;
img {
width: 100%;
height: 100%;
}
}
.tips {
padding-top: 10px;
text-align: center;
}
.btnBox {
padding: 15px 40px;
display: flex;
justify-content: space-evenly;
> div {
padding: 10px 20px;
color: #000;
background-color: #fff;
border: #000 solid 1px;
border-radius: 10px;
transition: all 0.3s;
cursor: pointer;
&:hover {
color: #fff;
background-color: #000;
}
}
}
}
</style>

+ 10
- 1
vite.config.ts Dosyayı Görüntüle

@@ -8,7 +8,7 @@ import { ElementPlusResolver } from "unplugin-vue-components/resolvers";

import Icons from "unplugin-icons/vite";
import IconsResolver from "unplugin-icons/resolver";
import postCssPxToRem from 'postcss-pxtorem'
import { createSvgIconsPlugin } from "vite-plugin-svg-icons";

import UnoCSS from "unocss/vite";
@@ -35,6 +35,15 @@ export default defineConfig(({ mode }: ConfigEnv): UserConfig => {
`,
},
},
postcss: {
plugins: [
postCssPxToRem({
rootValue: 192, // 1rem 的大小
propList: ['*'], // 需要转换的属性,*(全部转换)
unitPrecision: 6 // 转换精度,保留的小数位数
})
]
}
},
server: {
host: "0.0.0.0",


+ 642
- 135
yarn.lock
Dosya farkı çok büyük olduğundan ihmal edildi
Dosyayı Görüntüle


Yükleniyor…
İptal
Kaydet