(转) 淘淘商城系列——商品类目选择的实现

http://blog.csdn.net/yerenyuan_pku/article/details/72786989

毛主席讲过:“会当水击三千里,自信人生两百年”。当我们实现商品列表查询这个功能之后,我相信大家都信心倍增,但这只是走完万里长征的第一步,后面还有嵩山峻岭需要我们不懈攀登,所以千万不可懈怠人生。本文我就来教大家如何实现商品类目的选择。 
首先,当然还是启动我们的zookeeper注册中心,然后启动taotao-manager工程和taotao-manager-web工程,启动后我们点击”新增商品”,然后点击”选择类目”,打开的对话框是空白的,没有任何信息。这是因为我们还没有实现该界面的展示。 

我们还是从index.jsp页面开始说起,如下图所示,可以看到当我们点击左侧菜单中的”新增商品”时,是会自动去请求item-add.jsp页面的。我们在之前就已经实现了展示后台界面的功能。 

我们下面来看看item-add.jsp页面的代码,我们从中可以看到,这个页面其实也是个代码片段,”选择类目”按钮其实是个链接,样式为”easyui-linkbutton”,我们会发现该链接并没有指定onclick事件,那么该链接是怎样触发鼠标点击事件的呢?其实是”.selectItemCat”样式来处理的,我们在item-add.jsp页面当中搜索会发现并没有定义”selectItemCat”,那么它到底是在哪儿定义的呢?

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<link href="/js/kindeditor-4.1.10/themes/default/default.css" type="text/css" rel="stylesheet">
<script type="text/javascript" charset="utf-8" src="/js/kindeditor-4.1.10/kindeditor-all-min.js"></script>
<script type="text/javascript" charset="utf-8" src="/js/kindeditor-4.1.10/lang/zh_CN.js"></script>
<div style="padding:10px 10px 10px 10px">
    <form id="itemAddForm" class="itemForm" method="post">
        <table cellpadding="5">
            <tr>
                <td>商品类目:</td>
                <td>
                    <a href="javascript:void(0)" class="easyui-linkbutton selectItemCat">选择类目</a>
                    <input type="hidden" name="cid" style=" 280px;"></input>
                </td>
            </tr>
            <tr>
                <td>商品标题:</td>
                <td><input class="easyui-textbox" type="text" name="title" data-options="required:true" style=" 280px;"></input></td>
            </tr>
            <tr>
                <td>商品卖点:</td>
                <td><input class="easyui-textbox" name="sellPoint" data-options="multiline:true,validType:'length[0,150]'" style="height:60px; 280px;"></input></td>
            </tr>
            <tr>
                <td>商品价格:</td>
                <td><input class="easyui-numberbox" type="text" name="priceView" data-options="min:1,max:99999999,precision:2,required:true" />
                    <input type="hidden" name="price"/>
                </td>
            </tr>
            <tr>
                <td>库存数量:</td>
                <td><input class="easyui-numberbox" type="text" name="num" data-options="min:1,max:99999999,precision:0,required:true" /></td>
            </tr>
            <tr>
                <td>条形码:</td>
                <td>
                    <input class="easyui-textbox" type="text" name="barcode" data-options="validType:'length[1,30]'" />
                </td>
            </tr>
            <tr>
                <td>商品图片:</td>
                <td>
                     <a href="javascript:void(0)" class="easyui-linkbutton picFileUpload">上传图片</a>
                     <input type="hidden" name="image"/>
                </td>
            </tr>
            <tr>
                <td>商品描述:</td>
                <td>
                    <textarea style="800px;height:300px;visibility:hidden;" name="desc"></textarea>
                </td>
            </tr>
            <tr class="params hide">
                <td>商品规格:</td>
                <td>

                </td>
            </tr>
        </table>
        <input type="hidden" name="itemParams"/>
    </form>
    <div style="padding:5px">
        <a href="javascript:void(0)" class="easyui-linkbutton" onclick="submitForm()">提交</a>
        <a href="javascript:void(0)" class="easyui-linkbutton" onclick="clearForm()">重置</a>
    </div>
