后台UI框架开发(一)

后台UI框架开发(一)

众所周知,现在的后台管理系统的前端页面基本上都是一个样子……

在这里插入图片描述

那既然,每个后台管理页面的样子都是这样的,那我们能不能设计一个页面,专门写成这个样子,只需要以面向对象的方式去使用某些方法来修改标题、快捷菜单、功能菜单、导航菜单……

整体设计

我们直接来套用一下Vue的目录结构:

  • src
    • api
      • index.js
    • router
      • index.js
  • static
  • pages
    • styles
    • scripts
    • simple1.html
    • simple2.html
    • child1.html
    • child2.html
    • child3.html
    • child4.html
    • child5.html
    • child6.html
  • index.html
  • main.js

然后我们看一下每一个目录/文件都是干什么的。

目录:src/api/index.js

const host = "http://example.com";
const request = {
	doGet: (url, data, func) => {
		let ajax = new XMLHttpRequest();
		let params = "";
		for (let param in data) {
			params = params + param + "=" + data[param] + "&";
		}
		params = params.substr(0, params.length - 1);
		ajax.open('GET', host + url + "?" + params);
		ajax.send();
		ajax.onreadystatechange = function() {
			if (ajax.readyState == 4 && ajax.status == 200) {
				let json = JSON.parse(ajax.responseText);
				func(json);
			}
		}
	},
	doPost: (url, data, func) => {
		let ajax = new XMLHttpRequest();
		ajax.open("POST", host + url, true);
		ajax.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
		let params = "";
		for (let param in data) {
			params = params + param + "=" + data[param] + "&";
		}
		ajax.send(params);
		ajax.onreadystatechange = function() {
			if (ajax.readyState == 4 && ajax.status == 200) {
				let json = JSON.parse(ajax.responseText);
				func(json);
			}
		}
	}
};
const userApi = {
	userReg: (params, func) => request.doPost("/userApi/userReg", params, func),
	// 用户注册
	userLogin: (params, func) => request.doPost("/userApi/userLogin", params, func),
	// 用户登录
};

目录:src/router/index.js

const routes = [
	{
		name:"下拉标题1",
		children:[
			{
				to:"/child1",
				name:"子级菜单1",
				path:"/child1.html"
			},
			{
				to:"/child2",
				name:"子级菜单2",
				path:"/child2.html"
			},
			{
				to:"/child3",
				name:"子级菜单3",
				path:"/child3.html"
			},
		]
	},
	{
		name:"下拉标题2",
		children:[
			{
				to:"/child4",
				name:"子级菜单4",
				path:"/child4.html"
			},
			{
				to:"/child5",
				name:"子级菜单5",
				path:"/child5.html"
			},
			{
				to:"/child6",
				name:"子级菜单6",
				path:"/child6.html"
			},
		]
	},
	{
		to:"/simple1",
		name:"简单导航1",
		path:"/simple1.html"
	},
	{
		to:"/simple2",
		name:"简单导航2",
		path:"/simple2.html"
	},
]

然后我们编写一下,这个后台页面

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8" />
		<title></title>
		<link rel="stylesheet" type="text/css" href="css/main.css"/>
	</head>
	<body>
		<div class="box">
			<div class="header-box">
				<div class="logo-box">
					<div class="logo">logo站位</div>
				</div>
				<div class="tool-box">
					<div class="control-box"></div>
					<div class="info-box"></div>
				</div>
			</div>
			<div class="content-box">
				<div class="nav-box"></div>
				<div class="main-box">
					<object class="frame" id="obj" data="" type="text/html"></object>
				</div>
			</div>
		</div>
	</body>
	<script src="src/router/index.js" type="text/javascript" charset="utf-8"></script>
	<script src="main.js" type="text/javascript" charset="utf-8"></script>
</html>

现在介绍一下各个class对应的元素的意义:

  • box:包围盒

    • header-box:头部包围盒

      • logo-box:Logo包围盒
      • logo:Logo站位
      • tool-box:快捷导航包围盒
        • control-box:控制器包围盒
        • info-box:信息包围盒
    • content-box:主体包围盒

      • nav-box:导航菜单包围盒
      • main-box:主体内容包围盒
        • frame:类似于“iframe”一样的产物,进行页面内部跳转

