最近一天真的都不知道自己在忙什么,当了回Scrum Master。给自己的感觉就是像大学时期,饭堂经理看员工给学生打饭太慢,自己抡起大勺子给学生打菜,偶尔有些学生有情绪还要忍着。说到饭堂,最近都不知道该吃啥。黄焖鸡,biangbiang面,镇川碗托,麻辣香锅,自助餐都吃的没啥可吃了。偶尔�A一碗麦利鸡汤刀削面或者米线凉皮沙县。不说了,这会也确实有些饿,洗个苹果吃一下。
上面这一碗就是我经常吃的biangbiang面。
今天我们主要是来看一下新闻链接的修改和新增,先上图,无图无真相。
看到了吧,这个实现其实很简单,我们在展示每行数据的时候,我们就生成一个编辑模版,只不过编辑模版是隐藏起来的,看代码。
<div ng-repeat="website in WebSites" class="a-list"> <div class="row" style="line-height:40px"> <div class="col-md-1"> <input class="chklist-vertical-align" type="checkbox" ng-model="website.IsChecked" /> </div> <div class="col-md-5"> <a href="{{website.WebSiteURL}}">{{website.WebSiteName}}</a> </div> <div class="col-md-2"> <select class="form-control" ng-change="modifyTopCount(website.TransactionNumber,website.TopNewsCount)" ng-model="website.TopNewsCount" ng-options="number for number in numbers"> @*<option ng-repeat="num in numbers" label="{{num}}" value="{{num}}"></option>*@ </select> </div> <div class="col-md-2"> <span style="cursor: pointer;" class="glyphicon glyphicon-pencil span-grid" ng-click="showedit(website.TransactionNumber)"></span> <span ng-click="newslinkdeleteSingle(website.TransactionNumber)" style="cursor:pointer;margin-left:5px;" class="glyphicon glyphicon-trash span-grid"></span> </div> <div class="col-md-2"> <span style="cursor:pointer;" ng-hide="{{$index==0}}" class="glyphicon glyphicon-arrow-up span-grid" ng-click="modifyPriority(website.TransactionNumber,1)"></span> <span style="cursor: pointer; margin-left: 5px;" ng-hide="{{$index==TotalCount-1}}" ng-click="modifyPriority(website.TransactionNumber,0)" class="glyphicon glyphicon-arrow-down span-grid"></span> </div> </div> <div ng-show="website.IsEditShow" class="form-inline" style="line-height: 40px; background-color: #99CCFF;"> 网站名称: <input id="txt_websitename_{{website.TransactionNumber}}" type="text" maxlength="30" class="form-control" style="width:150px;margin-right:20px" value="{{website.WebSiteName}}" /> URL: <input id="txt_websiteurl_{{website.TransactionNumber}}" placeholder="请以http或者https开头" type="text" maxlength="200" class="form-control" style="width:300px" value="{{website.WebSiteURL}}" /> <img src="~/Images/Base/save.png" ng-click="editsubmit(website.TransactionNumber)" title="保存" alt="保存" class="img-submit" /> <img src="~/Images/Base/undo.png" ng-click="editcancel(website.TransactionNumber)" title="取消" alt="取消" class="img-submit" /> </div> <div class="line-seperate-style"></div> </div>
看到了吧,就是<div ng-show="website.IsEditShow">这一个div用来编辑信息。当我们点击铅笔的时候
调用showedit,并传入本行的主键值TransactionNumber,看一下这个方法。
$scope.showedit = function (transactionNumber) { angular.forEach($scope.WebSites, function (value, key) { if (value.TransactionNumber == transactionNumber) { value.IsEditShow = true; //无法break } }); }
其实这里呢很简单的做了一下循环,当集合中的对象的TransactionNumber和传递的TransactionNumber相等时,将该对象的IsEditShow修改为true,编辑模版就显示出来了。
OK,我们再看一下编辑模版的保存和取消,保存时调用editsubmit方法。
$scope.editsubmit = function (transactionNumber) { if (transactionNumber == undefined) return; var webisteUrl = $.trim(angular.element("#txt_websiteurl_" + transactionNumber).val()); var websiteName = $.trim(angular.element("#txt_websitename_" + transactionNumber).val()); if (websiteName == "") { alert("网站名称不能为空!"); return; } if (webisteUrl == "") { alert("URL不能为空!"); return; } var param = { requestJson: JSON.stringify({ TransactionNumber: transactionNumber, WebSiteURL: webisteUrl, WebSiteName: websiteName }) }; $http({ method: "Put", url: "/NewsManage/ModifyNewsLink", headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, data: $.param(param) }).success(function (data, status, headers, config) { alert(data.msg); if (data.suc == 1) { initData(); } }).error(function (data, status, headers, config) { alert(status); }); };
这里很简单,拿到修改的值去调用后台action做修改,这里后台比较简单,就不说了。
接着我们看取消,点击取消,调用editcancel方法。
$scope.editcancel = function (transactionNumber) { for (var i = 0; i < $scope.WebSites.length; i++) { if ($scope.WebSites[i].TransactionNumber == transactionNumber) { $scope.WebSites[i].IsEditShow = false; angular.element("#txt_websitename_" + transactionNumber).val($scope.WebSites[i].WebSiteName); angular.element("#txt_websiteurl_" + transactionNumber).val($scope.WebSites[i].WebSiteURL); break; } } }
如果取消的话,就将当前的编辑模版隐藏,并将表单值恢复为原始值。在上面我们使用了angular.foreach循环,因为这种写法无法break,所以在这里我还是用for循环好了。
OK,到此,编辑就讲完了,接着我们看一下优先级的设置。
<span style="cursor:pointer;" ng-hide="{{$index==0}}" class="glyphicon glyphicon-arrow-up span-grid" ng-click="modifyPriority(website.TransactionNumber,1)"></span> <span style="cursor: pointer; margin-left: 5px;" ng-hide="{{$index==TotalCount-1}}" ng-click="modifyPriority(website.TransactionNumber,0)" class="glyphicon glyphicon-arrow-down span-grid"></span>
优先级这里,当我们是优先级最高时,不能再升,最低时不能再降。所以这里的ng-hide="{{$index==0}}"和ng-hide="{{$index==TotalCount-1}}"就标识了优先级按钮的显示隐藏,效果如下。
我们看一下这个设置优先级的方法modifyPriority
$scope.modifyPriority = function (transactionNumber, priorityType) { var param = { requestJson: JSON.stringify({ TransactionNumber: transactionNumber, PriorityType: priorityType }) }; $http({ method: "Put", url: "/NewsManage/ModifyNewsPriority", headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, data: $.param(param) }).success(function (data, status, headers, config) { initData(); }).error(function (data, status, headers, config) { alert(status); }); }
没什么逻辑,就是将主键和调整类型(调高还是调低)传递到后台,我们主要还是看下后台的脚本。
DECLARE @TempTranNumber INT DECLARE @TempPriority INT SELECT TOP(1) @TempPriority = Priority FROM dbo.InformationNewsLink WITH(NOLOCK) WHERE TransactionNumber = @TransactionNumber IF @PriorityType = 1 BEGIN SELECT TOP(1) @TempTranNumber = TransactionNumber FROM dbo.InformationNewsLink WITH(NOLOCK) WHERE Priority < @TempPriority ORDER BY Priority DESC IF @TempTranNumber IS NOT NULL BEGIN UPDATE TOP(1) dbo.InformationNewsLink SET Priority = Priority + 1, LastEditDate = GETDATE(), LastEditUser = @UserID WHERE TransactionNumber = @TempTranNumber UPDATE TOP(1) dbo.InformationNewsLink SET Priority = Priority - 1, LastEditDate = GETDATE(), LastEditUser = @UserID WHERE TransactionNumber = @TransactionNumber END END ELSE BEGIN SELECT TOP(1) @TempTranNumber = TransactionNumber FROM dbo.InformationNewsLink WITH(NOLOCK) WHERE Priority > @TempPriority ORDER BY Priority ASC IF @TempTranNumber IS NOT NULL BEGIN UPDATE TOP(1) dbo.InformationNewsLink SET Priority = Priority - 1, LastEditDate = GETDATE(), LastEditUser = @UserID WHERE TransactionNumber = @TempTranNumber UPDATE TOP(1) dbo.InformationNewsLink SET Priority = Priority + 1, LastEditDate = GETDATE(), LastEditUser = @UserID WHERE TransactionNumber = @TransactionNumber END END
很简单,成功之后,调用initData方法刷新界面。
OK,时间不早了,也不知道老吉这家伙睡了没,这家伙就是那个《程序员,你伤不起》的作者,走火入魔.net什么框架的开发者。刚才还在上微信,你说这人都快40了,怎么精力还这么大,得向人家学习呢,不过比谁睡得晚,老吉肯定比不过我。
OK,最后我们看一下新增连接的保存。
<div class="row" style="line-height:40px"> <div class="col-md-1"> <div class="div-circal-list">1</div> </div> <div class="col-md-1"> <input disabled type="checkbox" ng-model="IsChecked" class="chklist-vertical-align" /> </div> <div class="col-md-4"> <input type="text" ng-model="WebSiteNameModel" maxlength="30" style="width:100%" class="form-control" /> </div> <div class="col-md-6"> <input type="text" placeholder="请以http或者https开头" maxlength="200" ng-model="WebSiteURLModel" style="width:100%" class="form-control" /> </div> </div> <div ng-repeat="website in AddedWebSites"> <div class="row" style="line-height:40px"> <div class="col-md-1"> <div class="div-circal-list">{{$index + 2}}</div> </div> <div class="col-md-1"> <input type="checkbox" ng-model="website.IsChecked" class="chklist-vertical-align" /> </div> <div class="col-md-4"> <input type="text" ng-model="website.WebSiteName" maxlength="30" style="width:100%" class="form-control" /> </div> <div class="col-md-6"> <input type="text" placeholder="请以http或者https开头" maxlength="200" ng-model="website.WebSiteURL" style="width:100%" class="form-control" /> </div> </div> </div> <div style="margin-top:20px"> <input type="button" class="btn btn-primary" value="新增" ng-click="addnewlink()" /> <input type="button" class="btn btn-info" value="保存" ng-click="saveWebsiteLink()" /> <input type="button" class="btn btn-danger" value="移除" ng-click="removelink()" />
在这里,上节讲到,第一行就是默认存在的,后面的行就通过AddedWebSites这个对象集合来增加。
这里我看只看保存。
$scope.saveWebsiteLink = function () { var tempAddedWebSites = []; angular.copy($scope.AddedWebSites, tempAddedWebSites); tempAddedWebSites.push({ WebSiteName: $scope.WebSiteNameModel, WebSiteURL: $scope.WebSiteURLModel }); for (var i = 0; i < tempAddedWebSites.length; i++) { if ($.trim(tempAddedWebSites[i].WebSiteName) == "" || $.trim(tempAddedWebSites[i].WebSiteName) == undefined || $.trim(tempAddedWebSites[i].WebSiteURL) == "" || $.trim(tempAddedWebSites[i].WebSiteURL) == undefined) { alert("网站名称和网站URL不能为空!"); return; } if (!angular.uppercase($.trim(tempAddedWebSites[i].WebSiteURL)).startWith("HTTP") && !angular.uppercase($.trim(tempAddedWebSites[i].WebSiteURL)).startWith("HTTPS")) { alert("第" + (i + 1) + "条网站URL没有以Http或者Https开头!"); return; } } var param = { requestJson: JSON.stringify({ WebSiteList: tempAddedWebSites }) }; $http({ method: "Post", url: "/NewsManage/AddNewsLink", headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, data: $.param(param) }).success(function (data, status, headers, config) { alert(data.msg); if (data.suc == 1) { initData(); $scope.WebSiteNameModel = ""; $scope.WebSiteURLModel = ""; $scope.AddedWebSites = []; } }).error(function (data, status, headers, config) { alert(status); }); }
在这里上面是一些check,判断非空和URL格式的,在这里再次强调一下,任何的前端验证都是不可靠的,还是要靠后台验证。我们看一下后端的处理
[HttpPost] public JsonResult AddNewsLink() { string requestJson = Request["requestJson"]; List<NewsLinkEntity> newsLinkList = JsonBuilder.BuildRequestList<NewsLinkEntity>(requestJson, "WebSiteList"); int suc = NewsMngBiz.GetInstance().AddNewsLink(newsLinkList, UserID); if (suc > 0) { return GetJsonMessage("CM_001", JsonMsgType.SUCCESS); } return GetJsonMessage("CM_004"); }
首先得到发送的json数据,然后反序列化为List<NewsLinkEntity>。
public static List<T> BuildRequestList<T>(string requestJson, string key) where T : class,new() { if (string.IsNullOrWhiteSpace(requestJson)) return new List<T>(); JObject jObj = (JObject)JsonConvert.DeserializeObject(requestJson); JToken jToken = jObj[key]; return (List<T>)JsonConvert.DeserializeObject<List<T>>(jToken.ToString()); }
然后调用Biz层
public int AddNewsLink(List<NewsLinkEntity> newLinkEntityList, string userID) { DataTable dt = new DataTable(); dt.Columns.Add("WebSiteName", typeof(string)); dt.Columns.Add("WebSiteURL", typeof(string)); DataRow drow = null; foreach (var newsLinkEntity in newLinkEntityList) { drow = dt.NewRow(); drow.SetField<string>("WebSiteName", newsLinkEntity.WebSiteName); drow.SetField<string>("WebSiteURL", newsLinkEntity.WebSiteURL); dt.Rows.Add(drow); } return NewsMngDAL.GetInstance().AddNewsLink(dt, userID); }
在这里将List<T>转成了DataTable,为什么呢?还记得之前的文章提到过sqlServer处理批量数据。一种是传xml,一种是使用表值变量(table-valued variable)。今天我们就使用一下SqlServer2008的表值变量。首先我们在sqlserver中创建表值变量。
USE [ChinaInformation] GO CREATE TYPE [dbo].[NewsLinkTable] AS TABLE( [WebSiteName] [nvarchar](60) NOT NULL, [WebSiteURL] [varchar](200) NOT NULL ) GO
创建好之后,可以在这里查看
OK,创建好了,我们接着看DAL层。
public int AddNewsLink(DataTable dt,string userID) { string sqlScript = string.Empty; try { sqlScript = DBScriptManager.GetScript(this.GetType(), "AddNewsLink"); SqlParameter[] sqlParameters = { new SqlParameter("@NewLinkTable",SqlDbType.Structured), new SqlParameter("@UserID",SqlDbType.VarChar,15) }; sqlParameters[0].Value = dt; sqlParameters[0].TypeName = "dbo.NewsLinkTable"; sqlParameters[1].Value = userID; return SqlHelper.ExecuteNonQuery(ConstValues.CON_DBConnection, CommandType.StoredProcedure, sqlScript, sqlParameters); } catch (Exception ex) { LogHelper.WriteExceptionLog(MethodBase.GetCurrentMethod(), ex); return -1; } }
在这里需要注意我们的参数@NewLinkTable,它的类型是Structured,它接受的值可以是DataTable,可以是SqlDataReader。这里我们传入DataTable,同时还要指定其TypeName,要不然系统无法判断是哪一种表值。OK,最后,我们看一下保存的脚本sp。
USE ChinaInformation GO ALTER PROCEDURE dbo.UP_NewLinkAddNew ( @NewLinkTable dbo.NewsLinkTable READONLY, @UserID VARCHAR(15) ) AS DECLARE @LastPriority INT DECLARE @WebSiteName NVARCHAR(60) DECLARE @WebSiteURL VARCHAR(20) SELECT TOP(1) @LastPriority = MAX(Priority) FROM dbo.InformationNewsLink WITH(NOLOCK) DECLARE WebSiteLinkCursor CURSOR LOCAL FORWARD_ONLY READ_ONLY FOR SELECT WebSiteName,WebSiteURL FROM @NewLinkTable OPEN WebSiteLinkCursor FETCH NEXT FROM WebSiteLinkCursor INTO @WebSiteName,@WebSiteURL WHILE @@FETCH_STATUS = 0 BEGIN SET @LastPriority = @LastPriority + 1 INSERT INTO dbo.InformationNewsLink ( WebSiteName, WebSiteURL, Priority, InDate, InUser, LastEditDate, LastEditUser ) SELECT @WebSiteName, @WebSiteURL, @LastPriority, GETDATE(), @UserID, GETDATE(), @UserID FETCH NEXT FROM WebSiteLinkCursor INTO @WebSiteName,@WebSiteURL END CLOSE WebSiteLinkCursor DEALLOCATE WebSiteLinkCursor
OK,这里就不多说了,注意传入的第一个参数。如果想要测试该sp,用如下代码即可
DECLARE @Tab AS dbo.NewsLinkTable INSERT INTO @Tab ( WebSiteName, WebSiteURL ) VALUES('1','www.nnn.com'),('1','www.nnn.com') EXEC dbo.UP_NewLinkAddNew @Tab,''
OK,都三点了,老夫也该睡了,磨豆浆,蒸包子的这会也该起来了。