• 管理系统如何做模块化开发?


      最近一直在给公司做各种控件,一开始设计控件的时候,老板要求尽量使用简单,最好是开发人员调用一个方法,控件就可以自动生成。所以在控件设计上,都是基于jquery插件的写法,并把所有的事件都封装到内部,然而随着控件越来越多,应用场景越来越广泛,发现这么设计有很大的弊端,比如:不管什么页面,想要用到控件,要把所有的控件js都加载进来(js是经过压缩的)如下图,由于每个控件耦合度较高,在多个控件组合使用时,代码组织起来相当麻烦。使用上的麻烦,维护上的困难,再加上性能问题,导致不得不对其进行重构。

      重构思路:基于模块化开发,用reuqirejs进行管理,把每个控件都做成模块化,页面上要使用时,单独的去引用某个模块就可以。多个控件如果要组合使用,以及和页面上的元素做交互的话,在控件名对应的extra.js中去做处理。

          下面就是贴代码时间(由于公司有保密规定,这里不方便把整个代码打包和大家分享,只能展示部分代码):

       1、工程目录:

     2、ProvinceAndCity.js 省份城市多选控件代码

    /**
     * @author:tengri
     * @time:2017-07-26
     * @desc: 酒店城市、省份多选控件
     */
    
    define(["jquery"],function($){
    
        /**
         * 默认配置参数
         * @type {{}}
         */
        var defaultOpts = {
            simpleData: {
                id: "id",
                name: "name"
            },
            splitStr: "," //分割符
        };
    
        /**
         * 构造函数
         * @param {Object} options
         * @param {Object} cbFn 回调函数
         */
        function ProvinceAndCity(elem, options, cbFn) {
            this.id = "vetech-provinceAndCity-" + String(Math.random()).replace(/D/g, "");
            this.elem = elem;
            this.$elem = $(elem);
            this.opts = $.extend(true, {}, defaultOpts, options);
            this.callback = cbFn || $.noop; //回调函数
            this.$hiddenInput = $("#" + (this.opts.hiddenName || ""));
            this.init();
        }
    
        /**
         * 初始化整个容器
         */
        ProvinceAndCity.prototype.init = function () {
            //外层容器
            this.$container = $('<div class="vetech-provinceAndCity-container">');
            //toolbar
            this.$toolbar = $('<div class="toolbar"></div>');
            this.$toolbar.append('<strong>选择地区</strong><input type="button" value="确定" />');
            //checkedArea
            this.$checkedArea = $('<div class="checkedArea clear">');
            //content
            this.$content = $('<div class="content clear">');
            //cityPanel
            this.$cityPanel = $('<div class="cityPanel">');
    
            this.$container.append(this.$toolbar).append(this.$checkedArea).append(this.$content);
    
            $(document.body).append(this.$container);
    
            this.addEvent();
            this.bindEvent();
    
        };
    
        /**
         * 加载数据
         * @param url  可以传一个url地址,也可以传一个数据对象
         * @param callback 回调函数
         */
        ProvinceAndCity.prototype.load = function(url,callback){
            callback = (callback && $.type(callback) === "function") ? callback :function(){};
            var _this = this;
            if($.type(url) === "string"){
                $.ajax({
                    url:url,
                    data:this.opts.qDatas,
                    success:function(data){
                        _this.opts.data = data;
                        callback.call(_this);
                    }
                })
            }else{
                this.opts.data = url;
                callback.call(_this);
            }
        };
    
        /**
         * 渲染
         */
        ProvinceAndCity.prototype.render = function () {
            var provinceData = this.opts.data || [];
            var _this = this;
            $.each(provinceData, function (i, item) {
                var $span = $(addItem.call(_this, item));
                $span.data("citys", item["citys"] || []); //省对应的城市数据
                $span.find(".province").data("data", item); //省的数据
                _this.$content.append($span);
    
            });
        };
    
        ProvinceAndCity.prototype.writeValue = function () {
            var _this = this;
            if (this.$hiddenInput.length && this.$hiddenInput.val()) {
                var value = this.$hiddenInput.val();
                value = value.split(this.opts.splitStr);
                //过滤数据,先找出省份,再找出城市
                var provinceDatas = [],
                    cityDatas = {}, //城市的用对象字面量来表示
                    values = [], //文本框中要显示的信息
                    indexName = this.opts.simpleData.id;
    
                for (var i = 0, len = value.length; i < len; i++) {
                    var id = value[i];
    
                    innerNoop://命名内圈语句
                        for (var j = 0, len2 = this.opts.data.length; j < len2; j++) {
                            var provinceItem = this.opts.data[j];
                            if (id === provinceItem[indexName]) {
                                provinceDatas.push(provinceItem);
                                _this.addCheckedItem.call(_this, provinceItem, "province");
                                values.push(provinceItem[_this.opts.simpleData.name]);
                                break;
                            } else {
                                var citys = provinceItem["citys"]; //城市
                                for (var k = 0, len3 = citys.length; k < len3; k++) {
                                    var city = citys[k];
                                    if (id === city[indexName]) {
                                        cityDatas[city.pid] = cityDatas[city.pid] || [];
                                        cityDatas[city.pid].push(city);
                                        _this.addCheckedItem.call(_this, city, "city");
                                        values.push(city[_this.opts.simpleData.name]);
                                        break innerNoop;
                                    }
                                }
                            }
                        }
                }
    
                this.$elem.val(values.join(this.opts.splitStr));
    
                //省份回调
                $.each(provinceDatas, function (i, provinceValue) {
                    var $span = $("#" + provinceValue[_this.opts.simpleData.id]),
                        $input = $span.find(".province");
                    $span.css("color", "orange");
                    setChecked($input,true);
                    _this.$container.trigger("choose.provinceAndCity", $input);
                });
    
                //城市进行回调
                $.each(this.$container.find(".province"), function (i, input) {
                    var $input = $(input),
                        currData = $input.data("data"),
                        $span = $input.parents(".item"),
                        checkedCitys = cityDatas[currData[_this.opts.simpleData.id]];
                    if (checkedCitys) {
                        $span.data("checkedData", checkedCitys);
                        $span.css("color", "orange");
                    }
                });
            }
        };
    
        ProvinceAndCity.prototype.bindEvent = function () {
            var _this = this;
            this.$container.on("click.provinceAndCity", "span.item", function (ev) {
                //由于cityPanel加在span中,所以点击cityPanel中的内容时,也会出发该事件,所以在这里要判断一下
                if ($(ev.target).hasClass("item") || $(ev.target).hasClass("collapse")) {
                    _this.$container.find(".detail").hide();
                    _this.$container.trigger("expand.provinceAndCity", this);
                }
            }).on("mouseleave.provinceAndCity", "span.item", function () {
                _this.$container.trigger("collapse.provinceAndCity", this);
            }).on("click.provinceAndCity", "input[type='checkbox']", function () {
                _this.$container.trigger("choose.provinceAndCity", this);
            }).on("click.provinceAndCity", ".expand", function () {
                _this.$container.trigger("collapse.provinceAndCity", $(this).parents(".item"));
            });
    
            this.$checkedArea.on("click.provinceAndCity", ".close", function () {
                _this.$container.trigger("delete.provinceAndCity", $(this).parent());
            });
    
            this.$toolbar.on("click.provinceAndCity", "input", function () {
                _this.$container.trigger("sure.provinceAndCity");
                _this.hide();
            });
    
    
        };
    
        /**
         * 添加自定义事件
         */
        ProvinceAndCity.prototype.addEvent = function () {
            var _this = this;
            //自定义展开函数(点击省份,展开城市)
            this.$container.on("expand.provinceAndCity", function (e, span) {
                var $span = $(span);
                $span.find(".detail").show();
                _this.$cityPanel.html("");
                $.each($span.data("citys"), function (i, item) {
                    var $label = $('<label id="' + item[_this.opts.simpleData.id] + '" class="ellipsis" title="' + item[_this.opts.simpleData.name] + '"><input type="checkbox" />' + item[_this.opts.simpleData.name] + '</label>');
                    $label.find("input").data("data", item);
                    _this.$cityPanel.append($label);
                });
                //展开城市的时候,要进行回填
                if ($span.find(".province").is(":checked")) {
                    var $inputs = _this.$cityPanel.find("input");
                    setChecked($inputs,true);
                    $inputs.attr("disabled","disabled");
                } else {
                    var tempData = $span.data("checkedData") || [];
                    $.each(tempData, function (i, item) {
                        setChecked(_this.$cityPanel.find("#" + item[_this.opts.simpleData.id]).find("input"),true);
                    });
                }
    
                $span.append(_this.$cityPanel.show());
            });
    
            //自定义折叠函数(点击折叠,关闭城市弹出层)
            this.$container.on("collapse.provinceAndCity", function (e, span) {
                var $span = $(span);
                $span.find(".detail").hide().end().find(".cityPanel").hide();
            });
    
            //input checked事件
            this.$container.on("choose.provinceAndCity", function (e, input) {
                var $span = $(input).parents(".item"),
                    $input = $(input),
                    currData = $input.data("data"); //当前选择的input框对应的数据值
                if ($input.hasClass("province")) {
                    if ($input.is(":checked")) {
                        var $inputs = _this.$cityPanel.find("input");
                        setChecked($inputs,true);
                        $inputs.attr("disabled","disabled");
                        $span.data("checkedData", []); //清除选中的data
                        _this.addCheckedItem.call(_this, currData, "province");
                    } else {
                        var $inputs = _this.$cityPanel.find("input");
                        setChecked($inputs,false);
                        $inputs.removeAttr("disabled");
                        $.each(_this.$checkedArea.find(".checkedItem"), function (i, item2) {
                            var $item = $(item2),
                                td = $item.data("data");
                            if (currData[_this.opts.simpleData.id] === td[_this.opts.simpleData.id]) $item.remove();
                        });
                    }
    
                } else {
                    var tempData = $span.data("checkedData") || [];
                    if ($input.is(":checked")) {
                        //把选择的城市放入缓存
                        tempData.push(currData);
                        _this.addCheckedItem.call(_this, currData, "city");
                    } else { //当没有选中的时候,要在数据缓存中过滤掉被取消勾选的数据
                        var newData = tempData;
                        tempData = [];
                        $.each(newData, function (i, item) {
                            if (currData[_this.opts.simpleData.id] !== item[_this.opts.simpleData.id]) {
                                tempData.push(item);
                            } else {
                                $.each(_this.$checkedArea.find(".checkedItem"), function (i, item2) {
                                    var $item = $(item2),
                                        td = $item.data("data");
                                    if (item[_this.opts.simpleData.id] === td[_this.opts.simpleData.id]) $item.remove();
                                });
                            }
                        });
                    }
                    $span.data("checkedData", tempData);
                }
            });
    
            //删除操作(拿到所有省的节点,判断当前删除的是不是省的数据,如果是省的数据,直接删除已经选择的节点,把该省对应的checkbox设置为不选中并执行choose.provinceAndCity函数,如果不是省的数据,则要遍历每个省下面的缓存已选中的城市,并踢出该城市)
            this.$container.on("delete.provinceAndCity", function (e, span) {
                var currData = $(span).data("data");
                $.each(_this.$container.find(".province"), function (i, input) {
                    var $input = $(input);
                    var tempData = $input.data("data");
                    if (currData[_this.opts.simpleData.id] === tempData[_this.opts.simpleData.id]) {
                        setChecked($input,false);
                        _this.$container.trigger("choose.provinceAndCity", input);
                    }
    
                    var $pSpan = $input.parents(".item");
                    var newData = [];
                    $.each($pSpan.data("checkedData") || [], function (i, itemValue) {
                        if (itemValue[_this.opts.simpleData.id] !== currData[_this.opts.simpleData.id]) {
                            newData.push(itemValue);
                        }
                    });
                    $pSpan.data("checkedData", newData);
                });
                $(span).remove();
            });
    
            //确认操作
            this.$container.on("sure.provinceAndCity", function () {
                var resultData = [],
                    ids = [],
                    values = [];
                $.each(_this.$checkedArea.find(".checkedItem ") || [], function (i, item) {
                    var itemValue = $(item).data("data");
                    ids.push(itemValue[_this.opts.simpleData.id]);
                    values.push(itemValue[_this.opts.simpleData.name]);
                    resultData.push(itemValue);
                });
    
                _this.$elem.val(values.join(_this.opts.splitStr));
                _this.$hiddenInput.length && _this.$hiddenInput.val(ids.join(_this.opts.splitStr));
                //执行回调
                _this.callback(resultData);
            });
    
        };
    
        /**
         * 添加选择的项目
         * @param {Object} data
         * @param {Object} type
         */
        ProvinceAndCity.prototype.addCheckedItem = function (data, type) {
            var needAdd = true; //标记是否需要添加
            if (type === "province") {
                //先检查已经选择的数据中,是否有省内的城市,如果有省内城市,则清除
                var id = this.opts.simpleData.id;
                $.each(this.$checkedArea.find(".checkedItem"), function (i, item) {
                    var $item = $(item),
                        tempData = $item.data("data");
                    if (tempData["pid"] === data[id]) {
                        $item.remove();
                    }else if(tempData[id] === data[id]){
                        needAdd= false;
                        return false;
                    }
                });
            }
            needAdd &&  this.$checkedArea.append(createCheckedItem.call(this, data));
        };
    
        function createCheckedItem(data) {
            var $span = $('<span class="checkedItem ellipsis">' + data[this.opts.simpleData.name] + '<i class="close"></i></span>');
            $span.data("data", data);
            return $span;
        }
    
        /**
         *显示
         */
        ProvinceAndCity.prototype.show = function () {
            this.setPos();
            this.$container.css("visibility", "visible");
        };
    
    
        ProvinceAndCity.prototype.setPos = function () {
            var pointer = this.$elem.offset();
            this.$container.css({"left": pointer.left, "top": pointer.top + this.$elem.outerHeight()});
        };
    
        ProvinceAndCity.prototype.hide = function () {
            this.$container.css({"visibility": "hidden", "left": "-1000px", "top": "-1000px"});
        };
    
        ProvinceAndCity.prototype.destroy = function () {
            //移除该控件绑定的所有的事件
            this.clear();
            this.$container.remove();
            delete this;
        };
    
        ProvinceAndCity.prototype.clear = function(){
            this.$container.off(".provinceAndCity");
            this.$checkedArea.off(".provinceAndCity");
            this.$toolbar.off(".provinceAndCity");
        };
    
    
        /**
         * 添加item
         * @param {Object} item
         */
        function addItem(item) {
            return '<span class="item" id="' + item[this.opts.simpleData.id] + '">' +
                '<i class="collapse"></i>' + item[this.opts.simpleData.name] + '' +
                '<div class="detail">' +
                '<i class="expand"></i>' +
                '<label class="ellipsis"><input type="checkbox" class="province"  value="' + item[this.opts.simpleData.id] + '" />' + item[this.opts.simpleData.name] + '</label>' +
                '</div>' +
                '</span>';
        }
    
        /**
         * 设置选中
         * @param $inputs
         * @param state
         */
        function setChecked($inputs,state){
            $.each($inputs, function(i,input) {
                input.checked = state;
            });
        }
    
    
        return ProvinceAndCity;
    });
    View Code

    3、provinceAndCityExtra.js 代码的二次封装

    /**
     * Created by zh on 2017/8/11.
     * @desc:酒店身份城市多选控件封装
     */
    define(["jquery","js/core/ProvinceAndCity"],function($,ProvinceAndCity){
        var data = ssqx;
        var provinceData = data[0].data,
            cityData = data[1].data;
    
        var newData = [];
        $.each(provinceData, function(i,item) {
            item.type = "p";
            item.citys = getCitysByProvinceId(item.id,cityData);
            newData.push(item);
        });
        delete data;
        delete provinceData;
        delete cityData;
    
    
        $.fn.showProvinceAndCity = function(options,cbFn){
    
            var pc = new ProvinceAndCity(this.get(0),options,cbFn);
    
            pc.load(newData,function(){
                this.render();
                this.writeValue();
            });
    
            this.on("click",function(){
                pc.show();
            });
        };
    
    
        function getCitysByProvinceId(pId,cityData){
            var cityDatas = [];
            $.each(cityData, function(i,item) {
                if(item.pid === pId){
                    item.type ="c";
                    cityDatas.push(item);
                }
            });
            return cityDatas;
        }
    
    });

    4、页面入口文件main.js

    /**
     * Created by zh on 2017/8/11.
     */
    requirejs.config({
        baseUrl:"http://localhost:63343/WS_webStorm/components/",  //后面应用到项目中,会是项目的一个地址,用全局变量basePath代替
        paths:{
            "jquery":"lib/jquery-1.9.1",
            "hotel":"js/fl/hotel/",
            "common":"js/fl/common/"
        }
    });
    
    
    require(["jquery","hotel/provinceAndCityExtra","common/yearAndMonthExtra"],function($,b){
    
        $("#txt1").showProvinceAndCity({
            hiddenName:"txt2"
        },function(data){
            console.log(data);
        });
    
    
        $("#yearAndMonthDiv").showYearAndMonth({
            startTime:1933,
            disabledLater:true,
            yearName:"year",
            montName:"month"
        },function(select,value){
            console.log(select);
            console.log(value);
        });
    
    
    
    });

    5、页面文件

    <!DOCTYPE html>
    <html>
        <head>
            <meta charset="UTF-8">
            <title>所有控件例子入口</title>
            <link rel="stylesheet" type="text/css" href="../css/provinceAndCity.css"/>
    
            <!--这里是静态数据文件,模拟ajax请求-->
            <script type="text/javascript" src="../data/ssqx.js" ></script>
            <script type="text/javascript" src="../lib/require.js" data-main="main"></script>
    
        </head>
        <body>
            <h1>酒店省份和城市多选控件</h1>
            请选择省市:<input type="text" id="txt1" /><input type="text" id="txt2" value="10114,10113,10435,10333,02013,10176,10452,10858,10189,10216,10444,02028"  />
            <h1>年月控件</h1>
            <div id="yearAndMonthDiv"></div>
    
        </body>
    </html>

    6、效果图:

    以上看似风平浪静,顺理成章。然而,突然眉头一紧,模块化以后,拆分了这么多js文件,怎么进行压缩合并呢?

    先在requirejs官网上找了一把,看到了r.js,然后再一搜索,找到了一篇文章。地址:http://www.oschina.net/translate/optimize-requirejs-projects

    真是柳暗花明又一村,二话不说,直接跟着文章上的描述开始操作,一把成功。截图如下:

    卵。。。。。。。

    我司项目都是管理型的后台项目,又是十几个产品线在一起的,每个产品线又有几十个甚至上百个jsp文件。按照requirejs提供的压缩方式(根据入口函数,把每个页面需要的模块合并压缩到main.js中),我这要如何去做?难道每次新增一个入口函数,就要去改build.js配置文件?我在想,是否有自动化构建的工具呢,可以直接根据每个文件夹下的main.js文件进行打包呢?还是说有更好的办法?

    我已黔驴技穷,但是我相信互联网的力量。我想正在看的你,还有即将看的你,肯定会有自己的想法,那为什么不留下你的想法和交流方式呢?山不转水转,说不定哪天我们又有缘相见呢?

    求赐教!!!

  • 相关阅读:
    Switch
    java 函数 运算符
    java 基本类型
    更新时电话查重
    微信公众平台发送模板消息时连发三遍的最简单解决办法
    Yii2.0 发送邮件时中文附件乱码的问题
    Yii2.0 发送文件
    Yii2.0 请求
    Yii2.0随笔 路由
    yii2.0 在save保存之前的操作(放在模型model文件内)
  • 原文地址:https://www.cnblogs.com/tengri/p/7374293.html
Copyright © 2020-2023  润新知