Guns二次开发(七):商品分类管理之【查】字诀(3)

关于博客中使用的Guns版本问题请先阅读   Guns二次开发目录     

 

紧接上一篇博客 Guns二次开发(七):商品分类管理之【查】字诀(2)

 

6、查询功能的实现代码

     接下来附上本篇所讲的查询功能的所有前端和后端实现代码,不包含测试的sql数据,因为前面已经粘贴过了。文件的目录结果是:

 

 

 

 

 

 

 

 


 

(1)PageInfo.java

package cn.stylefeng.guns.elephish.bean;

import lombok.Data;

/**
 * 自定义分页的一些属性
 * Created by hqq on 2020/4/16.
 */
@Data
public class PageInfo {

    private Integer currentPage=1;//要查询第几页,默认是1,防止空指针
    private Integer totalPage=1;//总页数
    private Integer totalCount=1;//总记录数
    private Integer limit=1;//每页查询的条数,默认每页查询1条,防止空指针
    private Integer size=0;//本页查询到的实际条数

}

 

 

(2)QueryParam.java

package cn.stylefeng.guns.elephish.bean;

import lombok.Data;

/**
 * 封装条件查询的参数
 * Created by hqq on 2020/4/18.
 */
@Data
public class QueryParam {

    private String name;//按名称查询
    private Integer depth;//按照层级查询
    private int status;//按照状态查询,大于0时才表示选择了该条件
    private boolean searchChild=false;//是否查询子类,默认不查询,节省开销


}

 

 

(3)CategoryController.java

package cn.stylefeng.guns.elephish.controller;

import cn.stylefeng.guns.core.common.constant.factory.PageFactory;
import cn.stylefeng.guns.elephish.bean.PageInfo;
import cn.stylefeng.guns.elephish.bean.QueryParam;
import cn.stylefeng.guns.elephish.wrapper.CategoryWrapper;
import cn.stylefeng.roses.core.base.controller.BaseController;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.plugins.Page;
import org.beetl.core.misc.BeetlUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.beans.factory.annotation.Autowired;
import cn.stylefeng.guns.core.log.LogObjectHolder;
import org.springframework.web.bind.annotation.RequestParam;
import cn.stylefeng.guns.elephish.model.Category;
import cn.stylefeng.guns.elephish.service.ICategoryService;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * 分类管理控制器
 *
 * @author fengshuonan
 * @Date 2020-04-13 11:02:46
 */
@Controller
@RequestMapping("/category")
public class CategoryController extends BaseController {

    private Logger logger = LoggerFactory.getLogger(getClass());

    private String PREFIX = "/elephish/category/";

    @Autowired
    private ICategoryService categoryService;


    /**
     * 跳转到添加分类管理
     */
    @RequestMapping("/category_add")
    public String categoryAdd() {
        return PREFIX + "category_add.html";
    }

    /**
     * 跳转到修改分类管理
     */
    @RequestMapping("/category_update/{categoryId}")
    public String categoryUpdate(@PathVariable Integer categoryId, Model model) {
        Category category = categoryService.selectById(categoryId);
        model.addAttribute("item",category);
        LogObjectHolder.me().set(category);
        return PREFIX + "category_edit.html";
    }

    /**
     * 跳转到分类管理首页
     */
    @RequestMapping("")
    public String index() {

        return PREFIX + "category.html";
    }


    /**
     * 获取分类管理列表
     */
    @RequestMapping(value = "/list")
    @ResponseBody
    public Object list(QueryParam queryParam,PageInfo pageInfo) {

        List<Map<String, Object>> list = categoryService.listCategory(queryParam,pageInfo);

        //因为是自定义分页,所以返回的数据格式需要做特殊封装,主要是两个属性名的定义要固定
        JSONObject jo=new JSONObject();//也可以使用 Map<String,Object>

        //属性名必须是【data】,对应的值是List<Map<String, Object>>格式
        jo.put("data",new CategoryWrapper(list).wrap());
        jo.put("pageInfo",pageInfo);//属性名必须是 pageInfo,

        return jo;
    }

    /**
     * 新增分类管理
     */
    @RequestMapping(value = "/add")
    @ResponseBody
    public Object add(Category category) {
        categoryService.insert(category);
        return SUCCESS_TIP;
    }

    /**
     * 删除分类管理
     */
    @RequestMapping(value = "/delete")
    @ResponseBody
    public Object delete(@RequestParam Integer categoryId) {
        categoryService.deleteById(categoryId);
        return SUCCESS_TIP;
    }

    /**
     * 修改分类管理
     */
    @RequestMapping(value = "/update")
    @ResponseBody
    public Object update(Category category) {
        categoryService.updateById(category);
        return SUCCESS_TIP;
    }

    /**
     * 分类管理详情
     */
    @RequestMapping(value = "/detail/{categoryId}")
    @ResponseBody
    public Object detail(@PathVariable("categoryId") Integer categoryId) {
        return categoryService.selectById(categoryId);
    }
}

 


 

(4)CategoryServiceImpl.java

package cn.stylefeng.guns.elephish.service.impl;

import cn.stylefeng.guns.elephish.bean.PageInfo;
import cn.stylefeng.guns.elephish.bean.QueryParam;
import cn.stylefeng.guns.elephish.model.Category;
import cn.stylefeng.guns.elephish.dao.CategoryMapper;
import cn.stylefeng.guns.elephish.service.ICategoryService;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.mapper.Wrapper;
import com.baomidou.mybatisplus.plugins.Page;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author hqq
 * @since 2020-04-13
 */
