layui.define(['jquery', 'layer'], function (exports) {
    // 获取layui框架中引入的jQuery模块，并将其赋值给变量$，方便后续使用jQuery的相关功能，如DOM操作、AJAX请求等
    var $ = layui.jquery;
    // 创建一个空对象chekedArr，用于存储一些被选中的数据相关信息（从后续代码推测可能是记录过滤组件中被选中的选项等情况），其结构和具体用途会在后续代码中逐渐清晰
    var chekedArr = {};
    // 创建一个名为layfilter的对象，用于封装与查询过滤组件相关的一系列功能方法，比如初始化组件、获取选中值、绑定事件等操作
    var layfilter = {
        // 定义名为render的方法，用于渲染查询过滤组件，该方法接收一个名为options的参数，参数中应该包含了组件渲染所需的各种配置信息，如请求数据的URL、筛选条件等
        render: function (options) {
            // 从传入的options参数中获取名为url的属性值，该值可能是用于获取过滤组件数据的接口地址，后续根据这个地址来发起数据请求
            var url = options.url;
            // 定义一个布尔变量flag，并初始化为true，用于标记数据获取及组件渲染过程中的一些状态，比如数据获取是否成功等情况，后续会根据不同情况修改这个值来控制流程
            var flag = true;
            // 判断如果传入的url属性存在（即有值），则执行以下代码块，意味着如果配置了数据请求地址，就按照这个地址去获取数据来渲染组件
            if (url) {
                // 使用jQuery的$.getJSON方法发起一个GET类型的AJAX请求，请求的URL就是前面获取到的url，同时传入options.where作为请求参数（可能是一些筛选条件等数据），请求成功后的回调函数用于处理返回的数据
                $.getJSON(url, options.where, function (res) {
                    // 判断返回数据中的code属性是否等于0，如果等于0通常表示请求成功，数据正常获取到了，可以进行后续的组件渲染等操作
                    if (res.code == 0) {
                        // 从返回的数据中获取名为data的属性值，这应该就是实际用于渲染组件的数据源，例如包含了各种过滤选项的数据列表等内容
                        var data = res.data;
                        // 此处疑似拼写错误，将flase（应该是想写false）赋值为true，不过从逻辑上推测可能是想重置某个用于判断的标志位，但这个变量名错误可能会导致意想不到的问题，先按照正确的逻辑理解此处应该是设置一个表示数据获取成功等相关含义的标志为true
                        flase = true;
                        // 调用layfilter对象自身的init方法，传入options和获取到的data数据，进行查询过滤组件的初始化操作，将数据展示在页面上形成可交互的过滤组件
                        layfilter.init(options, data);
                    } else {
                        // 如果返回数据中的code属性不等于0，表示请求出现异常，比如服务器端返回错误等情况，使用layer弹出提示框显示相应的错误信息，优先显示res.msg中的内容，如果res.msg不存在则显示默认的提示信息'查询过滤组件数据异常'，同时设置提示框的图标为2（通常表示错误图标）
                        layer.msg(res.msg || '查询过滤组件数据异常', {icon: 2});
                        // 将flag标志位设置为false，表示数据获取或处理出现问题，后续可能根据这个标志来决定是否继续进行组件渲染等操作
                        flag = false
                    }
                })
            }
            // 判断如果flag为false（即前面的数据获取或处理出现问题了），则直接使用return语句结束当前函数的执行，不再进行后续可能的组件渲染等操作
            if (!flag) {
                return;
            }
        },
        // 定义名为init的方法，用于初始化查询过滤组件，接收两个参数，options包含了组件的各种配置信息，dataSource则是具体要展示在组件中的数据列表等数据源信息
        init: function (options, dataSource) {
            // 从options参数中获取名为elem的属性值，该值可能是一个DOM元素的选择器或者DOM元素本身，用于指定查询过滤组件在页面中要挂载的位置，后续会将生成的组件内容添加到这个元素内
            var elem = options.elem;
            // 使用jQuery根据elem选择器（或者传入的就是DOM元素本身）获取对应的DOM元素，并赋值给变量$dom，方便后续对这个挂载元素进行操作，比如添加子元素等
            var $dom = $(elem);
            // 从options参数中获取名为itemWidth的属性值，从变量名推测可能是用于设置组件中每个选项元素的宽度相关信息，后续会根据这个值来进行宽度的具体设置
            var itemWidth = options.itemWidth
            // 创建一个空对象arr，同样从后续代码推测可能是用于临时存储一些与组件选项相关的数据结构，具体用途会随着代码执行逐渐清晰
            var arr = {};
            // 使用jQuery创建一个<table>元素，并添加类名"filterTable"，这个表格元素将用于构建查询过滤组件的可视化布局，后续会往里面添加行、列以及具体的选项内容
            var $table = $('<table class="filterTable"></table>');
            // 开始循环遍历数据源dataSource，dataSource应该是一个数组，里面每个元素代表一组过滤选项数据等相关内容，循环是为了逐个处理并展示这些数据到组件中
            for (var i = 0; i < dataSource.length; i++) {
                // 使用jQuery创建一个<tr>元素，代表表格中的一行，用于放置一组过滤选项的相关标题和具体选项内容，后续会将这行添加到前面创建的表格$table中
                var $tr = $('<tr></tr>');
                // 使用jQuery创建一个<td>元素，作为表格的单元格，添加类名"item-title"，并将当前数据源中对应元素的title属性值（应该是这组过滤选项的标题名称）添加到单元格内作为文本内容，用于展示这组选项的标题
                var $td1 = $('<td class="item-title">' + dataSource[i].title + ':</td>');
                // 使用jQuery创建另一个<td>元素，添加类名"items"，这个单元格将用于放置具体的过滤选项内容（如单选框、复选框等选项列表），后续会将构建好的选项列表添加到这个单元格内
                var $td2 = $('<td class="items"></td>');
                // 从当前数据源中对应元素获取名为type的属性值，该值可能用于指定这组过滤选项的类型，比如是单选、复选等类型，后续会根据这个类型来构建不同样式和交互逻辑的选项元素
                var type = dataSource[i].type;
                // 判断如果type属性不存在（即为空），则在控制台输出警告信息，提示第几个元素的类型[type]为空，并将其默认设置为'radio'（单选类型），以保证组件能正常构建，避免因类型未定义出现错误
                if (!type) {
                    console.warn('第' + (i + 1) + '个元素的类型[type]为空设为默认值[radio]');
                    type = 'radio';
                }
                // 使用jQuery创建一个<ul>元素，添加类名"layfilter-ul"，并设置type和name属性，type属性使用前面获取到的（或者默认设置的）选项类型值，name属性则使用当前数据源中对应元素的name属性值（可能用于唯一标识这组选项等用途），这个<ul>元素将作为具体选项的容器，后续会往里面添加一个个具体的选项<li>元素
                var $ul = $('<ul class="layfilter-ul" type="' + type + '" name="' + dataSource[i].name + '"></ul>');
                // 根据前面获取到的itemWidth属性值来计算每个选项的宽度，逻辑是如果itemWidth存在且长度大于0，再判断其长度是否大于当前循环的索引i，如果大于则使用itemWidth中对应索引位置的值作为宽度，否则使用最后一个值作为宽度，如果itemWidth不符合条件（不存在或者长度为0），则默认宽度设置为80像素，这样可以灵活地配置每个选项的宽度
                var width = itemWidth && itemWidth.length > 0? (itemWidth.length > i? itemWidth[i] : itemWidth[itemWidth.length - 1]) : 80;
                // 在arr对象中，以当前数据源中对应元素的name属性值为键，创建一个空数组作为值，从后续代码推测可能是用于存储这组选项中被选中的具体数据等情况，方便后续获取和操作选中的数据
                arr[dataSource[i].name] = [];
                // 开始内层循环，遍历当前数据源中对应元素的data属性（应该是包含了这组选项的具体每个选项的数据列表），用于逐个构建并添加具体的选项元素到前面创建的<ul>容器中
                for (var j = 0; j < dataSource[i].data.length; j++) {
                    // 获取当前数据源中对应元素的data属性（也就是这组选项的具体每个选项的数据列表），不过此处变量名使用item可能不太准确，容易和外层循环的含义混淆，从逻辑上理解这里应该是获取具体的一个选项数据对象
                    var item = dataSource[i].data;
                    // 创建一个类名变量className，并初始化为'layfilter-item'，这个类名可能用于设置选项元素的基本样式等，后续会根据选项是否被选中等情况来动态修改这个类名，添加额外的样式类
                    var className = 'layfilter-item';
                    // 判断当前选项数据对象中的checked属性是否存在且值为'true'，如果满足这个条件，表示这个选项是初始被选中的状态，那么就修改className类名，添加额外的'layfilter-item-checked'类名，用于应用选中状态的样式（比如改变背景色等样式效果），同时将这个选项的相关数据（name和value属性值）添加到前面创建的arr对象中对应name的数组里，用于记录被选中的选项信息
                    if (item[j].checked && item[j].checked == 'true') {
                        className = "layfilter-item layfilter-item-checked";
                        arr[dataSource[i].name].push({name: item[j].name, value: item[j].value});
                    }
                    // 判断当前选项数据对象中的disabled属性是否存在且值为'true'，如果满足这个条件，表示这个选项是被禁用的状态，那么在构建选项元素时，添加'disabled'属性，并应用'layui-disabled'类名（可能是layui框架中用于表示禁用样式的类），使得这个选项呈现出被禁用的视觉效果（如灰色显示、不可点击等），并添加到<ul>容器中
                    if (item[j].disabled && item[j].disabled == 'true') {
                        $ul.append('<li value="' + item[j].value + '" style="width:' + width + 'px;height: 28px;line-height: 28px;" class="' + className + '"><a disabled="disabled" class="layui-disabled">' + item[j].name + '</a></li>');
                    } else {
                        // 如果选项不是被禁用状态，则正常构建选项元素，添加到<ul>容器中，设置相应的value属性、宽度、高度、行高等样式属性，并应用前面定义的className类名，选项内容显示为其name属性值（即用户看到的选项文本）
                        $ul.append('<li value="' + item[j].value + '" style="width:' + width + 'px;height: 28px;line-height: 28px;" class="' + className + '"><a>' + item[j].name + '</a></li>');
                    }

                }
                // 将构建好的包含具体选项的<ul>元素添加到前面创建的用于放置选项的<td>单元格（$td2）中
                $td2.append($ul);
                // 将包含标题的<td>单元格（$td1）和包含选项的<td>单元格（$td2）添加到代表一行的<tr>元素（$tr）中
                $tr.append($td1).append($td2);
                // 将构建好的这一行（$tr）添加到前面创建的表格元素（$table）中，这样逐行添加，最终形成完整的查询过滤组件的可视化表格布局
                $table.append($tr);
            }
            // 将构建好的包含所有过滤选项内容的表格元素（$table）添加到前面获取到的用于挂载组件的DOM元素（$dom）中，使得组件在页面上显示出来
            $dom.append($table);
            // 将记录选中选项信息的arr对象赋值给全局的chekedArr对象，这样在其他方法中（如获取选中值、处理点击事件等操作时）可以访问和操作这些选中的数据信息
            chekedArr = arr;
            // 使用jQuery的bind方法为'.filterTable tr td li a'（也就是查询过滤组件中每个选项内的<a>链接元素，通常用于触发选项的选择操作等交互）绑定点击事件处理函数，当用户点击这些元素时，会执行以下的函数逻辑来处理选项的选中、取消选中等操作以及更新相关数据状态
            $('.filterTable tr td li a').bind('click', function () {
                // 判断当前点击的<a>元素是否有'disabled'属性（即是否是被禁用的选项），如果有则直接使用return语句结束当前函数执行，不做后续的选中相关操作，因为被禁用的选项不应该响应点击选择操作
                if ($(this).attr('disabled')) {
                    return;
                }
                // 获取当前点击的<a>元素的父元素（<li>元素）的父元素（<ul>元素）的type属性值，也就是获取这个选项所属的那组选项的类型（如单选、复选等类型），用于后续根据不同类型来处理选项的选中逻辑
                var itemType = $(this).parent().parent().attr('type');
                // 获取当前点击的<a>元素的父元素（<li>元素）的父元素（<ul>元素）的name属性值，也就是获取这个选项所属的那组选项的唯一标识名称，用于后续在记录选中数据的对象中查找和更新对应的数据
                var name = $(this).parent().parent().attr('name');
                // 判断当前点击的<a>元素的父元素（<li>元素）是否有'layfilter-item-checked'类名，即判断这个选项当前是否是被选中状态，如果是，则执行以下取消选中的相关操作逻辑
                if ($(this).parent().hasClass('layfilter-item-checked')) {
                    // 如果选项当前是被选中状态，使用removeClass方法移除'layfilter-item-checked'类名，使其视觉上呈现未选中的样式效果（比如取消背景色等选中样式）
                    $(this).parent().removeClass('layfilter-item-checked');
                    // 获取chekedArr对象中以当前选项所属的name为键对应的数组（可能包含了这组选项中已被选中的其他选项数据等情况），如果不存在则创建一个空数组，用于后续操作
                    var obj = chekedArr[name] || [];
                    // 循环遍历这个数组，查找与当前点击的选项的value属性值相等的元素（即找到当前要取消选中的那个选项在已记录选中数据中的位置），找到后使用splice方法从数组中删除这个元素，实现取消选中的操作，并更新记录选中数据的对象
                    for (var i = 0; i < obj.length; i++) {
                        if (obj[i].value == $(this).parent().attr('value')) {
                            obj.splice(i, 1);
                            break;
                        }
                    }
                    // 将更新后的数组重新赋值给chekedArr对象中对应的name键，完成取消选中操作后的数据更新
                    chekedArr[name] = obj;
                } else {
                    // 如果当前点击的选项不是被选中状态，则执行以下选中相关的操作逻辑，首先判断选项所属的类型是否存在并且是'checbox'（应该是拼写错误，正确的是'checkbox'，复选框类型）或者'radio'（单选框类型），如果满足这个条件，则根据不同类型来进行相应的选中操作处理
                    if (itemType && ('checbox' == itemType || 'radio' == itemType)) {
                        // 判断如果是单选类型（'radio' == itemType），则获取当前点击的<a>元素的父元素（<li>元素）的所有兄弟元素（也就是这组单选选项中的其他选项元素），然后循环遍历这些兄弟元素，使用removeClass方法移除它们的'layfilter-item-checked'类名，确保在单选模式下，只有当前点击的选项被选中，其他选项都变为未选中状态，实现单选的互斥效果
                        if ('radio' == itemType) {
                            var objs = $(this).parent().siblings();
                            chekedArr[name] = [];
                            for (var i = 0; i < objs.length; i++) {
                                objs.eq(i).removeClass('layfilter-item-checked');
                            }
                        }
                        // 获取chekedArr对象中以当前选项所属的name为键对应的数组（可能为空数组，如果之前没有选中的选项），用于后续操作
                        var obj = chekedArr[name] || [];
                        // 将当前点击的选项的相关数据（name和value属性对应的文本和值，通过$(this).text()和$(this).parent().attr('value')获取）封装成一个对象，添加到这个数组中，表示将这个选项标记为选中状态，并更新记录选中数据的对象
                        obj.push({name: $(this).text(), value: $(this).parent().attr('value')});
                        chekedArr[name] = obj;
                        // 为当前点击的选项的父元素（<li>元素）添加'layfilter-item-checked'类名，使其视觉上呈现选中的样式效果（比如添加背景色等选中样式）
                        $(this).parent().addClass('layfilter-item-checked');
                    } else {
                        // 如果选项所属的类型不是预期的复选或单选类型
                        console.error('复选或单选类型为空？');
                    }
                }
            });
        },
        // 定义名为getValue的方法，它是layfilter对象的一个方法，该方法接收一个名为callback的参数，
        // 其作用应该是用于获取过滤组件中当前被选中项的数据，并通过传入的回调函数callback将数据传递出去
        getValue:function(callback){
            // 调用内部定义的getCheckData函数，该函数的作用从名字推测是获取经过整理后的已选中数据，
            // 并将返回的结果赋值给变量obj，这个obj应该是包含了符合特定格式的已选中数据信息的对象
            var obj = getCheckData();
            // 通过call方法调用传入的回调函数callback，并将当前的this上下文以及获取到的已选中数据对象obj作为参数传递进去，
            // 这样在外部调用getValue方法并传入相应回调函数时，外部就能在回调函数中拿到并处理这些选中数据了
            callback.call(this,obj);
        },
        // 定义名为on的方法，它也是layfilter对象的一个方法，用于给特定的元素绑定指定的事件以及对应的回调函数，
        // 接收两个参数，filter参数可能是一个包含事件和选择器等信息的字符串，callback则是对应的事件触发时要执行的回调函数
        on:function(filter,callback){
            // 通过substring方法截取filter字符串中从开头到第一个'('字符之前的内容，赋值给变量f，
            // 推测这里是获取事件名称部分，例如可能是'click'等事件类型
            var f = filter.substring(0,filter.indexOf('('));
            // 通过substring方法截取filter字符串中从第一个'('字符之后到倒数第二个字符（去掉最后的')'字符）的内容，赋值给变量e，
            // 推测这里是获取选择器相关的部分，用于在后续代码中找到要绑定事件的具体DOM元素
            var e =  filter.substring(filter.indexOf('(')+1,filter.length-1);
            // 使用typeof操作符判断传入的callback参数是否是一个函数类型，
            // 如果是函数类型，表示传入的回调函数是合法有效的，就执行后续的绑定事件逻辑，否则执行下面的错误提示逻辑
            if(typeof callback === "function"){
                // 使用jQuery的on方法给满足选择器条件（通过$("[lay-filter='"+e+"']"构造选择器，其中e就是前面截取出来的选择器相关部分）的DOM元素绑定指定的事件（f就是前面截取出来的事件名称部分），
                // 当绑定的事件触发时，会执行传入的回调函数，在回调函数内部，先调用getCheckData函数获取已选中数据，然后再通过call方法将当前上下文以及已选中数据传递给外部传入的callback函数进行相应处理
                $("[lay-filter='"+e+"']").on(f,function(){
                    var obj = getCheckData();

                    callback.call(this,obj);
                });
            }else{
                // 如果传入的callback参数不是一个函数类型，就在控制台输出错误信息，提示'传入的参数不是一个函数'，方便开发调试时发现参数使用错误的问题
                console.error('传入的参数不是一个函数');
            }
        }
    }
        // 使用layui框架提供的link方法加载名为'layfilter/layfilter.css'的CSS样式文件，
        // 从路径推测这个CSS文件应该是用于设置查询过滤组件的样式，使得组件在页面上呈现出预期的外观效果，
        // 这里的layui.cache.base可能是layui框架中用于获取基础路径的一种方式，拼接上后面的相对路径就能准确找到对应的CSS文件了
        layui.link(layui.cache.base + 'layfilter/layfilter.css');

        // 定义一个名为getCheckData的函数，从名字可以看出它主要用于获取并整理查询过滤组件中已选中项的数据，
        // 最终将这些数据整理成特定的格式并返回，供其他方法（如getValue、on方法等）使用
        function getCheckData(){
        // 创建一个空对象valueJson，从后续代码来看，它用于存储已选中项的value值（可能是选项对应的实际值等信息），
        // 以选项对应的名称（比如选项所属的分组名称等作为键）来存储对应的value值字符串（多个值会用逗号拼接等方式整理）
        var valueJson = {};
        // 创建一个空对象nameJson，与valueJson类似，不过它主要用于存储已选中项的name值（可能是选项显示的文本名称等信息），
        // 同样以选项对应的名称作为键，存储对应的name值字符串（多个值用逗号拼接等方式整理）
        var nameJson = {};
        // 使用for...in循环遍历chekedArr对象，chekedArr前面在代码中应该是用于记录各个选项组中已选中项相关信息的对象，
        // 循环遍历它的目的是提取出每个选项组中已选中项的name和value值，并整理到valueJson和nameJson对象中
        for(var name in chekedArr){
            // 获取chekedArr对象中以当前循环的name为键对应的数组（前面代码中应该是将每个选项组的已选中项数据存为数组形式），赋值给变量json，
            // 这个数组中的每个元素应该是包含了name和value等属性的对象，代表了一个已选中的选项信息
            var json = chekedArr[name];
            // 创建一个空字符串values，用于拼接当前选项组中已选中项的value值，后续会通过循环遍历json数组，将每个选中项的value值添加到这个字符串中
            var values = '';
            // 创建一个空字符串names，用于拼接当前选项组中已选中项的name值，同样会通过循环遍历json数组，把每个选中项的name值添加进来
            var names = '';
            // 开始循环遍历当前选项组对应的已选中项数组json，目的是逐个提取并拼接已选中项的name和value值到对应的字符串中
            for(var i=0;i<json.length;i++){
                // 判断如果当前索引i不是数组的最后一个元素（即不是最后一个选中项），则执行以下代码块，
                // 将当前选中项的value值添加到values字符串后面，并添加一个逗号作为分隔符，方便后续解析等操作，name值同理添加到names字符串后面并添加逗号分隔
                if(i!=json.length-1){
                    values+=json[i].value+",";
                    names +=json[i].name+",";
                }else{
                    // 如果当前索引i是数组的最后一个元素（即最后一个选中项），则直接将当前选中项的value值添加到values字符串后面，
                    // 不需要添加逗号分隔了，name值也同样直接添加到names字符串后面，完成整个选项组的已选中项name和value值的拼接
                    values+=json[i].value;
                    names +=json[i].name;
                }

            }
            // 将拼接好的当前选项组的已选中项value值字符串，以当前选项组的名称name作为键，存储到valueJson对象中，
            // 这样valueJson对象就记录了各个选项组的已选中项的value值信息了
            valueJson[name]=values;
            // 将拼接好的当前选项组的已选中项name值字符串，同样以当前选项组的名称name作为键，存储到nameJson对象中，
            // 使得nameJson对象记录了各个选项组的已选中项的name值信息
            nameJson[name]=names;
        }
        // 将整理好的包含已选中项的value值和name值信息的两个对象（valueJson和nameJson）封装成一个新的对象并返回，
        // 返回的对象结构方便外部方法根据需要获取和使用这些已选中的数据信息，例如可以方便地获取某个选项组的已选中项的名称和对应的值等内容
        return {values:valueJson,names:nameJson};
    }

// 使用layui框架提供的exports方法，将名为layfilter的对象（包含了前面定义的一系列查询过滤组件相关的方法和属性等内容）导出，
// 这样在其他使用layui框架的模块中就可以通过引入这个模块来使用layfilter对象提供的功能了
    exports('layfilter', layfilter);
})