前言

现在的大前端技术来势凶猛,Vue&React&Angular三足鼎立。如果为了开发一个内部使用的管理系统需要去学习Node&Webpack等各种新概念,况且我们的系统并没有那么复杂到需要用上现在这些新技术。

我要的仅仅是一个单页应用,兼容IE8 ,能基于固定模式添加功能,基于Bootstrap和各种jQuery插件进行开发即可。我开始寻找后端管理系统模板,最后将目光锁定在adminlte。其实现效果如下:

adminlite-plus​

单页应用

在没有接触Angular.js之前我都是通过jQuery.load来进行页面的分片加载,这种模式下需要手动去管理和切换视图。既然摈弃了前端框架的路由视图切换来实现单页,我决定使用knockout.js和require.js来实现。

关于knockout(后边一律简称ko),可能大家都不太熟悉。这个库应该是数据绑定的早期实践者。选择他的原因是支持IE6 ,毕竟我所处的政务行业至少需要支持到IE8。ko提供了数据到视图的双向绑定,但他有个缺陷就是model数据无法嵌套。其默认只有一个全局的作用域,如果需要对DOM进行分区域进行数据绑定则需要用到knockout-multimodels插件。如果你不了解这些技术可以看看其文档即可快速掌握。然后是require,主要是用来对JS分模块进行管理和加载。

接下来我们需要一个简单的路由功能,切换导航菜单页面不需要手动进行DOM的替换。这个功能我们选择使用director.js来实现。其原理主要是监听浏览器hash,当切换菜单触发浏览器地址栏发生变化,调用相应模块的加载。

动手实现

从用户点击一个导航菜单到页面加载,数据请求和视图渲染的过程如下:

对于一个后台管理系统其视图结构就是一个主页index.html,然后按照tab来进行视图的切换。在上面加载html环节就是替换当前tab的内容,html模板一般会有浏览器缓存只会请求一次。

实现路由

1,我们首选需要顶一个路由数据,其实也就是导航菜单的数据:

js/framework/routes.js

/*** 定义系统路由信息*/
define(['jquery', 'common','index'], function ($, common,index) {var routes = {404: {title: '系统出错了'}};var sysNavMenusObj = index.sysNavMenus;function getResource(parentid) {for (var i = 0; i < sysNavMenusObj.length; i  ) {var obj = sysNavMenusObj[i];if (obj.resourceid == parentid) {return obj;}}}if (sysNavMenusObj && sysNavMenusObj.length > 0) {for (var i = 0; i < sysNavMenusObj.length; i  ) {var obj = sysNavMenusObj[i];if (obj.routeUrl && obj.type ==1) {routes[obj.routeUrl] = obj;if (obj.parentid)obj.parent = getResource(obj.parentid).routeUrl;}}}routes.sysNavMenus = sysNavMenusObj;return routes;
});

