knockoutJs在项目中的使用

最早接触到knockoutJs应该是2017年初了。彼时只是了解了一下大概信息,写了一些简单的例子,没有怎么实际使用过。现在有机会再项目中实战一下,记录一下。

1.项目架构
前端:bootstrap和layer负责页面展示,knockoutJs负责mvvm,引入jquery.validate做表单校验,还有一些其他不怎么重要的,这里没有涉及到,不介绍
后端:ssm框架

2.功能介绍
做一个工作流管理的菜单,没有采用图像化的做法,直接做数据库的增删改查。包括:工作流信息、工作流节点信息、节点关联关系
3.引入knockoutJs

因为是后期功能,因此没有在项目全局引用,只在涉及到的功能中引用了

<%--knockoutJS,mvvm框架的前端部分,可以实现页面数据和js对象的相互映射--%>
<script type="text/javascript" src="${pageContext.request.contextPath}/home/js/knockout-3.4.2.js"></script>
<%--knockoutJS的一个帮助工具:将普通的json对象转为knocoutJS所需的对象--%>
<script type="text/javascript" src="${pageContext.request.contextPath}/home/js/knockout.mapping.js"></script>
knockout.mapping.js是一个knockout的帮助工具。后边会说明,使用时,必须放在knockoutJS之后

4.前端JSP
基本结构如下
每一个div对应js中的一个ViewModel


代码如下,其中涉及到knockout的标签是data-bind=“”
data-bind="click: $root.init"    表示给元素绑定点击事件$root.init,$root代表的是绑定到该div的viewmodel,后边讲解js代码的时候会提到
data-bind="textinput:queryFlowName"    表示input中的value对应ko监控的值 queryFlowName,textinpu是在form中可以输入的元素中使用的,如果只是显示的元素,如td,则用text(data-bind="text: flowName")
foreach循环如下:

<tbody id="linkTbody" data-bind="foreach: linkItems">
                <tr>
                    <td data-bind="text: $index() + 1"></td>
                    <td>
                        <a href="#" data-bind="click : $root.delLink">删除</a>
                        <a href="#" data-bind="click : $root.updLink">修改</a>
                    </td>
                    <td data-bind="text: linkName"></td>
                    <td data-bind="text: nodeName"></td>
                    <td data-bind="text: nextNodeName"></td>
                    <td data-bind="text: conditionStatus"></td>
                </tr>
            </tbody>
其中data-bind="foreach: linkItems"表示要迭代的数组名称是viewmodel中的linkItems
data-bind="text: $index() + 1"    循环时会有一个默认的索引$index(),从0开始
后边的linkName、nodeName等都是linkItems中的对象中拥有的key

绑定多个事件
data-bind="event : {click : $root.trClick, mouseover : $root.trMouseOver, mouseout : $root.trMouseout}"

<%--knockoutJS,mvvm框架的前端部分,可以实现页面数据和js对象的相互映射--%>
<script type="text/javascript" src="${pageContext.request.contextPath}/home/js/knockout-3.4.2.js"></script>
<%--knockoutJS的一个帮助工具:将普通的json对象转为knocoutJS所需的对象--%>
<script type="text/javascript" src="${pageContext.request.contextPath}/home/js/knockout.mapping.js"></script>
<style>
    #linkEditDiv td:nth-child(odd){
        text-align: right;
    }
    #linkEditDiv td:nth-child(even){
        text-align: left;
    }
    #linkEditDiv tr{
        height:50px;
    }
</style>
<%--流程列表--%>
<div class="row">
    <div class="col-md-12" style="text-align:left" id='sysFlowManagePage'>
        <!-- 按钮工具栏 -->
        <ol class="breadcrumb" style="height:25px;padding-top:0;">
            <li>
                <a href="javascript:void(0);" data-bind="click: $root.init">
                    <span class="glyphicon glyphicon-search" style=" font-weight:bold;"></span> 搜索
                </a>
            </li>
            <li>
                <a href="javascript:void(0);" data-bind="click: $root.addFlow">
                    <span class="glyphicon glyphicon-plus" style=" font-weight:bold;"></span> 新增
                </a>
            </li>
        </ol>
        <form id="queryPage">
            <table style="width:100%;" align="center">
                <caption class="table-caption"></caption>
                <tbody>
                <tr>
                    <td class="query_td_right">流程名称:</td>
                    <td class="query_td_left">
                        <input type="text" class="txt query-text" name="flowName" data-bind="textinput:queryFlowName">
                    </td>
                </tr>
                </tbody>
            </table>
        </form>
        <br/>
        <!-- 查询条件table end-->
        <!-- 列表 -->
        <div class="table-responsive">
            <table id="cpaAcctAppList" align="center" class="table table-striped table-bordered table-hover table-fix">
                <thead>
                <tr>
                    <th width="50">序号 </th>
                    <th width="70">操作 </th>
                    <th width="150">流程名称</th>
                    <th width="150">流程描述</th>
                </tr>
                </thead>
                <tbody data-bind="foreach: items">
                <tr>
                    <td data-bind="text: $index() + 1"></td>
                    <td>
                        <a href="#" data-bind="click: $root.updFlow">修改</a>
                        <a href="#" data-bind="click: $root.delFlow">删除</a>
                        <a href="#" data-bind="click: $root.opeFlowLink">流程连接</a>
                        <a href="#" data-bind="click: $root.opeFlowNode">流程节点</a>
                    </td>
                    <td data-bind="text: flowName"></td>
                    <td data-bind="text: flowDesc"></td>
                </tr>
                </tbody>
            </table>
        </div>
    </div>
