flaunt-web/src/components/common/AppHeader.vue

570 lines
14 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<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>