</div>
<script type="text/javascript">
    var itemAddEditor ;
    //页面初始化完毕后执行此方法
    $(function(){
        //创建富文本编辑器
        itemAddEditor = TAOTAO.createEditor("#itemAddForm [name=desc]");
        //初始化类目选择和图片上传器
        TAOTAO.init({fun:function(node){
            //根据商品的分类id取商品 的规格模板,生成规格信息。第四天内容。
            //TAOTAO.changeItemParam(node, "itemAddForm");
        }});
    });
    //提交表单
    function submitForm(){
        //有效性验证
        if(!$('#itemAddForm').form('validate')){
            $.messager.alert('提示','表单还未填写完成!');
            return ;
        }
        //取商品价格,单位为“分”
        $("#itemAddForm [name=price]").val(eval($("#itemAddForm [name=priceView]").val()) * 100);
        //同步文本框中的商品描述
        itemAddEditor.sync();
        //取商品的规格
        /*
        var paramJson = [];
        $("#itemAddForm .params li").each(function(i,e){
            var trs = $(e).find("tr");
            var group = trs.eq(0).text();
            var ps = [];
            for(var i = 1;i<trs.length;i++){
                var tr = trs.eq(i);
                ps.push({
                    "k" : $.trim(tr.find("td").eq(0).find("span").text()),
                    "v" : $.trim(tr.find("input").val())
                });
            }
            paramJson.push({
                "group" : group,
                "params": ps
            });
        });
        //把json对象转换成字符串
        paramJson = JSON.stringify(paramJson);
        $("#itemAddForm [name=itemParams]").val(paramJson);
        */
        //ajax的post方式提交表单
        //$("#itemAddForm").serialize()将表单序列号为key-value形式的字符串
        $.post("/item/save",$("#itemAddForm").serialize(), function(data){
            if(data.status == 200){
                $.messager.alert('提示','新增商品成功!');
            }
        });
    }

    function clearForm(){
        $('#itemAddForm').form('reset');
        itemAddEditor.html('');
    }
</script>
  • 1

我们可以使用Ctrl+H快捷键来打开全文搜素对话框,在”File Search”一栏的下面那个输入框中输入我们想要搜素的内容,下面指定在哪些类型的文件中搜素,然后点击”Search”。 

搜索完之后,可以看到如下图所示的搜索结果,我们双击进去。 

可以看到在common.js文件中有关于”.selectItemCat”的事件定义。那么大家可能也注意到了,在item-add.jsp页面当中并没有引用common.js文件,为何能使用这个js文件呢,其实很简单,这是因为在index.jsp页面中引用了该js文件,而item-add.jsp页面只是index.jsp页面的一个代码片段而已,因此可以直接使用index.jsp页面中引用的js文件。 

那么问题来了,这个选择类目按钮是什么时候被初始化的呢?我们可以从item-add.jsp下面的js代码中找到答案,如下图所示,可以看到,$(function){}是页面加载完之后会执行的脚本,在这里面有TAOTAO.init方法来初始化类目选择,那么问题又来了,这个TAOTAO又是在哪儿定义的呢? 

其实TAOTAO也是在common.js文件中定义的,如下所示,可以看到TAOTAO中定义了一个init方法,在该方法中调用了this.initItemCat(data);来初始化类目选择组件。 

this.initItemCat(data);所对应的代码如下:

// 初始化选择类目组件
initItemCat : function(data){
    $(".selectItemCat").each(function(i,e){
        var _ele = $(e);
        if(data && data.cid){
            _ele.after("<span style='margin-left:10px;'>"+data.cid+"</span>");
        }else{
            _ele.after("<span style='margin-left:10px;'></span>");
        }
        _ele.unbind('click').click(function(){
            $("<div>").css({padding:"5px"}).html("<ul>")
            .window({
                '500',
                height:"450",
                modal:true,
                closed:true,
                iconCls:'icon-save',
                title:'选择类目',
                onOpen : function(){
                    var _win = this;
                    $("ul",_win).tree({
                        url:'/item/cat/list',
                        animate:true,
                        onClick : function(node){
                            if($(this).tree("isLeaf",node.target)){
                                // 填写到cid中
                                _ele.parent().find("[name=cid]").val(node.id);
                                _ele.next().text(node.text).attr("cid",node.id);
                                $(_win).window('close');
                                if(data && data.fun){
                                    data.fun.call(this,node);
                                }
                            }
                        }
                    });
                },
                onClose : function(){
                    $(this).window("destroy");
                }
            }).window('open');
        });
    });
}, 
  • 1

