ASP.NET MVC4+BootStrap 实战(十一)

最近一天真的都不知道自己在忙什么,当了回Scrum Master。给自己的感觉就是像大学时期,饭堂经理看员工给学生打饭太慢,自己抡起大勺子给学生打菜,偶尔有些学生有情绪还要忍着。说到饭堂,最近都不知道该吃啥。黄焖鸡,biangbiang面,镇川碗托,麻辣香锅,自助餐都吃的没啥可吃了。偶尔咥一碗麦利鸡汤刀削面或者米线凉皮沙县。不说了,这会也确实有些饿,洗个苹果吃一下。

技术分享

上面这一碗就是我经常吃的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;">
        网站名称:&nbsp;
        <input id="txt_websitename_{{website.TransactionNumber}}" type="text" maxlength="30" class="form-control" style="width:150px;margin-right:20px" value="{{website.WebSiteName}}" />
        URL:&nbsp;
        <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,都三点了,老夫也该睡了,磨豆浆,蒸包子的这会也该起来了。

本文出自 “技术创造价值” 博客,请务必保留此出处http://leelei.blog.51cto.com/856755/1622924

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。