@Service
public class CategoryServiceImpl extends ServiceImpl<CategoryMapper, Category> implements ICategoryService {

    @Autowired
    private CategoryMapper categoryMapper;

    @Override
    public List<Map<String, Object>> listCategory(QueryParam queryParam, PageInfo pageInfo) {

        //设置排序
        String sortField = "sort";//排序字段
        boolean isAsc = true;//是否正序排序

        //构建查询条件
        Wrapper<Category> wrapper = buildWrapper(queryParam, sortField,isAsc);

        Page<Map<String,Object>> page=new Page<>(pageInfo.getCurrentPage(),pageInfo.getLimit());
        List<Map<String, Object>> maps = categoryMapper.selectMapsPage(page, wrapper);

        //设置总页数
        int total = (int) page.getTotal();
        pageInfo.setTotalPage((int)Math.ceil(1.0*total/pageInfo.getLimit()));//总页数
        pageInfo.setTotalCount(total);//总记录数

        if(maps.isEmpty()){
            return maps;
        }

        //设置查询到的本页记录数,因为默认值为0,所以大于0的时候才需要设置
        pageInfo.setSize(maps.size());

        //如果不查询子类菜单,直接返回
        if(!queryParam.isSearchChild()){
            return maps;
        }

        //遍历查询其子类
        List<Map<String, Object>> list = new ArrayList<>();
        for(int i = 0; i<maps.size();i++){
            findChildCategory(list, maps.get(i),sortField,isAsc,queryParam.getStatus());
        }

        return list;
    }

    /**
     * 封装 category 的查询条件,
     * 注意:这些查询条件是针对顶级菜单的,
     * 子级菜单的查询条件只有一个parent_id和排序方式
     * @param queryParam
     * @return
     */
    private Wrapper<Category> buildWrapper(QueryParam queryParam,String sortField,boolean isAsc){

        int status = queryParam.getStatus();

        Wrapper<Category> wrapper = new EntityWrapper<>();

        //设置排序字段和排序方式
        wrapper.orderBy(sortField,isAsc);

        //是否按照层级查询
        Integer depth =queryParam.getDepth();
        if(depth != null){
            wrapper.eq("depth", depth);
        }

        //是否按照分类名称查询
        if(StringUtils.isNotBlank(queryParam.getName())){
            wrapper.like("name",queryParam.getName());
        }else{
            //只有不按分类名称查询,并且没有指定深度,才设置默认的parentId为0
            if(depth == null){
                wrapper.eq("parent_id", 0);
            }
        }

        //是否按照状态查询
        if(status> 0){
            if(!(status==PRODUCT_CATEGORY_STATUS_START || status==PRODUCT_CATEGORY_STATUS_STOP)){
                //ILLEGAL_STATUS_VALUE(400,"状态字段的值异常"),
                throw new ServiceException(BizExceptionEnum.ILLEGAL_STATUS_VALUE);
            }

            wrapper.eq("status",queryParam.getStatus());
        }else{
            //否则,只查询未删除的记录
            wrapper.in("status",new Integer[]{PRODUCT_CATEGORY_STATUS_START,PRODUCT_CATEGORY_STATUS_STOP});
        }

        return wrapper;

    }


    /**
     * 递归算法,算出子级菜单
     */
    private List<Map<String, Object>> findChildCategory(List<Map<String, Object>> result,
                  Map<String, Object> category,String sortField, boolean isAsc, int status){
        result.add(category);

        //封装子级菜单的查询条件,
        // 子级菜单的查询条件只有一个parent_id和排序方式
        Wrapper<Category> wrapper = new EntityWrapper<>();
        wrapper.orderBy(sortField,isAsc)
                .eq("parent_id", new Integer(category.get("id").toString()));

        if(status>0){
            wrapper.eq("status",status);
        }else{
            //否则,只查询未删除的记录
            wrapper.in("status",new Integer[]{PRODUCT_CATEGORY_STATUS_START,PRODUCT_CATEGORY_STATUS_STOP});

        }

        //查找子节点,递归算法一定要有一个退出的条件
        List<Map<String, Object>> childList = categoryMapper.selectMaps(wrapper);
        for (Map<String, Object> temp : childList) {
            findChildCategory(result,temp,sortField,isAsc, status);
        }
        return result;
    }
}

 

 

(5)CategoryWrapper.java

package cn.stylefeng.guns.elephish.wrapper;

import cn.stylefeng.guns.core.common.constant.factory.ConstantFactory;
import cn.stylefeng.guns.elephish.constants.WrapperDictNameConstant;
import cn.stylefeng.roses.core.base.warpper.BaseControllerWrapper;

import java.util.List;
import java.util.Map;

/**
 * 商品分类管理的包装类
 *
 * Created by hqq on 2020/4/15.
 */
public class CategoryWrapper extends BaseControllerWrapper {

    public CategoryWrapper(List<Map<String, Object>> multi) {
        super(multi);
    }

    /**
     * 此处自定义添加需要包装的字段的字典信息
     */
    @Override
    protected void wrapTheMap(Map<String, Object> map) {
        //状态名称
        map.put("statusName", ConstantFactory.me().getDictById(
                WrapperDictNameConstant.MALL_CATEGORY_STATUS_ID, map.get("status").toString()));

    }
}

 

(6)bootstrap-treetable.js