routes 对象讲routerUrl作为key遍历菜单数据,在后边依据routerUrl就可以找到对应菜单数据。其中sysNavMenus是菜单数据,包括每个菜单下的子菜单,其结构如下:

 sysNavMenus: [{"resourceid": 1,"name": "首页","resIco": "fa fa-dashboard","resurl": "/dashboard","hrefUrl": "/","type": 1,"displayorder": 100,"routeUrl": "dashboard","childResourceList": []}, {"resourceid": 2,"name": "租户管理","resIco": "fa fa-link","resurl": "/tenant","hrefUrl": "/tenant","type": 1,"displayorder": 200,"routeUrl": "tenant","childResourceList": []}, {"resourceid": 3,"name": "用户管理","resIco": "fa fa-user-circle-o","resurl": "/unit","hrefUrl": "/user","type": 1,"displayorder": 300,"routeUrl": "user","childResourceList": [{"resourceid": 4,"name": "组织机构管理","resIco": "fa fa-university","resurl": "/unit","hrefUrl": "/user/unit","type": 1,"parentid": 3,"displayorder": 310,"routeUrl": "unit","childResourceList": []}, {"resourceid": 5,"name": "岗位管理","resIco": "fa fa-id-card-o","resurl": "/role","hrefUrl": "/user/role","type": 1,"parentid": 3,"displayorder": 320,"routeUrl": "role","childResourceList": []}, {"resourceid": 6,"name": "人员管理","resIco": "fa fa-user","resurl": "/human","hrefUrl": "/user/human","type": 1,"parentid": 3,"displayorder": 330,"routeUrl": "human","childResourceList": []}, {"resourceid": 7,"name": "权限管理","resIco": "fa fa-lock","resurl": "/auth","hrefUrl": "/user/auth","type": 1,"parentid": 3,"displayorder": 340,"routeUrl": "auth","childResourceList": []}]}, {"resourceid": 4,"name": "组织机构管理","resIco": "fa fa-university","resurl": "/unit","hrefUrl": "/user/unit","type": 1,"parentid": 3,"displayorder": 310,"routeUrl": "unit","childResourceList": []}, {"resourceid": 5,"name": "岗位管理","resIco": "fa fa-id-card-o","resurl": "/role","hrefUrl": "/user/role","type": 1,"parentid": 3,"displayorder": 320,"routeUrl": "role","childResourceList": []}, {"resourceid": 6,"name": "人员管理","resIco": "fa fa-user","resurl": "/human","hrefUrl": "/user/human","type": 1,"parentid": 3,"displayorder": 330,"routeUrl": "human","childResourceList": []}, {"resourceid": 7,"name": "权限管理","resIco": "fa fa-lock","resurl": "/auth","hrefUrl": "/user/auth","type": 1,"parentid": 3,"displayorder": 340,"routeUrl": "auth","childResourceList": []}, {"resourceid": 10,"name": "代码示例","resIco": "fa fa-lock","resurl": "/demo","hrefUrl": "/demo","type": 1,isIframe:true,"displayorder": 340,"routeUrl": "demo","childResourceList": []}]

在我的系统每一个菜单其实就是一个权限实体定义。resurl,hrefUrl,routeUrl分别代表资源地址(可是菜单的链接地址也可以是某个权限的访问地址),导航菜单连接地址,路由定义的key。这里的定义可以依据自己实际情况进行修改。

2,路由和视图切换绑定

js/framework/router.js

define(['knockout', 'controller', 'routes', 'router', 'jquery'], function (ko, controller, routes, Router, $) {function dispatch(path) {var route = routes[path];if (!route.hrefUrl) return;if (route.hrefUrl.indexOf('#') == -1)route.hrefUrl = '#'   route.hrefUrl;controller.initJSAndCSS(path, route);}//初始化路由var router = new Router().configure({//404notfound: function () {dispatch('404');},html5history: false});//根据系统菜单设置路由var sysNavMenus = routes.sysNavMenus;if (sysNavMenus && sysNavMenus.length > 0) {for (var i = 0; i < sysNavMenus.length; i  ) {var obj = sysNavMenus[i];//非导航类数据if (obj.type != 1)continue;//立即执行路由绑定(function (obj) {router.on(obj.hrefUrl, function () {var url = obj.routeUrl;//直接打开子菜单链接if (obj.parentid)dispatch(url);else {//当前是父菜单var childResourceList = obj.childResourceList;//有二级菜单,如果有三级菜单可以继续判断下一级if (childResourceList && childResourceList.length > 0)dispatch(childResourceList[0].routeUrl);else//只有一个根菜单dispatch(url);}});})(obj);}}var urlNotAtRoot = window.location.pathname && (window.location.pathname != baseUrl);if (urlNotAtRoot) {router.init();} else {router.init('/');}//默认跳转到第一个菜单document.location.href = "#"   sysNavMenus[0].hrefUrl;return router;
});

这里的路由配置使用的director.js提供的Router对象监听菜单跳转地址。需要注意的是在遍历菜单数据进行路由绑定的时候需要使用自执行函数进行立即执行路由绑定。

3,加载js/css和数据绑定

router.js中的dispatch方法实现了根据菜单跳转地址进行后续的操作,包括资源加载和数据绑定。实现在js/framework/controller.js