然后我们需要构思,如何能够直接通过router/index.js中的数据来直接生成导航菜单呢

js献上

// 生成导航菜单
const navBox = document.querySelector(".nav-box");
// 获取导航菜单包围盒
routes.forEach(item => {
	if (!item.children) {
		// 简单导航
		let simple = document.createElement("div");
		simple.classList.add("simple-nav");
		simple.innerHTML = "<i class='icon'></i><span>" + item.name + "</span>";
		simple.dataset.to = item.to;
		navBox.appendChild(simple);
	} else {
		// 下拉菜单
		let multip = document.createElement("dl");
		multip.classList.add("nav-list");
		let dt = document.createElement("dt");
		dt.innerHTML = "<i class='icon'></i><span>" + item.name + "</span>";
		navBox.appendChild(multip);
		multip.appendChild(dt);
		item.children.forEach(child => {
			let dd = document.createElement("dd");
			dd.classList.add("multip");
			dd.innerHTML = "<span>" + child.name + "</span>";
			dd.dataset.to = child.to;
			multip.appendChild(dd);
		});
	}
});
// 这个操作,应该所有人都看得懂吧,我就不多于陈述了。

// 菜单添加效果
const navLists = document.querySelectorAll(".nav-list>dt");
// 我们首先要把所有的下拉菜单取出
const clearElementsClass = (doms, className) => {
	doms.forEach(item => {
		item.classList.remove(className);
	});
};
// 定义一个方法,可以将"NodeList"中的所有元素的某一个className删除
const clearElementParentsClass = (doms, className) => {
	doms.forEach(item => {
		item.parentNode.classList.remove(className);
	});
};
// 定义一个方法,可以将"NodeList"中的所有元素的父级元素的某一个className删除
navLists.forEach(item => {
	item.addEventListener("click", e => {
		clearElementParentsClass(navLists, "active");
		item.parentNode.classList.add("active");
	});
});
// 现在来遍历所有的下拉菜单,清除所有下拉菜单中的"active"类,并为当前鼠标单击行添加"active"类,这里是实现效果
let martJokes = document.querySelectorAll(".multip");
// 获取所有的下拉菜单项
let coverJokes = document.querySelectorAll(".simple-nav");
// 获取所有的简单导航项
const clearJokesClass = () => {
	martJokes.forEach(item => {
		item.classList.remove("ajok");
	});
	coverJokes.forEach(item => {
		item.classList.remove("ajok");
	});
};
// 定义一个方法,可以清除掉下拉菜单项和简单导航项的"ajok"类
martJokes.forEach(item => {
	item.addEventListener("click", e => {
		clearJokesClass();
		item.classList.add("ajok");
	});
});
coverJokes.forEach(item => {
	item.addEventListener("click", e => {
		clearJokesClass();
		item.classList.add("ajok");
	});
});
// 和上面清除"active"类效果一样,清除"ajok"类,并为当前鼠标单击项添加"ajok"

至此,我们想要实现的效果就已经完成了。

那么接下来,我们就要考虑,如何在鼠标点击导航项之后,让“#obj”的内联框架进行跳转了。

js献上

const links = document.querySelectorAll("[data-to]");
// 现将上面操作中所有含有dataset.to的项取出。
const frame = document.querySelector("#obj");
// 再将内联框架取出。
const foundPath = itemTo => {
	let path = "";
	routes.forEach(item => {
		if (!item.children) {
			if (item.to == itemTo) {
				path = "pages" + item.path;
			}
		} else {
			item.children.forEach(child => {
				if (child.to == itemTo) {
					path = "pages" + child.path;
				}
			});
		}
	});
	return path;
};
// 定义方法,检索每个"to"所对应的"path"
links.forEach(item => {
	item.addEventListener("click", e => {
		frame.data = foundPath(item.dataset.to);
	});
});
// 为每个导航菜单项添加一个单击事件监听,通过标签上的"data-to"找到对应的"path",并让内联框架进行跳转。