/**
 * 查找当前这个节点的所有节点(包含子节点),并进行折叠或者展开操作
 *
 * @param item 被点击条目的子一级条目
 * @param target 整个bootstrap tree table实例
 * @param globalCollapsedFlag 如果为true,则表示当前操作是收缩(折叠),如果是false,表示当前操作是展开
 * @param options 存放了一些常量,例如展开和收缩的class
 */
function extracted($, item, target, globalCollapsedFlag, options) {

    var itemCodeName = $(item).find("td[name='"+options.code+"']").text();
    // var itemCodeName = $(item).find("td[name='code']").text();
    var subItems = target.find("tbody").find(".tg-" + itemCodeName);//下一级,改为下所有级别

    if (subItems.size() > 0) {
        $.each(subItems, function (nIndex, nItem) {
            extracted($, nItem, target, globalCollapsedFlag, options);
        });
    }
    $.each(subItems, function (pIndex, pItem) {

        //如果是展开,判断当前箭头是开启还是关闭
        var expander = $(item).find("td[name='name']").find(".treetable-expander");
        if (!globalCollapsedFlag) {
            var hasExpander = expander.hasClass(options.expanderExpandedClass);
            if (hasExpander) {
                $(pItem).css("display", "table");
            } else {
                $(pItem).css("display", "none");
            }
        } else {
            //如果是折叠,就把当前开着的都折叠掉
            $(pItem).css("display", "none");
            expander.removeClass(options.expanderExpandedClass);
            expander.addClass(options.expanderCollapsedClass);
        }
    });
}

