# 业务组件

# 组件概述

上一章节《功能组件》我们详细的阐述了偏底层的功能函数的封装,最终形成以组件形式对外提供API进行调用的方法,那么在这一章节中,我们将重点给大家介绍在功能组件UI组件模块JS之前起到桥梁作用的业务JS组件,其实最终对外提供API组件接口的是业务组件功能组件其实是对业务组件的支撑,可以理解为基石

# 列表组件

这里给大家介绍使用频率最高的一个组件table,此组件也是基于Layui的table组件,在此我们做了写封装和增强,组件的参数我们都是从外面传入,我们都预留了相应的API接口,在使用过程中我们只需要正常的调用,传入参数即可:

tableIns: function (cols, tableName, callback = null, url = '', tableSort = false) {
    _tableName = tableName;
    _callback = callback;

    // 初始化网络请求URL
    if (!url || url == '') {
        url = cUrl + "/list";
    }

    // 初始化网络请求参数
    var param = $("#param").val();
    if (param) {
        param = JSON.parse(param);
        if ($.isArray(param)) {
            for (var i in param) {
                if (url.indexOf("?") >= 0) {
                    // 包含?
                    url += "&" + param[i];
                } else {
                    // 不包含?
                    url += "?" + param[i];
                }
            }
        }
    }

    // 初始化TABLE组件
    _tableIns = table.render({
        elem: "#" + _tableName
        , url: url
        // , toolbar: '#toolbar_header'
        // , title: '用户数据表'
        // , totalRow: true
        , method: 'post'
        , cellMinWidth: 150
        // , page: true
        , page: {
            // 限定条数   总数、计数  上一页     页     下一页    到第几页、跳
            layout: ['refresh', 'prev', 'page', 'next', 'skip', 'count', 'limit'] //自定义分页布局
            , curr: 1
            , groups: 10 //显示 连续页码
            , first: '首页'
            , last: '尾页'
        }
        // //初始排序
        // , initSort: {
        //     field: 'id', //排序字段,对应 cols 设定的各字段名
        //     type: 'desc' //排序方式  asc: 升序、desc: 降序、null: 默认排序
        // }
        , height: "full-100"
        , limit: 20
        , limits: [20, 30, 40, 50, 60, 70, 80, 90, 100, 150, 200, 1000]
        , even: true // 开启隔行背景
        , cols: [cols]
        , loading: true
        , done: function (res, curr, count) {
            // 新增监听table行双击事件
            if (_isDbclick) {
                var tbody = $('.layui-table-body').find("table").find("tbody");
                var tr = tbody.children("tr");
                tr.on('dblclick', function () {
                    var index = tbody.find(".layui-table-hover").data('index');
                    var obj = res.data[index];
                    common.edit(_title, obj.id, _width, _height);
                });
            }

        }
    });

    // 监听头工具栏事件
    table.on("toolbar(" + _tableName + ")", function (obj) {
        var checkStatus = table.checkStatus(obj.config.id);
        switch (obj.event) {
            case 'getCheckData':
                var data = checkStatus.data;
                layer.alert(JSON.stringify(data));
                break;
            case 'getCheckLength':
                var data = checkStatus.data;
                layer.msg('选中了:' + data.length + ' 个');
                break;
            case 'isAll':
                layer.msg(checkStatus.isAll ? '全选' : '未全选');
                break;
        }
        ;
    });

    // 监听行工具事件
    table.on("tool(" + _tableName + ")", function (obj) {
        var data = obj.data
            , layEvent = obj.event;

        if (layEvent === 'edit') {
            // 编辑记录
            common.edit(_title, data.id, _width, _height);
        } else if (layEvent === 'detail') {
            // 记录详情
            common.detail(_title, data.id, _width, _height);
        } else if (layEvent === 'del') {
            // 删除记录
            common.delete(data.id, function (data, res) {
                if (res) {
                    obj.del();
                } else {
                }
            });
        } else if (layEvent === 'cache') {
            // 重置缓存
            common.cache(data.id);
        } else if (layEvent === 'copy') {
            // 一键复制
            common.copy(_title, data.id, _width, _height);
        } else {
            // 其他操作,函数回调
            if (_callback) {
                _callback(layEvent, data);
            }
        }
    });

    // 监听复选框
    table.on("checkbox(" + _tableName + ")", function (obj) {
        // console.log(obj.checked); //当前是否选中状态
        // console.log(obj.data); //选中行的相关数据
        // console.log(obj.type); //如果触发的是全选,则为:all,如果触发的是单选,则为:one
    });

    // 监听单元格编辑
    table.on('edit(' + _tableName + ')', function (obj) {
        var value = obj.value //得到修改后的值
            , data = obj.data //得到所在行所有键值
            , field = obj.field; //得到字段

        var json_data = {};
        json_data['id'] = data.id;
        json_data[field] = value;

        // JSON字符串
        var json_str = JSON.stringify(json_data);

        // JSON数据
        var json = JSON.parse(json_str);

        // 发起网络请求
        var url = cUrl + "/update";
        common.ajaxPost(url, json, function (res, success) {
            // console.log("字段【" + field + "】:【" + value + "】值更新成功");
        }, '更新中...');

    });

    // 监听行单击事件
    table.on("row(" + _tableName + ")", function (obj) {
        // 标注选中样式
        obj.tr.addClass('layui-table-click').siblings().removeClass('layui-table-click');
        var data = obj.data;
    });

    // 监听排序事件
    if (tableSort) {
        table.on("sort(" + _tableName + ")", function (obj) {
            // // 当前排序的字段名
            // console.log(obj.field);
            // // 当前排序类型:desc(降序)、asc(升序)、null(空对象,默认排序)
            // console.log(obj.type);
            // // 当前排序的 th对象
            // console.log(this);

            // 请求服务端进行动态排序
            table.reload(_tableName, {
                initSort: obj
                , where: {
                    field: obj.field //排序字段
                    , order: obj.type //排序方式
                }
            });
        });
    }
    return this;

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183

上述代码比较丰富,整体架构基于Layui,方法中我们做了部分业务数据和参数相关的处理以及动态传值的功能,大家可以不去过多的关注,会用即可,

调用方式:

func.tableIns(cols, tableName, callback = null, url = '', tableSort = false);
1
  • 参数说明:
  1. cols:数据列表的字段列表信息,下面会举例说明;
  2. tableNametable数据表的名称;
  3. callback:回调函数,看上面的源码我们可以知道,内置了添加编辑删除详情一键复制重置缓存等节点操作事件,那么当我们自定义了一些按钮之后,我们该如何去接收和捕捉到事件呢,这是callback回调事件帮我们实现了功能,除上述意外的节点之外,我们都可以在调用的时候做回调接收;
  4. url:自定义URL,一般情况下这个参数不需要我们传,获取数据列表的方法我们统一使用list方法,当需要变更请求方法时可以自定义完成的URL请求地址
  5. tableSort:是否开启排序时间,默认false,当前开启时我们可以调用后端的方法进行整体的排序,而不是目前只能在当前界面进行某个字段的数据排序;

使用案例:

var cols = [
    {type: 'checkbox', fixed: 'left'}
    , {field: 'id', width: 80, title: 'ID', align: 'center', sort: true, fixed: 'left'}
    , {field: 'name', width: 200, title: '职级名称', align: 'center'}
    , {field: 'status', width: 100, title: '状态', align: 'center', templet: '#statusTpl'}
    , {field: 'sort', width: 100, title: '显示顺序', align: 'center'}
    , {field: 'createUserName', width: 100, title: '添加人', align: 'center'}
    , {field: 'createTime', width: 180, title: '创建时间', align: 'center'}
    , {field: 'updateUserName', width: 100, title: '更新人', align: 'center'}
    , {field: 'updateTime', width: 180, title: '更新时间', align: 'center'}
    , {fixed: 'right', width: 150, title: '功能操作', align: 'center', toolbar: '#toolBar'}
];

//【渲染TABLE】
func.tableIns(cols, "tableList");
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

内容分析:

  1. tableList:这是数据表的控件名称,对应的是HTML中的控件,如下所示:
<table class="layui-hide" id="tableList" lay-filter="tableList"></table>
1
  1. #statusTpl:这里是一个绑定参数,在HTML中有一点对应的代码,如下所示:
<script type="text/html" id="statusTpl">
    <input type="checkbox" name="status" value="{{ d.id }}" lay-skin="switch" lay-text="正常|停用" lay-filter="status" {{ d.status == 1 ? 'checked' : '' }} >
</script>
1
2
3
  1. '#toolBar': 这里是一个绑定参数,在HTML中有一点对应的代码,如下所示:
<script type="text/html" id="toolBar">
    <widget:btnEdit name="编辑"/>
    <widget:btnDel name="删除"/>
</script>
1
2
3
4

备注:具体的效果请看演示站给大家所呈现的实际效果;

# 树状列表

在常见的数据列表中,一种就是上述我们所描述的普通table数据列表,还有一种就是tree树状数据列表,下面我们特地给大家详细的介绍下树状列表;

treetable: function (cols = [], tableName, isExpand = true, treeSpid = 0, treePidName = '', callback = null, url = '') {
    _tableName = tableName;
    // 初始化请求URL
    if (!url) {
        url = cUrl + "/list";
    }

    // 加载treetable
    var insTb = treeTable.render({
        elem: '#' + tableName, //表格id
        url: url,
        method: "POST",
        height: 'full-50',
        cellMinWidth: 80,
        // toolbar: 'default',
        tree: {
            iconIndex: 1,
            idName: 'id',
            pidName: treePidName ? treePidName : "pid",
            isPidData: true
        },
        cols: [cols],
        done: function (res, curr, count) {
            // // res 可以获取文件的数据,或者是ajax请求的数据
            // console.log(res);
            // //得到当前页码
            // console.log(curr);
            // //得到数据总量
            // console.log(count);
            // 关闭加载
            layer.closeAll('loading');
        },
        style: 'margin-top:0;'
    });

    // 工具条点击事件
    treeTable.on('tool(' + tableName + ')', function (obj) {
        var data = obj.data;
        var layEvent = obj.event;
        // 当前记录ID
        var id = data.id;
        if (layEvent === 'add') {
            // 添加记录
            common.edit(_title, 0, _width, _height, ['pid=' + id]);
        } else if (layEvent === 'edit') {
            // 修改记录
            common.edit(_title, id, _width, _height);
        } else if (layEvent === 'del') {
            // 删除记录
            common.delete(id, function (data, isSuc) {
                if (isSuc) {
                    obj.del();
                } else {
                }
            });
        } else {
            // 其他操作(回调函数)
            if (callback) {
                callback(layEvent, id, 0);
            }
        }
    });

    // 全部折叠
    $('#collapse').on('click', function () {
        insTb.foldAll();
        return false;
    });

    // 全部展开
    $('#expand').on('click', function () {
        insTb.expandAll();
        return false;
    });

    // 刷新页面
    $('#refresh').on('click', function () {
        insTb.refresh();
        return false;
    });

    // 搜索
    $('#search').click(function () {
        var keywords = $('#keywords').val();
        if (keywords) {
            insTb.filterData(keywords);
        } else {
            insTb.clearFilter();
        }
        return false;
    });
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92

上述代码比较丰富,整体架构基于第三方开源组件库,方法中我们做了部分业务数据和参数相关的处理以及动态传值的功能,大家可以不去过多的关注,会用即可,

调用方式:

func.treetable(cols, tableName, isExpand = true, treeSpid = 0, treePidName = '', callback = null, url = '');
1
  • 参数说明:
  1. cols:数据列表的字段列表信息,下面会举例说明;
  2. tableNametable数据表的名称;
  3. isExpand:是否默认展开,默认:true;
  4. treeSpid:数据表的主键参数值,默认id;
  5. treePidName:上下级关系中的父级参数,默认是pid;
  6. callback:回调函数,看上面的源码我们可以知道,内置了添加编辑删除详情一键复制重置缓存等节点操作事件,那么当我们自定义了一些按钮之后,我们该如何去接收和捕捉到事件呢,这是callback回调事件帮我们实现了功能,除上述意外的节点之外,我们都可以在调用的时候做回调接收;
  7. url:自定义URL,一般情况下这个参数不需要我们传,获取数据列表的方法我们统一使用list方法,当需要变更请求方法时可以自定义完成的URL请求地址

使用案例:

var cols = [
        {field: 'id', width: 80, title: 'ID', align: 'center', sort: true}
    , {field: 'name', width: 300, title: '部门名称', align: 'left'}
    , {field: 'type', width: 100, title: '类型', align: 'center', templet(d) {
        var cls = "";
        if (d.type == 1) {
            // 公司
            cls = "layui-btn-normal";
        } else if (d.type == 2) {
            // 部门
            cls = "layui-btn-danger";
        } 
        return '<span class="layui-btn ' + cls + ' layui-btn-xs">'+d.typeName+'</span>';
    }}
    , {field: 'sort', width: 100, title: '排序', align: 'center'}
    , {field: 'createUserName', width: 100, title: '添加人', align: 'center'}
    , {field: 'createTime', width: 180, title: '添加时间', align: 'center'}
    , {field: 'updateUserName', width: 100, title: '更新人', align: 'center'}
    , {field: 'updateTime', width: 180, title: '更新时间', align: 'center'}
    , {width: 220, title: '功能操作', align: 'left', toolbar: '#toolBar'}
];

//【渲染TABLE】
func.treetable(cols, "tableList");
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

内容分析:

  1. tableList:这是数据表的控件名称,对应的是HTML中的控件,如下所示:
<table class="layui-hide" id="tableList" lay-filter="tableList"></table>
1
  1. '#toolBar': 这里是一个绑定参数,在HTML中有一点对应的代码,如下所示:
<script type="text/html" id="toolBar">
    <widget:btnEdit name="编辑"/>
    <widget:btnDel name="删除"/>
    {{#  if(d.hasChild == 1){ }}
    <widget:btnAddZ name="添加"/>
    {{#  } }}
</script>
1
2
3
4
5
6
7

备注:具体的效果请看演示站给大家所呈现的实际效果;

# 窗体参数

为了简化开发和使用,我们特地涉及了弹窗等常规的参数,在使用中只需要在模块JS中调用此方法设置好参数,窗体等主键会自动解析这里传入的参数,如下图:

setWin: function (title, width = 0, height = 0) {
        _title = title;
        _width = width;
        _height = height;
        return this;
    }
1
2
3
4
5
6

# 查询搜索

这里的查询搜索主要是对接UI层级的时间了,这里进行事件的捕捉,最终调用的是《搜索功能》中提供的API接口方法,内容很简单,通俗易懂,如下图:

searchForm: function (searchForm, tableList) {
    // 搜索功能
    form.on("submit(" + searchForm + ")", function (data) {
        common.searchForm(table, data, tableList);
        return false;
    });
}
1
2
3
4
5
6
7

# 多行获取

在上一章节《批量操作》中我们介绍了批量处理的功能,比如批量删除,那么我们如何去获取选中的行参数值呢,这里我们提供了特定的方法,如下所示:

getCheckData: function (tableName) {
    if (!tableName) {
        tableName = _tableName;
    }
    var checkStatus = table.checkStatus(tableName)
        , data = checkStatus.data;
    return data;

}
1
2
3
4
5
6
7
8
9

# 初始化日期

UI组件在初始化界面时,会调动业务JS组件去初始化日期控件,这里便是对日期初始化机制的支持,如下所示:

initDate: function (item, callback = null) {
    common.initDate(item, function (value, date) {
        if (callback) {
            callback(value, date);
        }
    });
}
1
2
3
4
5
6
7

# 页面弹窗

在上一章节《窗体弹窗》中我们详细的各式的批量操作的功能,在业务功能JS中,我们做了对接UI相关的上层调用及回调,如下所示:

showWin: function (title, url, width = 0, height = 0, param = [], type = 2, btn = [], callback = null) {
    common.showWin(title, url, width, height, param, type, btn, function (layero, index) {
        if (callback) {
            callback(layero, index);
        }
    });
}
1
2
3
4
5
6
7

# 网络请求

在业务JS中我们撰写的专门的对外的API接口的《网络请求》,然后对外提供接口服务,最终调用的还是功能组件中的网络请求服务,如下所示:

  • POST请求
ajaxPost: function (url, data, callback = null, msg = '处理中...') {
    common.ajaxPost(url, data, callback, msg);
}
1
2
3
  • GET请求
ajaxGet: function (url, data, callback = null, msg = '处理中...') {
    common.ajaxGet(url, data, callback, msg);
}
1
2
3

# Switch开关

在上一章节中我们详细的介绍了《开关事件》的具体实现方式,这里我们依然做了对接业务层级的调用,捕捉UI页面操作时间,然后调用底层功能组件来实现功能,如下所示:

formSwitch: function (name, url = '', callback = null) {
    // 方法调用
    common.formSwitch(name, url, function (data, res) {
        if (callback) {
            callback(data, res);
        }
    });
}
1
2
3
4
5
6
7
8

# 上传文件

上传文件的具体实现方式我们在上节《上传文件》中做了详细的说明,这里不做过多累赘的表述,在业务组件中做了调用及业务层面的对接,如下所示:

uploadFile: function (elem_id, callback = null, url = '', exts = 'xls|xlsx', size = 10240, data = {}) {
    // 方法调用
    common.uploadFile(elem_id, function (res, isSucc) {
        if (callback) {
            callback(res, isSucc);
        }
    }, url, exts, size, data);
}
1
2
3
4
5
6
7
8