</div>

<%--修改流程信息--%>
<div id="flowEditDiv" style="display: none;height:140px;" class="layer_notice">
    <ol class="breadcrumb" style="height:25px;padding-top:0;text-align: left;">
        <li>
            <a data-bind="click : $root.ok" href="javascript:void(0);">
                <span class="glyphicon glyphicon-search" style=" font-weight:bold;"></span> 保存
            </a>
        </li>
        <li>
            <a data-bind="click : $root.no" href="javascript:void(0);">
                <span class="glyphicon glyphicon-search" style=" font-weight:bold;"></span> 取消
            </a>
        </li>
    </ol>
    <form role="form" id="updFlowForm">
        <div style="margin-top: 20px">
            <label>流程名称:</label><input data-bind="textinput:flowItem.flowName" type="text" name="roleName" style="width: 180px;"/>
        </div>
        <div style="padding:10px 0 10px 0">
            <label>流程描述:</label><input data-bind="textinput:flowItem.flowDesc" type="text" name="roleDesc" style="width: 180px;"/>
        </div>
    </form>
</div>
<%--显示连接列表--%>
<div id="flowLinkDiv" style="display: none;height:300px;margin:0 auto;" class="layer_notice">
    <ol class="breadcrumb" style="height:25px;padding-top:0;text-align: left;">
        <li>
            <a data-bind="click : $root.addLink" href="javascript:void(0);">
                <span class="glyphicon glyphicon-search" style=" font-weight:bold;"></span> 新增
            </a>
        </li>
    </ol>
    <div class="table-responsive" style="width:800px;margin:10px auto;">
        <table id="linkTable" align="center" class="table table-striped table-bordered table-hover table-fix">
            <thead>
            <tr>
                <th width="50">序号</th>
                <th width="70">操作</th>
                <th width="150">连接名称</th>
                <th width="150">节点名称</th>
                <th width="150">下一个节点名称</th>
                <th width="100">条件状态</th>
            </tr>
            </thead>
            <tbody id="linkTbody" data-bind="foreach: linkItems">
                <tr>
                    <td data-bind="text: $index() + 1"></td>
                    <td>
                        <a href="#" data-bind="click : $root.delLink">删除</a>
                        <a href="#" data-bind="click : $root.updLink">修改</a>
                    </td>
                    <td data-bind="text: linkName"></td>
                    <td data-bind="text: nodeName"></td>
                    <td data-bind="text: nextNodeName"></td>
                    <td data-bind="text: conditionStatus"></td>
                </tr>
            </tbody>
        </table>
    </div>
</div>

<%--修改节点信息--%>
<div id="nodeEditDiv" style="display: none;height:190px;" class="layer_notice">
    <ol class="breadcrumb" style="height:25px;padding-top:0;text-align: left;">
        <li>
            <a data-bind="click : $root.ok" href="javascript:void(0);">
                <span class="glyphicon glyphicon-search" style=" font-weight:bold;"></span> 保存
            </a>
        </li>
        <li>
            <a data-bind="click : $root.no" href="javascript:void(0);">
                <span class="glyphicon glyphicon-search" style=" font-weight:bold;"></span> 取消
            </a>
        </li>
    </ol>
    <form role="form" id="updNodeForm">
        <table style="margin: 15px 40px;">
            <tr style="height: 50px;">
                <td>节点名称:</td>
                <td>
                    <input data-bind="textinput:nodeItem.nodeName" type="text" name="" style="width: 180px;" required/>
                </td>
                <td>所属流程:</td>
                <td>
                    <input data-bind="textinput:nodeItem.flowName" type="text" name="flowName" style="width: 180px;" readonly required/>
                    <span class="glyphicon glyphicon-search" data-bind="click:$root.selFlow"/>
                </td>
            </tr>
            <tr style="height: 50px;">
                <td>节点描述:</td>
                <td><input data-bind="textinput:nodeItem.nodeDesc" type="text" name="nodeDesc" style="width: 180px;"/></td>
                <td>节点类型:</td>
                <td>
                    <input data-bind="textinput:nodeItem.nodeType" type="text" name="_nodeType" style="width: 180px;" required/>
                </td>
            </tr>
        </table>
    </form>
