UNPKG

lu2

Version:

Simple and flexible UI component library based on native HTML and JavaScript

543 lines (475 loc) 17.3 kB
/** * @Table.js * @author xunxuzhang * @version * Created: 15-06-28 * Edited: 17-07-18 非模块化使用支持 **/ (function (global, factory) { if (typeof define === 'function' && (define.amd || define.cmd)) { define(factory); } else { global.Table = factory(); } }(this, function (require) { if (typeof require == 'function') { require('common/ui/Checkbox'); var DropList = require('common/ui/DropList'); var Pagination = require('common/ui/Pagination'); // 黑色提示 require('common/ui/Tips'); // 加载动效 require('common/ui/Loading'); } else if (!$().dropList) { if (window.console) { window.console.error('need DropList.js'); } return {}; } else if (!$().pagination) { if (window.console) { window.console.error('need Pagination.js'); } return {}; } else if (!$().loading) { if (window.console) { window.console.error('need Loading.js'); } return {}; } DropList = DropList || window.DropList; Pagination = Pagination || window.Pagination; /** * 项目表格组件 * @使用示例 * new Table($('#table'), {}); */ var LOADING = 'loading'; var CHECKED = 'checked'; var DISABLED = 'disabled'; var SELECTED = 'selected'; var selector = '[type=checkbox]'; // 一些元素类名 var CL = { // 列表结构相关 container: 'table-x', size: 'table-size', empty: 'table-null-x', // 错误 error: 'table-error-x', // 加载 loading: 'ui-loading', // 下面是分页相关的: // 总数据量 total: 'table-num-length', // 每页系那是数目 every: 'table-num-every', // 分页左侧容器 data: 'table-page-data', // 分页列表(右侧)容器 page: 'table-page' }; // 表格 var Table = function(el, options) { if (!el || !el.length) { return this; } var defaultPageOptions = { // 总数据量 length: 0, // 每页显示数目 every: 15, // 当前的页数 current: 1, // 与后台交互时候UI分页需要的参数和后台分页参数对应关系 // 下面注释的是起点个人中心的接口对应关系 // 其中key表示后台分页数据在那个接口名称下,例如,下面注释内容表示的JSON数据结构是: // { // "code": 0, // "data": { // "pageInfo": { // "pageIndex": 1, // "pageSize": 20, // "pageMax": 6, // "totalCount": 113 // } // } // } // keyMap: { // key: 'pageInfo', // length: 'totalCount', // every: 'pageSize', // current: 'pageIndex' // } // 下面这里未注释的是默认设定 keyMap: { key: '', length: 'total', every: 'every', current: 'current' } }; // 默认参数 var defaults = { // 请求列表数据的Ajax参数,同jQuery $.ajax() 参数 // 默认dataType为'json' ajaxOptions: { }, // 分页数目下拉内容 pageList: [15, 30, 50], // 一般情况下,pageoptions参数都是使用默认值 pageOptions: defaultPageOptions, // 对请求的JSON数据进行处理,并返回 parse: function(data) { if (typeof data == 'string') { return data; } return ''; }, events: { // 事件,例如 // 'a.icon-del:click': function() { /* ... */ } }, // 列表内容显示的回调 onShow: $.noop, // 点击分页的回调 // 如果存在,会替换本组件内置的分页Ajax处理 onPage: null, // 点击复选框的回调 onCheck: $.noop }; var params = $.extend({}, defaults, options || {}); var self = this; // 表格列表需要的一些元素 var container, table, size, empty, total, every, data, page; var loading = $(); if (el.hasClass(CL.container)) { container = el; } else { container = el.parents('.' + CL.container); } table = container.find('table'); size = container.find('.' + CL.size); empty = container.find('.' + CL.empty); if (params.ajaxOptions.url) { // 这句话2作用,1是可能没有初始的loading HTML, 2是为之后的动画做准备 loading = container.find('.' + CL.loading); } total = container.find('.' + CL.total); every = container.find('.' + CL.every); data = container.find('.' + CL.data); page = container.find('.' + CL.page); // 元素之类 this.el = { container: container, size: size, table: table, empty: empty, loading: loading, total: total, every: every, data: data, page: page }; // 回调之类 this.callback = { parse: params.parse, page: params.onPage, show: params.onShow }; // 数据之类 this.num = $.extend({}, defaultPageOptions, params.pageOptions); // 一些事件 // 单复选框选中效果 table.delegate(selector, 'click', function() { // 如果不是第一td中的checkbox,忽略 if ($(this).parents('tr').find(':first-child ' + selector)[0] !== this) { return; } var tdCheckbox = table.find('td:first-child ' + selector); var isAllChecked = false; var isAllUnchecked = false; // 全选 if ($(this).parents('th').length) { isAllChecked = $(this).prop(CHECKED); isAllUnchecked = !isAllChecked; tdCheckbox.prop(CHECKED, isAllChecked); } else { var lengthChecked = tdCheckbox.filter(':checked').length; // 是否取消全选 isAllChecked = (tdCheckbox.length == lengthChecked); // 是否全部非选 isAllUnchecked = (lengthChecked == 0); } // 改变全选复选框的状态 table.find('th ' + selector).prop(CHECKED, isAllChecked); // IE7-IE8必须根据真实选中态初始化属性值 if ($.fn.propMatch) { table.find(selector).propMatch(); } // 根据复选框状态决定表格行样式 tdCheckbox.each(function() { $(this).parents('tr')[$(this).prop(CHECKED) ? 'addClass' : 'removeClass'](SELECTED); }); // 回调 params.onCheck.call(this, isAllChecked, isAllUnchecked, table); }); // 点击列表,只有不是a元素或者是复选框本事,选中当前复选框 table.on('click', function(event) { var target = event.target; var checkbox = null; if (target && /^a|input|label|th$/i.test(target.tagName) == false) { checkbox = $(target).parents('tr').find('td:first ' + selector); if (checkbox.length && checkbox.prop(DISABLED) == false) { checkbox.trigger('click'); } } }); // 其他些事件 $.each(params.events, function(key, value) { var arrSelectorType = key.split(':'); if (arrSelectorType.length == 2 && $.isFunction(value)) { table.delegate(arrSelectorType[0], arrSelectorType[1], value); } }); // 切换每页数目的dropList // 求得当前选中的分页数 // 优先本地存储 var storeId = table.attr('id'); var currentEvery = params.pageOptions.every; if (storeId && window.localStorage && localStorage[storeId] && every.length) { currentEvery = localStorage[storeId]; self.num.every = currentEvery; } // 赋值 every.html(currentEvery); this.pageList = new DropList(every.parent(), $.map(params.pageList, function(number) { return { value: '<span class="' + CL.every + '">' + number + '</span>' }; }), { width: 60, onSelect: function() { var everyNew = $(this).text(); if (window.localStorage && storeId) { localStorage[storeId] = everyNew; } // 如果分页有变 if (self.num.every != everyNew) { // 改变分页数目 self.num.every = everyNew; // 当前页重置为1 self.num.current = 1; // 重新刷新 self.ajax(); } } }); // ajax数据 this.ajaxOptions = params.ajaxOptions; // 获得数据 if (table.find('tbody').html() == '') { this.ajax(); } else { this.page(); } }; Table.prototype.ajax = function(options) { options = options || {}; var self = this; // 防止连续请求 if (self.ajaxing == true) return this; // 一些存储在实例对象上的数据 var el = this.el; var callback = this.callback; var num = this.num; // 列表容器元素 var tbody = el.table.find('tbody'); // ajax请求参数 var ajaxParams = $.extend({ dataType: 'json' }, this.ajaxOptions, options); // ajax地址是必需项 if (!ajaxParams.url) return this; // ajax的data数据和几个回调走的是合并策略 var data = options.data || {}; var dataOptions = {}; if ($.isFunction(this.ajaxOptions.data)) { dataOptions = this.ajaxOptions.data() || {}; } // 发送给后台的分页数据的键值是 var dataPage = {}; var keyMap = num.keyMap; dataPage[keyMap['current']] = num.current; dataPage[keyMap['every']] = num.every; // current和every ajaxParams.data = $.extend({}, dataPage, dataOptions, data); num.current = ajaxParams.data[keyMap['current']]; num.every = ajaxParams.data[keyMap['every']]; // 回调额合并 var success = ajaxParams.success; var error = ajaxParams.error; var complete = ajaxParams.complete; // ajax成功的处理 ajaxParams.success = function(json) { // 出错处理 // 0认为是成功 // 关键字支持code或者error // { code: 0 } 或 { error: 0 } 都认为成功 if ((json.hasOwnProperty('code') && json.code !== 0) || (json.hasOwnProperty('error') && json.error !== 0)) { ajaxParams.error(data.msg || '返回数据出现异常,请稍后重试'); return; } var html = callback.parse(json); // 如果解析后无数据,显示空提示信息 tbody.html(html || ''); if (!$.trim(html)) { el.empty.show(); } // 获得后端返回的分页总数 var jsonKey = keyMap.key; // 总数目 var total; if (jsonKey) { total = json.data[jsonKey][keyMap['length']]; } else { total = json.data[keyMap['length']]; } // 修改总数值并显示 if (total || total == 0) { el.total.html(total); self.num.length = total; } else { // 如果ajax返回的没有列表总数值,使用之前的值 // total = num.length; } // 左侧切换分页数目元素显示 el.data.show(); self.page(); // tips效果 el.table.find('.ui-tips').tips(); if ($.isFunction(success)) { success(json); } }; // ajax发生错误的处理 ajaxParams.error = function(xhr, status) { var elError = el.size.find('.' + CL.error); var msg = ''; if (typeof xhr == 'string') { msg = xhr; } if (elError.size()) { elError.show(); } else { elError = $('<div class="' + CL.error + '">网络异常,数据没有获取成功,您可以稍后重试!</div>'); el.size.append(elError); } if (msg) { elError.html(msg); } if ($.isFunction(error) && !msg) { error(xhr, status); } }; // ajax完成的处理 ajaxParams.complete = function() { // 去除中间的大loading el.loading.hide(); // 去掉分页的小loading if (this.pagination) { this.pagination.el.container.find('.' + LOADING).removeClass(LOADING); } if ($.isFunction(complete)) { complete(); } // 呈现与动画 self.show(); // 请求结束标志量 self.ajaxing = false; }; // 滚动到表格上边缘 var scrollTop = $(window).scrollTop(); if (el.table.offset().top < scrollTop) { scrollTop = el.container.offset().top; $('html, body').animate({ scrollTop: scrollTop }, 'fast', function() { if (this == document.body) { self.ajaxing = true; $.ajax(ajaxParams); } }); } else { self.ajaxing = true; $.ajax(ajaxParams); } // 显示loading el.loading.css('top', '0'); if (el.loading.css('display') == 'none') { el.loading.height(el.size.height() - el.table.find('thead').height()); } // 微调圈圈的位置 var distance = el.loading.height() - $(window).height(); if (distance > 0) { el.loading.css('top', -0.5 * distance); } // loading显示 el.loading.show(); // 其他元素隐藏 el.empty.hide(); el.size.find('.' + CL.error).hide(); el.table.find('tbody').empty(); this.scrollTop = scrollTop; return this; }; Table.prototype.page = function (total) { var self = this; var el = self.el; var num = self.num; // 显示分页 if (self.pagination) { self.pagination.num = num; self.pagination.show(); } else { self.pagination = new Pagination(el.page, { length: num.length, current: num.current, every: num.every, mode: num.mode || 'long', onClick: function(page, current) { // 更新分页 self.num.current = current; // 自定义分页事件 if ($.isFunction(self.callback.page)) { self.callback.page.call(this, page, current); } else { // 显示小loading $(this).addClass(LOADING); // ajax再次 self.ajax(); } } }); } }, Table.prototype.show = function() { this.el.size.unloading(this.el.size.data('animation')); // 显示loading this.el.loading.css('top', '0'); // Chrome, IE10+高度变小时候,会先置顶,在变化,导致晃动,影响体验,通过记录scrollTop修正,FireFox没有此问题 $(window).scrollTop(this.scrollTop); //没有全选 var chkAll = this.el.table.find('th input' + selector); if (chkAll.length) { chkAll.prop(CHECKED, false); if ($.fn.propMatch) { chkAll.propMatch(); } } // 列表显示的回调 if ($.isFunction(this.callback.show)) { this.callback.show.call(this); } return this; }; return Table; }));