2017年12月30日 10:02
原创作品,转载时请务必以超链接形式标明文章原始出处,否则将追究法律责任。

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

6365022501440937784598647.jpg

上面这一碗就是我经常吃的biangbiang面。


今天我们主要是来看一下新闻链接的修改和新增,先上图,无图无真相。

mvclineedit.PNG

看到了吧,这个实现其实很简单,我们在展示每行数据的时候,我们就生成一个编辑模版,只不过编辑模版是隐藏起来的,看代码。

 <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}}"就标识了优先级按钮的显示隐藏,效果如下。

wKioL1UNuVSRHmg9AAJCzcBL_1s560.jpg

我们看一下这个设置优先级的方法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了,怎么精力还这么大,得向人家学习呢,不过比谁睡得晚,老吉肯定比不过我。

wKiom1UNuiez_7hrAAm73IZnD6w270.jpg


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

创建好之后,可以在这里查看

wKioL1UNwN3SPXnrAACnztvDArc870.jpg

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,都三点了,老夫也该睡了,磨豆浆,蒸包子的这会也该起来了。


发表评论
匿名  
用户评论
暂无评论