@@ -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", | |||
@@ -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; | |||
@@ -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> | |||
@@ -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> | |||
@@ -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 "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); | |||
@@ -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(); | |||
@@ -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'} | |||
} | |||
] | |||
} | |||
] | |||
}, | |||
] | |||
}*/ | |||
]; | |||
/** | |||
@@ -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']> | |||
@@ -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 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", | |||