從後端到前端之Vue(四)小試牛刀——真實專案的應用(樹、tab、資料列表和分頁)
學以致用嘛,學了這麼多,在真實專案裡面怎麼應用呢?帶著問題去學習才是最快的學習方式。還是以前的那個專案,前後端分離,現在把前端換成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)">x</em> 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">GO</a> 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()">debug</span> <span id="Span1" onclick="DebugCache()">cache</span> 83 <span id="Span2" onclick="DebugShow()">event</span> 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了。
下一步是研究一下表單的問題了。
&n