570 lines
14 KiB
Vue
570 lines
14 KiB
Vue
<template>
|
||
<div style="height: 112px;"></div>
|
||
<header class="app-header">
|
||
<div class="group_47637">
|
||
<div v-if="true" class="notice-banner-wrapper">
|
||
<img class="banner-bg" src="@/public/header/down-app-banner-bg.png" alt="heightFix">
|
||
<div class="banner-content">
|
||
<span class="main-text">【 装样大师 】APP已上线!</span>
|
||
<span class="sub-text">各大应用市场均可下载</span>
|
||
<img class="down-app-img" src="@/public/header/down-app-btn.png" alt="" @click="openAppCodeDialog">
|
||
</div>
|
||
<!-- <img class="close-icon" src="@/public/header/close.png" alt=""> -->
|
||
</div>
|
||
<div class="app-header-bar">
|
||
<div class="left-box">
|
||
<img class="logo-img" src="@/public/header/logo-img.png" />
|
||
<img class="logo-name" src="@/public/header/logo-name.png" mode="widthFix" />
|
||
<div class="line"></div>
|
||
<div class="menu-box">
|
||
<template v-for="item in navList" :key="item.key">
|
||
<div v-if="item.children" class="dropdown-wrapper">
|
||
<span :class="['item', { active: isItemActive(item) }]"> {{ item.name }} </span>
|
||
<div class="dropdown-panel" style="padding-top: 22px;">
|
||
<div class="panel-content">
|
||
<div class="dropdown-item" v-for="child in item.children" :key="child.key"
|
||
@click="handleNavClick(child)">
|
||
<img class="icon" :src="child.icon" alt=""> {{ child.name }}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<span v-else :class="['item', { active: isItemActive(item) }]" @click="handleNavClick(item)">
|
||
{{ item.name }}
|
||
</span>
|
||
</template>
|
||
</div>
|
||
</div>
|
||
<div class="right-box">
|
||
<img v-if="!userStore.isVip && isLoggedIn" @click="handleCommand('vip')" class="vip-logo"
|
||
src="@/public/header/kaitonghuiyuan.png" mode="heightFix" />
|
||
<img v-else-if="isLoggedIn" class="vip-logo" src="@/public/header/zunguihuiyuan.png" mode="heightFix" />
|
||
<img v-if="!isLoggedIn" @click="openLoginDialog" class="vip-logo" src="@/public/header/login-btn.png"
|
||
mode="heightFix" />
|
||
<div v-else class="dropdown-wrapper">
|
||
<div class="user-info-trigger">
|
||
<div class="user-avatar">
|
||
<img class="user-logo-img" :src="userStore.avatarUrl" />
|
||
</div>
|
||
<span class="user-name"> {{ userStore.username || '已登录用户' }} </span>
|
||
</div>
|
||
<div class="dropdown-panel">
|
||
<div class="panel-content" style="width: 200px;">
|
||
<div class="dropdown-item mg-7" @click="handleCommand('profile')">
|
||
<img class="icon" src="@/public/header/gerenzhongxin.png" alt=""> 个人中心
|
||
</div>
|
||
<div class="dropdown-item mg-7" @click="handleCommand('vip')">
|
||
<img class="icon" src="@/public/header/kaitonghuiyuanmenu.png" alt=""> 开通会员
|
||
</div>
|
||
<div class="dropdown-item mg-7" @click="handleCommand('works')">
|
||
<img class="icon" src="@/public/header/wodezuoping.png" alt=""> 我的作品
|
||
</div>
|
||
<div style="border-top: 0.5px solid #D8D8D8;height: 1px;"></div>
|
||
<div class="dropdown-item mg-7" style="border-radius: 0 0 8px 8px;" @click="handleCommand('logout')">
|
||
<img class="icon" src="@/public/header/tuichudenglu.png" alt="">退出登录
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<LoginDialog ref="loginDialogRef" />
|
||
<BaseDialog ref="appCodeRef" v-model="showAppCode" width="260px" @close="showAppCode = false" title="下载APP">
|
||
<div class="qrCode-box">
|
||
<img style="width: 200px;height: 200px;margin: 0 auto;" :src="appCodeImg" alt="">
|
||
</div>
|
||
</BaseDialog>
|
||
</header>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { computed, inject, ref, onMounted, watch } from 'vue'
|
||
import { useRouter, useRoute } from 'vue-router'
|
||
import { useUserStore } from '@/stores/user'
|
||
import LoginDialog from '@/components/LoginDialog.vue'
|
||
import BaseDialog from '@/components/common/BaseDialog.vue'
|
||
import authService from '@/api/authService.js'
|
||
import { ElMessage } from 'element-plus'
|
||
|
||
const router = useRouter()
|
||
const route = useRoute()
|
||
const userStore = useUserStore()
|
||
const showPayment = inject('showPayment')
|
||
const loginDialogRef = ref(null)
|
||
const appCodeRef = ref(null)
|
||
const showAppCode = ref(false)
|
||
const appCodeImg = ref('')
|
||
|
||
|
||
async function openAppCodeDialog() {
|
||
showAppCode.value = true
|
||
if (!appCodeImg.value) {
|
||
try {
|
||
const res = await authService.getAppCode()
|
||
console.log('获取APP二维码响应结果:', res)
|
||
const data = res.data || res
|
||
appCodeImg.value = data.qrcode || data.qr_code || data.qrcode_url || data.url || (typeof data === 'string' ? data : '')
|
||
} catch (e) {
|
||
console.log(e.message)
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
onMounted(() => {
|
||
if (localStorage.getItem('token')) {
|
||
userStore.fetchProfile().catch(() => { })
|
||
}
|
||
if (route.query.showLogin) {
|
||
loginDialogRef.value?.open()
|
||
const query = { ...route.query }
|
||
delete query.showLogin
|
||
router.replace({ query })
|
||
}
|
||
})
|
||
|
||
watch(() => route.query.showLogin, (newVal) => {
|
||
if (newVal) {
|
||
loginDialogRef.value?.open()
|
||
const query = { ...route.query }
|
||
delete query.showLogin
|
||
router.replace({ query })
|
||
}
|
||
})
|
||
|
||
const isLoggedIn = computed(() => userStore.isLoggedIn)
|
||
|
||
const navList = [
|
||
{ name: '首页', key: 'index', path: '/' },
|
||
{
|
||
name: '微信模拟',
|
||
key: 'wechat',
|
||
children: [
|
||
{ name: '模拟聊天', key: 'chat', path: '/chat', icon: '/src/public/header/chat-icon.png' },
|
||
{ name: '余额模拟', key: 'balance', path: '/balance', icon: '/src/public/header/money-icon.png' }
|
||
]
|
||
},
|
||
{
|
||
name: '娱乐模拟',
|
||
key: 'entertainment',
|
||
children: [
|
||
{ name: '工资单', key: 'gongzidan', path: '/gongzidan', icon: '/src/public/header/zhengjianmoni.png' },
|
||
{ name: '飞机票', key: 'flight', path: '/flight', icon: '/src/public/header/feijipiao.png' },
|
||
{ name: '火车票', key: 'train', path: '/train', icon: '/src/public/header/huochepiao.png' },
|
||
{ name: '购物详情', key: 'shopping', path: '/shopping', icon: '/src/public/header/gouwuxiangqing.png' },
|
||
{ name: '情侣证', key: 'couple', path: '/couple', icon: '/src/public/header/qinglvzheng.png' },
|
||
{ name: '视频聊天', key: 'video', path: '/video', icon: '/src/public/header/shipinliaotian.png' }
|
||
]
|
||
},
|
||
{ name: '作品库', key: 'works', path: '/works' },
|
||
{ name: '关于我们', key: 'about', path: '/about' },
|
||
{ name: '文章', key: 'article', path: '/article' }
|
||
|
||
]
|
||
|
||
|
||
function handleNavClick(item) {
|
||
if (isItemActive(item)) return
|
||
if (item.key === 'works' && !isLoggedIn.value) {
|
||
loginDialogRef.value?.open()
|
||
return
|
||
}
|
||
if (item.path) {
|
||
router.push(item.path)
|
||
}
|
||
}
|
||
|
||
function isItemActive(item) {
|
||
if (item.path === route.path) return true
|
||
if (item.key === 'article' && route.path.startsWith('/article')) return true
|
||
if (item.children) {
|
||
return item.children.some(child => child.path === route.path)
|
||
}
|
||
return false
|
||
}
|
||
|
||
async function handleCommand(cmd) {
|
||
if (cmd === 'logout') {
|
||
try { await api.auth.logout() } catch { }
|
||
userStore.reset()
|
||
localStorage.removeItem('token')
|
||
localStorage.removeItem('user_info')
|
||
ElMessage.success('已退出登录')
|
||
router.push('/')
|
||
} else if (cmd === 'works') {
|
||
if (!isLoggedIn.value) {
|
||
loginDialogRef.value?.open()
|
||
return
|
||
}
|
||
router.push('/works')
|
||
} else if (cmd === 'profile') {
|
||
router.push('/profile')
|
||
} else if (cmd === 'vip') {
|
||
showPayment?.()
|
||
}
|
||
}
|
||
|
||
|
||
function openLoginDialog() {
|
||
loginDialogRef.value?.open()
|
||
}
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.app-header {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
margin: 0 auto;
|
||
max-width: 2000px;
|
||
min-width: 1200px;
|
||
background: #fff;
|
||
box-shadow: 0 1px 8px rgba(0, 0, 0, .08);
|
||
z-index: 100;
|
||
|
||
.header-inner {
|
||
max-width: 1200px;
|
||
margin: 0 auto;
|
||
padding: 0 20px;
|
||
height: 100%;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 40px;
|
||
}
|
||
|
||
.logo {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 6px;
|
||
font-size: 18px;
|
||
font-weight: bold;
|
||
color: #07c160;
|
||
|
||
.logo-icon {
|
||
font-size: 24px;
|
||
}
|
||
}
|
||
|
||
.nav-links {
|
||
display: flex;
|
||
gap: 24px;
|
||
flex: 1;
|
||
align-items: center;
|
||
|
||
a {
|
||
color: #333;
|
||
font-size: 14px;
|
||
padding: 4px 0;
|
||
|
||
&.router-link-active {
|
||
color: #07c160;
|
||
}
|
||
}
|
||
|
||
.nav-dropdown-trigger {
|
||
cursor: pointer;
|
||
font-size: 14px;
|
||
color: #333;
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 2px;
|
||
padding: 4px 0;
|
||
}
|
||
}
|
||
|
||
.user-area {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 10px;
|
||
}
|
||
|
||
.user-name {
|
||
cursor: pointer;
|
||
font-size: 14px;
|
||
color: #666;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 2px;
|
||
}
|
||
}
|
||
|
||
|
||
.app-header {
|
||
height: auto;
|
||
}
|
||
|
||
.notice-banner-wrapper {
|
||
position: relative;
|
||
min-width: 1200px;
|
||
height: 52px;
|
||
display: flex;
|
||
justify-content: center;
|
||
background: linear-gradient(258deg, #C05BFF 0%, #2E95FF 68.44%, #55D5FC 88.34%, #6CFFEA 100%);
|
||
|
||
.banner-text {
|
||
height: 52px;
|
||
}
|
||
|
||
.banner-content {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
line-height: 52px;
|
||
|
||
.main-text {
|
||
font-weight: 700;
|
||
font-size: 20px;
|
||
color: #FFFFFF;
|
||
letter-spacing: 2px;
|
||
margin-right: 8px;
|
||
}
|
||
|
||
.sub-text {
|
||
font-weight: 400;
|
||
font-size: 20px;
|
||
color: #FFFFFF;
|
||
line-height: 52px;
|
||
letter-spacing: 5px;
|
||
}
|
||
|
||
.down-app-img {
|
||
height: 26px;
|
||
margin-left: 18px;
|
||
cursor: pointer;
|
||
animation: pulseScale 1.5s infinite ease-in-out;
|
||
|
||
&:hover {
|
||
opacity: 0.9;
|
||
}
|
||
}
|
||
}
|
||
|
||
@keyframes pulseScale {
|
||
0% {
|
||
transform: scale(0.9);
|
||
}
|
||
|
||
50% {
|
||
transform: scale(1.1);
|
||
}
|
||
|
||
100% {
|
||
transform: scale(0.9);
|
||
}
|
||
}
|
||
|
||
.banner-bg {
|
||
position: absolute;
|
||
left: 50%;
|
||
top: 50%;
|
||
transform: translate(-50%, -50%);
|
||
z-index: 0;
|
||
height: 52px;
|
||
}
|
||
|
||
.close-icon {
|
||
position: absolute;
|
||
right: 34px;
|
||
top: 50%;
|
||
transform: translateY(-50%);
|
||
width: 34px;
|
||
height: 34px;
|
||
cursor: pointer;
|
||
}
|
||
}
|
||
|
||
.app-header-bar {
|
||
width: 100%;
|
||
padding: 22px 72px;
|
||
height: 60px;
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
|
||
.left-box {
|
||
display: flex;
|
||
align-items: center;
|
||
|
||
.logo-img {
|
||
width: 36px;
|
||
height: 36px;
|
||
margin-right: 12px;
|
||
}
|
||
|
||
.logo-name {
|
||
width: 87px;
|
||
}
|
||
|
||
.line {
|
||
margin-left: 30px;
|
||
width: 2px;
|
||
height: 26px;
|
||
background: #D8D8D8;
|
||
}
|
||
|
||
.menu-box {
|
||
display: flex;
|
||
margin-left: 48px;
|
||
gap: 50px;
|
||
margin-right: 100px;
|
||
|
||
.item {
|
||
font-weight: 500;
|
||
font-size: 18px;
|
||
color: #767676;
|
||
line-height: 20px;
|
||
white-space: nowrap;
|
||
cursor: pointer;
|
||
|
||
&.active {
|
||
position: relative;
|
||
font-weight: 700;
|
||
color: #1A1A1A;
|
||
|
||
&::after {
|
||
content: '';
|
||
position: absolute;
|
||
bottom: -10px;
|
||
left: 50%;
|
||
transform: translateX(-50%);
|
||
width: 26px;
|
||
height: 4px;
|
||
background: linear-gradient(215deg, #A679F4 7.14%, #778CF5 100%);
|
||
border-radius: 129px 129px 129px 129px;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.right-box {
|
||
display: flex;
|
||
align-items: center;
|
||
|
||
.vip-logo {
|
||
height: 32px;
|
||
margin-right: 16px;
|
||
cursor: pointer;
|
||
}
|
||
|
||
.user-avatar {
|
||
height: 45px;
|
||
width: 45px;
|
||
margin-right: 16px;
|
||
border-radius: 50%;
|
||
background-color: #eaeaea;
|
||
cursor: pointer;
|
||
flex-shrink: 0;
|
||
|
||
.user-logo-img {
|
||
width: 45px;
|
||
height: 45px;
|
||
border-radius: 50%;
|
||
object-fit: cover;
|
||
}
|
||
}
|
||
|
||
.user-name {
|
||
font-weight: 500;
|
||
font-size: 18px;
|
||
color: #1A1A1A;
|
||
line-height: 18px;
|
||
cursor: pointer;
|
||
}
|
||
}
|
||
}
|
||
|
||
.dropdown-wrapper {
|
||
position: relative;
|
||
display: flex;
|
||
align-items: center;
|
||
height: 100%;
|
||
|
||
&:hover .dropdown-panel {
|
||
display: block;
|
||
opacity: 1;
|
||
transform: translateX(-50%) translateY(0);
|
||
}
|
||
}
|
||
|
||
.dropdown-panel {
|
||
display: none;
|
||
position: absolute;
|
||
top: 100%;
|
||
left: 50%;
|
||
transform: translateX(-50%) translateY(-10px);
|
||
padding-top: 15px;
|
||
z-index: 100;
|
||
transition: all 0.3s ease;
|
||
|
||
.panel-content {
|
||
background: #FFFFFF;
|
||
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1);
|
||
border-radius: 8px;
|
||
padding: 1px 0;
|
||
min-width: 120px;
|
||
position: relative;
|
||
|
||
&::before {
|
||
content: '';
|
||
position: absolute;
|
||
top: -6px;
|
||
left: 50%;
|
||
transform: translateX(-50%);
|
||
border-left: 6px solid transparent;
|
||
border-right: 6px solid transparent;
|
||
border-bottom: 6px solid #fff;
|
||
filter: drop-shadow(0 -2px 2px rgba(0, 0, 0, 0.05));
|
||
}
|
||
}
|
||
|
||
.dropdown-item {
|
||
display: flex;
|
||
align-items: center;
|
||
height: 34px;
|
||
padding: 12px 20px;
|
||
font-size: 14px;
|
||
border-radius: 6px;
|
||
color: #1A1A1A;
|
||
cursor: pointer;
|
||
white-space: nowrap;
|
||
margin: 3px 4px;
|
||
transition: all 0.2s;
|
||
|
||
&.mg-7 {
|
||
margin: 7px;
|
||
height: 40px;
|
||
}
|
||
|
||
.icon {
|
||
width: 18px;
|
||
height: 18px;
|
||
margin-right: 8px;
|
||
object-fit: contain;
|
||
}
|
||
|
||
&:hover {
|
||
background: #F7F8FF;
|
||
}
|
||
}
|
||
}
|
||
|
||
.user-info-trigger {
|
||
display: flex;
|
||
align-items: center;
|
||
cursor: pointer;
|
||
height: 100%;
|
||
}
|
||
|
||
.qrCode-box {
|
||
width: 100%;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
padding: 20px 0;
|
||
}
|
||
</style>
|