学以致用嘛,学了这么多,在真实项目里面怎么应用呢?带着问题去学习才是最快的学习方式。还是以前的那个项目,前后端分离,现在把前端换成vue的,暂时采用脚本化的方式,然后在尝试工程化的方式。
现在先实现功能节点(树)、动态tab、数据列表、分页这几个主要功能。在前面几章里面代码都已经介绍了,好吧分页没说,不过也比较简单了,加个模板,把数据接上,再加个事件就可以了。
一、同一段代码,一个项目里实现多个数据列表
先看一下效果:GIF动图,1.5s一张不要太着急。另外截图没截好,不太清晰大家多担待。(左上角是一个水印。)
二、同样的代码,在实现其他项目实现各种数据列表
动图里面只演示了两个模块(页面),其实不仅可以实现这两个页面,所有的基础列表页面都可以实现,即使换一个新的项目,也只需要改几个参数就行(不需要修改代码),如下图:
三、页面级的抽象
实现这些功能,(前端)的代码(html+vue)不超过300行(只需要一段,不用各种copy)。首先是vue很给力,代码上面可以做到很精简,另一个就是面向对象的基础——抽象!
虽然功能模块非常多,但是数据列表的模式是一样的,区别就是——字段不一样,其他的还不都是一样的吗?所以我们可以针对所有的数据列表需求做一个抽象,把共同的功能拿出来做成代码(或者组件),把差异化的需求也拿出来做成json包。这样代码就一样了,不用一次一次的copy。我们只需要维护好json包就可以。
这么做有几大优点:
1、 减少bug的出现机会,因为代码很少,想出bug都难。而且会经过很多业务模块、项目的测试,可以及时发现bug。
2、 便于修改bug,因为代码就一处,改了一处就是所有(项目)这类的bug都被修改了。
3、 减少了很多代码量,让程序员有更多的时间休息,,,,哦不对,是更多的时间去思考更复杂的业务逻辑如何实现。
4、 便于升级,因为代码只有一处,想要升级修改这一处就好,其他所有的列表功能就都跟着一起升级了。
5、 便于入门学习,就这一处代码,还学不会?
四、完整代码
1、页面、模板
1 <html> 2 <head> 3 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> 4 <title>树和列表以及分页title> 5 <meta charset="utf-8" /> 6 <script type="text/javascript" src="vue.js">script> 7 <script type="text/javascript" src="vue-resource.min.js">script> 8 <link type="text/css" rel="stylesheet" media="screen" href="/Manage/css/mangoGlobal.css"> 9 <link type="text/css" rel="stylesheet" media="screen" href="/Manage/css/mis-style-p.css"> 10 <link type="text/css" rel="stylesheet" media="screen" href="/Manage/css/MisStyle_v2.css"> 11 <link type="text/css" rel="stylesheet" media="screen" href="/Manage/css/debugCss.css"> 12 <link type="text/css" rel="stylesheet" media="screen" href="/Manage/css/css2.css"> 13 <script type="text/javascript"> 14 var dbid = "25,27"; //定义项目标识,区分不同的项目 15 var dataBaseId = 25; 16 var projectId = "1,2,3"; 17 script> 18 head> 19 <body> 20 <div id="foo">div> —加载动画--> 21 <div id="div_Show" class="clearfix"> 22 <div id="div_logo"> 23 <span id="showMe">span> 24 <span id="webTitle">后台管理 span> 25 <span id="exit" onclick="delCookie('saleUserID___')"> 退 出 span> 26 div> 27 <div style="width:200px;"> 28 <div >导航菜单div> 29 <div id="divTree"> 30 <div :key="'tree_' + tree.ModuleID" v-for="tree in trees" v-on:click="treeClick(tree.ModuleID,tree.ModuleName)" v-bind:class="'tree' + tree.ModuleLevel">{{tree.ModuleName}}div> 31 div> 32 div> 33 <div id="div_Main" class="inner"> 34 <ul id="ulTab" class="tabs left"> 35 <li v-for="t in tabs" v-bind:class="{'selectTag':t.isShow}"> 36 <a v-on:click="tabClick(t.id)" href="javascript:void(0)"> 37 {{t.title}} {{t.id==='1'?tabNumber:''}} 38 <em class="arrup" v-on:click.stop="closeTab(t.id)">xem> 39 a> 40 li> 41 ul> 42 <div id="divIframe"> 43 <div v-for="t in tabMains" v-bind:class="{'selectTag':t.isShow}" v-show="t.isShow"> 44 {{ t.message }} 45 <table class="table_default1" style="" v-show="t.message!=='欢迎使用!'"> 46 <tr> 47 <th v-for="key in t.orderBy"> 48 {{t.tableTh[key].title}} 49 th> 50 tr> 51 <tr v-for="(tr,i) in t.dataList"> 52 <td v-for="index in t.orderBy" v-bind:align="t.tableTh[index].align"> 53 {{tr[index]}} 54 td> 55 tr> 56 table> 57 58 <div id="divQuickPage" v-show="t.message!=='欢迎使用!'"> 59 <div id="quickPage"> 60 <div id="quickPager" class="pagesize1"> 61 <span class="pagetext1" style="cursor: pointer;">共<strong>{{t.pageTurn.recordCount}}strong>条记录span> 62 <span class="pagetext1" style="cursor: pointer;">第<strong>{{t.pageTurn.pageIndex}}strong>/<strong>{{t.pageTurn.pageCount}}strong>页span> 63 <span class="pagetext1" style="cursor: pointer;">每页<strong>{{t.pageTurn.pageSize}}strong>条记录span> 64 <span id="navi" style="cursor: pointer;"> 65 <a v-for="(p,index) in t.pageTurn.pageCount" v-on:click="naviClick(t.tabId,index+1)">{{index+1}}a> 66 span> 67 <a id="first" class="disabled">首页a> 68 <a id="prev" class="disabled">上一页a> 69 <a id="next" v-on:click="naviClick(t.tabId,2)">下一页a> 70 <a id="last">末页a> 71 <input type="text" id="txtGo" size="3" class="cssTxt"> 72 <a id="spanGo">GOa> 73 div> 74 div> 75 div> 76 div> 77 div> 78 div> 79 div> 80 81 <div id="div_Copyright"> 82 <span id="divDegub" onclick="DebugShow()">debugspan> <span id="Span1" onclick="DebugCache()">cachespan> 83 <span id="Span2" onclick="DebugShow()">eventspan> by 自然框架之UI Vue + Json + ashx 84 div> 85 body>
Vue的代码
1 //树的数据包 2 var tree = new Vue({ 3 el: '#divTree', 4 data: { 5 trees: [ 6 { 7 IsHidden: 0, 8 ModuleID: -10, 9 ModuleLevel: 1, 10 ModuleName: "系统管理", 11 ParentID: 0, 12 ParentIDAll: "0", 13 Target: "_self", 14 URL: "#" 15 } 16 ] 17 }, 18 methods: { 19 treeClick: function (id,title) { 20 //隐藏前一个的tab 21 var oldId = tab.currentTabId; //记录切换前tab的id 22 tab.beforeTabId = oldId; 23 tab.tabs["tab" + oldId].isShow = false; //隐藏切换前的tab 24 tabDiv.tabMains["tab" + oldId].isShow = false; //隐藏切换前的tab容器 25 tab.currentTabId = id; //记录新的id 26 //创建tab 27 var newTab = { 28 id: id, //标签识别标示 29 title: title, 30 isShow: true, //是否显示 31 message: title 32 }; 33 34 //创建tab 的容器 35 var main = { 36 message:title, 37 isShow: true, //是否显示 38 tabId: id //标签识别标示 39 }; 40 41 if (typeof (tab.tabs["tab" + id]) === "undefined") 42 tab.tabNumber = tab.tabNumber + 1; 43 44 Vue.set(tab.tabs, "tab" + id, newTab); 45 46 if (typeof (tabDiv.tabMains["tab" + id]) === "undefined") { 47 //没有加载描述,加载表格的描述信息 48 this.$http.get('/MetaData/GetMeta.ashx?action=grid&mdid=' + id + '&mpvid=' + id + '01&dbid=' + dbid + '&_=1563').then(function (res) { 49 //创建table 50 var table = { 51 message: title, 52 isShow: true, //是否显示 53 tabId: id, //标签识别标示 54 orderBy: [], //可以控制字段的先后顺序,想调整列的先后顺序,改这个数组就行,可以做个性化设置 55 tableTh: {}, //表头的描述信息 56 dataList: [] //数据包,字段名作为关键字,便于列的调整先后顺序 57 }; 58 59 for (var key in res.body.data) { 60 var d = res.body.data[key]; 61 62 table.tableTh["" + d.ColumnID] = { 63 title: d.ColName, 64 align: d.ColAlign 65 }; 66 table.orderBy.push("" + d.ColumnID); 67 } 68 Vue.set(tabDiv.tabMains, "tab" + id, table); 69 //获取数据 70 getDataList(id); 71 72 }, function () { 73 console.log('请求表头失败'); 74 }); 75 } else { 76 //获取数据 77 getDataList(id); 78 } 79 80 //获取数据 81 function getDataList(id) { 82 tabDiv.$http.get('/Data/GetData.ashx?action=list&mdid=' + id + '&mpvid=' + id + '01&fpvid=' + id + '02&frid=-2&frids=-2&pageno=1&pagerc=0&dbid=' + dbid + '&webappid=1&_=' + Date.now()).then(function (res) { 83 var tableData = tabDiv.tabMains["tab" + id]; 84 85 tableData.dataList = res.body.data; 86 tableData.pageTurn = res.body.pageTurn; 87 tableData.isShow = true; //显示切换前的tab容器 88 89 Vue.set(tabDiv.tabMains, "tab" + id, tableData); 90 91 }, function () { 92 console.log('请求数据失败'); 93 }); 94 } 95 } 96 } 97 }); 98 99 //标签的数据包,只有标签,没有标签下面的容器 100 var tab = new Vue({ 101 el: '#ulTab', 102 data: { 103 tabNumber: 1, //标签数量,这个是临时的,便于自动重新绑定 104 currentTabId: 1, //当前激活的tab的id 105 beforeTabId: 1, //上一个被激活的tab的id 106 tabs: { 107 tab1: { //可以有多个标签, 108 id: "1", //标签识别标示 109 title: "我的桌面", 110 isShow: true, //是否显示 111 message: '桌面' 112 } 113 } 114 }, 115 methods: { 116 tabClick: function (id) { 117 //切换tab 118 var oldId = tab.currentTabId; //记录切换前tab的id 119 tab.beforeTabId = oldId; 120 tab.tabs["tab" + oldId].isShow = false; //隐藏切换前的tab 121 tabDiv.tabMains["tab" + oldId].isShow = false; //隐藏切换前的tab容器 122 123 tab.currentTabId = id; //记录切换后的id 124 tab.tabs["tab" + id].isShow = true; //显示切换后的tab 125 tabDiv.tabMains["tab" + id].isShow = true; //显示切换前的tab容器 126 }, 127 closeTab: function (id) { 128 if (id === "1") { 129 alert("这是桌面,建议不要关闭哦:)"); 130 return; 131 } 132 133 delete tab.tabs["tab" + id]; //不知道有没有更好的办法 134 tab.tabNumber = tab.tabNumber - 1; 135 136 //设置“激活”状态 137 var oldId = tab.beforeTabId; //上一个激活tab 138 var nowId = tab.currentTabId; //现在激活tab 139 140 if (nowId === id) { 141 //关掉的是激活tab,需要设置上一个tab为激活状态 142 tab.currentTabId = oldId; 143 tab.tabs["tab" + oldId].isShow = true; 144 tab.beforeTabId = 1; 145 } 146 else if (oldId === id) { 147 //关闭的是上一个激活tab,修改前一个tab的id 148 tab.beforeTabId = 1; 149 } else { 150 //需要强制修改一下,否则不会刷新 151 tab.currentTabId = nowId; 152 } 153 } 154 } 155 }); 156 157 //tab下面的容器的数据包 158 var tabDiv = new Vue({ 159 el: '#divIframe', 160 data: { 161 tabMains: { 162 tab1: { //可以有多个标签, 163 message: '欢迎使用!', 164 isShow: true, //是否显示 165 tabId: "1", //标签识别标示 166 orderBy: [], //可以控制字段的先后顺序,想调整列的先后顺序,改这个数组就行,可以做个性化设置 167 tableTh: {}, //表头的描述信息 168 dataList: [], //数据包,字段名作为关键字,便于列的调整先后顺序 169 pageTurn: { 170 //naviCount: 5, //显示页码数量 171 //pageCount: 3, //总页数 172 //pageIndex: 1, //当前页数 173 //pageSize: 20, //一页记录数 174 //recordCount: 58 //总记录数 175 } 176 } 177 } 178 }, 179 methods: { 180 naviClick: function(id,pageIndex) { 181 tabDiv.$http.get('/Data/GetData.ashx?action=list&mdid=' + id + '&mpvid=' + id + '01&fpvid=' + id + '02&frid=-2&frids=-2&pageno=' + pageIndex + '&pagerc=0&dbid=' + dbid + '&webappid=1&_=' + Date.now()).then(function (res) { 182 183 var tableData = tabDiv.tabMains["tab" + id]; 184 tableData.dataList = res.body.data; 185 tableData.pageTurn = res.body.pageTurn; 186 187 Vue.set(tabDiv.tabMains, "tab" + id, tableData); 188 189 }, function () { 190 console.log('请求数据失败'); 191 }); 192 } 193 } 194 }); 195 196 //从后端获取树,然后绑定。以前的项目,现成的接口先拿来用了。 197 tree.$http.get('/MetaData/GetMeta.ashx?action=tree&mdid=0&dbid=25&ProjectID=1,2,3&cacheKey=0&webappid=1&_=15640190').then(function (res) { 198 tree.trees = res.body.data; //后端的数据直接赋值,然后就自动绑定上了。 199 }, function () { 200 console.log('请求失败处理'); 201 });
总结
1、 因为是初学vue,所以代码还不够规范,有些地方并不是很理想。还有很多需要优化和改进的地方。
2、 关于脚本和工程化。脚本比较简单直接,引用vue.Js就可以开鲁,便于试验自己的想法,而且vue的大部分方法也都是支持脚本方式的,还没学到路由那一块。现在先用脚本的方式来实现,然后在逐步转成工程化的方式。或者保持两种方式共存。这样更灵活吧,反正代码也没多少。
3、 虽然现在代码不多,但是实现的功能类型也不多,只是简单的数据列表,还没加上查询和按钮组,还有更复杂的表单控件。这些功能都加上之后,代码会变得更加的臃肿,也就意味着一步步走向深渊——不可维护、不可扩展。那么怎么办呢?
4、 代码的可维护性——组件化。做成组件的方式来分割代码。Emmmm,不知道vue还有没有更好的方式。总之有好的方法都可以拿来用。
5、 代码的可扩展性,目前tab里面只能放数据列表,还没想到更好的方式放其他类型的内容,以前用了一个粗暴的方式——iframe,想放啥都可以。现在不想用iframe了。
下一步是研究一下表单的问题了。