(function ($) {
    "use strict";

    $.fn.bootstrapTreeTable = function (options, param) {
        var allData = null;//用于存放格式化后的数据

        // 如果是调用方法
        if (typeof options == 'string') {
            return $.fn.bootstrapTreeTable.methods[options](this, param);
        }
        // 如果是初始化组件
        options = $.extend({}, $.fn.bootstrapTreeTable.defaults, options || {});

        // 是否有radio或checkbox
        var hasSelectItem = false;
        var target = $(this);

        // 在外层包装一下div,样式用的bootstrap-table的
        var _main_div = $("<div class='bootstrap-tree-table fixed-table-container'></div>");
        target.before(_main_div);
        _main_div.append(target);
        target.addClass("table table-hover treetable-table table-bordered");
        if (options.striped) {
            target.addClass('table-striped');
        }
        // 工具条在外层包装一下div,样式用的bootstrap-table的
        if (options.toolbar) {
            var _tool_div = $("<div class='fixed-table-toolbar'></div>");
            var _tool_left_div = $("<div class='bs-bars pull-left'></div>");
            _tool_left_div.append($(options.toolbar));
            _tool_div.append(_tool_left_div);
            _main_div.before(_tool_div);
        }
        // 格式化数据,优化性能
        target.formatData = function (data) {

            var _root = options.rootCodeValue ? options.rootCodeValue : null
            $.each(data, function (index, item) {
                // 添加一个默认属性,用来判断当前节点有没有被显示
                item.isShow = false;
                // 这里兼容几种常见Root节点写法
                // 默认的几种判断
                var _defaultRootFlag = item[options.parentCode] == '0'
                    || item[options.parentCode] == 0
                    || item[options.parentCode] == null
                    || item[options.parentCode] == '';
                if (!item[options.parentCode] || (_root ? (item[options.parentCode] == options.rootCodeValue) : _defaultRootFlag)) {
                    if (!allData["_root_"]) {
                        allData["_root_"] = [];
                    }
                    allData["_root_"].push(item);
                } else {
                    if (!allData["_n_" + item[options.parentCode]]) {
                        allData["_n_" + item[options.parentCode]] = [];
                    }
                    allData["_n_" + item[options.parentCode]].push(item);
                }
            });
        }
        // 得到根节点
        target.getRootNodes = function () {
            return allData["_root_"];
        };
        // 递归获取子节点并且设置子节点
        target.handleNode = function (parentNode, lv, tbody) {
            var _ls = allData["_n_" + parentNode[options.code]];
            var tr = target.renderRow(parentNode, _ls ? true : false, lv);
            tbody.append(tr);
            if (_ls) {
                $.each(_ls, function (i, item) {
                    target.handleNode(item, (lv + 1), tbody)
                });
            }
        };
        // 绘制行
        target.renderRow = function (item, isP, lv) {
            // 标记已显示
            item.isShow = true;
            var tr = $('<tr class="tg-' + item[options.parentCode] + '"></tr>');
            var _icon = options.expanderCollapsedClass;
            if (options.expandAll) {
                tr.css("display", "table");
                _icon = options.expanderExpandedClass;
            } else if (options.expandFirst && lv <= 2) {
                tr.css("display", "table");
                _icon = (lv == 1) ? options.expanderExpandedClass : options.expanderCollapsedClass;
            } else {
                tr.css("display", "none");
                _icon = options.expanderCollapsedClass;
            }
            $.each(options.columns, function (index, column) {
                // 判断有没有选择列
                if (index == 0 && column.field == 'selectItem') {
                    hasSelectItem = true;
                    var td = $('<td style="text-align:center;width:36px"></td>');
                    if (column.radio) {
                        var _ipt = $('<input name="select_item" type="radio" value="' + item[options.id] + '"></input>');
                        td.append(_ipt);
                    }
                    if (column.checkbox) {
                        var _ipt = $('<input name="select_item" type="checkbox" value="' + item[options.id] + '"></input>');
                        td.append(_ipt);
                    }
                    tr.append(td);
                } else {
                    var td = $('<td title="' + item[column.field] + '" name="' + column.field + '" style="' + ((column.width) ? ('width:' + column.width) : '') + '"></td>');
                    // 增加formatter渲染
                    if (column.formatter) {
                        td.html(column.formatter.call(this, item[column.field], item, index));
                    } else {
                        td.text(item[column.field]);
                    }
                    if (options.expandColumn == index) {
                        if (!isP) {
                            td.prepend('<span class="treetable-expander"></span>')
                        } else {
                            td.prepend('<span class="treetable-expander ' + _icon + '"></span>')
                        }
                        for (var int = 0; int < (lv - 1); int++) {
                            td.prepend('<span class="treetable-indent"></span>')
                        }
                    }
                    tr.append(td);
                }
            });
            return tr;
        }
        // 加载数据
        target.load = function (parms) {
            // 加载数据前先清空
            allData = {};
            // 加载数据前先清空
            target.html("");
            // 构造表头
            var thr = $('<tr></tr>');
            $.each(options.columns, function (i, item) {
                var th = null;
                // 判断有没有选择列
                if (i == 0 && item.field == 'selectItem') {
                    hasSelectItem = true;
                    th = $('<th style="width:36px"></th>');
                } else {
                    th = $('<th style="' + ((item.width) ? ('width:' + item.width) : '') + '"></th>');
                }
                th.text(item.title);
                thr.append(th);
            });
            var thead = $('<thead class="treetable-thead"></thead>');
            thead.append(thr);
            target.append(thead);
            // 构造表体
            var tbody = $('<tbody class="treetable-tbody"></tbody>');
            target.append(tbody);
            // 添加加载loading
            var _loading = '<tr><td colspan="' + options.columns.length + '"><div style="display: block;text-align: center;">正在努力地加载数据中,请稍候……</div></td></tr>'
            tbody.html(_loading);
            // 默认高度
            if (options.height) {
                tbody.css("height", options.height);
            }

            $.ajax({
                type: options.type,
                url: options.url,
                data: parms ? parms : options.ajaxParams,
                dataType: "JSON",
                success: function (result, textStatus, jqXHR) {

                    //### 开始修改guns原来的bootstrap-treetable.js ###
                    var data =result ;

                    //判断是否是标准的列表查询,这个很重要,
                    // 因为我是直接在bootstrap-treetable.js修改的,
                    //新作的修改必须保证原来的功能不受影响。
                    if(result && typeof(result.pageInfo)!='undefined'){
                        data = result.data;

                        PageTool.buildPageDiv(result.pageInfo);
                    }
                    //### 结束修改gun v5.1-final 原来的bootstrap-treetable.js ###

                    // 加载完数据先清空
                    tbody.html("");
                    if (!data || data.length <= 0) {
                        var _empty = '<tr><td colspan="' + options.columns.length + '"><div style="display: block;text-align: center;">没有找到匹配的记录</div></td></tr>'
                        tbody.html(_empty);
                        return;
                    }

                    target.formatData(data);

                    // 开始绘制
                    var rootNode = target.getRootNodes();
                    if (rootNode) {
                        $.each(rootNode, function (i, item) {
                            target.handleNode(item, 1, tbody);
                        });
                    }

                    // 下边的操作主要是为了查询时让一些没有根节点的节点显示
                    $.each(data, function (i, item) {
                        if (!item.isShow) {
                            var tr = target.renderRow(item, false, 1);
                            tbody.append(tr);
                        }
                    });

                    target.append(tbody);
                    //动态设置表头宽度
                    thead.css("width", tbody.children(":first").css("width"));
                    // 行点击选中事件
                    target.find("tbody").find("tr").click(function () {
                        if (hasSelectItem) {
                            var _ipt = $(this).find("input[name='select_item']");
                            if (_ipt.attr("type") == "radio") {
                                _ipt.prop('checked', true);
                                target.find("tbody").find("tr").removeClass("treetable-selected");
                                $(this).addClass("treetable-selected");
                            } else {
                                if (_ipt.prop('checked')) {
                                    _ipt.prop('checked', false);
                                    $(this).removeClass("treetable-selected");
                                } else {
                                    _ipt.prop('checked', true);
                                    $(this).addClass("treetable-selected");
                                }
                            }
                        }
                    });
                    // 小图标点击事件--展开缩起
                    target.find("tbody").find("tr").find(".treetable-expander").click(function () {
                        var tr = $(this).parent().parent();
                        var _code = tr.find("input[name='select_item']").val();
                        if (options.id == options.code) {
                            _code = tr.find("input[name='select_item']").val();
                        } else {
                            _code = tr.find("td[name='" + options.code + "']").text();
                        }
                        var _ls = target.find("tbody").find(".tg-" + _code);//下一级,改为下所有级别
                        if (_ls && _ls.length > 0) {
                            var _flag = $(this).hasClass(options.expanderExpandedClass);
                            $.each(_ls, function (index, item) {

                                //查找当前这个节点的所有节点(包含子节点),如果是折叠都显示为不显示,如果是展开,则根据当前节点的状态
                                extracted($, item, target, _flag, options);

                                $(item).css("display", _flag ? "none" : "table");
                            });
                            if (_flag) {
                                $(this).removeClass(options.expanderExpandedClass)
                                $(this).addClass(options.expanderCollapsedClass)
                            } else {
                                $(this).removeClass(options.expanderCollapsedClass)
                                $(this).addClass(options.expanderExpandedClass)
                            }
                        }
                    });


                },
                error: function (xhr, textStatus) {
                    var _errorMsg = '<tr><td colspan="' + options.columns.length + '"><div style="display: block;text-align: center;">' + xhr.responseText + '</div></td></tr>'
                    tbody.html(_errorMsg);
                    debugger;
                },
            });


        }
        if (options.url) {
            target.load();
        } else {
            // 也可以通过defaults里面的data属性通过传递一个数据集合进来对组件进行初始化....有兴趣可以自己实现,思路和上述类似
        }


        return target;
    };

    // 组件方法封装........
    $.fn.bootstrapTreeTable.methods = {
        // 返回选中记录的id(返回的id由配置中的id属性指定)
        // 为了兼容bootstrap-table的写法,统一返回数组,这里只返回了指定的id
        getSelections: function (target, data) {
            // 所有被选中的记录input
            var _ipt = target.find("tbody").find("tr").find("input[name='select_item']:checked");
            var chk_value = [];
            // 如果是radio
            if (_ipt.attr("type") == "radio") {
                var _data = {id: _ipt.val()};
                var _tds = _ipt.parent().parent().find("td");
                _tds.each(function (_i, _item) {
                    if (_i != 0) {
                        _data[$(_item).attr("name")] = $(_item).text();
                    }
                });
                chk_value.push(_data);
            } else {
                _ipt.each(function (_i, _item) {
                    var _data = {id: $(_item).val()};
                    var _tds = $(_item).parent().parent().find("td");
                    _tds.each(function (_ii, _iitem) {
                        if (_ii != 0) {
                            _data[$(_iitem).attr("name")] = $(_iitem).text();
                        }
                    });
                    chk_value.push(_data);
                });
            }
            return chk_value;
        },
        // 刷新记录
        refresh: function (target, parms) {
            if (parms) {
                target.load(parms);
            } else {
                target.load();
            }
        },
        // 组件的其他方法也可以进行类似封装........
    };

    $.fn.bootstrapTreeTable.defaults = {
        id: 'id',// 选取记录返回的值
        code: 'id',// 用于设置父子关系
        parentCode: 'parentId',// 用于设置父子关系
        rootCodeValue: null,//设置根节点code值----可指定根节点,默认为null,"",0,"0"
        data: [], // 构造table的数据集合
        type: "GET", // 请求数据的ajax类型
        url: null, // 请求数据的ajax的url
        ajaxParams: {}, // 请求数据的ajax的data属性
        expandColumn: null,// 在哪一列上面显示展开按钮
        expandAll: true, // 是否全部展开
        expandFirst: false, // 是否默认第一级展开--expandAll为false时生效
        striped: false, // 是否各行渐变色
        columns: [],
        toolbar: null,//顶部工具条
        height: 0,
        expanderExpandedClass: 'glyphicon glyphicon-chevron-down',// 展开的按钮的图标
        expanderCollapsedClass: 'glyphicon glyphicon-chevron-right'// 缩起的按钮的图标

    };
})(jQuery);

 