这样一来,基本的模型就设计完成了。

但是,我们文章开头时说过,要进行面向对象的操作,也就是XXX.setXXX()和XXX.getXXX()来进行操作,但是现在并未拥有这个能力,这该怎么搞呢?

现在,我们拥有三种方法:

  • 类似于JQuery一样,去修改某些模型的“prototype”原型及原型链
  • 定义一个class直接在class中定义方法进行操作
  • 定义一个对象,在对象中定义方法进行操作,其实这个和第二个一样,但是方便呀,所以,我们暂且称之为第三种方法,并且,我们就选择这种方法了。

既然如此,我们要将上面的js合并到一个对象中去了,js献上

const martApp = {
	// init nav-list.
	startApp:()=>{
		const navLists = document.querySelectorAll(".nav-list>dt");
		const clearElementsClass = (doms,className) =>{
			doms.forEach(item=>{
				item.classList.remove(className);
			});
		};
		const clearElementParentsClass = (doms,className) =>{
			doms.forEach(item=>{
				item.parentNode.classList.remove(className);
			});
		};
		navLists.forEach(item=>{
			item.addEventListener("click",e=>{
				clearElementParentsClass(navLists,"active");
				item.parentNode.classList.add("active");
			});
		});
		let martJokes = document.querySelectorAll(".multip");
		let coverJokes = document.querySelectorAll(".simple-nav");
		const clearJokesClass = () => {
			martJokes.forEach(item=>{
				item.classList.remove("ajok");
			});
			coverJokes.forEach(item=>{
				item.classList.remove("ajok");
			});
		};
		martJokes.forEach(item=>{
			item.addEventListener("click",e=>{
				clearJokesClass();
				item.classList.add("ajok");
			});
		});
		coverJokes.forEach(item=>{
			item.addEventListener("click",e=>{
				clearJokesClass();
				item.classList.add("ajok");
			});
		});
	},
	initNavList:()=>{
		const navBox = document.querySelector(".nav-box");
		routes.forEach(item=>{
			if(!item.children){
				// 简单导航
				let simple = document.createElement("div");
				simple.classList.add("simple-nav");
				simple.innerHTML="<i class='icon'></i><span>" + item.name + "</span>";
				simple.dataset.to = item.to;
				navBox.appendChild(simple);
			}else{
				// 下拉菜单
				let multip = document.createElement("dl");
				multip.classList.add("nav-list");
				let dt = document.createElement("dt");
				dt.innerHTML = "<i class='icon'></i><span>" + item.name + "</span>";
				navBox.appendChild(multip);
				multip.appendChild(dt);
				item.children.forEach(child=>{
					let dd = document.createElement("dd");
					dd.classList.add("multip");
					dd.innerHTML = "<span>" + child.name + "</span>";
					dd.dataset.to = child.to;
					multip.appendChild(dd);
				});
			}
		});
	},
	style:{
		setLogoBgColor:color=>{
			document.querySelector(".logo-box").style["backgroundColor"] = color;
		}
	},
	initRoutes:()=>{
		const links = document.querySelectorAll("[data-to]");
		const frame = document.querySelector("#obj");
		const foundPath = itemTo => {
			let path = "";
			routes.forEach(item=>{
				if(!item.children){
					if(item.to==itemTo){
						path = "pages" + item.path;
					}
				}else{
					item.children.forEach(child=>{
						if(child.to==itemTo){
							path = "pages" + child.path;
						}
					});
				}
			});
			return path;
		};
		links.forEach(item=>{
			item.addEventListener("click",e=>{
				frame.data = foundPath(item.dataset.to);
			});
		});
	}
}

我这个写的有点乱,但是,我们可以清晰的看到,如果想要调整某部分的样式,例如:Logo的背景颜色,我们只需要在script标签中这样写:

martApp.style.setLogoBgColor("#012345");

因为我们这个是初步的模型,所以,我们没有编写那么多的方法,以后会完善的,现在我们看一下初步的执行顺序:

