提示:本例为页面结构基本学习案例,实际项目中多采用组件库快速搭建页面结构
视频预览
本案例页面结构参考维普管理系统页面纯手写HTML+CSS+JS实现,无组件引入仅供学习参考
menu
一、页面初解及实现功能
页面可初分为三个部分,1.左侧菜单栏,2.右侧标签状态栏(上),3.右侧内容区域(下)
1.菜单栏
菜单栏初分为一级、二级菜单,该部分均通过JS动态渲染HTML结构。具体实现功能:点击一级菜单展示当前一级菜单下的二级菜单目录,同时关闭其他展示的二级菜单目录,再次点击已展示的一级菜单收起;点击二级菜单时,右侧标签状态栏依次添加当前点击的二级标签同时内容区域展示当前相关部分内容(内容区域本案例未做具体实现,可自行完善)
2.右侧标签状态栏(上)
动态添加当前点击的二级菜单标签,可点击关闭当前标签页,其他标签页及所有标签页,状态栏超过页面宽度时自动隐藏超出部分,可点击左右箭头滚动展示。
3.右侧内容区域(下)
该部分通过JS动态切换盒子以实现结构或内容的更替与菜单所示内容对应
本案例中仅预留内容部分位置未进行具体实现,可自行布局设计补充完善。
二、代码部分
代码部分仅注释了部分代码,若有疑问欢迎评论区留言。
1.HTML
<div class="menu">
<div class="logo_box">后台管理系统</div>
<ul class="menu_list">
<div class="lineIndicator_bar"></div>
</ul>
</div>
<div class="all_cont">
<div class="header">
<div class="account">
<div class="user">用户名</div>
</div>
<div class="navigate">
<span class="slider_left">
<svg t="1715750948172" class="icon" viewBox="0 0 1024 1024" version="1.1"
xmlns="http://www.w3.org/2000/svg" p-id="1202" width="15" height="15">
<path
d="M487.253333 535.893333l386.133334-386.133333c13.226667-13.226667 13.226667-34.986667 0-48.213333-13.226667-13.226667-34.986667-13.226667-48.213334 0l-386.133333 386.133333c-13.226667 13.226667-13.226667 34.986667 0 48.213333 13.226667 13.226667 34.986667 13.226667 48.213333 0z"
fill="#bfbfbf" p-id="1203"></path>
<path
d="M873.386667 873.813333l-386.133334-386.133333c-13.226667-13.226667-34.986667-13.226667-48.213333 0-13.226667 13.226667-13.226667 34.986667 0 48.213333l386.133333 386.133334c13.226667 13.226667 34.986667 13.226667 48.213334 0 13.226667-13.226667 13.226667-34.986667 0-48.213334z"
fill="#bfbfbf" p-id="1204"></path>
<path
d="M218.88 536.32l386.133333-386.133333c13.226667-13.226667 13.226667-34.986667 0-48.213334-13.226667-13.226667-34.986667-13.226667-48.213333 0L170.666667 488.106667c-13.226667 13.226667-13.226667 34.986667 0 48.213333 13.226667 13.653333 34.986667 13.653333 48.213333 0z"
fill="#bfbfbf" p-id="1205"></path>
<path
d="M605.013333 874.24l-386.133333-386.133333c-13.226667-13.226667-34.986667-13.226667-48.213333 0-13.226667 13.226667-13.226667 34.986667 0 48.213333l386.133333 386.133333c13.226667 13.226667 34.986667 13.226667 48.213333 0 13.226667-13.226667 13.226667-34.56 0-48.213333z"
fill="#bfbfbf" p-id="1206"></path>
</svg>
</span>
<div>
<span><svg t="1715671029966" class="icon" viewBox="0 0 1024 1024" version="1.1"
xmlns="http://www.w3.org/2000/svg" p-id="5916" width="13" height="13">
<path
d="M647.1 665.11c10.88 12.86 9.28 32.1-3.58 42.98-37.31 31.57-78.05 47.88-121.36 47.88s-84.05-16.31-121.38-47.88a30.513 30.513 0 0 1-10.3-28.72 30.497 30.497 0 0 1 19.72-23.28 30.499 30.499 0 0 1 30.02 5.44c26.69 22.63 53.71 33.44 81.93 33.44 28.2 0 55.22-10.82 81.93-33.44a30.515 30.515 0 0 1 22.24-7.12c8.08 0.67 15.55 4.52 20.78 10.7z"
p-id="5917" fill="#8a8a8a"></path>
<path
d="M934.19 386.74L553.05 79.27c-23.93-19.41-58.19-19.41-82.12 0L89.8 386.72c-15.86 12.81-25.09 32.27-25.09 52.86l0.1 3.82c1.67 30.37 23.08 55.38 51.38 61.91l2.34 0.47 0.02 344.53 0.1 4.8c2.91 58.61 49.81 104.18 106.84 104.18H798.5l4.72-0.1 4.51-0.35c55.32-5.14 97.71-51.95 97.71-108.53l-0.02-344.53 3.54-0.77a66.073 66.073 0 0 0 36.03-23.6c22.69-29.2 17.85-71.57-10.8-94.67zM897.09 443c-1.08 1.36-2.68 2.15-4.39 2.15h-17.93c-16.71 0-30.25 13.83-30.25 30.9v374.87c0 25.23-19.38 46.03-43.94 47.29l-3.17 0.08H225.82c-24.7 0-45.05-19.82-46.29-44.93l-0.06-3.23v-374.1c0-17.06-13.54-30.9-30.25-30.88h-17.93c-3.09 0-5.59-2.56-5.59-5.73 0-1.73 0.77-3.37 2.11-4.47l380.74-308a5.493 5.493 0 0 1 6.91 0l380.72 308.02a5.821 5.821 0 0 1 0.91 8.03z"
p-id="5918" fill="#8a8a8a"></path>
</svg></span>
<span>首页</span>
</div>
<ul class="nav_list"></ul>
<span class="slider_right">
<svg t="1715750916984" class="icon" viewBox="0 0 1024 1024" version="1.1"
xmlns="http://www.w3.org/2000/svg" p-id="845" width="15" height="15">
<path
d="M556.8 535.893333L170.666667 149.76c-13.226667-13.226667-13.226667-34.986667 0-48.213333 13.226667-13.226667 34.986667-13.226667 48.213333 0l386.133333 386.133333c13.226667 13.226667 13.226667 34.986667 0 48.213333-13.226667 13.226667-34.986667 13.226667-48.213333 0z"
fill="#bfbfbf" p-id="846"></path>
<path
d="M170.666667 873.813333l386.133333-386.133333c13.226667-13.226667 34.986667-13.226667 48.213333 0 13.226667 13.226667 13.226667 34.986667 0 48.213333l-386.133333 386.133334c-13.226667 13.226667-34.986667 13.226667-48.213333 0a33.493333 33.493333 0 0 1 0-48.213334z"
fill="#bfbfbf" p-id="847"></path>
<path
d="M825.173333 536.32l-386.133333-386.133333c-13.226667-13.226667-13.226667-34.986667 0-48.213334 13.226667-13.226667 34.986667-13.226667 48.213333 0l386.133334 386.133334c13.226667 13.226667 13.226667 34.986667 0 48.213333a33.493333 33.493333 0 0 1-48.213334 0z"
fill="#bfbfbf" p-id="848"></path>
<path
d="M439.04 874.24l386.133333-386.133333c13.226667-13.226667 34.986667-13.226667 48.213334 0 13.226667 13.226667 13.226667 34.986667 0 48.213333l-386.133334 386.133333c-13.226667 13.226667-34.986667 13.226667-48.213333 0-13.226667-13.226667-13.226667-34.56 0-48.213333z"
fill="#bfbfbf" p-id="849"></path>
</svg>
</span>
<span class="nav_close">
<svg t="1715751458630" class="icon" viewBox="0 0 1024 1024" version="1.1"
xmlns="http://www.w3.org/2000/svg" p-id="3274" width="18" height="18">
<path
d="M512 704c8.288 0 15.776-3.232 21.456-8.4l0.064 0.08 352-320-0.08-0.08c6.448-5.856 10.56-14.208 10.56-23.6a32 32 0 0 0-32-32c-8.288 0-15.76 3.232-21.456 8.4l-0.08-0.08L512 628.752 181.536 328.32l-0.08 0.08A31.776 31.776 0 0 0 160 320a32 32 0 0 0-32 32c0 9.376 4.112 17.744 10.544 23.6l-0.08 0.08 352 320 0.08-0.08c5.68 5.168 13.168 8.4 21.456 8.4z"
p-id="3275" fill="#bfbfbf"></path>
</svg>
</span>
</div>
<ul class="close_tab">
<li>关闭当前标签页</li>
<li>关闭其他标签页</li>
<li>关闭所有标签页</li>
</ul>
</div>
</div>
2.CSS
* {
margin: 0;
padding: 0;
list-style: none;
border: 0;
}
.menu {
width: 12%;
height: calc(100vh);
background: rgb(52, 64, 88);
color: #ccc;
float: left;
}
.logo_box {
width: 100%;
height: 60px;
text-align: center;
line-height: 60px;
font-size: 15px;
box-shadow: 0 5px 5px -5px rgb(33, 42, 59);
}
.lineIndicator_bar {
width: 0;
height: 60px;
background: skyblue;
position: absolute;
left: 0;
display: inline-block;
transition: all .3s;
}
.menu_list {
width: 100%;
position: relative;
cursor: pointer;
font-size: 15px;
display: flex;
flex-wrap: wrap;
}
.list_item {
width: 100%;
}
.list_item div {
width: 100%;
height: 60px;
line-height: 60px;
text-indent: 1.5em;
}
.list_item dl {
overflow: hidden;
transition: all .5s;
text-indent: 2em;
background: rgb(36, 45, 61);
transition: .5s;
}
.list_item dd {
width: 100%;
height: 0;
line-height: 60px;
transition: all .5s;
position: relative;
}
.list_item dd::after {
content: '';
width: 0;
height: 100%;
position: absolute;
left: 0;
top: 0;
background: skyblue;
transition: all .5s;
}
.list_item dd:hover:after {
width: 4px;
}
.list_item dd:hover {
padding-left: 4px;
}
.list_item .dl_display dd {
height: 60px;
}
.list_item .dl_display dd:first-child {
margin-top: 5px;
}
.list_item .dl_display dd:last-child {
margin-bottom: 5px;
}
.dd_bg {
background: rgb(115, 150, 190);
}
.all_cont {
width: 88%;
height: calc(100vh);
background: rgb(242, 242, 242);
float: left;
overflow: hidden;
}
.header {
width: 100%;
height: 90px;
box-shadow: 0 5px 5px -5px #ccc;
color: #000000a9;
float: left;
background: #fff;
}
.account {
width: 100%;
height: 50%;
border-bottom: 1px solid #cccccc43;
box-sizing: border-box;
font-size: 13px;
text-align: center;
line-height: 45px;
}
.account .user {
padding: 0 30px;
float: right;
cursor: pointer;
}
.navigate {
width: 100%;
height: 45px;
position: relative;
overflow: hidden;
}
.navigate div {
line-height: 45px;
padding-left: 10px;
font-size: 13px;
padding-right: 10px;
border-right: 1px solid #cccccc43;
cursor: pointer;
background: #fff;
position: absolute;
left: 36px;
z-index: 9;
}
.navigate span {
float: left;
}
.navigate div span:first-child {
margin-top: 2px;
}
.navigate div span:last-child {
margin-top: 0;
margin-left: 2px;
}
.nav_list {
width: 180%;
height: 45px;
position: absolute;
left: 98px;
display: flex;
flex-direction: row;
transition: .3s;
}
.nav_list li {
float: left;
line-height: 45px;
font-size: 13px;
padding-left: 30px;
padding-right: 2px;
border-right: 1px solid #cccccc43;
cursor: pointer;
box-sizing: border-box;
}
.nav_list li span:nth-child(2) {
margin-top: 5px;
margin-left: 10px;
display: flex;
}
#page_close:hover path {
fill: #000;
}
.slider_left,
.slider_right,
.nav_close {
display: flex;
float: left;
background: #fff;
height: 45px;
line-height: 45px;
cursor: pointer;
padding: 0 10px;
padding-top: 15px;
}
.slider_left:hover path,
.slider_right:hover path,
.nav_close:hover path {
fill: #7d7c7c;
}
.slider_left {
position: absolute;
left: 0;
z-index: 9;
border-right: 1px solid #cccccc43;
}
.slider_right {
position: absolute;
margin-left: 80%;
padding: 0 15px;
padding-top: 15px;
right: 48px;
top: 0;
}
.nav_select {
background: rgb(246, 246, 246);
font-weight: bold;
}
.nav_close {
position: absolute;
margin-left: 0;
padding: 0 15px;
padding-top: 15px;
right: 0;
top: 0;
}
.close_tab {
float: right;
padding: 5px 0;
background: #fff;
margin-top: 1px;
display: none;
}
.close_tab li {
width: 100px;
height: 30px;
text-align: center;
line-height: 30px;
font-size: 13px;
position: relative;
padding: 0 10px;
transition: .3s;
cursor: pointer;
}
.close_tab li::before {
content: '';
position: absolute;
left: 0;
width: 0px;
height: 30px;
background: rgb(59, 133, 244);
transition: .3s;
}
.close_tab li:hover::before {
width: 3px;
}
.close_tab li:hover {
background: rgb(242, 242, 242);
}
3.JS
const menuList = document.querySelector('.menu_list')
//菜单列表,level_1 一级菜单,level_2二级菜单
const titleName = [
{ title_1: '通告管理', title_2: ['系统通告', '新闻通告'] },
{ title_1: '信息管理', title_2: ['用户信息', '客服信息'] },
{ title_1: '仓储管理', title_2: ['商品库存', '快递包装库存'] },
{ title_1: '轮播图管理', title_2: ['轮播管理'] },
{ title_1: '智推管理', title_2: ['商品智推', '新闻智推', '系统公式智推'] },
{ title_1: '商品管理', title_2: ['羽毛球拍管理', '羽毛球管理', '服饰管理', '配件管理'] },
{ title_1: '订单管理', title_2: ['已支付订单', '待发货订单', '待收货订单', '已完成订单'] },
{ title_1: '个人中心', title_2: ['个人信息', '修改密码'] },
]
//菜单列表渲染
function render() {
titleName.forEach((item, index) => {
let menuTitle_2 = ''
titleName[index].title_2.forEach((item) => {
menuTitle_2 += `<dd class="yiguan-nav-item">${item}</dd>`
})
menuList.innerHTML += `<li class="list_item">
<div>${item.title_1}</div>
<dl>${menuTitle_2}</dl>
</li>`
})
}
render()
const menu = document.querySelector('.menu')
const lineIndicatorBar = document.querySelector('.lineIndicator_bar')
const list_item = document.querySelectorAll('.list_item')
const menuTitle = document.querySelectorAll('.list_item div')
const header = document.querySelector('.header')
const navTab = document.querySelector('.nav_list')
let oldIndex = -1
let navlist = ['']
let selected = ''
//标签渲染函数
function navListRender() {
navTab.innerHTML = ''
navlist.forEach((item, index) => {
if (index > 0) {
navTab.innerHTML += `<li class="pages"><span>${item}</span>
<span id="page_close"><svg t="1715665903815" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4531" width="16" height="16"><path d="M744.106667 768a42.666667 42.666667 0 0 1-29.866667-12.373333L249.6 290.986667a42.666667 42.666667 0 0 1 0-60.586667 42.666667 42.666667 0 0 1 60.16 0l464.64 464.64a42.666667 42.666667 0 0 1-30.293333 72.96z" fill="#cdcdcd" p-id="4532"></path><path d="M279.893333 768a42.666667 42.666667 0 0 1-30.293333-72.96L714.24 230.4a42.666667 42.666667 0 0 1 60.16 0 42.666667 42.666667 0 0 1 0 60.586667L309.76 755.626667a42.666667 42.666667 0 0 1-29.866667 12.373333z" fill="#cdcdcd" p-id="4533"></path></svg></span>
</li>`
}
})
}
//标签栏移动函数
function navMove() {
const tabPages = document.querySelectorAll('.pages')
let navWidth = 0
//获取导航栏内已有页签
tabPages.forEach((item) => {
//已有页签总长度
navWidth += item.clientWidth
})
//导航栏最大可移动数值,【已有页签总长度-(头部宽度-(按钮遮挡宽度+已有页签数))】
const maxMove = navWidth - (header.clientWidth - (192 + tabPages.length))
//只有当最大可移动数值大于导航栏最大显示宽度时,可移动
navWidth > header.clientWidth - 192 ? navTab.style.transform = `translateX(-${maxMove}px)` : ''
}
//鼠标移出菜单栏,指示条消失
menu.addEventListener('mouseleave', () => {
setTimeout(() => {
lineIndicatorBar.style.width = 0
}, 300)
})
//菜单栏功能实现模块
for (let i = 0; i < list_item.length; i++) {
//滑动指示条
list_item[i].onmouseover = () => {
lineIndicatorBar.style.width = '5px'
lineIndicatorBar.style.top = `${list_item[i].offsetTop}px`
}
//点击一级菜单,展示二级菜单
menuTitle[i].onclick = () => {
document.querySelector('.dl_display') ? document.querySelector('.dl_display').classList.remove('dl_display') : ''
list_item[i].lastElementChild.classList.add('dl_display')
//一级菜单反复点击处理
if (oldIndex == i) {
document.querySelector('.dl_display').classList.remove('dl_display')
oldIndex = -1
} else {
oldIndex = i
}
}
//二级菜单点击相关操作
list_item[i].onclick = (e) => {
if (e.target.tagName === 'DD') {
selected = e.target.textContent
document.querySelector('.dd_bg') ? document.querySelector('.dd_bg').classList.remove('dd_bg') : ''
e.target.classList.add('dd_bg')
//查询数组,如果不存在相同值则追加到数组中
const addId = navlist.indexOf(e.target.textContent)
if (addId == -1) {
navlist.push(e.target.textContent)
//遍历变化后的数组,渲染导航栏标签
navListRender()
navMove()
document.querySelectorAll('.pages')[navlist.length - 1 - 1].classList.add('nav_select')
} else {
const tabPages = document.querySelectorAll('.pages')
document.querySelector('.nav_select') ? document.querySelector('.nav_select').classList.remove('nav_select') : ''
tabPages[addId - 1].classList.add('nav_select')
tabPages[addId - 1].offsetLeft + tabPages[addId - 1].clientWidth > header.clientWidth - 200 ? navMove() : navTab.style.transform = `translateX(0)`
}
}
}
}
document.querySelector('.header').onmouseover = () => {
let delWidth = 0
maxMove = 0
//页签点击关闭
document.querySelectorAll('#page_close').forEach((item, index) => {
item.onclick = (e) => {
document.querySelector('.dd_bg') ? document.querySelector('.dd_bg').classList.remove('dd_bg') : ''
const bul = item.parentNode.offsetLeft + item.parentNode.clientWidth > header.clientWidth - 192
navlist.splice(index + 1, 1)
navListRender()
navlist.indexOf(selected) == -1 ? '' : document.querySelectorAll('.pages')[navlist.indexOf(selected) - 1].classList.add('nav_select')
bul == true ? navMove() : navTab.style.transform = `translateX(0)`
e.stopPropagation();
}
})
//获取导航栏内已有页签
document.querySelectorAll('.pages').forEach((item) => {
item.onclick = (e) => {
selected = e.target.textContent
document.querySelector('.dd_bg') ? document.querySelector('.dd_bg').classList.remove('dd_bg') : ''
document.querySelector('.nav_select') ? document.querySelector('.nav_select').classList.remove('nav_select') : ''
item.classList.add('nav_select')
}
})
document.querySelector('.slider_right').onclick = () => {
navMove()
}
document.querySelector('.slider_left').onclick = () => {
navTab.style.transform = `translateX(0)`
}
}
document.querySelector('.nav_close').onmouseover = () => {
document.querySelector('.close_tab').style.display = 'block'
}
document.querySelector('.close_tab').onmouseleave = () => {
setTimeout(() => {
document.querySelector('.close_tab').style.display = 'none'
}, 300)
}
document.querySelectorAll('.close_tab li').forEach((item, index) => {
item.onclick = () => {
//标签页操作只需要对navlist标签数组数据进行操作,后再调用渲染函数重新渲染
switch (index) {
//关闭当前标签页
case 0:
if (document.querySelector('.nav_select')) {
//获取选中的标签内文本
const tabText = document.querySelector('.nav_select').innerText
//从数组中删除选中的选中的标签内文本
navlist.splice(navlist.indexOf(tabText), 1)
navListRender()
navMove()
}
break
//关闭其他标签页
case 1:
if (document.querySelector('.nav_select')) {
const tabText = document.querySelector('.nav_select').innerText
const temp = navlist.splice(navlist.indexOf(tabText), 1)[0]
selected = temp
navlist = ['']
navlist.push(temp)
navListRender()
//重新渲染后,标签样式选中样式清楚,重新添加选中样式
navlist.indexOf(selected) == -1 ? '' : document.querySelectorAll('.pages')[navlist.indexOf(selected) - 1].classList.add('nav_select')
navTab.style.transform = `translateX(0)`
}
break
//关闭所有标签页
case 2:
navlist = ['']
navListRender()
break
}
}
})
总结
本案例仅供学习参考,可根据自己想法思路优化代码实现。