(7)category.js

/**
 * 分类管理管理初始化
 */
var Category = {
    id: "CategoryTable",	//表格id
    seItem: null,		//选中的条目
    table: null,
    layerIndex: -1
};



/**
 * 初始化表格的列
 */
Category.initColumn = function () {
    return [
        {field: 'selectItem', radio: true},
            {title: '分类名称', field: 'name', visible: true, align: 'center', valign: 'middle'},
            {title: '分类编号', field: 'id', visible: true, align: 'center', valign: 'middle'},
            {title: '分类父编号', field: 'parentId', visible: true, align: 'center', valign: 'middle'},
        // {title: '当前类别的所有父类别id,包含多层级别的父类别id,方便查找', field: 'parentIds', visible: true, align: 'center', valign: 'middle'},
            {title: '层级', field: 'depth', align: 'center', valign: 'middle', sortable: true},
            {title: '排序', field: 'sort', visible: true, align: 'center', valign: 'middle'},
            {title: '状态', field: 'statusName', visible: true, align: 'center', valign: 'middle'},
            {title: '创建时间', field: 'createTime', visible: true, align: 'center', valign: 'middle',
                formatter: function (value, row, index) {
                    return typeof(value)=="undefined"?"":moment(+value).format('YYYY-MM-DD HH:mm:ss');
                }
            },
            {title: '更新时间', field: 'updateTime', visible: true, align: 'center', valign: 'middle',
                formatter: function (value, row, index) {
                    return typeof(value)=="undefined"?"":moment(+value).format('YYYY-MM-DD HH:mm:ss');
                }
            }
    ];
};

/**
 * 检查是否选中
 */
Category.check = function () {
    var selected = $('#' + this.id).bootstrapTable('getSelections');
    if(selected.length == 0){
        Feng.info("请先选中表格中的某一记录!");
        return false;
    }else{
        Category.seItem = selected[0];
        return true;
    }
};

/**
 * 点击添加分类管理
 */
Category.openAddCategory = function () {
    var index = layer.open({
        type: 2,
        title: '添加分类管理',
        area: ['800px', '420px'], //宽高
        fix: false, //不固定
        maxmin: true,
        content: Feng.ctxPath + '/category/category_add'
    });
    this.layerIndex = index;
};

/**
 * 重置查询条件条件
 */
Category.reset = function () {
    $("#byStatus").find("option[text='正常']").attr("selected",true);
    $("#byStatus").find("option[text!='正常']").attr("selected",false);

    $("#searchChild").find("option[text='是']").attr("selected",true);
    $("#searchChild").find("option[text!='是']").attr("selected",false);

    $("#currentPage").val("1");//当前页
    $("#limit").val("5");//每页查询条数
    $("#byName").val("");//分类名称
    $("#byDepth").val("");//层级
    $("#searchChild").attr("disabled",false);
}

