| @@ -17,17 +17,20 @@ | |||||
| "@vueuse/core": "^9.1.1", | "@vueuse/core": "^9.1.1", | ||||
| "@wangeditor/editor": "^5.0.0", | "@wangeditor/editor": "^5.0.0", | ||||
| "@wangeditor/editor-for-vue": "^5.1.10", | "@wangeditor/editor-for-vue": "^5.1.10", | ||||
| "amfe-flexible": "^2.2.1", | |||||
| "axios": "^1.3.4", | "axios": "^1.3.4", | ||||
| "browser-image-compression": "^2.0.2", | "browser-image-compression": "^2.0.2", | ||||
| "dayjs": "^1.11.8", | "dayjs": "^1.11.8", | ||||
| "echarts": "^5.2.2", | "echarts": "^5.2.2", | ||||
| "element-plus": "^2.3.1", | "element-plus": "^2.3.1", | ||||
| "fabric": "^5.3.0", | |||||
| "js-audio-recorder": "^1.0.7", | "js-audio-recorder": "^1.0.7", | ||||
| "nprogress": "^0.2.0", | "nprogress": "^0.2.0", | ||||
| "path-browserify": "^1.0.1", | "path-browserify": "^1.0.1", | ||||
| "path-to-regexp": "^6.2.0", | "path-to-regexp": "^6.2.0", | ||||
| "pinia": "^2.0.33", | "pinia": "^2.0.33", | ||||
| "pinia-plugin-persistedstate": "^3.1.0", | "pinia-plugin-persistedstate": "^3.1.0", | ||||
| "postcss-pxtorem": "^6.0.0", | |||||
| "screenfull": "^6.0.0", | "screenfull": "^6.0.0", | ||||
| "vue": "^3.2.45", | "vue": "^3.2.45", | ||||
| "vue-i18n": "9", | "vue-i18n": "9", | ||||
| @@ -37,6 +40,7 @@ | |||||
| "@commitlint/cli": "^17.5.1", | "@commitlint/cli": "^17.5.1", | ||||
| "@commitlint/config-conventional": "^17.4.4", | "@commitlint/config-conventional": "^17.4.4", | ||||
| "@iconify-json/ep": "^1.1.8", | "@iconify-json/ep": "^1.1.8", | ||||
| "@types/fabric": "^5.3.3", | |||||
| "@types/nprogress": "^0.2.0", | "@types/nprogress": "^0.2.0", | ||||
| "@types/path-browserify": "^1.0.0", | "@types/path-browserify": "^1.0.0", | ||||
| "@typescript-eslint/eslint-plugin": "^5.51.0", | "@typescript-eslint/eslint-plugin": "^5.51.0", | ||||
| @@ -143,7 +143,7 @@ watch( | |||||
| display: flex; | display: flex; | ||||
| align-items: center; | align-items: center; | ||||
| justify-content: flex-end; | justify-content: flex-end; | ||||
| height: 80px; | |||||
| height: 100%; | |||||
| padding: 0 20px; | padding: 0 20px; | ||||
| color: #fff; | color: #fff; | ||||
| background-color: #000; | background-color: #000; | ||||
| @@ -1,8 +1,4 @@ | |||||
| <script lang="ts" setup> | <script lang="ts" setup> | ||||
| import { useSettingsStore } from "@/store/modules/settings"; | |||||
| const settingsStore = useSettingsStore(); | |||||
| defineProps({ | defineProps({ | ||||
| collapse: { | collapse: { | ||||
| type: Boolean, | 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)]"> | <div class="w-full h-[50px] bg-gray-800 dark:bg-[var(--el-bg-color-overlay)]"> | ||||
| <transition name="sidebarLogoFade"> | <transition name="sidebarLogoFade"> | ||||
| <router-link | <router-link | ||||
| style="position: relative" | |||||
| v-if="collapse" | v-if="collapse" | ||||
| key="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="/" | to="/" | ||||
| > | > | ||||
| <img | <img | ||||
| v-if="settingsStore.sidebarLogo" | |||||
| src="../../../assets/img/left-top-logo.png" | 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> | ||||
| <router-link | <router-link | ||||
| v-else | v-else | ||||
| style="position: relative" | |||||
| key="expand" | 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="/" | to="/" | ||||
| > | > | ||||
| <img | <img | ||||
| v-if="settingsStore.sidebarLogo" | |||||
| src="../../../assets/img/left-top-logo.png" | 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> | </router-link> | ||||
| </transition> | </transition> | ||||
| </div> | </div> | ||||
| @@ -9,24 +9,12 @@ | |||||
| </div> | </div> | ||||
| {{ $t("createVideo.title") }} | {{ $t("createVideo.title") }} | ||||
| </div> | </div> | ||||
| <!-- <div class="myRouter-item" @click="routerTo('/myCreating')"> | |||||
| <div class="myRouter-item" @click="routerTo('/moveImg')"> | |||||
| <div class="icon"> | <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> | </div> | ||||
| {{ $t("shop.title") }} | |||||
| {{ $t("createVideo.title") }}123 | |||||
| </div> | </div> | ||||
| <div class="myRouter-item API" @click="routerTo('/myStore')"> | |||||
| <div class="icon"> | |||||
| <el-icon><i-ep-DocumentAdd /></el-icon> | |||||
| </div> | |||||
| API | |||||
| </div> --> | |||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| </template> | </template> | ||||
| @@ -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> | |||||
| @@ -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> | |||||
| @@ -17,11 +17,11 @@ import "element-plus/theme-chalk/dark/css-vars.css"; | |||||
| import "@/styles/index.scss"; | import "@/styles/index.scss"; | ||||
| import "uno.css"; | import "uno.css"; | ||||
| import "@/styles/my.scss"; // 引入自己定义的样式 | import "@/styles/my.scss"; // 引入自己定义的样式 | ||||
| import 'amfe-flexible'; | |||||
| // 引入组件 | // 引入组件 | ||||
| import myLoading from "@/components/myLoading.vue"; | import myLoading from "@/components/myLoading.vue"; | ||||
| import myLoadingIcon from "@/components/myLoadingIcon.vue"; | import myLoadingIcon from "@/components/myLoadingIcon.vue"; | ||||
| const app = createApp(App); | const app = createApp(App); | ||||
| // 全局注册 自定义指令(directive) | // 全局注册 自定义指令(directive) | ||||
| setupDirective(app); | setupDirective(app); | ||||
| @@ -9,7 +9,7 @@ NProgress.configure({ showSpinner: false }); // 进度条 | |||||
| const permissionStore = usePermissionStoreHook(); | const permissionStore = usePermissionStoreHook(); | ||||
| // 白名单路由 | // 白名单路由 | ||||
| const whiteList = ["/login", "/signSucceed",'/createVideo','/myStore']; | |||||
| const whiteList = ["/login", "/signSucceed",'/createVideo','/moveImg']; | |||||
| router.beforeEach(async (to, from, next) => { | router.beforeEach(async (to, from, next) => { | ||||
| NProgress.start(); | NProgress.start(); | ||||
| @@ -5,17 +5,7 @@ export const Layout = () => import("@/layout/index.vue"); | |||||
| // 静态路由 | // 静态路由 | ||||
| export const constantRoutes: RouteRecordRaw[] = [ | export const constantRoutes: RouteRecordRaw[] = [ | ||||
| // { | |||||
| // path: "/redirect", | |||||
| // component: Layout, | |||||
| // meta: { hidden: true }, | |||||
| // children: [ | |||||
| // { | |||||
| // path: "/redirect/:path(.*)", | |||||
| // component: () => import("@/views/redirect/index.vue"), | |||||
| // }, | |||||
| // ], | |||||
| // }, | |||||
| // 登录 | // 登录 | ||||
| { | { | ||||
| path: "/login", | path: "/login", | ||||
| @@ -66,120 +56,25 @@ export const constantRoutes: RouteRecordRaw[] = [ | |||||
| }, | }, | ||||
| ], | ], | ||||
| }, | }, | ||||
| // 个人账户 | |||||
| { | { | ||||
| path: "/", | path: "/", | ||||
| component: Layout, | component: Layout, | ||||
| redirect: "/myAccount", | |||||
| redirect: "/moveImg", | |||||
| children: [ | children: [ | ||||
| { | { | ||||
| path: "myAccount", | |||||
| component: () => import("@/views/myAccount/index.vue"), | |||||
| name: "myAccount", | |||||
| path: "moveImg", | |||||
| component: () => import("@/views/moveImg/index.vue"), | |||||
| name: "moveImg", | |||||
| meta: { | meta: { | ||||
| title: "myAccount", | |||||
| title: "moveImg", | |||||
| icon: "homepage", | icon: "homepage", | ||||
| affix: true, | 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'} | |||||
| } | |||||
| ] | |||||
| } | |||||
| ] | |||||
| }, | |||||
| ] | |||||
| }*/ | |||||
| ]; | ]; | ||||
| /** | /** | ||||
| @@ -2,7 +2,6 @@ | |||||
| export {} | export {} | ||||
| declare global { | declare global { | ||||
| const EffectScope: typeof import('vue')['EffectScope'] | const EffectScope: typeof import('vue')['EffectScope'] | ||||
| const ElForm: typeof import('element-plus/es')['ElForm'] | |||||
| const ElMessage: typeof import('element-plus/es')['ElMessage'] | const ElMessage: typeof import('element-plus/es')['ElMessage'] | ||||
| const ElMessageBox: typeof import('element-plus/es')['ElMessageBox'] | const ElMessageBox: typeof import('element-plus/es')['ElMessageBox'] | ||||
| const asyncComputed: typeof import('@vueuse/core')['asyncComputed'] | const asyncComputed: typeof import('@vueuse/core')['asyncComputed'] | ||||
| @@ -271,7 +270,6 @@ import { UnwrapRef } from 'vue' | |||||
| declare module 'vue' { | declare module 'vue' { | ||||
| interface ComponentCustomProperties { | interface ComponentCustomProperties { | ||||
| readonly EffectScope: UnwrapRef<typeof import('vue')['EffectScope']> | 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 ElMessage: UnwrapRef<typeof import('element-plus/es')['ElMessage']> | ||||
| readonly ElMessageBox: UnwrapRef<typeof import('element-plus/es')['ElMessageBox']> | readonly ElMessageBox: UnwrapRef<typeof import('element-plus/es')['ElMessageBox']> | ||||
| readonly asyncComputed: UnwrapRef<typeof import('@vueuse/core')['asyncComputed']> | readonly asyncComputed: UnwrapRef<typeof import('@vueuse/core')['asyncComputed']> | ||||
| @@ -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> | |||||
| @@ -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" | |||||
| } | |||||
| } | |||||
| @@ -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> | |||||
| @@ -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> | |||||
| @@ -8,7 +8,7 @@ import { ElementPlusResolver } from "unplugin-vue-components/resolvers"; | |||||
| import Icons from "unplugin-icons/vite"; | import Icons from "unplugin-icons/vite"; | ||||
| import IconsResolver from "unplugin-icons/resolver"; | import IconsResolver from "unplugin-icons/resolver"; | ||||
| import postCssPxToRem from 'postcss-pxtorem' | |||||
| import { createSvgIconsPlugin } from "vite-plugin-svg-icons"; | import { createSvgIconsPlugin } from "vite-plugin-svg-icons"; | ||||
| import UnoCSS from "unocss/vite"; | 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: { | server: { | ||||
| host: "0.0.0.0", | host: "0.0.0.0", | ||||