</div>

<%--修改连接信息--%>
<div id="linkEditDiv" style="display: none;height:190px;;" class="layer_notice">
    <ol class="breadcrumb" style="height:25px;padding-top:0;text-align: left;">
        <li>
            <a data-bind="click : $root.ok" href="javascript:void(0);">
                <span class="glyphicon glyphicon-search" style=" font-weight:bold;"></span> 保存
            </a>
        </li>
        <li>
            <a data-bind="click : $root.no" href="javascript:void(0);">
                <span class="glyphicon glyphicon-search" style=" font-weight:bold;"></span> 取消
            </a>
        </li>
    </ol>
    <form role="form" id="updLinkForm">
        <table style="margin: 15px 40px;">
            <tr>
                <td>流转名称:</td>
                <td><input data-bind="textinput:item.linkName" type="text" name="linkName" style="width: 180px;" required/></td>
                <td>本节点名称:</td>
                <td>
                    <input data-bind="textinput:item.nodeName" type="text" name="_nodeName" style="width: 180px;" readonly required/>
                    <span class="glyphicon glyphicon-search" data-bind="click:$root.selNode" desc="node"/>
                </td>
            </tr>
            <tr>
                <td>流转描述:</td>
                <td><input data-bind="textinput:item.linkDesc" type="text" name="linkDesc" style="width: 180px;"/></td>
                <td>下个节点名称:</td>
                <td>
                    <input data-bind="textinput:item.nextNodeName" type="text" name="nextNodeName" style="width: 180px;" readonly required/>
                    <span class="glyphicon glyphicon-search" data-bind="click:$root.selNextNode" desc="nextNode"/>
                </td>
            </tr>
            <tr>
                <td>条件状态:</td>
                <td>
                    <input data-bind="textinput:item.conditionStatus" type="text" name="conditionStatus" style="width: 180px;" required/>
                </td>
                <td></td>
                <td></td>
            </tr>
        </table>
    </form>
</div>

<%--选择节点--%>
<div id="selNode" style="display: none;height:50px;margin:0 auto;" class="layer_notice">
    <ol class="breadcrumb" style="height:25px;padding-top:0;text-align: left;">
        <li>
            <a data-bind="click : $root.addNode" href="javascript:void(0);">
                <span class="glyphicon glyphicon-search" style=" font-weight:bold;"></span> 新增
            </a>
        </li>
    </ol>
    <div class="table-responsive" style="width: 800px;margin: 10px auto;">
        <table id="nodeTable" align="center" class="table table-striped table-bordered table-hover table-fix">
            <thead>
            <tr>
                <th width="50">序号</th>
                <th width="60">操作</th>
                <th width="150">节点名称</th>
                <th width="50">节点类型</th>
                <th width="150">节点描述</th>
            </tr>
            </thead>
            <tbody id="nodeTbody" data-bind="foreach: nodeItems">
            <tr data-bind="event : {click : $root.trClick, mouseover : $root.trMouseOver, mouseout : $root.trMouseout}">
                <td data-bind="text: $index() + 1"></td>
                <td>
                    <a href="javascript:void(0)" data-bind="click : $root.updNode">修改</a>
                    <a href="javascript:void(0)" data-bind="click : $root.delNode">删除</a>
                </td>
                <td data-bind="text: nodeName"></td>
                <td data-bind="text: nodeType"></td>
                <td data-bind="text: nodeDesc"></td>
            </tr>
            </tbody>
        </table>
    </div>
</div>

<%--选择流程--%>
<div id="selFlow" style="display: none;height:50px;margin:0 auto;" class="layer_notice">
    <div class="table-responsive" style="width: 800px;margin: 10px auto;">
        <table id="flowTable" align="center" class="table table-striped table-bordered table-hover table-fix">
            <thead>
            <tr>
                <th width="50">序号</th>
                <%--<th width="60">操作</th>--%>
                <th width="150">流程名称</th>
                <th width="150">流程描述</th>
            </tr>
            </thead>
            <tbody id="flowTbody" data-bind="foreach: flowItems">
            <tr data-bind="event : {click : $root.trClick, mouseover : $root.trMouseOver, mouseout : $root.trMouseout}">
                <td data-bind="text: $index() + 1"></td>
                <%--<td>--%>
                    <%--<a href="javascript:void(0)" data-bind="click : $root.updNode">修改</a>--%>
                    <%--<a href="javascript:void(0)" data-bind="click : $root.delNode">删除</a>--%>
                <%--</td>--%>
                <td data-bind="text: flowName"></td>
                <td data-bind="text: flowDesc"></td>
            </tr>
            </tbody>
        </table>
    </div>
