目录
- 一、介绍
- 二、准备
- 三、目标
- 四、代码
- 五、完成
一、介绍
在商城管理系统中,超级管理员和普通管理员因为权限不同,登录进入后看到的菜单也会是不同的。
本题需要你完成商城管理系统中权限数据的处理。
二、准备
开始答题前,需要先打开本题的项目代码文件夹,目录结构如下:
├── css
├── images
├── js
│ ├── auth.js
│ └── menu.js
├── effect.gif
├── login.html
└── index.html
其中:
css 是样式文件夹。
images 是图片文件夹。
login.html 是登录页。
index.html 是商城首页。
js/menu.js 是渲染左侧菜单的 js 文件。
js/auth.js 是需要补充代码的 js 文件。
effect.gif 是最终完成的效果图。
在浏览器中预览 login.html 页面效果如下
三、目标
请在 js/auth.js 文件中补全 getMenuListAndAuth 函数代码。
在登录页 login.html 点击管理员登录和超级管理员登录时会根据管理员的权限不同,在商城首页 index.html 的左侧显示不同的菜单列表。但是后端同学给的数据是不符合前端展示要求的,所以我们需要做些处理,假如后端提供的数据是这样的:
[
{ parentId: -1, name: "商品管理", id: 1, auth: "cart" },
{ parentId: 1, name: "商品列表", id: 4, auth: "cart-list" },
{ parentId: -1, name: "添加管理员", id: 10, auth: "admin" },
];
其中数据中对象字段的含义说明:
- id 表示当前节点
- parentId 表示父级节点,如果为 -1 则表示顶级数据
- auth 表示权限
具体需求如下:
- 将待处理数据(一维数组)根据 parentId 字段值处理成树形结构存入 menus 变量中。
把数据处理成如下格式(每一项都必须有 children 字段,没有子级时,children 为空数组):
[
{
parentId: -1,
name: "商品管理",
id: 1,
auth: "cart",
children: [
{
parentId: 1,
name: "商品列表",
id: 4,
auth: "cart-list",
children: [],
},
],
},
{
parentId: -1,
name: "添加管理员",
id: 10,
auth: "admin",
children: [],
},
];
注意:js/auth.js 中的 menuList 仅为后端返回的数据结构示例,非固定数据。实际使用中,后端返回的数据转化成树形结构时层级可能更多,封装方法时务必考虑通用性。
- 将待处理数据中的 auth 字段提取出来并存入 auths 变量中,左侧菜单会根据传入的权限列表数组和 auths 对比后进行菜单渲染。
经过 getMenuListAndAuth 处理后 auths 的结果为:
["cart", "cart-list", "admin"];
完成后的效果见文件夹下面的 gif 图,图片名称为 effect.gif(提示:可以通过 VS Code 或者浏览器预览 gif 图片)。
四、代码
login.html
<!DOCTYPE html>
<html>
<head>
<title>商城管理系统</title>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
<link href="css/login.css" rel="stylesheet" type="text/css">
<body>
<div class="main">
<div class="container">
<h2>立即登录</h2>
<input type="text" value="admin" />
<input value="PASSWORD" name="Password" type="password" />
<span><input type="checkbox" checked />记住我</span>
<h6><a href="#">忘记密码?</a></h6>
<div class="clear">
<input type="submit" onclick="adminLogin()" value="管理员登录">
<input type="submit" onclick="superLogin()" value="超级管理员登录">
</div>
<p>还没有账号?<a href="#">立即注册</a></p>
</div>
</div>
<footer>
<p>© 2022 蓝桥杯 | 商城管理系统 <a>bootstrap搭建</a></p>
</footer>
<script>
let adminLogin = () => {
window.location.href = "./index.html?name=admin";
}
let superLogin = () => {
window.location.href = "./index.html?name=super";
}
</script>
</body>
</html>
index.html
<!DOCTYPE html>
<html>
<head>
<title>商城管理系统</title>
<meta charset="UTF-8" />
<link href="css/bootstrap.min.css" rel="stylesheet" type="text/css" />
<link href="css/style.css" rel="stylesheet" type="text/css" />
</head>
<body class="skin-black">
<header class="header">
<a href="index.html" class="logo"> 商城管理系统 </a>
<nav class="navbar navbar-static-top">
<div class="navbar-right">
<ul class="nav navbar-nav">
<li class="dropdown">
<a href="./login.html" class="dropdown-toggle">
<span>退出</span>
</a>
</li>
</ul>
</div>
</nav>
</header>
<div class="wrapper row-offcanvas row-offcanvas-left">
<div class="left-side sidebar-offcanvas">
<section class="sidebar">
<div class="user-panel">
<div class="pull-left image">
<img src="images/admin.jpg" class="img-circle" alt="User Image" />
</div>
<div class="pull-left info">
<p id="admin">你好,管理员</p>
<a href="#">
<i class="text-success">在线</i>
</a>
</div>
</div>
<!-- 左侧菜单栏 -->
<ul id="sidebarmenu" class="text-success"></ul>
</section>
</div>
<div class="right-side">
<section class="content">
<div class="row" style="margin-bottom: 5px">
<div class="col-md-3">
<div class="sm-st clearfix">
<span class="sm-block-icon block-red">1</span>
<div class="sm-block-info">
<span>3200</span>
商城访问量
</div>
</div>
</div>
<div class="col-md-3">
<div class="sm-st clearfix">
<span class="sm-block-icon block-violet">2</span>
<div class="sm-block-info">
<span>2200</span>
售出商品数
</div>
</div>
</div>
<div class="col-md-3">
<div class="sm-st clearfix">
<span class="sm-block-icon block-blue">3</span>
<div class="sm-block-info">
<span>100,320</span>
总销售额
</div>
</div>
</div>
<div class="col-md-3">
<div class="sm-st clearfix">
<span class="sm-block-icon block-green">4</span>
<div class="sm-block-info">
<span>4567</span>
总利润
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-12">
<section class="panel">
<header class="panel-heading">热销商品</header>
<div class="panel-body table-responsive">
<table class="table table-hover">
<thead>
<tr>
<th>#</th>
<th>商品名称</th>
<th>库存</th>
<th>上架时间</th>
<th>状态</th>
<th>售出量</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>macbook pro 2021</td>
<td>1000</td>
<td>1/5/2022</td>
<td><span class="label label-info">余量充足</span></td>
<td><span class="badge badge-info">50%</span></td>
</tr>
<tr>
<td>2</td>
<td>macbook pro 2019</td>
<td>3000</td>
<td>06/08/2021</td>
<td><span class="label label-success">已售罄</span></td>
<td><span class="badge badge-success">100%</span></td>
</tr>
<tr>
<td>3</td>
<td>iphone 12</td>
<td>520</td>
<td>10/12/2019</td>
<td><span class="label label-info">余量充足</span></td>
<td><span class="badge badge-info">75%</span></td>
</tr>
<tr>
<td>4</td>
<td>Xiaomi 12 Pro</td>
<td>400</td>
<td>03/22/2022</td>
<td><span class="label label-info">余量充足</span></td>
<td><span class="badge badge-info">65%</span></td>
</tr>
<tr>
<td>5</td>
<td>Hua Wei Meta 40</td>
<td>730</td>
<td>03/11/2022</td>
<td>
<span class="label label-warning">即将售罄</span>
</td>
<td><span class="badge badge-danger">95%</span></td>
</tr>
<tr>
<td>6</td>
<td>Hua Wei Meta V30</td>
<td>300</td>
<td>05/11/2019</td>
<td>
<span class="label label-warning">即将售罄</span>
</td>
<td><span class="badge badge-danger">95%</span></td>
</tr>
</tbody>
</table>
</div>
</section>
</div>
</div>
</section>
</div>
</div>
<script src="./js/auth.js "></script>
<script src="./js/menu.js "></script>
</body>
</html>
js/auth.js
// menuList 仅为示例数据,非实际使用数据,实际使用数据层级不确定(可能是四级五级六级等),数据结构与 menuList 一致
// 1. `parentId` 如果为 `-1`,则表示此条数据为顶级数据。
// 2. `parentId` 为该条数据的父级数据的 `id`。
let menuList = [
{ parentId: -1, name: "添加管理员", id: 10, auth: "admin" },
{ parentId: 10, name: "管理员权限分配", id: 11, auth: "admin-auth" },
{ parentId: -1, name: "商品管理", id: 1, auth: "product" },
{ parentId: 1, name: "商品列表", id: 4, auth: "productList" },
{ parentId: 4, name: "商品分类", id: 5, auth: "category" },
{ parentId: 5, name: "添加分类", id: 8, auth: "addClassification" },
{ parentId: 4, name: "商品上架", id: 6, auth: "product" },
{ parentId: -1, name: "评论管理", id: 2, auth: "comments" },
{ parentId: -1, name: "个人中心", id: 3, auth: "profile" },
];
/**
* @param {*} menuList 传入的数据
* @return {*} menus 转化后的树形结构数据,auths 转化后的权限列表数组
*/
const getMenuListAndAuth = (menuList) => {
// TODO:待补充代码
return { menus, auths }; // menus 转化后的树形结构数据,auths 转化后的权限列表数组
};
// 请勿删除和修改以下代码
try {
module.exports = { getMenuListAndAuth };
} catch (e) {}
js/menu.js
let admin = document.getElementById("admin");
let authList;
let url = location.href;
// 获取最终要渲染的菜单列表
let res = getMenuListAndAuth(menuList);
function getUrlParam(key) {
let args = {};
const arr = url.match(/\w+=\w+/gi).map((kv) => kv.split("="));
for (let item of arr) {
args[item[0]] = item[1];
}
return args[key];
}
let role = getUrlParam("name");
if (role == "admin") {
// 管理员
authList = ["profile", "comments", "product", "productList", "category"];
admin.innerHTML = `你好,管理员`;
} else {
// 超级管理员
authList = [
"profile",
"comments",
"product",
"productList",
"category",
"addClassification",
"admin",
"product",
"admin-auth",
];
admin.innerHTML = `你好,超级管理员`;
}
// 全部的菜单列表
let sidelistall = res.menus;
let sidebarmenu = document.querySelector("#sidebarmenu");
/**
* @param {*} auth 传入的权限列表
* @return {*} 根据传入的权限列表返回需要显示的菜单列表
*/
const getNav = (auth) => {
const filter = (sidelistall) => {
return sidelistall.filter((item) => {
if (auth.includes(item.auth)) {
if (item.children) {
item.children = filter(item.children);
}
return true;
}
});
};
return filter(sidelistall);
};
renderSide();
// 根据权限渲染左侧菜单
function renderSide() {
let menuListside = getNav(authList);
const rendermenu = (children) => {
let childrenhtml = "";
childrenhtml += children
.map((child) => {
let childrenH2 = "";
if (child.children && !!child.children.length) {
childrenH2 = rendermenu(child.children);
}
return `<ul >
<li style="line-height: 28px;">${child.name}
${childrenH2 ? `<i> ${childrenH2}</i>` : ""}
</ul>`;
})
.join("");
return childrenhtml;
};
// 渲染菜单
menuListside.map((item, index) => {
let childrenhtml = "";
// 首先判断 item 有没有子级
if (item.children && !!item.children.length) {
childrenhtml = rendermenu(item.children);
}
sidebarmenu.innerHTML += `
<li class="active">
<a>
${item.name}
</a>
<i style="color:#27C24C">${
childrenhtml ? childrenhtml : ""
}</i>
</li>`;
});
}
css/login.css
body {
font-family: Verdana;
background: url("../images/bg.jpg") no-repeat 62% 34%;
background-size: cover;
text-align: center;
}
.container h2 {
color: #fff;
font-size: 29px;
letter-spacing: 2px;
text-transform: uppercase;
padding-bottom: 15px;
}
.container {
width: 25%;
margin: 0 auto;
background: rgba(0, 0, 0, 0.34);
padding: 42px 35px;
}
.main {
margin-top: 100px;
}
input[type="text"],
input[type="password"] {
width: 88%;
padding: 15px 0px 15px 42px;
border: 1px solid #fff;
outline: none;
font-size: 14px;
color: #fff;
margin: 14px 0px;
}
input[type="text"] {
background: url("../images/user.svg") no-repeat 10px 11px;
background-size: 24px;
}
input[type="password"] {
background: url("../images/key.svg") no-repeat 8px 9px;
background-size: 28px;
}
.container span {
font-size: 16px;
color: #fff;
float: left;
width: 32%;
margin-top: 8px;
}
.container h6 {
font-size: 16px;
float: right;
width: 37%;
color: #fff;
letter-spacing: 1px;
margin-top: 8px;
text-decoration: underline;
}
.container a {
color: #fff;
transition: 0.5s all;
-webkit-transition: 0.5s all;
-o-transition: 0.5s all;
-moz-transition: 0.5s all;
-ms-transition: 0.5s all;
}
.container a:hover {
color: rgba(208, 95, 216, 0.9);
transition: 0.5s all;
-webkit-transition: 0.5s all;
-o-transition: 0.5s all;
-moz-transition: 0.5s all;
-ms-transition: 0.5s all;
}
.clear {
clear: both;
}
input[type="submit"] {
padding: 12px 38px;
font-size: 1px;
text-transform: uppercase;
letter-spacing: 2px;
background: #003b64;
color: white;
border: none;
outline: none;
cursor: pointer;
margin: 45px auto 31px;
transition: 0.5s all;
-webkit-transition: 0.5s all;
-o-transition: 0.5s all;
-moz-transition: 0.5s all;
-ms-transition: 0.5s all;
}
input[type="submit"]:hover {
background: rgba(213, 38, 133, 0.64);
transition: 0.5s all;
-webkit-transition: 0.5s all;
-o-transition: 0.5s all;
-moz-transition: 0.5s all;
-ms-transition: 0.5s all;
}
.container p a {
text-transform: uppercase;
font-size: 18px;
text-decoration: underline;
letter-spacing: 1px;
color: #fff;
padding: 10px;
}
.container p {
font-size: 17px;
color: #fff;
}
footer p {
margin: 4em 0em;
color: #fff;
font-size: 15px;
font-weight: 300;
letter-spacing: 2px;
}
footer a {
color: rgba(208, 95, 216, 0.9);
}
footer a:hover {
color: rgba(19, 129, 206, 0.82);
text-decoration: underline;
}
@media (max-width: 1440px) {
.container {
width: 28%;
}
}
@media (max-width: 1366px) {
.container {
width: 30%;
}
}
@media (max-width: 1280px) {
.container {
width: 33%;
}
.agileheader h1 {
font-size: 41px;
}
.container h2 {
font-size: 27px;
}
}
@media (max-width: 1080px) {
.container {
width: 49%;
}
}
css/style.css
html,
body {
overflow-x: hidden !important;
-webkit-font-smoothing: antialiased;
min-height: 100%;
background: #f1f2f7;
}
a {
color: #3c8dbc;
}
.right-side,
.left-side {
min-height: 100%;
display: block;
}
.right-side {
background-color: #f1f2f7;
margin-left: 220px;
}
.left-side {
position: absolute;
width: 220px;
top: 0;
}
@media screen and (min-width: 992px) {
.left-side {
top: 50px;
}
.right-side.strech {
margin-left: 0;
}
.right-side.strech > .content-header {
margin-top: 0px;
}
.left-side.collapse-left {
left: -220px;
}
}
.content {
padding: 20px 15px;
background: #f0f3f4;
overflow: auto;
}
img {
max-width: 100% !important;
}
.badge-success {
background-color: #27c24c;
color: #dff5e4;
}
.badge-info {
background-color: #23b7e5;
color: #e3f6fc;
}
.badge-danger {
background-color: #f05050;
color: #fae6e6;
}
body > .header .navbar {
height: 50px;
margin-bottom: 0;
margin-left: 220px;
}
body > .header .logo {
float: left;
font-size: 20px;
line-height: 50px;
text-align: center;
padding: 0 10px;
width: 220px;
font-weight: 500;
height: 50px;
display: block;
}
.user-panel {
padding: 10px;
}
.user-panel:before,
.user-panel:after {
display: table;
content: " ";
}
.user-panel:after {
clear: both;
}
.user-panel > .image > img {
width: 45px;
height: 45px;
}
@media screen and (max-width: 992px) {
.relative {
position: relative;
}
body.fixed .sidebar-offcanvas {
margin-top: 50px;
left: -220px;
}
body.fixed .row-offcanvas-left.active .navbar {
left: 220px !important;
right: 0;
}
body.fixed .row-offcanvas-left.active .sidebar-offcanvas {
left: 0px;
}
}
.skin-black .navbar {
background-color: #ffffff;
border-bottom: 1px solid #eee;
}
.skin-black .navbar .nav a {
color: #333333;
}
.skin-black .logo {
background-color: #283744;
color: #f9f9f9;
}
.skin-black .logo > a {
color: #f9f9f9;
}
.skin-black .right-side > .content-header {
background: #fff;
box-shadow: none;
}
.skin-black .user-panel > .image > img {
border: 1px solid #444;
}
.skin-black .user-panel > .info,
.skin-black .user-panel > .info > a {
color: #eee;
}
.skin-black .sidebar {
border-bottom: 1px solid #39435c;
}
.skin-black .sidebar > .sidebar-menu > li > .treeview-menu {
margin: 0 1px;
background: #35404d;
}
.skin-black .left-side {
background: #39435c;
padding-top: 20px;
}
.skin-black .sidebar a {
color: #eee;
}
.sm-st {
background: #fff;
padding: 20px;
-webkit-border-radius: 3px;
-moz-border-radius: 3px;
border-radius: 3px;
margin-bottom: 20px;
-webkit-box-shadow: 0 1px 0px rgba(0, 0, 0, 0.05);
box-shadow: 0 1px 0px rgba(0, 0, 0, 0.05);
}
.sm-block-icon {
width: 60px;
height: 60px;
line-height: 60px;
text-align: center;
font-size: 30px;
background: #eee;
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
border-radius: 5px;
float: left;
margin-right: 10px;
color: #fff;
}
.sm-block-info {
font-size: 12px;
padding-top: 2px;
}
.sm-block-info span {
display: block;
font-size: 24px;
font-weight: 600;
}
.stat-elem {
background-color: #fff;
padding: 18px;
border-radius: 40px;
}
.stat-info {
text-align: center;
background-color: #fff;
border-radius: 5px;
margin-top: -5px;
padding: 8px;
-webkit-box-shadow: 0 1px 0px rgba(0, 0, 0, 0.05);
box-shadow: 0 1px 0px rgba(0, 0, 0, 0.05);
font-style: italic;
}
.stat-icon {
text-align: center;
margin-bottom: 5px;
}
.block-red {
background-color: #f05050;
}
.block-green {
background-color: #27c24c;
}
.block-violet {
background-color: #7266ba;
}
.block-blue {
background-color: #23b7e5;
}
#sidebarmenu {
margin-top: 20px;
line-height: 20px;
}
#sidebarmenu li {
color: #fff;
line-height: 40px;
}
#sidebarmenu ul {
padding: 0 20px;
}
.pull-left.info {
margin-left: 20px;
}
五、完成
js/auth.js
/**
* @param {*} menuList 传入的数据
* @return {*} menus 转化后的树形结构数据,auths 转化后的权限列表数组
*/
const getMenuListAndAuth = (menuList) => {
// TODO:待补充代码
const auths = dealUserAuths(menuList)
const menus = dealUserMenu(menuList)
return { menus, auths }; // menus 转化后的树形结构数据,auths 转化后的权限列表数组
};
function dealUserMenu(users,rootId=-1){
let newArr=[]
users.forEach(item=>{
if(rootId==item.parentId){
newArr.push(item)
item.children=dealUserMenu(users,item.id)
}
})
return newArr
}
function dealUserAuths(users){
let newArr=[]
users.forEach(item=>{
newArr.push(item.auth)
})
return newArr
}