/**
 * 打开查看分类管理详情
 */
Category.openCategoryDetail = function () {
    if (this.check()) {
        var index = layer.open({
            type: 2,
            title: '分类管理详情',
            area: ['800px', '420px'], //宽高
            fix: false, //不固定
            maxmin: true,
            content: Feng.ctxPath + '/category/category_update/' + Category.seItem.id
        });
        this.layerIndex = index;
    }
};

/**
 * 删除分类管理
 */
Category.delete = function () {
    if (this.check()) {
        var ajax = new $ax(Feng.ctxPath + "/category/delete", function (data) {
            Feng.success("删除成功!");
            Category.table.refresh();
        }, function (data) {
            Feng.error("删除失败!" + data.responseJSON.message + "!");
        });
        ajax.set("categoryId",this.seItem.id);
        ajax.start();
    }
};



/**
 * 查询分类管理列表
 */
Category.search = function () {
    Category.table.refresh({query: Category.formParams()});

};


/**
 rootCodeValue: null,//设置根节点code值----可指定根节点,默认为null,"",0,"0"
 id : "id",               // 选取记录返回的值
 code : "id",               // 用于设置父子关系
 parentCode : "parentId",       // 用于设置父子关系
 type: 'get',                   // 请求方式(*)
 url: "./data.json",             // 请求后台的URL(*)
 ajaxParams : {},               // 请求数据的ajax的data属性
 expandColumn : 0,            // 在哪一列上面显示展开按钮
 expandAll : false,                // 是否全部展开
 expandFirst : true, // 是否默认第一级展开--expandAll为false时生效
 toolbar: null,//顶部工具条
 height: 0,
 expanderExpandedClass : 'glyphicon glyphicon-chevron-down',// 展开的按钮的图标
 expanderCollapsedClass : 'glyphicon glyphicon-chevron-right',// 缩起的按钮的图标
 **/

$(function () {

    var defaultColunms = Category.initColumn();
    var table = new BSTreeTable(Category.id, "/category/list", defaultColunms);
    table.setExpandColumn(1);//设置第一列展示下拉列表
    table.setIdField("id");//分类编号
    table.setCodeField("id");//分类父编号,用于设置父子关系
    table.setParentCodeField("parentId");//分类父编号,用于设置父子关系
    table.setExpandAll(true);

    //设置请求时的参数
    var queryData = Category.formParams();
    queryData['limit'] = 5;//
    // alert(JSON.stringify(queryData));
    table.setData(queryData);
    table.init();
    Category.table = table;

    //设置每页的查询的默认条数
    $("#limit").val(5);

    //设置当前对象的名称,分页时需要使用
    PageTool.callerName="Category";

});


/**
 * 查询表单提交参数对象
 * @returns {{}}
 */
Category.formParams = function() {
    var queryData = {};

    queryData['name'] = $("#byName").val().trim();//名称条件
    queryData['depth'] = $("#byDepth").val();//层级条件
    queryData['status'] = $("#byStatus").val();//状态条件
    queryData['searchChild'] = $("#searchChild").val();//是否查询子菜单
    queryData['limit'] = $("#limit").val();//设置每页查询条数

    return queryData;
}

/**
 * 【分类名称】输入框失去焦点事件
 */
$("#byName").on('blur',function(){
    setSearchChildSelected();
});

/**
 *【深度】输入框失去焦点事件
 */
$("#byDepth").on('blur',function(){
    setSearchChildSelected();
});

/**
 * 设置【是否查询子菜单】选择框是否可用
 */
function setSearchChildSelected() {
    var byName = $("#byName").val().trim();
    var byDepth = $("#byDepth").val().trim();


    if(byName && !byDepth){
        //当选择分类名称查询,不选择层级查询时,默认无法查询子类菜单,这样是为了防止查重和查出不必要的数据
        $("#searchChild").val("false");
        $("#searchChild").attr("disabled",true);
    }else{
        //其它情况,都可以自主觉得是否查询子菜单
        $("#searchChild").val("true");
        $("#searchChild").attr("disabled",false);
    }
}

(8)moment.js

这个文件请按照上一篇博客 Guns二次开发(七):商品分类管理之【查】字诀(2) 中的方法获取,无法引入到此处,因为字数太多。




(9)paging.js

/**
 * 本分页功能是在bootrap-treetable.js基础上实现的,使用前必须严格遵守guns生成的代码规范
 * 使用说明:
 * (1)使用之前,请先配套获取修改后的 bootstrap-treetable.js,并替换掉guns v5.1-final
 *      原来的bootstrap-treetable.js,
 *      guns原js修改的地方也才几行代码,你也可以自己修改,而不用获取本处提供的
 * (2)将本文件 paging.js 引入到你需要分页的html页面中,比如本篇博客的 category.html页面中
 * (3)在你需要插入【分页条】的地方(html页面中的某处位置)引入这段div :<div id="pageDiv"></div>
 * (4)接口返回的数据需是json格式的,并且必须保证用以下两个字段封装对应的数据:
        "data" : 列表所需要的实体类数据,用list集合封装
        "pageInfo" : 分页信息

        注:
        data 集合中实体类需符合原 guns v5.1-final 【菜单管理】列表查询功能的标准;
        pageInfo 需包含以下5个字段:{
             currentPage : 要查询第几页,默认是1,防止空指针
             totalPage :总页数,默认值0
             totalCount : 总记录数,默认值0
             limit :每页查询的条数,默认值每页查询1条,防止空指针
             size : 本页实际查询到的条数
        }
 * (5)在bootstrap表格相关的js中,本示例就category.js文件,在这个文件中,
 * 对象的名称是"Category",此时需要在该js文件的$(function () {})中做如下
 * 赋值操作:
 * $(function () {
 *      PageTool.callerName("Category");
 * })
 *
 * (6)尽量使用guns代码生成的页面和js,要确保你的js中拥有guns代码生成的
 * 两个方法,A、对象.formParams()  ; B、对象.table.refresh();
 * 该分页函数是以guns为基础做的补充,所以使用时请遵守Guns原有的规范
 *
 */