</div>

<script type="text/javascript" src="${pageContext.request.contextPath}/js/sys_flow/sys_flow.js"></script>
5.前端js
基本结构如下
代码如下

(function () {
    var linkUpdLayer,selNodeLayer,selFlowLayer,flowUpdLayer,
        flowList ,
        baseUrl = contentPath + '/sysFlow/';
    $(function () {
        var vm = new TableViewModel();
        ko.applyBindings(vm, $('#sysFlowManagePage')[0]);
        vm.init();
    });

    /**
     * 流程节点ViewModel
     * @param linkItemKo 连接信息
     * @param flowItemKo 流程信息
     * @param t 类型
     * @constructor
     */
    function NodeListViewModel(linkItemKo,flowItemKo,t) {
        var self = this;
        self.linkItem = linkItemKo;
        self.flowItem = flowItemKo;
        self.nodeItems = ko.observableArray([]);
        //是否是流程操作
        self.isLink = (function () {
            return !(linkItemKo === null || !linkItemKo);
        })();
        //初始化节点列表
        self.initNodeTable = function () {
            $.postAjax(baseUrl+'queryFlowNode',{flowId:self.isLink?linkItemKo.flowId():flowItemKo.flowId()},function (data) {
                self.nodeItems(data);
            });
        };
        //鼠标移入事件
        self.trMouseOver = function (item,event) {
            if(!self.isLink){
                return;
            }
            trMouseOver($(event.target));
        };

        //鼠标移出事件
        self.trMouseout = function(){
            if(!self.isLink){
                return;
            }
            trMouseOut($(event.target));
        };
        //鼠标点击事件
        self.trClick = function (item) {
            if(!self.isLink){
                return;
            }
            switch (t){
                case 1:
                    self.linkItem.nodeName(item.nodeName);
                    self.linkItem.nodeId(item.nodeId);
                    break;
                case 2:
                    self.linkItem.nextNodeName(item.nodeName);
                    self.linkItem.nextNodeId(item.nodeId);
                    break;
            }
            if(selNodeLayer){
                layer.close(selNodeLayer);
            }
        };
        self.addNode = function () {
            var newNodeItem = {
                flowId: '',flowName : '',
                nodeName: '', nodeDesc: '',nodeType:''
            };
            var nodeItemKo = new NodeEditViewModel(ko.mapping.fromJS(newNodeItem),self);
            nodeOpenCommon('新增节点信息',nodeItemKo);
        };
        //删除节点
        self.delNode = function(item){
            if($.isEmpty(item.nodeId)){
                layer.alert('发生错误!');
                return;
            }
            var con = layer.confirm('确定删除该记录吗?', {
                btn: ['确定','取消'] //按钮
            }, function(){
                layer.close(con);
                $.postAjax(baseUrl+'delNode',{nodeId : item.nodeId},function (state) {
                    if(state.success){
                        layer.msg('删除成功!');
                        self.initNodeTable();
                    }else{
                        layer.alert('删除失败!失败原因:' + state.message);
                    }
                });
            }, function(){});

        };
        //修改节点
        self.updNode = function (item) {
            if(flowList){
                for(var i=0;i<flowList.length;i++){
                    if(item.flowId === flowList[i].flowId){
                        item.flowName = flowList[i].flowName;
                        break;
                    }
                }
            }
            var updNodeVm = new NodeEditViewModel(ko.mapping.fromJS(item),self);
            nodeOpenCommon('修改节点信息',updNodeVm);
        };
        /**
         * 节点信息修改ViewModel
         * @param nodeItemKo
         * @param nodeTableViewModel
         * @constructor
         */
        function NodeEditViewModel(nodeItemKo,nodeTableViewModel) {
            var nodeSelf = this;
            nodeSelf.nodeItem = nodeItemKo;
            //取消
            nodeSelf.no = function () {
                closeNodeOpenCommon();
            };

            //选择流程
            nodeSelf.selFlow = function (item) {
                var selFlow = document.getElementById('selFlow'),
                    $flowTbody = $('#flowTbody'),
                    flowTbodyHtml = $flowTbody.html(),
                    flowListViewModel = new FlowListViewModel(ko.mapping.fromJS(item));
                selFlowLayer = layer.open({
                    title: '选择流程',
                    type: 1,
                    content: $(selFlow),
                    area: ['880px', '460px'],
                    success : function () {
                        ko.cleanNode(selFlow);
                        ko.applyBindings(flowListViewModel, selFlow);
                        flowListViewModel.initFlowTable();
                    },
                    end : function () {
                        $flowTbody.html(flowTbodyHtml);
                        $(selFlow).hide();
                    }
                });
            };
            /**
             * 选择流程
             * @constructor
             */
            function FlowListViewModel(nodeItemKo) {
                var self = this;
                self.nodeItem = nodeItemKo;
                self.flowItems = ko.observableArray([]);
                //初始化列表
                self.initFlowTable = function () {
                    $.postAjax(baseUrl + 'queryFlow', null, function (data) {
                        self.flowItems(data);
                    });
                };

                //鼠标移入事件
                self.trMouseOver = function () {
                    trMouseOver($(event.target));
                };

                //鼠标移出事件
                self.trMouseout = function(){
                    trMouseOut($(event.target));
                };
                //鼠标点击事件
                self.trClick = function (item) {
                    self.nodeItem.nodeItem.flowName(item.flowName);
                    self.nodeItem.nodeItem.flowId(item.flowId);
                    if(selFlowLayer){
                        layer.close(selFlowLayer);
                    }
                };
            }
            //校验并保存
            nodeSelf.ok = function (item) {
                var validateOptions = Validation;
                validateOptions.validateOptions = true;
                validateOptions.messages = {
                    _nodeName: '请输入流转名称',
                    _nodeType : '请输入节点类型',
                    flowName : '请选择所属流程'
                };
                validateOptions.errorPlacement = function (label, element) {};
                validateOptions.submitHandler=function (form) {};
                var $updNodeForm = $('#updNodeForm');
                $updNodeForm.validate(validateOptions);
                var is = $updNodeForm.valid();
                if(!is){
                    layer.alert('信息不完整');
                    return;
                }
                $.postAjax(baseUrl+'saveOrUpdFlowNode',ko.mapping.toJS(item.nodeItem),function (state) {
                    if(state.success){
                        layer.alert('保存成功!');
                        closeNodeOpenCommon();
                        nodeTableViewModel.initNodeTable();
                    }else{
                        layer.alert('保存失败!');
                    }
                });
            };
        }
        // nodeEditDiv
        var nodeLayer,
            nodeEditDiv = document.getElementById('nodeEditDiv');

        /**
         * 关闭nodeLayer
         */
        function closeNodeOpenCommon() {
            if(nodeLayer){
                layer.close(nodeLayer);
            }
        }

        /**
         * 新增和修改节点信息的公用layer
         * @param title
         * @param nodeItemKo
         */
        function nodeOpenCommon(title,nodeItemKo) {
            nodeLayer = layer.open({
                title: title,
                type: 1,
                content: $(nodeEditDiv),
                area: ['600px', '250px'],
                success : function () {
                    ko.cleanNode(nodeEditDiv);
                    ko.applyBindings(nodeItemKo, nodeEditDiv);
                },
                end : function () {
                    $(nodeEditDiv).hide();
                }
            });
        }
    }
    /**
     * 修改Link信息
     * @constructor
     */
    function LinkEditViewModel(linkItem,linkTableViewModel) {
        var self = this;
        self.item = ko.mapping.fromJS(linkItem);
        //选择本节点
        self.selNode = function () {
            selNodeCommon(1,self.item,null,'选择本节点');
        };
        //选择下一个节点
        self.selNextNode = function () {
            selNodeCommon(2,self.item,null,'选择下一个节点');
        };

        //取消
        self.no = function () {
            closeLinkUpdLayer();
        };
        //保存
        self.ok = function () {
            var validateOptions = Validation;
            validateOptions.validateOptions = true;
            validateOptions.messages = {
                linkName: '请输入流转名称',
                conditionStatus : '请输入条件状态',
                _nodeName : '请选择本节点名称',
                nextNodeName : '请选择下一个节点名称'
            };
            validateOptions.errorPlacement = function (label, element) {};
            validateOptions.submitHandler=function (form) {};
            var $updLinkForm = $('#updLinkForm');
            $updLinkForm.validate(validateOptions);
            var is = $updLinkForm.valid();
            if(!is){
                layer.alert('信息不完整');
                return;
            }
            $.postAjax(baseUrl+'saveOrUpdFlowLink',ko.mapping.toJS(self.item),function (state) {
                if(state.success){
                    layer.alert('保存成功!');
                    closeLinkUpdLayer();
                    linkTableViewModel.initLinkTable();
                }else{
                    layer.alert('保存失败!');
                }
            });
        };

        //关闭layer弹出框
        function closeLinkUpdLayer() {
            if(linkUpdLayer){
                layer.close(linkUpdLayer);
            }
        }
    }

    /**
     * 鼠标移入事件
     */
    function trMouseOver() {
        var $target = $(event.target);
        if($target.is('td')){
            $target = $target.parent();
        }
        $target.css('background-color','#CCFF80');
        $target.css('cursor','pointer');
    }

    /**
     * 鼠标移除事件
     */
    function trMouseOut() {
        var $target = $(event.target);
        if($target.is('td')){
            $target = $target.parent();
        }
        $target.css('background-color','white');
        $target.css('cursor','pointer');
    }
    /**
     * 流程节点
     * @param t
     * @param linkItemKo
     * @param flowItemKo
     * @param title
     */
    function selNodeCommon(t,linkItemKo,flowItemKo,title) {
        var selNode = document.getElementById('selNode'),
            $nodeTbody = $('#nodeTbody'),
            nodeTbodyHtml = $nodeTbody.html(),
            nodeListViewModel = new NodeListViewModel(linkItemKo,flowItemKo,t);
        selNodeLayer = layer.open({
            title: title,
            type: 1,
            content: $(selNode),
            area: ['880px', '460px'],
            success : function () {
                ko.cleanNode(selNode);
                ko.applyBindings(nodeListViewModel, selNode);
                nodeListViewModel.initNodeTable();
            },
            end : function () {
                $nodeTbody.html(nodeTbodyHtml);
                $(selNode).hide();
            }
        });
    }
    /**
     * 查看流程Link信息的ViewModel
     * @param flowItem
     * @constructor
     */
    function LinkTableViewModel(flowItem){
        var self = this;
        var linkEditDiv = document.getElementById('linkEditDiv'),flowId = $.trim(flowItem.flowId);
        self.linkItems = ko.observableArray([]);
        //初始化列表
        self.initLinkTable = function () {
            $.postAjax(baseUrl + 'selectLinkByFlowId', {flowId : flowId}, function (data) {
                self.linkItems(data);
            });
        };
        //新增
        self.addLink = function(){
            var newItem = {
                flowId: flowId,
                linkName: '', linkDesc: '',
                nodeId: '', nodeName: '',
                nextNodeId: '', nextNodeName: '',
                conditionStatus: ''
            };
            var updLinkVm = new LinkEditViewModel(ko.mapping.fromJS(newItem),self);
            linkOpenCommon('新增连接信息',updLinkVm);
        };
        //修改
        self.updLink = function (item) {
            var updLinkVm = new LinkEditViewModel(item,self);
            linkOpenCommon('修改连接信息',updLinkVm);
        };
        //删除
        self.delLink = function (item) {
            if($.isEmpty(item.linkId)){
                layer.alert('发生错误!');
                return;
            }
            var con = layer.confirm('确定删除该记录吗?', {
                btn: ['确定','取消'] //按钮
            }, function(){
                layer.close(con);
                $.postAjax(baseUrl+'delLink',{linkId : item.linkId},function (state) {
                    if(state.success){
                        layer.msg('删除成功!');
                        self.initLinkTable();
                    }else{
                        layer.alert('删除失败!失败原因:' + state.message);
                    }
                });
            }, function(){});

        };
        /**
         * link的layer弹出框公用部分
         * @param title layer弹出框的标题
         * @param updLinkVm
         */
        function linkOpenCommon(title,updLinkVm) {
            linkUpdLayer = layer.open({
                title: title,
                type: 1,
                content: $(linkEditDiv),
                area: ['600px', '250px'],
                success : function () {
                    ko.cleanNode(linkEditDiv);//取消绑定
                    ko.applyBindings(updLinkVm, linkEditDiv);//绑定
                },
                end :function () {
                    $(linkEditDiv).hide();
                }
            });
        }
    }

    /**
     * 列表页的ViewModel
     * @constructor
     */
    function TableViewModel() {
        var self = this,
            flowEditDiv = document.getElementById('flowEditDiv');
        self.items = ko.observableArray([]);
        self.queryFlowName = ko.observable('');
        self.delFlow = function (item) {
            if($.isEmpty(item.flowId)){
                layer.alert('发生错误!');
                return;
            }
            //TODO:注意,此处尚未做级联删除
            var con = layer.confirm('如果删除该记录,该记录下的节点信息将同时删除.<br>确定删除该记录吗?', {
                btn: ['确定','取消'] //按钮
            }, function(){
                layer.close(con);
                $.postAjax(baseUrl+'delFlow',{flowId : item.flowId},function (state) {
                    if(state.success){
                        layer.msg('删除成功!');
                        self.init();
                    }else{
                        layer.alert('删除失败!失败原因:' + state.message);
                    }
                });
            }, function(){});
        };
        //新增
        self.addFlow = function () {
            var addVm = ko.mapping.fromJS({flowName:'',flowDesc:''});
            flowOpenCommon(flowEditDiv,addVm,'新增工作流信息');
        };
        //修改
        self.updFlow = function (item) {
            var updVm = ko.mapping.fromJS(item);
            flowOpenCommon(flowEditDiv,updVm,'修改工作流信息');
        };

        function flowOpenCommon(flowEditDiv,updVm,title) {
            flowUpdLayer = layer.open({
                title: title,
                type: 1,
                content: $(flowEditDiv),
                area: ['320px', '215px'],
                success:function () {
                    ko.cleanNode(flowEditDiv);//取消绑定
                    var flowEditViewModel = new FlowEditViewModel(updVm);
                    ko.applyBindings(flowEditViewModel, flowEditDiv);//绑定
                },
                end:function () {
                    $(flowEditDiv).hide();
                }
            });
        }

        //初始化列表
        self.init = function () {
            $.postAjax(baseUrl + 'queryFlow', {flowName: $.trim(self.queryFlowName())}, function (data) {
                flowList = data;
                self.items(data);
            });
        };

        //流程节点
        self.opeFlowNode = function (item) {
            selNodeCommon(null,null,ko.mapping.fromJS(item),'流程节点信息');
        };

        function FlowEditViewModel(flowItemKo) {
            var flowSelf = this;
            flowSelf.flowItem = flowItemKo;

            flowSelf.ok = function () {
                var validateOptions = Validation;
                validateOptions.validateOptions = true;
                validateOptions.messages = {
                    flowName: '请输入工作流名称'
                };
                validateOptions.errorPlacement = function (label, element) {};
                validateOptions.submitHandler=function (form) {};
                var $updFlowForm = $('#updFlowForm');
                $updFlowForm.validate(validateOptions);
                var is = $updFlowForm.valid();
                if(!is){
                    layer.alert('信息不完整');
                    return;
                }
                $.postAjax(baseUrl+'saveOrUpdFlow',ko.mapping.toJS(flowSelf.flowItem),function (state) {
                    if(state.success){
                        layer.alert('保存成功!');
                        closeFlowLayer();
                        self.init();
                    }else{
                        layer.alert('保存失败!');
                    }
                });
            };
            flowSelf.no = function () {
                closeFlowLayer();
            };
            function closeFlowLayer() {
                if(flowUpdLayer){
                    layer.close(flowUpdLayer);
                }
            }
        }
        //流程连接
        self.opeFlowLink = function (item) {
            //将tbody缓存起来,解决数据重复问题
            var $linkTbody = $('#linkTbody'),
                tbody = $linkTbody.html(),
                flowLinkDiv = document.getElementById('flowLinkDiv');
            linkUpdLayer = layer.open({
                title: '工作流连接信息',
                type: 1,
                content: $(flowLinkDiv),
                area: ['880px', '450px'],
                success : function(){
                    var linkTableViewModel = new LinkTableViewModel(item);
                    ko.cleanNode(flowLinkDiv);//取消绑定
                    ko.applyBindings(linkTableViewModel, flowLinkDiv);
                    linkTableViewModel.initLinkTable();
                },
                end:function () {
                    $(flowLinkDiv).hide();
                    $linkTbody.html(tbody);
                }
            });
    }
})();
关键代码说明
/**
    * 声明一个TableViewModel
    */
    function TableViewModel() {
        var self = this,
            flowEditDiv = document.getElementById('flowEditDiv');
        //监控对象是一个数组
        self.items = ko.observableArray([]);
        //监控对象是一个字符串
        self.queryFlowName = ko.observable('');
        self.delFlow = function (item) {
            if($.isEmpty(item.flowId)){
                layer.alert('发生错误!');
                return;
            }
            var con = layer.confirm('如果删除该记录,该记录下的节点信息将同时删除.<br>确定删除该记录吗?', {
                btn: ['确定','取消'] //按钮
            }, function(){
                layer.close(con);
                $.postAjax(baseUrl+'delFlow',{flowId : item.flowId},function (state) {
                    if(state.success){
                        layer.msg('删除成功!');
                        self.init();
                    }else{
                        layer.alert('删除失败!失败原因:' + state.message);
                    }
                });
            }, function(){});
        };
        //新增
        self.addFlow = function () {
            //注意这里的ko.mapping.fromJS,刚开始的时候,我们不是提到了一个ko.mapping.js吗?这里就是其中的一个功能
            //ko监控的对象结构如下:
            //{key:ko.observable(value)}
            //也就是需要ko.observable声明一下,如果需要监控的对象键值对比较多,一个个转就太麻烦了
            //使用ko.mapping.fromJS就可以直接转换了
            //另外,ko.mapping.toJS则可以反过来将一个ko监控对象转为正常的json对象
            var addVm = ko.mapping.fromJS({flowName:'',flowDesc:''});
            flowOpenCommon(flowEditDiv,addVm,'新增工作流信息');
        };
        //修改
        self.updFlow = function (item) {
            var updVm = ko.mapping.fromJS(item);
            flowOpenCommon(flowEditDiv,updVm,'修改工作流信息');
        };

        function flowOpenCommon(flowEditDiv,updVm,title) {
            flowUpdLayer = layer.open({
                title: title,
                type: 1,
                content: $(flowEditDiv),
                area: ['320px', '215px'],
                success:function () {
                    ko.cleanNode(flowEditDiv);//取消绑定
                    var flowEditViewModel = new FlowEditViewModel(updVm);
                    ko.applyBindings(flowEditViewModel, flowEditDiv);//绑定
                },
                end:function () {
                    $(flowEditDiv).hide();
                }
            });
        }

        //初始化列表
        self.init = function () {
            $.postAjax(baseUrl + 'queryFlow', {flowName: $.trim(self.queryFlowName())}, function (data) {
                flowList = data;
                //给ko监控对象赋值,不能使用=,
                //因为ko监控对象的结构并不是简单的json对象格式
                //需要使用()赋值,如下
                self.items(data);
            });
        };

        //流程节点
        self.opeFlowNode = function (item) {
            selNodeCommon(null,null,ko.mapping.fromJS(item),'流程节点信息');
        };

        function FlowEditViewModel(flowItemKo) {
            var flowSelf = this;
            flowSelf.flowItem = flowItemKo;

            flowSelf.ok = function () {
                var validateOptions = Validation;
                validateOptions.validateOptions = true;
                validateOptions.messages = {
                    flowName: '请输入工作流名称'
                };
                validateOptions.errorPlacement = function (label, element) {};
                validateOptions.submitHandler=function (form) {};
                var $updFlowForm = $('#updFlowForm');
                $updFlowForm.validate(validateOptions);
                var is = $updFlowForm.valid();
                if(!is){
                    layer.alert('信息不完整');
                    return;
                }
                $.postAjax(baseUrl+'saveOrUpdFlow',ko.mapping.toJS(flowSelf.flowItem),function (state) {
                    if(state.success){
                        layer.alert('保存成功!');
                        closeFlowLayer();
                        self.init();
                    }else{
                        layer.alert('保存失败!');
                    }
                });
            };
            flowSelf.no = function () {
                closeFlowLayer();
            };
            function closeFlowLayer() {
                if(flowUpdLayer){
                    layer.close(flowUpdLayer);
                }
            }
        }
        //流程连接
        self.opeFlowLink = function (item) {
            //将tbody缓存起来,解决数据重复问题
            var $linkTbody = $('#linkTbody'),
                tbody = $linkTbody.html(),
                flowLinkDiv = document.getElementById('flowLinkDiv');
            linkUpdLayer = layer.open({
                title: '工作流连接信息',
                type: 1,
                content: $(flowLinkDiv),
                area: ['880px', '450px'],
                success : function(){
                    var linkTableViewModel = new LinkTableViewModel(item);
                    ko.cleanNode(flowLinkDiv);//取消绑定
                    ko.applyBindings(linkTableViewModel, flowLinkDiv);
                    linkTableViewModel.initLinkTable();
                },
                end:function () {
                    $(flowLinkDiv).hide();
                    $linkTbody.html(tbody);
                }
            });
    }
//上边提到的$root指的既是这里new出来的TableViewModel
//$('#sysFlowManagePage')[0]要绑定的元素,也是TableViewModel的作用域,也就是说在$('#sysFlowManagePage')[0])中出现的data-bind归TableViewModel管
//注意,不能重复绑定,如果需要清除,使用ko.cleanNode($('#sysFlowManagePage')[0]);清除绑定
$(function () {
        var vm = new TableViewModel();
        ko.applyBindings(vm, $('#sysFlowManagePage')[0]);
        vm.init();
    });


6.后端接口
结构
比较简单,查出需要的数据返回前端,前端需要保存删除的数据后端删除等

7.注意点
ko监控对象的结构不再是简单的json结构,这也是引入ko.mapping的原因
注意重复绑定的问题,如果ko.applyBindings没有指定绑定作用域的话,默认是当前的document


  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值