BootstrapTable+KnockoutJS相結(jié)合實(shí)現(xiàn)增刪改查解決方案(三)兩個(gè)Viewmodel搞定增刪改查
前言:之前博主分享過(guò)knockoutJS和BootstrapTable的一些基礎(chǔ)用法,都是寫(xiě)基礎(chǔ)應(yīng)用,根本談不上封裝,僅僅是避免了html控件的取值和賦值,遠(yuǎn)遠(yuǎn)沒(méi)有將MVVM的精妙展現(xiàn)出來(lái)。最近項(xiàng)目打算正式將ko用起來(lái),于是乎對(duì)ko和bootstraptable做了一些封裝,在此分享出來(lái)供園友們參考。封裝思路參考博客園大神蕭秦,如果園友們有更好的方法,歡迎討論。
KnockoutJS系列文章:
BootstrapTable與KnockoutJS相結(jié)合實(shí)現(xiàn)增刪改查功能【一】
BootstrapTable與KnockoutJS相結(jié)合實(shí)現(xiàn)增刪改查功能【二】
一、第一個(gè)viewmodel搞定查詢
demo的實(shí)現(xiàn)還是延續(xù)上次的部門(mén)管理功能。以下展開(kāi)通過(guò)數(shù)據(jù)流向來(lái)說(shuō)明。
1、后臺(tái)向View返回viewmodel的實(shí)現(xiàn)
public ActionResult Index() { var model = new { tableParams = new { url = "/Department/GetDepartment", //pageSize = 2, }, urls = new { delete = "/Department/Delete", edit = "/Department/Edit", add = "/Department/Edit", }, queryCondition = new { name = "", des = "" } }; return View(model); }
代碼釋疑:這里返回的model包含三個(gè)選項(xiàng)
•tableParams:頁(yè)面表格初始化參數(shù)。由于js里面定義了默認(rèn)參數(shù),所以這里設(shè)置的參數(shù)是頁(yè)面特定的初始化參數(shù)。
•urls:包含增刪改請(qǐng)求的url路徑。
•queryCondition:頁(yè)面的查詢條件。
2、cshtml頁(yè)面代碼
Index.cshtml頁(yè)面代碼如下:
@{ Layout = null; } <!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width" /> <title>Index</title> <link href="~/Content/bootstrap/css/bootstrap.min.css" rel="stylesheet" /> <link href="~/Content/bootstrap-table/bootstrap-table.min.css" rel="stylesheet" /> <script src="~/scripts/jquery-1.9.1.min.js"></script> <script src="~/Content/bootstrap/js/bootstrap.min.js"></script> <script src="~/Content/bootstrap-table/bootstrap-table.min.js"></script> <script src="~/Content/bootstrap-table/locale/bootstrap-table-zh-CN.js"></script> <script src="~/scripts/knockout/knockout-3.4.0.min.js"></script> <script src="~/scripts/knockout/extensions/knockout.mapping-latest.js"></script> <script src="~/scripts/extensions/knockout.index.js"></script> <script src="~/scripts/extensions/knockout.bootstraptable.js"></script><script type="text/javascript"> $(function(){ var data = @Html.Raw(Newtonsoft.Json.JsonConvert.SerializeObject(Model)); ko.bindingViewModel(data, document.getElementById("div_index")); }); </script> </head> <body> <div id="div_index" class="panel-body" style="padding:0px;overflow-x:hidden;"> <div class="panel panel-default"> <div class="panel-heading">查詢條件</div> <div class="panel-body"> <form id="formSearch" class="form-horizontal"> <div class="form-group"> <label class="control-label col-xs-1">部門(mén)名稱</label> <div class="col-xs-3"> <input type="text" class="form-control" data-bind="value:queryCondition.name"> </div> <label class="control-label col-xs-1">部門(mén)描述</label> <div class="col-xs-3"> <input type="text" class="form-control" data-bind="value:queryCondition.des"> </div> <div class="col-xs-4" style="text-align:right;"> <button type="button"data-bind="click:clearClick" class="btn">清空</button> <button type="button"data-bind="click:queryClick" class="btn btn-primary">查詢</button> </div> </div> </form> </div> </div> <div id="toolbar" class="btn-group"> <button data-bind="click:addClick" type="button" class="btn btn-default"> <span class="glyphicon glyphicon-plus" aria-hidden="true"></span>新增 </button> <button data-bind="click:editClick" type="button" class="btn btn-default"> <span class="glyphicon glyphicon-pencil" aria-hidden="true"></span>修改 </button> <button data-bind="click:deleteClick" type="button" class="btn btn-default"> <span class="glyphicon glyphicon-remove" aria-hidden="true"></span>刪除 </button> </div> <table data-bind="bootstrapTable:bootstrapTable"> <thead> <tr> <th data-checkbox="true"></th> <th data-field="Name">部門(mén)名稱</th> <th data-field="Level">部門(mén)級(jí)別</th> <th data-field="Des">描述</th> <th data-field="strCreatetime">創(chuàng)建時(shí)間</th> </tr> </thead> </table> </div> </body> </html>
代碼釋疑:和上篇一樣,需要引用JQuery、bootstrap、bootstraptable、knockout等相關(guān)文件。這里重點(diǎn)說(shuō)明下兩個(gè)文件:
•knockout.index.js:封裝了查詢頁(yè)面相關(guān)的屬性和事件綁定。
•knockout.bootstraptable.js:封裝了bootstrapTable的初始化和自定義knockout綁定的方法。
以上所有的頁(yè)面交互都封裝在了公共js里面,這樣就不用在頁(yè)面上面寫(xiě)大量的DOM元素取賦值、事件的綁定等重復(fù)代碼,需要在本頁(yè)面寫(xiě)的js只有以上兩句,是不是很easy。
3、JS封裝
重點(diǎn)來(lái)看看上面的說(shuō)的兩個(gè)js文件knockout.bootstraptable.js和knockout.index.js。
(1)knockout.bootstraptable.js
(function ($) { //向ko里面新增一個(gè)bootstrapTableViewModel方法 ko.bootstrapTableViewModel = function (options) { var that = this; this.default = { toolbar: '#toolbar', //工具按鈕用哪個(gè)容器 queryParams: function (param) { return { limit: param.limit, offset: param.offset }; },//傳遞參數(shù)(*) pagination: true, //是否顯示分頁(yè)(*) sidePagination: "server", //分頁(yè)方式:client客戶端分頁(yè),server服務(wù)端分頁(yè)(*) pageNumber: 1, //初始化加載第一頁(yè),默認(rèn)第一頁(yè) pageSize: 10, //每頁(yè)的記錄行數(shù)(*) pageList: [10, 25, 50, 100], //可供選擇的每頁(yè)的行數(shù)(*) method: 'get', search: true, //是否顯示表格搜索,此搜索是客戶端搜索,不會(huì)進(jìn)服務(wù)端,所以,個(gè)人感覺(jué)意義不大 strictSearch: true, showColumns: true, //是否顯示所有的列 cache:false, showRefresh: true, //是否顯示刷新按鈕 minimumCountColumns: 2, //最少允許的列數(shù) clickToSelect: true, //是否啟用點(diǎn)擊選中行 showToggle: true, }; this.params = $.extend({}, this.default, options || {}); //得到選中的記錄 this.getSelections = function () { var arrRes = that.bootstrapTable("getSelections") return arrRes; }; //刷新 this.refresh = function () { that.bootstrapTable("refresh"); }; }; //添加ko自定義綁定 ko.bindingHandlers.bootstrapTable = { init: function (element, valueAccessor, allBindingsAccessor, viewModel) { //這里的oParam就是綁定的viewmodel var oViewModel = valueAccessor(); var $ele = $(element).bootstrapTable(oViewModel.params); //給viewmodel添加bootstrapTable方法 oViewModel.bootstrapTable = function () { return $ele.bootstrapTable.apply($ele, arguments); } }, update: function (element, valueAccessor, allBindingsAccessor, viewModel) { } }; })(jQuery);
代碼釋疑:上面代碼主要做了兩件事
1.自定義了bootstrapTable初始化的ViewModel。
2.
添加ko自定義綁定。
如果園友不理解自定義綁定的使用,可以看看博主的前兩篇博文(一)和(二),有詳細(xì)介紹。
(2)knockout.index.js
(function ($) { ko.bindingViewModel = function (data, bindElement) { var self = this; this.queryCondition = ko.mapping.fromJS(data.queryCondition); this.defaultQueryParams = { queryParams: function (param) { var params = self.queryCondition; params.limit = param.limit; params.offset = param.offset; return params; } }; var tableParams = $.extend({}, this.defaultQueryParams, data.tableParams || {}); this.bootstrapTable = new ko.bootstrapTableViewModel(tableParams); //清空事件 this.clearClick = function () { $.each(self.queryCondition, function (key, value) { //只有監(jiān)控屬性才清空 if (typeof (value) == "function") { this(''); //value(''); } }); self.bootstrapTable.refresh(); }; //查詢事件 this.queryClick = function () { self.bootstrapTable.refresh(); }; //新增事件 this.addClick = function () { var dialog = $('<div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel"></div>'); dialog.load(data.urls.edit, null, function () { }); $("body").append(dialog); dialog.modal().on('hidden.bs.modal', function () { //關(guān)閉彈出框的時(shí)候清除綁定(這個(gè)清空包括清空綁定和清空注冊(cè)事件) ko.cleanNode(document.getElementById("formEdit")); dialog.remove(); self.bootstrapTable.refresh(); }); }; //編輯事件 this.editClick = function () { var arrselectedData = self.bootstrapTable.getSelections(); if (arrselectedData.length <= 0 || arrselectedData.length > 1) { alert("每次只能編輯一行"); return; } var dialog = $('<div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel"></div>'); dialog.load(data.urls.edit, arrselectedData[0], function () { }); $("body").append(dialog); dialog.modal().on('hidden.bs.modal', function () { //關(guān)閉彈出框的時(shí)候清除綁定(這個(gè)清空包括清空綁定和清空注冊(cè)事件) ko.cleanNode(document.getElementById("formEdit")); dialog.remove(); self.bootstrapTable.refresh(); }); }; //刪除事件 this.deleteClick = function () { var arrselectedData = self.bootstrapTable.getSelections(); if (!arrselectedData||arrselectedData.length<=0) { alert("請(qǐng)至少選擇一行"); return; } $.ajax({ url: data.urls.delete, type: "post", contentType: 'application/json', data: JSON.stringify(arrselectedData), success: function (data, status) { alert(status); self.bootstrapTable.refresh(); } }); }; ko.applyBindings(self, bindElement); }; })(jQuery);
代碼釋疑:這個(gè)js主要封裝了頁(yè)面元素的屬性和事件綁定,需要說(shuō)明的幾個(gè)地方
•this.queryCondition = ko.mapping.fromJS(data.queryCondition):這一句的作用是將后臺(tái)傳過(guò)來(lái)的查詢條件,從JSON數(shù)據(jù)轉(zhuǎn)換成監(jiān)控屬性。只有執(zhí)行了這一句,屬性和頁(yè)面元素才能雙向監(jiān)控。
•self.bootstrapTable.refresh():這一句的含義是刷新表格數(shù)據(jù),它實(shí)際上是調(diào)用的bootstrapTable的refresh方法,只不過(guò)博主在knockout.bootstraptable.js文件里面對(duì)它進(jìn)行了簡(jiǎn)單封裝。
•dialog.load(data.urls.edit, null, function () { }):在新增和編輯的時(shí)候使用了jQuery的load()方法,這個(gè)方法的作用是請(qǐng)求這個(gè)url的頁(yè)面元素,并執(zhí)行url對(duì)應(yīng)頁(yè)面的js代碼。此方法在動(dòng)態(tài)引用js文件并執(zhí)行js文件里面代碼這方面功能很強(qiáng)大。
最后附上后臺(tái)GetDepartment()方法對(duì)應(yīng)的代碼
[HttpGet] public JsonResult GetDepartment(int limit, int offset, string name, string des) { var lstRes = DepartmentModel.GetData(); if (!string.IsNullOrEmpty(name)) { lstRes = lstRes.Where(x => x.Name.Contains(name)).ToList(); } if (!string.IsNullOrEmpty(des)) { lstRes = lstRes.Where(x => x.Des.Contains(des)).ToList(); } lstRes.ForEach(x=> { x.strCreatetime = x.Createtime.ToString("yyyy-MM-dd HH:mm:ss"); }); var oRes = new { rows = lstRes.Skip(offset).Take(limit).ToList(), total = lstRes.Count }; return Json(oRes, JsonRequestBehavior.AllowGet); }
至此,查詢頁(yè)面的查詢、清空功能即可實(shí)現(xiàn)。
你是否還有一個(gè)疑問(wèn):如果我們需要自定義bootstrapTable的事件怎么辦?不能通過(guò)后臺(tái)的viewmodel傳過(guò)來(lái)吧?
確實(shí),從后臺(tái)是無(wú)法傳遞js事件方法的,所以需要我們?cè)谇岸俗远x事件的處理方法,比如我們可以這樣:
<script type="text/javascript"> $(function(){ var data = @Html.Raw(Newtonsoft.Json.JsonConvert.SerializeObject(Model)); data.tableParams.onLoadSuccess = function(data){ alert("加載成功事件"); }; ko.bindingViewModel(data, document.getElementById("div_index")); }); </script>
二、第二個(gè)viewmodel搞定編輯
上面的一個(gè)viewmodel搞定了查詢和刪除的功能,但是新增和編輯還需要另一個(gè)viewmodel的支持。下面來(lái)看看編輯的封裝實(shí)現(xiàn)。
1、ActionResult的實(shí)現(xiàn)
通過(guò)上面查詢的代碼我們可以知道,當(dāng)用戶點(diǎn)擊新增和編輯的時(shí)候,會(huì)請(qǐng)求另一個(gè)View視圖→/Department/Edit。下面來(lái)看看Edit視圖的實(shí)現(xiàn)
public ActionResult Edit(Department model) { var oResModel = new { editModel = model, urls = new { submit = model.id == 0 ? "/Department/Add" : "/Department/Update" } }; return View(oResModel); }
代碼釋疑:上述代碼很簡(jiǎn)單,就是向視圖頁(yè)面返回一個(gè)viewmodel,包含編輯的實(shí)體和提交的url。通過(guò)這個(gè)實(shí)體主鍵是否存在來(lái)判斷當(dāng)前提交是新增實(shí)體還是編輯實(shí)體。
2、cshtml代碼
Edit.cshtml代碼如下:
<form id="formEdit"> <div class="modal-dialog" role="document"> <div class="modal-content"> <div class="modal-header"> <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button> <h4 class="modal-title" id="myModalLabel">操作</h4> </div> <div class="modal-body"> <div class="form-group"> <label for="txt_departmentname">部門(mén)名稱</label> <input type="text" name="txt_departmentname" data-bind="value:editModel.Name" class="form-control" placeholder="部門(mén)名稱"> </div> <div class="form-group"> <label for="txt_departmentlevel">部門(mén)級(jí)別</label> <input type="text" name="txt_departmentlevel" data-bind="value:editModel.Level" class="form-control" placeholder="部門(mén)級(jí)別"> </div> <div class="form-group"> <label for="txt_des">描述</label> <input type="text" name="txt_des" data-bind="value:editModel.Des" class="form-control" placeholder="描述"> </div> </div> <div class="modal-footer"> <button type="button" class="btn btn-default" data-dismiss="modal"><span class="glyphicon glyphicon-remove" aria-hidden="true"></span>關(guān)閉</button> <button type="submit" class="btn btn-primary"><span class="glyphicon glyphicon-floppy-disk" aria-hidden="true"></span>保存</button> </div> </div> </div> </form> <link href="~/Content/bootstrapValidator/css/bootstrapValidator.css" rel="stylesheet" /> <script src="~/Content/bootstrapValidator/js/bootstrapValidator.js"></script> <script src="~/scripts/extensions/knockout.edit.js"></script> <script type="text/javascript"> $(function () { var editData = @Html.Raw(Newtonsoft.Json.JsonConvert.SerializeObject(Model)); ko.bindingEditViewModel(editData, {}); }); </script>
代碼釋疑:由于我們加了驗(yàn)證組件bootstrapValidator,所以需要引用相關(guān)js和css。knockout.edit.js這個(gè)文件主要封裝了編輯頁(yè)面的屬性和事件綁定。重點(diǎn)來(lái)看看這個(gè)js的實(shí)現(xiàn)代碼。
3、js封裝
knockout.edit.js代碼:
(function ($) { ko.bindingEditViewModel = function (data, validatorFields) { var that = {}; that.editModel = ko.mapping.fromJS(data.editModel); that.default = { message: '驗(yàn)證不通過(guò)', fields: { }, submitHandler: function (validator, form, submitButton) { var arrselectedData = ko.toJS(that.editModel); $.ajax({ url: data.urls.submit, type: "post", contentType: 'application/json', data: JSON.stringify(arrselectedData), success: function (data, status) { alert(status); } }); $("#myModal").modal("hide"); } }; that.params = $.extend({}, that.default, {fields: validatorFields} || {}); $('#formEdit').bootstrapValidator(that.params); ko.applyBindings(that, document.getElementById("formEdit")); }; })(jQuery);
代碼釋疑:這個(gè)js主要封裝了編輯模型的屬性和提交的事件綁定。由于用到了bootstrapValidator驗(yàn)證組件,所以需要表單提交。其實(shí)公共js里面是不應(yīng)該出現(xiàn)頁(yè)面id的,比如上面的“formEdit”和“myModal”,可以將此作為參數(shù)傳過(guò)來(lái),這點(diǎn)有待優(yōu)化。參數(shù)validatorFields表示驗(yàn)證組件的驗(yàn)證字段,如果表單不需要驗(yàn)證,則傳一個(gè)空的Json或者不傳都行。上文我們沒(méi)有做字段驗(yàn)證,其實(shí)一般來(lái)說(shuō),基礎(chǔ)表都會(huì)有一個(gè)或者幾個(gè)非空字段,比如我們可以加上部門(mén)名稱的非空驗(yàn)證。在Edit.cshtml頁(yè)面的代碼改成這樣:
<form id="formEdit"> <div class="modal-dialog" role="document"> <div class="modal-content"> <div class="modal-header"> <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button> <h4 class="modal-title" id="myModalLabel">操作</h4> </div> <div class="modal-body"> <div class="form-group"> <label for="txt_departmentname">部門(mén)名稱</label> <input type="text" name="Name" data-bind="value:editModel.Name" class="form-control" placeholder="部門(mén)名稱"> </div> <div class="form-group"> <label for="txt_departmentlevel">部門(mén)級(jí)別</label> <input type="text" name="Level" data-bind="value:editModel.Level" class="form-control" placeholder="部門(mén)級(jí)別"> </div> <div class="form-group"> <label for="txt_des">描述</label> <input type="text" name="Des" data-bind="value:editModel.Des" class="form-control" placeholder="描述"> </div> </div> <div class="modal-footer"> <button type="button" class="btn btn-default" data-dismiss="modal"><span class="glyphicon glyphicon-remove" aria-hidden="true"></span>關(guān)閉</button> <button type="submit" class="btn btn-primary"><span class="glyphicon glyphicon-floppy-disk" aria-hidden="true"></span>保存</button> </div> </div> </div> </form> <link href="~/Content/bootstrapValidator/css/bootstrapValidator.css" rel="stylesheet" /> <script src="~/Content/bootstrapValidator/js/bootstrapValidator.js"></script> <script src="~/scripts/extensions/knockout.edit.js"></script> <script type="text/javascript"> $(function () { var editData = @Html.Raw(Newtonsoft.Json.JsonConvert.SerializeObject(Model)); ko.bindingEditViewModel(editData, { Name: { validators: { notEmpty: { message: '名稱不能為空!' } } } }); }); </script>
那么就會(huì)在提交的時(shí)候自動(dòng)進(jìn)行驗(yàn)證:
注意:驗(yàn)證屬性Name對(duì)應(yīng)的是input標(biāo)簽的name屬性,所以要做驗(yàn)證,這個(gè)name屬性必須設(shè)置正確。
最好附上增刪改的后臺(tái)方法:
[HttpPost] public JsonResult Add(Department oData) { DepartmentModel.Add(oData); return Json(new { }, JsonRequestBehavior.AllowGet); } [HttpPost] public JsonResult Update(Department oData) { DepartmentModel.Update(oData); return Json(new { }, JsonRequestBehavior.AllowGet); } [HttpPost] public JsonResult Delete(List<Department> oData) { DepartmentModel.Delete(oData); return Json(new { }, JsonRequestBehavior.AllowGet); }
至此,我們整個(gè)頁(yè)面的增刪改查效果就OK了,簡(jiǎn)單看下效果:
三、總結(jié)
以上簡(jiǎn)單封裝了bootstrapTable+ko的增刪改查業(yè)務(wù),只是一個(gè)最初級(jí)的封裝。如果你需要將這些運(yùn)用都你的項(xiàng)目中,可能還需要一些簡(jiǎn)單的優(yōu)化措施,比如:
1、如果單純是一個(gè)頁(yè)面的viewmodel,是否可以不用從后臺(tái)的ActionResult里面返回,直接寫(xiě)在View頁(yè)面里面感覺(jué)更好,省去了序列化和參數(shù)傳遞的問(wèn)題。這點(diǎn)有待優(yōu)化。
2、公共js里面不應(yīng)該出現(xiàn)頁(yè)面元素的id,可以通過(guò)參數(shù)將頁(yè)面元素傳遞進(jìn)來(lái)。
3、新增和編輯事件方法里面彈出框的部分有很多重復(fù)代碼,這部分的最好做法是將彈出框封裝成一個(gè)單獨(dú)的組件去調(diào)用,可以減少大部分的js代碼。
4、如果查詢條件以及編輯的屬性里面存在select下拉框元素,可能還需要封裝下拉框的datasourse等屬性,這一部分是非常常見(jiàn)的,等博主整理好demo后將這塊加進(jìn)去。
以上所述是小編給大家介紹的BootstrapTable+KnockoutJS相結(jié)合實(shí)現(xiàn)增刪改查解決方案(三)兩個(gè)Viewmodel搞定增刪改查 ,希望對(duì)大家有所幫助,如果大家有任何疑問(wèn)請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)腳本之家網(wǎng)站的支持!
- knockoutjs模板實(shí)現(xiàn)樹(shù)形結(jié)構(gòu)列表
- 使用asp.net mvc,boostrap及knockout.js開(kāi)發(fā)微信自定義菜單編輯工具(推薦)
- Asp.net MVC利用knockoutjs實(shí)現(xiàn)登陸并記錄用戶的內(nèi)外網(wǎng)IP及所在城市(推薦)
- KnockoutJS 3.X API 第四章之表單textInput、hasFocus、checked綁定
- KnockoutJS 3.X API 第四章之?dāng)?shù)據(jù)控制流foreach綁定
- KnockoutJS 3.X API 第四章之?dāng)?shù)據(jù)控制流with綁定
- KnockoutJS 3.X API 第四章之事件event綁定
- KnockoutJS 3.X API 第四章之表單value綁定
- KnockoutJS數(shù)組比較算法實(shí)例詳解
相關(guān)文章
JavaScript股票的動(dòng)態(tài)買(mǎi)賣(mài)規(guī)劃實(shí)例分析下篇
這篇文章主要介紹了JavaScript對(duì)于動(dòng)態(tài)規(guī)劃解決股票問(wèn)題的真題例舉講解。文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-08-08JS倒計(jì)時(shí)兩種實(shí)現(xiàn)方式代碼實(shí)例
這篇文章主要介紹了JS倒計(jì)時(shí)兩種實(shí)現(xiàn)方式代碼實(shí)例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-07-07javascript獲取xml節(jié)點(diǎn)的最大值(實(shí)現(xiàn)代碼)
這篇文章主要介紹了利用javascript獲取xml節(jié)點(diǎn)的最大值。需要的朋友可以過(guò)來(lái)參考下,希望對(duì)大家有所幫助2013-12-12讓你的博文自動(dòng)帶上縮址的實(shí)現(xiàn)代碼,方便發(fā)到微博客上
添加以下代碼到你的博客中: (呵呵,抄襲至lulu Studio http://s8.hk/0itw)2010-12-12功能強(qiáng)大的Bootstrap使用手冊(cè)(一)
這篇文章主要為大家詳細(xì)介紹了功能強(qiáng)大的Bootstrap使用手冊(cè),分享了Bootstrap使用步驟和常用用法,感興趣的小伙伴們可以參考一下2016-08-08