var PageTool = {
    callerName:"" //设置顶级调用者的名称,用于创建该对象时使用
};


/**
 * 自定义的分页之构建构建div里面的内容
 *
 * 记住,使用之前,必须要在你需要显示页码条的位置引入这段div标签:
 * <div id="pageDiv"></div>
 */
PageTool.buildPageDiv = function  (pageInfo) {
    // alert(JSON.stringify(pageInfo));

    //先清空之前的内容
    $("#pageDiv").empty();

    var currentPage=pageInfo.currentPage;//当前页
    var totalPage=pageInfo.totalPage;//总页数
    var totalCount=pageInfo.totalCount;//符合条件的总条数(不包含子菜单)
    var size=pageInfo.size;//本页查询到记录数(不包含子菜单)
    var limit=pageInfo.limit;//每页查询的记录数

    //添加一个隐藏标签,用于记录当前页码
    var pageDiv = "<br/><input id='currentPage' type='hidden' value='"+currentPage+"'/>" +
        "<strong style='color:red'>注意:此处的分页只相对同级别的父类菜单而言,所有子类菜单的记录数不在分页之内" +
        "</strong><br/><ul class='pagination'>";
    <!-- 首页,如果当前页是第一页,则设置首页不可用-->
    if(currentPage==1){
        pageDiv +="<li><a href='javascript:void(0)' disabled='true' style='color:black;font-weight:bold;'>首页</a></li>";
    }else{
        pageDiv += "<li><a href='javascript:void(0)' onclick='PageTool.pageSkip(this)' currentPage='1'>首页</a></li>";
    }

    <!-- 上一页,先判断是不是第一页,第一页,就没有上一页 -->
    if(currentPage > 1){
        pageDiv += "<li><a href='javascript:void(0)' onclick='PageTool.pageSkip(this)'" +
            " currentPage='"+(currentPage - 1)+"'>上一页</a></li>";
    }

    <!-- #####展示页码功能 开始#####-->

    <!-- 定义两个模板变量 -->
    var begin=1;
    var end=5;

    if(totalPage <= 5){
        //当总的页数小于等于5的情况
        begin=1 ;
        end=totalPage;
    }else{
        // 总页码大于5的情况
        <!-- 如果当前页码为 1 - 3,那么 我们显示前面5个页码,也就是1-5 -->
        if(currentPage<=3){
            begin=1 ;
            end=5;
        }else if(currentPage >= totalPage-2 ){
            <!-- 如果当前页码为 末尾的3个,也就是 currentPage >=totalPage - 2 ,那么 就显示末尾5个页码 -->
            begin=totalPage-4 ;
            end=totalPage;
        }else{
            <!-- 其他中间的页码,显示 currentPage - 2 到 currentPage + 2  -->
            begin=currentPage-2 ;
            end=currentPage+2;
        }
    }

    <!-- 遍历页码  -->
    for(var i=begin; i <= end; i++){
        if(currentPage == i){
            <!-- 如果页码是当前页,则设置标签不可用 -->
            pageDiv += "<li><a href='javascript:void(0)' disabled='true' style='color:black;font-weight:bold;'>"+i+"</a></li>";

        }else{
            <!-- 如果页码不是当前页,则加连接 -->
            pageDiv += "<li><a href='javascript:void(0)' onclick='PageTool.pageSkip(this)' currentPage='"+i+"'>"+i+"</a></li>";
        }
    }
    <!-- #####展示页码功能 结束#####-->

    <!-- 下一页,先判断是不是最后一页,最后一页,就没有下一页 -->
    if(currentPage < totalPage ){
        pageDiv += "<li><a href='javascript:void(0)' onclick='PageTool.pageSkip(this)' currentPage='"+(currentPage + 1)+"'>下一页</a></li>";
    }

    <!-- 最后一页 -->
    if(currentPage==totalPage) {
        pageDiv += "<li><a href='javascript:void(0)' disabled='true' style='color:black;font-weight:bold;'>末页</a></li>";
    }else{
        pageDiv += "<li><a href='javascript:void(0)' onclick='PageTool.pageSkip(this)' currentPage='"+(totalPage)+"'>末页</a></li>";
    }

    pageDiv +="&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;当前第 "+(currentPage)+" 页," +
        "显示第 "+((currentPage-1)*limit+1)+" 到 "+((currentPage-1)*limit+size)+" 条记录" +
        "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 总共 "+totalPage+" 页,总共 "+totalCount+" 条记录数," +
        "可直接搜索第<input value='"+currentPage+"' id='pageNow' style='width:40px;text-align:center;'/> 页" +
        "<input type='button' class='btn btn-primary' currentPage='button' onclick='PageTool.pageSkip(this)' value='点击跳转'></ul>";

    $("#pageDiv").append(pageDiv);
    // alert(pageDiv);
}