我们可以看到$(".selectItemCat").each(function(i,e){这行代码,为何要用each来循环呢?这是因为使用.selectItemCat样式的地方可能不止一处,这样初始化就不能只初始化一处,而是应该把所有用到该样式的地方都进行初始化,i是遍历的第几个元素,e是指被遍历的对象,var _ele = $(e);的作用是把原生dom对象转换为jQuery对象,这样就能直接使用jQuery的方法了。_ele.unbind('click').click(function(){的意思是先进行解绑,然后再进行绑定,其中渲染了一个window窗口对象:

  • 设置了宽(width)和高(height)。
  • modal:true,是指是否使用模态(就是我们打开对话框之后,后面是灰色的背景,不能操作,只能操作当前打开的对话框),true就是使用的意思。
  • closed:true,的意思是节点是否是闭合的,如果节点下面还有节点,那么默认就应该是闭合的,如果没有子节点了,那么就应该是展开的。
  • onPen是当窗口要打开时候触发的函数,其中有url:'/item/cat/list',这句代码的意思是,会向这个地url址请求窗口中的数据源。

当我们发送url请求后,那么服务器应该返回什么样的数据呢?最后展示商品分类列表时,需要使用EasyUI的异步树控件展示,我们可从EasyUI的API中可以看到,返回的数据是对象的json数组,节点必须有的属性是id、text、state,children可有可无。id是节点的ID,text是节点的名称,state代表节点是否展开,如果节点下有子节点,那么state就为“closed”,如果没有子节点,那么state就为“open”。 

下面我们来看看我们的数据库中存储商品分类的表以及存储的数据,如下图所示,可以看到该表有id、parent_id、name、status、sort_order、is_parent等字段,其中parent_id是用来表示树形关系的,is_parent表示是不是父节点,1表示是父节点,0表示是叶子节点。我们在上图中知道了要展示树形结构数据,需要有固定的三个字段,分别是id、text和state,那么对应到数据库中的字段便是id、name、is_parent三个字段。 

下面我们在taotao-common工程中新建一个pojo类——EasyUITreeNode.java,该pojopojo用来描述tree的节点信息,包含三个属性id、text、state,如下图所示。 

由于该类要在服务端和客户端之间传输,因此一定要实现序列化接口。 
写完了pojo类,我们再看看Dao层,由于是单表查询,因此我们使用逆向工程自动生成的代码就可以,因此Dao层不用写。既然Dao层不用写,我们直接看Service层,我们需要新建一个ItemCatService接口类并定义一个方法,如下图所示。 

下面我们来新建ItemCatService接口的实现类,如下图所示。 

由于新增了服务实现类,因此我们需要在applicationContext-service.xml配置文件中发布dubbo服务,即声明需要暴露的服务接口,如下图所示。 

服务端写完之后,我们下面来写springmvc客户端,我们创建一个ItemCatController类,如下图所示。由于parentId与所传递过来的参数id名称不一致,因此需要使用@RequestParam(name="id")来关联起来,由于刚开始的时候是不传id参数的,因此需要给id一个默认值”0”,也即defaultValue="0"。 

注意,ItemCatController类中添加获取分类节点方法上面有这样一个注解——@RequestMapping("/item/cat/list"),这个注解中的请求路径是从我们common.js的窗口打开事件中访问的地址复制而来的,如下图所示。 

我们还可以通过控制台提示的信息找到请求的路径,如下图所示。 

既然在服务端使用dubbo发布了ItemCatService服务,那么在客户端我们便需要使用dubbo引用下该服务,方法是在springmvc.xml文件中进行引用,如下图所示。 

最后我们重新打包taotao-common、taotao-manager-interface工程到本地仓库,然后再重启taotao-manager和taotao-manager-web工程,这时再点击”选择类目”,这时便可以看到商品分类了,如下图所示。 

我们可以通过浏览器的控制台查看具体的请求及返回信息,我们发现第一次访问时并没有带id的信息,如下图所示。 

当我们展开”钟表”分类时,如下图所示,可以看到,这次访问便带上了参数id=290,这个id是从表单直接传递过来的。 

这样我们便实现了商品分类的展示及选择。

原文地址:https://www.cnblogs.com/telwanggs/p/6945218.html