martApp.initNavList();
martApp.initRoutes()
martApp.startApp();

这样,我们的页面就跑起来了,但是,好像忘记一个重点,没有样式呀!

css献上,因为东西都简单,所以就不适用预处理css了,大伙将就着看。

*{
	margin: 0;
	padding: 0;
	box-sizing: border-box;
	-webkit-user-select: none;
	-moz-user-select: none;
	-ms-user-select: none;
	user-select: none;
	font-family: "comic sans ms","microsoft yahei";
}
body{
	width: 100vw;
	height: 100vh;
	background-color: #F0F0F0;
}
.box{
	width: 100%;
	height: 100%;
}
.header-box{
	width: 100%;
	height: 60px;
	display: flex;
	box-shadow: 0 5px 10px rgba(0,0,0,.5);
	background-color: #FFFFFF;
}
.logo-box{
	width: 200px;
	height: 100%;
	background-color: #002548;
	padding: 16px 28px;
}
.logo{
	width: 100%;
	height: 100%;
	background-color: rgba(255,255,255,.2);
	display: flex;
	align-items: center;
	justify-content: center;
	color: #FFFFFF;
}
.tool-box{
	width: calc(100% - 200px);
	height: 100%;
	display: flex;
	align-items: center;
	justify-content: space-between;
	padding: 0 24px;
}
.control-box{
	height: 48px;
}
.info-box{
	height: 48px;
}
.content-box{
	width: 100%;
	height: calc(100% - 60px);
	display: flex;
}
.nav-box{
	width: 200px;
	height: 100%;
	background-color: #002548;
}
dl.nav-list{
	width: 100%;
	height: auto;
}
dl.nav-list>dt{
	width: 100%;
	height: 40px;
	color: rgba(255,255,255,.65);
	font-size: 14px;
	display: flex;
	align-items: center;
	padding: 0 34px 0 24px;
	transition-duration: .3s;
	cursor: pointer;
}
dl.nav-list>dt>i.icon{
	display: flex;
	align-items: center;
	justify-content: center;
	width: 14px;
	height: 14px;
	font-size: 14px;
	margin-right: 10px;
}
dl.nav-list>dt:hover{
	color: #FFFFFF;
}
dl.nav-list.active>dt{
	color: #FFFFFF;
}
dl.nav-list>dd{
	width: 100%;
	height: 0;
	overflow: hidden;
	font-size: 14px;
	color: rgba(255,255,255,.65);
	display: flex;
	align-items: center;
	padding: 0 16px 0 48px;
	background-color: #000000;
	transition-duration: .3s;
	cursor: pointer;
}
dl.nav-list.active>dd{
	height: 40px;
}
dl.nav-list.active>dd:hover{
	color: #FFFFFF;
}
dl.nav-list.active>dd.ajok{
	background-color: #1890ff;
	color: #FFFFFF;
}
.simple-nav{
	width: 100%;
	height: 40px;
	font-size: 14px;
	display: flex;
	align-items: center;
	padding: 0 34px 0 24px;
	transition-duration: .3s;
	cursor: pointer;
	color: rgba(255,255,255,.65);
}
.simple-nav>i.icon{
	display: flex;
	align-items: center;
	justify-content: center;
	width: 14px;
	height: 14px;
	font-size: 14px;
	margin-right: 10px;
}
.simple-nav:hover{
	color: #FFFFFF;
}
.simple-nav.ajok{
	background-color: #1890ff;
	color: #FFFFFF;
}
.main-box{
	width: calc(100% - 200px);
	height: 100%;
	padding: 12px 24px;
}
.frame{
	width: 100%;
	height: 100%;
	background-color: #FFFFFF;
	box-shadow: 0 5px 10px rgba(0,0,0,.5);
}

效果图奉上:

在这里插入图片描述

本篇文章就先到这里,如果有什么建议,请在评论区留言,大神们勿喷,这是自己想法的一个实践。

接下来会去封装组件,配合后台去写傻瓜式组件!

感谢您的阅读,下期见!!!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值