/**
 * 点击指定页码刷新数据
 */
PageTool.pageSkip =function(obj) {

    //获取设置当前页码
    var currentPage = obj.attributes["currentPage"].nodeValue;

    //如果是直接指定页码跳转的请求,则要从input标签中获取值
    if(currentPage == "button"){
        currentPage = $("#pageNow").val();
    }

    //如果最终没有值,设置默认值为1
    if(!currentPage){
        currentPage = 1;
    }

    //获取调用者的名称,并转换成对象
    if(!PageTool.callerName ||PageTool.callerName==''){
        Feng.error("请按paging.js开头的说明,设置对象的名称");
        return false;
    }
    //根据对象名称获取对象
    var caller = window[PageTool.callerName];
    if(!caller || typeof(caller)!='object' ){
        Feng.error("您设置的对象名称无法转换成对象,请阅读paging.js文件的使用说明");
        return false;
    }

    var queryData = caller.formParams();
    queryData['currentPage'] = currentPage;//当前再第几页
    //刷新table数据
    caller.table.refresh({query: queryData});

    //当前方法被写死了,必须动态获取到
    // 先获取公共的参数
    // var queryData = Category.formParams();
    //
    // //添加私有参数
    // queryData['currentPage'] = currentPage;//当前再第几页
    //
    // //刷新table数据
    // Category.table.refresh({query: queryData});

}

 

 

(11)category.html

@layout("/common/_container.html"){
<div class="row">
    <div class="col-sm-12">
        <div class="ibox float-e-margins">
            <div class="ibox-title">
                <h5 id="pageInfo">分类管理</h5>
            </div>
            <div class="ibox-content">
                <div class="row row-lg">
                    <div class="col-sm-12">
                        <div class="row">
                            <div class="col-sm-2">
                                <#NameCon id="byName" name="分类名称" />
                            </div>
                            <div class="col-sm-2">
                                <#NameCon id="byDepth" name="层级" />
                            </div>
                            <div class="col-sm-2">
                                <#SelectCon id="byStatus" name="状态" >
                                    <option value="0">全部</option>
                                    <option value="1">正常</option>
                                    <option value="2">已废弃</option>
                                </#SelectCon>
                            </div>
                        </div>
                        <div class="row">
                            <div class="col-sm-2">
                                <#SelectCon id="searchChild" name="是否查询子菜单" >
                                    <option value="true" selected="selected" >是</option>
                                    <option value="false">否</option>
                                </#SelectCon>
                            </div>
                            <div class="col-sm-2">
                                <#NameCon id="limit" name="每页查询条数"/>
                            </div>

                            <div class="col-sm-3">
                                <#button name="重置" icon="fa-repeat" clickFun="Category.reset()" space="true"/>
                                <#button name="搜索" icon="fa-search" clickFun="Category.search()"/>
                            </div>

                        </div>
                        <br/>
                        <div class="hidden-xs" id="CategoryTableToolbar" role="group">
                            @if(shiro.hasPermission("/category/add")){
                            <#button name="添加" icon="fa-plus" clickFun="Category.openAddCategory()"/>
                            @}
                            @if(shiro.hasPermission("/category/update")){
                            <#button name="修改" icon="fa-edit" clickFun="Category.openCategoryDetail()" space="true"/>
                            @}
                            @if(shiro.hasPermission("/category/delete")){
                            <#button name="删除" icon="fa-remove" clickFun="Category.delete()" space="true"/>
                            @}
                        </div>
                        <#table id="CategoryTable"/>

                    </div>

                </div>

                <!--定义一个空的div标签,用于分页内容的位置-->
                <br>
                <div  id="pageDiv"></div>

            </div>
        </div>
    </div>
</div>

<!-- 自定义分页引入 -->
<script src="${ctxPath}/static/modular/elephish/common/paging.js"></script>
<script src="${ctxPath}/static/modular/elephish/category/category.js?j=${date().time}"/>


@}



 

 

(12)StatusConstant.java

package cn.stylefeng.guns.elephish.constants;

/**
 * 数据库中的表格的 status字段状态的值的常量

 */
public interface StatusConstant {



    /**
     * 商品分类表 mall_product_category 中的status字段:
     * 1,启用中;2,停用中;3,已删除
     */
    int PRODUCT_CATEGORY_STATUS_START = 1;//表示 启用中 含义的值。
    int PRODUCT_CATEGORY_STATUS_STOP = 2;//表示 停用中 含义的值
    int PRODUCT_CATEGORY_STATUS_DELETE = 3;//表示 已删除 含义的值


}

 

(13)LimitationConstant.java

package cn.stylefeng.guns.elephish.constants;

/**
 * Created by hqq
 */
public interface LimitationConstant {

    /**
     * 商品分类菜单树形结构的最大层级,也就是叶子节点所在的层级
     */
    int MALL_CATEGORY_TREE_MAX_DEPTH = 3;
}

 

(14)WrapperDictNameConstant.java

package cn.stylefeng.guns.elephish.constants;

/**
 *
 * 字典包装类的字典名称常量
 *
 * Created by hqq  
 */
public interface WrapperDictNameConstant {

    /**
     * sys_user 表中 sex 字段对应的字典名称 的ID
     */
    int SYS_USER_SEX_ID = 127;

    /**
     * mall_product_category 表中status字段对应的 字典ID
     */
    int MALL_CATEGORY_STATUS_ID = 130;
}

 

 

 

至此,商品分类模块的查询功能实现。

 

该系列更多文章请前往 Guns二次开发目录

 

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值