從後端到前端之Vue(四)小試牛刀——真實專案的應用(樹、tab、資料列表和分頁)

金色海洋(jyk)陽光男孩發表於2019-07-25

 

  學以致用嘛,學了這麼多,在真實專案裡面怎麼應用呢?帶著問題去學習才是最快的學習方式。還是以前的那個專案,前後端分離,現在把前端換成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> &nbsp;
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}}&nbsp;{{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了。

 

下一步是研究一下表單的問題了。

 

相關文章