define(['common', 'knockout-multimodels', 'tab', 'jquery', 'router', 'routes', 'index'], function (common, ko, tab, $) {function isEndSharp() { // url end with #if (controller.lastUrl != "" && location.toString().indexOf(controller.lastUrl) != -1 &&location.toLocaleString().indexOf('#') != -1 && location.hash == "") {return true;}return false;}var controller = {/*** 当前激活的页面和路由参数* @param pageName* @param routes*/initJSAndCSS: function (pageName, route) {require([pageName   '-js', 'css!'   pageName   '-css'], function (page) {controller.init(pageName, page, route);});},init: function (pageName, pageData, route) {if (isEndSharp()) {return;}//使用TAB加载页面tab.addTabs({id: route.resurl,title: route.name,close: route.resurl == '/dashboard' ? false : true,url: paths[route.routeUrl   '-html'],isIframe: route.isIframe,urlType: "relative",modelId: route.routeUrl,pageData: pageData,callback: function () {pageData.init();//每一个TAB页签绑定一个数据模型,以modelId进行区分//绑定的数据模型对象也即每个define模块的返回值//attach代替原有的applyBindings,因为后者只支持一个对象绑定ko.attach(route.routeUrl, pageData);pageData.afterRender();}});}};return controller;
});

initJSAndCSS通过rquire加载模块的JS和CSS文件,接着调用init打开一个tab页。

tab的配置如下:

  tab.addTabs({id: route.resurl,title: route.name,close: route.resurl == '/dashboard' ? false : true,url: paths[route.routeUrl   '-html'],isIframe: route.isIframe,urlType: "relative",modelId: route.routeUrl,pageData: pageData,callback: function () {pageData.init();//每一个TAB页签绑定一个数据模型,以modelId进行区分//绑定的数据模型对象也即每个define模块的返回值//attach代替原有的applyBindings,因为后者只支持一个对象绑定ko.attach(route.routeUrl, pageData);pageData.afterRender();}});

close:决定tab页签是否可关闭,这里第一个菜单是首页,其resurl是'/dashboard'.
url:页面模板URL,其值是在main.js中配置的HTML模板名字,也就是routerUrl加上后缀
ifIframe:是否以iframe方式打开tab
pageData:js模块的返回值,通过他可以调用init,afterRender等
callback:加载完HTML模板执行的回调

addTabs的具体实现不在此讨论,其主要是通过jQuery获取模板文件然后插入到tab,页面模板完成加载后开始执行模块的回调,调用init()和afterRender()进行数据的初始化和视图的绑定。

如何使用

对于使用者来说不需要过多关注上边的具体实现,下载项目后添加新的模块只要按照下面的步骤即可:

项目目录结构如下:

我们需要添加的模块位于templates目录,其下按照业务模块划分,js/css/html文件都在一个目录。现在我要添加一个新的模块test:

1,在templates下新建test目录和文件test.js/test.css/test.html

2,在主模块main.js中添加相应的模块定义

注意:html文件模板后面的后缀要填写

3,编辑控制器模块test.js

define(['dialog', 'common', 'knockout', 'knockout-mapping', 'jquery', 'gotoTop'], function (dialog, common, ko, mapping, $) {ko.mapping = mapping;function test() {//数据初始化和KO绑定this.init = function () {};this.initUI = function () {this.initEvent();};this.initEvent = function () {};//渲染UIthis.afterRender = function () {this.initUI()};};return new test();
});

模块实现部分按照上边的模式进行编写即可,init主要是定义模型数据,afterRender则是发送请求获取数据然后通过ko进行数据的绑定。

4,页面编写

在test.html编写HTML代码即可

总结

这个后端开发框架只是传统的基于jQuery和knockout进行数据双向绑定,提供简单的路由视图切换功能。使用者只需按照固定的模式添加功能模块即可。因为使用require进行模块管理,也便于对系统进行模块划分和功能复用。

除了提供一个脚手架的开发框架之后,系统还默认继承了诸多的jquery插件,并对require进行试了适配,具体的相关库如下:

datatables
highcharts
highcharts-map
jquery.treegrid
jquery.fileupload
jquery.storageapi
jquery.mCustomScrollbar
jquery.imgareaselect
icheck
select2
ztree

项目地址:

gongxufan/adminlite-plus​

基于adminlte的后台管理系统开发相关推荐

  1. 视频教程-SSM后台管理系统开发实战-Java

    SSM后台管理系统开发实战 5年IT从业经验,目前职位是Java高级工程师.架构师,在gitchat发布有<SSM博客系统开发实战>达人课,CSDN博客专家,博客专栏作者,梦境网项目独立开 ...

  2. SSM后台管理系统开发实战

    一.简介: 通过这个课程带大家从零开发一款功能全面的后台管理系统,包括项目搭建.功能实现到最后的Linux系统部署全过程.本课程使用SpringMVC + Spring + Mybatis作为主体框架 ...

  3. 大事件后台管理系统开发实战(下)

    文章目录 续前篇:大事件后台管理系统开发实战(中) 1. 文章类别 1.1 点击编辑按钮展示修改文章分类的弹出层 1.2 为修改文章分类的弹出层填充表单数据 1.3 更新文章分类的数据 1.4 删除文 ...

  4. php快速搭建后台,基于thinkphp的后台管理系统模板快速搭建,thinkphp后台模板_PHP教程...

    基于thinkphp的后台管理系统模板快速搭建,thinkphp后台模板 当我们在搭建网站的时候,后端开发人员在编写后台的管理系统的时候,往往会因为缺少一个合适的后台管理系统的模板,而必须去重新编写一 ...

  5. 基于JAVA物业后台管理系统计算机毕业设计源码+系统+mysql数据库+lw文档+部署

    基于JAVA物业后台管理系统计算机毕业设计源码+系统+mysql数据库+lw文档+部署 基于JAVA物业后台管理系统计算机毕业设计源码+系统+mysql数据库+lw文档+部署 本源码技术栈: 项目架构 ...

  6. Vue 2.x 实战之后台管理系统开发(二)

    1. 导语 承接上文:Vue 2.x 实战之后台管理系统开发(一) 在上一篇文章中,我详细叙述了如何创建项目框架和引入各种后台常用插件,做好这些准备工作后,我们就可以着手进行页面的开发了.在开发过程中 ...

  7. 基于Android的人事管理系统 开发与设计

    基于Android的人事管理系统开发与设计 摘要: 由于手机app的迅速发展与广泛应用,基于android开发的手机软件被日益广泛的推广,并且占有市场较大的比重.本文为基于android开发的中小型企 ...

  8. Thinkphp实战教程后台管理系统开发

    目录 ├─Thinkphp5 后台管理开发.png ├─Thinkphp5 后台管理开发.xmind ├─Thinkphp实战教程后台管理系统开发-1.课程介绍及大纲.mp4 ├─Thinkphp实战 ...

  9. 基于zui的后台管理系统-麦穗博客

    基于zui的后台管理系统-麦穗博客 基于zui的后台管理系统-麦穗博客 posted on 2016-02-26 14:31 lexus 阅读(...) 评论(...) 编辑 收藏 转载于:https ...

最新文章

  1. linux ls什么意思,linux – 你如何确定bash ls的颜色是什么意思?
  2. fastText原理和文本分类实战
  3. 使用yangtools将yang文件转化成java
  4. 远程研发能有多高效?手淘新版本上线只用了5天!
  5. java基础知识一_Java基础知识(一)
  6. HTML中Css补充资料
  7. java 实现 PDF 转 TIF 【彩色压缩版】
  8. Python Re正则表达式之group(0)、group(1)
  9. 使用Python进行多个机器学习模型、多个评价指标表格绘制(AUC、敏感度、特异度、F1值、约登指数、MCC、Kappa等)
  10. layui表格时间显示格式
  11. 计算机芯片级维修包括哪些,计算机芯片级维修1
  12. 第十篇 面向对象的程序设计
  13. c语言输出法雷序列,法雷(法里)序列 - osc_h0wb1wlt的个人空间 - OSCHINA - 中文开源技术交流社区...
  14. Linux下如何安装MySQL 5.0
  15. 如何将网站转化为桌面应用
  16. 计算机软件添加信任,瑞星杀毒怎么添加信任软件 添加方法介绍
  17. 你真的知道如何在B站赚钱吗?
  18. 一个离职员工对中兴的回望
  19. 高斯判别分析(GDA)
  20. Java DecimalFormat 格式化数字,取2位小数,按位取小数,按要求格式化小数 float,double,int,等类型都支持

热门文章

  1. 【大一大二必看】计算机专业的同学应该参加哪些比赛?
  2. 百科全书: 光纤基础知识详解
  3. python编写计算您的周岁年龄
  4. 100m光纤测速多少正常_100M,200M,500M,1000M,带宽测速参照表,测试结果(建议收藏)...
  5. NGSIM数据集提取换道前4s周围车辆的特征数据
  6. UE4骨骼网格体没有影子
  7. 2021-2022学年——嵌入式系统实践实验5_Time实验
  8. 一点一滴分析LinkIt™ Smart 7688 webUI
  9. PSR开关电源设计的得与失
  10. vue3组合式Composition API之setup函数的具体用法