Commit b2d6e92b by qinjianhui

fix: 手机版 header 菜单

parent b50cfdfc
<template>
<nav class="header-nav">
<div
v-for="(n, i) in nav"
:key="i"
class="header-nav-item"
:class="{ active: isRouteActive(n) }"
>
<template v-if="n.children && n.children.length">
<span class="header-nav-title">{{ n.title }}</span>
<ul class="header-nav-children">
<li
v-for="(nn, j) in n.children"
:key="j"
class="header-nav-child"
:class="{ active: isRouteActive(nn) }"
>
<router-link
class="header-nav-title header-nav-link"
:to="nn.route"
>{{ nn.title }}</router-link
>
</li>
</ul>
</template>
<router-link
v-else
class="header-nav-title header-nav-link"
:to="n.route"
>{{ n.title }}</router-link
>
</div>
</nav>
</template>
<script>
export default {
name: 'HeaderNav',
props: {
nav: {
type: Array,
required: true,
},
},
methods: {
isRouteActive(nav) {
const path = this.$route.path
if (nav.route === path) return true
if (nav.children?.length) {
return nav.children.some((e) => e.route === path)
}
},
},
}
</script>
<style lang="scss">
.header-nav {
display: flex;
height: 100%;
}
@mixin header-nav-item-active($x) {
&::before,
&::after {
content: '';
display: block;
width: 50%;
transform: scaleX($x);
height: 4px;
background-color: var(--primary-color);
position: absolute;
top: 0;
transition: transform 0.3s;
}
&::before {
left: 0;
transform-origin: 100%;
}
&::after {
right: 0;
transform-origin: 0;
}
&:hover {
&::before,
&::after {
transform: scaleX(1);
}
}
}
.header-nav-item {
width: 120px;
height: 100%;
position: relative;
&:hover {
.header-nav-children {
display: flex;
}
}
&.active {
& > .header-nav-title {
@include header-nav-item-active(1);
color: var(--primary-color);
}
}
& > .header-nav-title {
@include header-nav-item-active(0);
}
}
.header-nav-children {
position: absolute;
top: 100%;
left: 0;
width: 100%;
margin: 0;
padding: 0;
display: none;
flex-direction: column;
background-color: #000;
color: #fff;
}
.header-nav-child {
width: 100%;
height: 60px;
margin: 0;
padding: 0;
list-style-type: none;
&.active {
color: var(--primary-color);
}
}
.header-nav-title {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
height: 100%;
&:hover {
color: var(--primary-color);
}
}
.header-nav-link {
color: inherit;
text-decoration: none;
cursor: pointer;
transition: 0.3s;
}
</style>
<template>
<div class="header-nav-mobile">
<a
class="header-nav-mobile-trigger icon-menu"
href="javascript:;"
@click="toggleShow"
></a>
<transition name="header-nav-mobile">
<div
v-show="showing"
class="header-nav-mobile-overlay"
@click="toggleShow"
>
<ul class="header-nav-mobile-items" @click.stop>
<li
v-for="(r, i) in nav"
:key="i"
class="header-nav-mobile-item"
:class="{ active: isRouteActive(r) }"
>
<template v-if="r.children && r.children.length">
<a
class="header-nav-mobile-link"
href="javascript:;"
@click="toggleExpand(r)"
>{{ r.title }}
<span class="expand-icon">{{
expandMap[r.title] ? '-' : '+'
}}</span></a
>
<ul
v-show="expandMap[r.title]"
class="header-nav-mobile-children"
>
<li
v-for="(rr, j) in r.children"
:key="j"
class="header-nav-mobile-child"
:class="{ active: isRouteActive(rr) }"
>
<router-link
class="header-nav-mobile-link"
:to="rr.route"
@click="toggleShow"
>{{ rr.title }}</router-link
>
</li>
</ul>
</template>
<router-link
v-else
class="header-nav-mobile-link"
:to="r.route"
@click="toggleShow"
>{{ r.title }}</router-link
>
</li>
</ul>
</div>
</transition>
</div>
</template>
<script>
export default {
name: 'HeaderNavMobile',
props: {
nav: {
type: Array,
required: true,
},
},
data() {
return {
showing: false,
expandMap: {},
}
},
watch: {
nav: {
immediate: true,
handler() {
this.buildExpandMap()
},
},
$route() {
this.buildExpandMap()
},
},
methods: {
toggleShow() {
this.showing = !this.showing
},
toggleExpand(r) {
this.expandMap[r.title] = !this.expandMap[r.title]
},
isRouteActive(nav) {
const path = this.$route.path
if (nav.route === path) return true
if (nav.children?.length) {
return nav.children.some((e) => e.route === path)
}
},
buildExpandMap() {
const m = {}
this.nav.forEach((r) => {
if (r.children?.length) {
m[r.title] = this.isRouteActive(r)
}
})
this.expandMap = m
},
},
}
</script>
<style lang="scss">
.header-nav-mobile-trigger {
display: block;
width: 25px;
height: 25px;
}
.header-nav-mobile-enter-active,
.header-nav-mobile-leave-active {
transition: 0.3s;
}
.header-nav-mobile-enter,
.header-nav-mobile-leave-to {
transform: translateX(-300px);
}
.header-nav-mobile-overlay {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
z-index: 99999;
}
.header-nav-mobile-items {
box-sizing: border-box;
width: 300px;
height: 100%;
background-color: #000;
margin: 0;
padding: 30px 20px;
}
.header-nav-mobile-item,
.header-nav-mobile-child {
list-style-type: none;
line-height: 50px;
letter-spacing: 4px;
&.active > .header-nav-mobile-link {
color: var(--primary-color);
}
}
.header-nav-mobile-item.active {
.header-nav-mobile-link {
position: relative;
&::after {
position: absolute;
top: 100%;
left: 0;
content: '';
display: block;
width: 80px;
height: 2px;
background-color: var(--primary-color);
}
}
}
.header-nav-mobile-item {
font-size: 16px;
color: #fff;
}
.header-nav-mobile-child {
font-size: 14px;
color: #999;
}
.header-nav-mobile-children {
margin: 0;
padding: 0;
}
.header-nav-mobile-link {
display: block;
width: 100%;
height: 100%;
color: inherit;
text-decoration: none;
cursor: pointer;
border-bottom: solid 1px #2c2c2c;
}
.header-nav-mobile-item > .header-nav-mobile-link {
display: flex;
.expand-icon {
margin-left: auto;
margin-right: 20px;
}
}
.header-nav-mobile-child > .header-nav-mobile-link {
padding-left: 10px;
&::after {
display: none !important;
}
}
</style>
......@@ -9,43 +9,17 @@
/>
</router-link>
</div>
<nav class="header-nav">
<div
v-for="(n, i) in nav"
:key="i"
class="header-nav-item"
:class="{ active: isRouteActive(n) }"
>
<template v-if="n.children && n.children.length">
<span class="header-nav-title">{{ n.title }}</span>
<ul class="header-nav-children">
<li
v-for="(nn, j) in n.children"
:key="j"
class="header-nav-child"
:class="{ active: isRouteActive(nn) }"
>
<router-link
class="header-nav-title header-nav-link"
:to="nn.route"
>{{ nn.title }}</router-link
>
</li>
</ul>
</template>
<router-link
v-else
class="header-nav-title header-nav-link"
:to="n.route"
>{{ n.title }}</router-link
>
</div>
</nav>
<HeaderNavMobile v-if="$isMobile" :nav="nav" />
<HeaderNav v-else :nav="nav" />
</header>
</template>
<script>
import HeaderNav from './HeaderNav.vue'
import HeaderNavMobile from './HeaderNavMobile.vue'
export default {
name: 'HomePageHeader',
components: { HeaderNav, HeaderNavMobile },
data() {
return {
nav: [
......@@ -66,15 +40,6 @@ export default {
],
}
},
methods: {
isRouteActive(nav) {
const path = this.$route.path
if (nav.route === path) return true
if (nav.children?.length) {
return nav.children.some((e) => e.route === path)
}
},
},
}
</script>
<style lang="scss" scoped>
......@@ -111,118 +76,14 @@ export default {
height: 80%;
}
.header-nav {
display: flex;
height: 100%;
}
@mixin header-nav-item-active($x) {
&::before,
&::after {
content: '';
display: block;
width: 50%;
transform: scaleX($x);
height: 4px;
background-color: var(--primary-color);
position: absolute;
top: 0;
transition: transform 0.3s;
}
&::before {
left: 0;
transform-origin: 100%;
}
&::after {
right: 0;
transform-origin: 0;
}
&:hover {
&::before,
&::after {
transform: scaleX(1);
}
}
}
.header-nav-item {
width: 120px;
height: 100%;
position: relative;
&:hover {
.header-nav-children {
display: flex;
}
}
&.active {
& > .header-nav-title {
@include header-nav-item-active(1);
color: var(--primary-color);
}
}
& > .header-nav-title {
@include header-nav-item-active(0);
}
}
.header-nav-children {
.header-nav-mobile {
position: absolute;
top: 100%;
left: 0;
width: 100%;
margin: 0;
padding: 0;
display: none;
flex-direction: column;
background-color: #000;
color: #fff;
}
.header-nav-child {
width: 100%;
height: 60px;
margin: 0;
padding: 0;
list-style-type: none;
&.active {
color: var(--primary-color);
}
}
.header-nav-title {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
height: 100%;
&:hover {
color: var(--primary-color);
}
}
.header-nav-link {
color: inherit;
text-decoration: none;
cursor: pointer;
transition: 0.3s;
left: 20px;
}
@media screen and (max-width: 1000px) {
.home-page-header {
justify-content: flex-start;
padding-left: 5%;
}
.header-nav {
display: none;
.header-logo {
margin-right: 0;
}
}
</style>
......@@ -24,6 +24,12 @@ router.afterEach((to) => {
}
})
const isMobile = () => window.innerWidth <= 1000
Vue.util.defineReactive(Vue.prototype, '$isMobile', isMobile())
window.addEventListener('resize', () => {
Vue.prototype.$isMobile = isMobile()
})
new Vue({
router,
store,
......
......@@ -33,4 +33,16 @@
.icon-thumb-gold-bg-gray {
background-size: 100%;
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAE4AAABOCAYAAACOqiAdAAAAAXNSR0IArs4c6QAABTRJREFUeAHtm3lPG0cUwGdm18dibGNcc5Qj0Cq0TZ2mimhC2kbNF2hV9dPxPdI/qlaqlBApEVLUNqSlIkkDNYcDBd/37kxnnMyyIMD2Y21WeFYyb473dp5/vNmdy3hxcfE+UlfHBEjHFsqgSUCBAwaCAqfAAQkAzVTEKXBAAkAzFXEKHJAA0ExFnAIHJAA0UxGnwAEJAM1UxClwQAJAMxVxQHA60K5nZgQxNGuuTsZoOm7QslFBA5Ws/l5m3ZdMmYywnjlyrCFPgzNQxXe78tPCAMtHpd9htI9G6qmpKfPlzFP/vad5MlySdb2Unu6q89Wfv3BCc4IxaCFys/bgJsEUO8t7lfYsuDFrIx6m2WEJok6M8p4+mSrhcE6WCaizjb8nZL6X0rNddbqxNi1B1MhA8UHwh4cm0qgou1u9fydMD+IiHaG7MYSubYp0Ly9PRpyOTBKnb0YliLQ2vSmhibKsltiXdSFaDMl0L6UnwU2aL8YwsuzekNLntp1QnK9SjJh6xkk449a6/dwqkkg2j2NlWecV6bmIC6CqL0b3EhLQf/qVLZn2kvQcuGlzbRwxJv1i69rVHS8Bk75IB2X+wuWYuWF30wIZPijjcPXCnTrBAU+BG2CFIB9m2GO3tEe7qeDoKXC8m77PfXr3lsR0U5/zZDf1HLhxa2NKOCWunJbYq6Bg423Oe389E3HXG0/m+PwzLBFtaR/0fDYg225H2oPMdpRb6UyYr0ZGrc2RVnrOej7QJVGWHQrySbssr5Bw4V//RzuoOcGSpSfLAC2F+GQ/eaQWY1ZDgXpRixW2ycxeA/utI/UuZFwFF2N7Q2PW+sx5/GJIM//033pG24Am2vGjWvDUNk0+i8XL1pb+4T+r/vmXDeZzDaBnuqqA0MDBym+Bb5Z3yWRG5N24MLO0ycba1a+rP34p3tpu3FPcw9WIczpFkW5WSKjgLJNpg5bCBJnNtsVyUZbE93MkkXmtf3pkMi/1j8saHqyUSPR0uIySAVYOOee7hpWP3q79cmsp+N0jE+ltxvPxlg/zXQNXIpHcUvDbx4dNHaacy0Ji5eO5b2HtsLZ16oWeTInPWZpiyX2EbsU+qT35zGCl5ktHLH5eqz3++Fng7l9n2bZT56mu2o7D7epQPhxM8y6/ZHy/VCSxA2k3QTeuGKjkl3movLTgJBDRLVeCX/3BEG52z+Yzz3xtr/VJvU7lpQcngGRQvMSHJvYzMWru2tO6ToFJ/b4AJ75sEQ/l5ZfmS1cBmYbKvgHXwD57+kaQpUGBSbu+ASe/sFtSgQOSVOAUOCABoJmKOAUOSABopiJOgQMSAJqpiFPggASAZiriFDggAaBZ30ScxvceJCPr3dqczENk34AL0Yy9/WgSowaB5bTpC3AhVPZHaI4feX178fN29jliWdap7Atw1+uPknJXjQNi29rsm05BHdfv2i7X8YYuIi82ZT6vPrwRo7v2HsO+PrGVJ+c/4dk1cNzp0Hz11xsnARN1sjxBd0a5niHzbkhCGDasXDiEimHHIUVUR4Hqim9h1Y02ugZOp7XgCErZp49Oc1ZsFBvo8Jczp+l1VH7CdrNJAtXfA/eWy3jw3C8G4UvXwHX0RbusnNHG0iv+O8+LOOLa6U5XwVXwYJkfP7V/g9BlHmfcnp9Wwv56WRsq8BdB+gAnTjyKccYNWla5Cu6VntwUn5atXgKFvhiOdOP/pMABqSpwChyQANBMRZwCByQANFMRp8ABCQDNVMQpcEACQDMVcQockADQTEWcAgckADRTEQcE9z9gH3lSlhK6vQAAAABJRU5ErkJggg==);
}
.icon-close {
background-size: 100%;
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAAAAXNSR0IArs4c6QAAAWJJREFUOBGVkkdOA0EQRS2WYBwAm+Akke7GliuwZ8+KI7BGIExYECUyLEgincO8Z7mEB0e+9Nw1VdV/2j2VajQa67ANhdQ/xZ45qMNaip9dULdQHNaL3hI8gdrSqAh3PiHXgWb0lCFMLojzzQMQFMATqXuY7nUyahV4BnUOuUQvCc1uQD1Ahxk5TV5AnUHSJBwpTME1qEeYaatVeX61gE4hG7WuKw2aXYHSbBZqECYnxP1NwpnGSQgz7+MD1DFkom+olQ0T4MWHvNieJiN9XD1++0YvdrxPf2eJN8/De+so/p3LVuzslDp3dMnQqEncyRFxGvLg4CnNyl22/qZoWIBPUIeQjipxDrwn5QeoRC2xUliEMDkgHks08EBOMwdROZhJMxJL8AVqHzpMwpRaFhxI5WxVmzWCZfgGVYeeJn/MHEz1BjWPG4k94tFoHrTSmwG/qNrRaBU2YGiTeAl7NNuElR9wOxcdDOaDOAAAAABJRU5ErkJggg==);
}
.icon-menu {
background-size: 100%;
background-repeat: no-repeat;
background-position: center;
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAcCAYAAAATFf3WAAAAyklEQVRYR+2WMQ6BQRCFvzmHgkaidgI3UEjcQUSBygV0NOIQhIQTcAK1kpM8GcV/gbHJSHb62fn2vZ2ZNUltYAN0yBUvYGmSTsAoF1tDc/4LwNwWJ7W2wbIKGFSgKhgUkKrgTxSU1M+46szs4ZtkDmyjNy2Uv3DAGzAoVCB67N0BJ8Ae0jWMgOm3iyX1AN/JmeJtZs86ZqKWVAWrglEFovn536CkLrDLOAeBmW+SCzCMWlEo/+qAB2BcqED02KMDtoB1xu8WsErfJB+rXD/DrYeGMwAAAABJRU5ErkJggg==);
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment