You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

516 lines
17 KiB

/**
* @author cai
* @date 2020-09-18
*/
interface IPagination {
readonly _layout: string[]
options: IPaginationOptions
element: HTMLElement | null
showSelector: boolean
pageNum: number
selectedIndex: number
uniqid: string
init: (options: IPaginationOptions) => void
render: () => void
home: () => HTMLElement
prev: () => HTMLElement
pager: () => HTMLElement
next: () => HTMLElement
last: () => HTMLElement
sizes: ()=> HTMLElement | boolean
total: () => HTMLElement
getBetween: () => IBetween
generateArray: (start: number, end: number) => number[]
createElement: (tag: string, classList?: string | string[]) => HTMLElement
handleChangePage: (index: number) => void
validate: (options: IPaginationOptions) => boolean | undefined
setOptions: (options: IPaginationOptions) => void
}
interface IBetween {
min: number
max: number
}
type Type = 1 | 2
interface IPaginationOptions {
element: string | ''
type: Type
pageIndex: number
pageSize: number
pageSizes: number[]
pageCount: number
total: number
layout: string | ''
prevText: string | ''
nextText: string | ''
ellipsis: boolean
singlePageHide: boolean
disabled: boolean
currentChange: (index: number, pageSize?: number) => void
[key: string]: any
}
interface IElementId {
_id: string
}
class Pagination implements IPagination {
// 布局可选参数
readonly _layout = ['home', 'prev', 'pager', 'total', 'sizes', 'jumper', 'next', 'last'];
options: IPaginationOptions = {
// 容器
element: '',
// 样式类型
type: 1,
// 当前页
pageIndex: 1,
// 每页显示数量
pageSize: 0,
// 每页显示几条
pageSizes: [],
// 按钮数量
pageCount: 9,
// 总条数
total: 0,
// 页面布局
layout: '',
// 上一页文字
prevText: '',
// 下一页文字
nextText: '',
// 页码显示省略
ellipsis: false,
// 单页隐藏
singlePageHide: true,
// 是否禁用
disabled: false,
/**
* @description 页码变化事件回调
* @param index [number] 当前页码
* @param pageSize [number] 每页显示条数 TODO: 只有使用sizes才有返回值
*/
currentChange: (index: number, pageSize?: number) => {},
};
element = null;
pageNum = 0;
showSelector = false;
selectedIndex = 0;
uniqid = Math.random().toString(36).substr(2);
constructor(options: IPaginationOptions) {
if (this.validate(options)) {
this.init(options);
}
}
// 初始化
init(options: IPaginationOptions): void {
// 参数赋值
this.setOptions(options);
// 渲染
this.render();
}
// 渲染
render(this: IPagination): void {
// 切换每页显示条数重新替换每页条数参数
if (this.options.layout.indexOf('sizes') !== -1 && this.options.pageSizes instanceof Array) {
this.options.pageSizes.forEach( (v, k) => {
if (v == this.options.pageSize) {
this.selectedIndex = k;
}
})
if (!isNaN(this.options.pageSizes[this.selectedIndex])) {
this.options.pageSize = this.options.pageSizes[this.selectedIndex];
}
}
// 总页数
this.pageNum = Math.ceil(this.options.total / this.options.pageSize);
// 单页隐藏
if (this.pageNum === 1 && this.options.singlePageHide) {
// 清空元素
(this as any).element.innerHTML = '';
return;
}
// 最大页码
if (this.options.pageIndex > this.pageNum) this.options.pageIndex = this.pageNum;
// 最小页码
if (this.options.pageIndex <= 0) this.options.pageIndex = 1;
let element = null;
// 主体容器
let container = this.createElement('div', '_page_container');
// layout 渲染
this.options.layout.split(',').forEach((v: string) => {
if (this._layout.indexOf(v.trim()) !== -1) {
element = (this as any)[v.trim()]();
element && container.appendChild(element);
}
});
let old_container = document.querySelector(`${this.options.element} ._page_container`);
if (old_container) {
// 保存元素
(this as any).element.replaceChild(container, old_container);
} else {
// 保存元素
(this as any).element.appendChild(container);
}
}
// 首页
home(): HTMLElement {
// 禁用样式
let disabled = this.options.pageIndex <= 1 ? ['_disabled_c'] : [];
// 手势禁止
if (this.options.pageIndex <= 1 && this.options.disabled) disabled.push('_disabled');
let element = this.createElement('div', ['_home' ,`_home_${this.options.type}`, ...disabled]) as HTMLLIElement;
element.innerText = '首页';
element.addEventListener('click', () => {
if (this.options.pageIndex > 1) {
this.handleChangePage(1);
}
});
return element;
}
// 上一页
prev(): HTMLElement {
// 禁用样式
let disabled = this.options.pageIndex <= 1 ? ['_disabled_c'] : [];
// 手势禁止
if (this.options.pageIndex <= 1 && this.options.disabled) disabled.push('_disabled');
let element = this.createElement('div', ['_prev', `_prev_${this.options.type}` , ...disabled, ...(this.options.prevText ? ['_prev_text'] : ['_iconfont', 'iconzuo'])]) as HTMLLIElement;
element.innerText = this.options.prevText || '';
// 上一页事件
element.addEventListener('click', () => {
if (this.options.pageIndex > 1) {
this.handleChangePage(this.options.pageIndex - 1);
}
});
return element;
}
// 页码
pager(): HTMLElement {
let _this = this,
li,
active,
attr;
// 页码容器
let ul = this.createElement('ul', ['_pages', `_pages_${this.options.type}`]);
// 区间值
let between = this.getBetween();
// 生成区间值
let arrs: any[] = this.generateArray(between.min, between.max);
// 显示省略页码
if (this.options.ellipsis) {
// 判断是否不存在最小页码
if (arrs[0] > 1) {
arrs.splice(1, 0, '...');
arrs[0] = 1;
}
// 判断是否不存在最大页码
if (arrs[arrs.length - 1] < this.pageNum) {
arrs.splice(arrs.length - 1, 0, '...');
arrs[arrs.length - 1] = this.pageNum;
}
}
// 生成页码
arrs.forEach( (v: number, k: number) => {
active = v === this.options.pageIndex ? [`_active_${this.options.type}`] : [];
// 手势禁止
if (v === this.options.pageIndex && this.options.disabled) active.push('_disabled');
li = this.createElement('li', [`_pages_li_${this.options.type}`, ...active]) as HTMLLIElement & IElementId;
if (isNaN(v)) {
if (k <= 1) {
attr = 'prev';
li.classList.add('_pager_prev');
} else {
attr = 'next';
li.classList.add('_pager_next');
}
li._id = attr;
} else {
li.innerText = v.toString();
}
li.addEventListener('click', function(this: HTMLElement & IElementId){
// 省略号向上跳转
if (this._id === 'prev') {
let prevIndex = _this.options.pageIndex - _this.options.pageCount + 2;
_this.handleChangePage(prevIndex < 1 ? 1 : prevIndex);
return;
}
// 省略号向下跳转
if (this._id === 'next') {
let nextIndex = _this.options.pageIndex + _this.options.pageCount - 2;
_this.handleChangePage(nextIndex > _this.pageNum ? _this.pageNum : nextIndex);
return;
}
if (v != _this.options.pageIndex) {
_this.handleChangePage(v);
}
});
ul.appendChild(li);
});
return ul;
}
// 下一页
next(): HTMLElement {
// 禁用下一页
let disabled = this.options.pageIndex >= this.pageNum ? ['_disabled_c'] : [];
// 手势禁止
if (this.options.pageIndex >= this.pageNum && this.options.disabled) disabled.push('_disabled');
// 下一页
let element = this.createElement('div', ['_next', `_next_${this.options.type}`, ...disabled, ...(this.options.nextText ? ['_next_text'] : ['_iconfont', 'iconGroup-'])]) as HTMLLIElement;
// 下一页文字
element.innerText = this.options.nextText || '';
// 下一页事件
element.addEventListener('click', () => {
if (this.options.pageIndex < this.pageNum) {
this.handleChangePage(this.options.pageIndex + 1);
}
});
return element;
}
// 尾页
last(): HTMLElement {
// 禁用下一页
let disabled = this.options.pageIndex >= this.pageNum ? ['_disabled_c'] : [];
// // 手势禁止
if (this.options.pageIndex >= this.pageNum && this.options.disabled) disabled.push('_disabled');
let element = this.createElement('div', ['_last', `_last_${this.options.type}` ,...disabled]) as HTMLLIElement;
element.innerText = '尾页';
element.addEventListener('click', () => {
if (this.options.pageIndex < this.pageNum) {
this.handleChangePage(this.pageNum);
}
});
return element;
}
// 输入框跳转
jumper(): HTMLElement {
let _this = this;
// 容器
let jumper = this.createElement('div', '_jumper');
// 总页码
let total = this.createElement('div', '_count');
total.innerText = `${this.pageNum}`;
jumper.appendChild(total);
let text_1 = this.createElement('span');
text_1.innerText = '前往';
jumper.appendChild(text_1);
let value = 0;
// 输入框
let input = this.createElement('input', ['_jumper_input', this.uniqid]) as HTMLInputElement;
input.type = 'number';
input.value = this.options.pageIndex.toString();
input.setAttribute('min', '1');
input.setAttribute('max', this.pageNum.toString());
let handle = ['blur', 'keydown'];
handle.forEach( (v: string) => {
input.addEventListener(v, function(this: HTMLInputElement, e:any) {
if (e.type === 'keydown' && e.keyCode !== 13) {
return;
}
value = ~~this.value;
if (value < 1) value = 1;
if (value > _this.pageNum) value = _this.pageNum;
// @ts-ignore
this.value = value;
if (value !== _this.options.pageIndex) _this.handleChangePage(value);
// 获取焦点
if (e.keyCode == 13) {
setTimeout(() => {
(document.querySelector(`.${_this.uniqid}`) as HTMLInputElement).focus()
})
}
});
});
jumper.appendChild(input);
let text_2 = this.createElement('span');
text_2.innerText = '页';
jumper.appendChild(text_2);
return jumper;
}
// 选择页码
sizes(): HTMLElement | boolean {
let _this = this;
let success = false;
let mode = '';
let lis = null;
let active = [];
let element = this.createElement('div', '_sizes');
// 选择框容器
let box = this.createElement('div', '_sizes_select_container');
// 每页条数选择框
let ul = this.createElement('ul', '_sizes_select');
if (this.options.pageSizes instanceof Array) {
// 渲染选择框
this.options.pageSizes.forEach( (v: number, key: number) => {
if (!isNaN(v)) {
success = true;
active = this.selectedIndex === key ? ['_sizes_select_active'] : [];
lis = this.createElement('li', ['_sizes_select_li', ...active]);
lis.innerText = `${v}条/页`;
lis.addEventListener('click', function() {
if (_this.selectedIndex !== key) {
_this.selectedIndex = key;
_this.handleChangePage(1);
} else {
let mode = _this.showSelector ? 'remove' : 'add';
(i as any).classList[mode]('_sizes_icon_rotate');
(box as any).classList[mode]('_sizes_select_container_show');
_this.showSelector = !_this.showSelector;
}
});
ul.appendChild(lis);
}
})
} else {
return false;
}
if (!success) return false;
box.appendChild(ul);
element.appendChild(box);
let text = this.createElement('div', '_sizes_text')
text.innerText = `${this.options.pageSizes[this.selectedIndex]}条/页`;
// 显示选择框、旋转icon
text.addEventListener('click', function() {
this.classList.add('_sizes_active');
mode = _this.showSelector ? 'remove' : 'add';
_this.showSelector = !_this.showSelector;
(i as any).classList[mode]('_sizes_icon_rotate');
(box as any).classList[mode]('_sizes_select_container_show');
});
let i = this.createElement('i', ['icon_down', '_iconfont', 'iconGroup-1']);
text.appendChild(i);
element.appendChild(text);
return element;
}
// 总页数
total(): HTMLElement {
let element = this.createElement('div', '_count');
element.innerText = `${this.options.total}`;
return element;
}
// 页码变化
handleChangePage(index: number): void {
this.options.pageIndex = index;
this.showSelector = false;
// 回调
if (typeof this.options.currentChange === 'function') {
if (this.options.pageSizes[this.selectedIndex])
this.options.pageSize = this.options.pageSizes[this.selectedIndex];
this.options.currentChange(index, this.options.pageSize);
}
// 重新渲染
this.render();
}
// 计算区间值
getBetween(): IBetween {
// 最小下标
let min = this.options.pageIndex - Math.floor(this.options.pageCount / 2);
// 最小下标最大值
if (min > this.pageNum - this.options.pageCount) {
min = this.pageNum - this.options.pageCount + 1;
}
// 最小值
if (min <= 1) min = 1;
// 最大下标
let max = this.options.pageIndex + Math.floor(this.options.pageCount / 2);
// 最大下标最小值
if (max < this.options.pageCount) max = this.options.pageCount;
// 最大值
if (max > this.pageNum) max = this.pageNum;
return {min, max};
}
// 生成区间数组
generateArray(start: number, end: number) {
let arr = [];
for(let i = start; i <= end; i++) {
arr.push(i);
}
return arr;
}
// 创建元素
createElement(tag: string, classList?: string | string[]): HTMLElement {
let dom = document.createElement(tag);
if (classList) {
if (classList instanceof Array) {
classList.forEach( v => {
dom.classList.add(v);
});
} else {
dom.classList.add(classList);
}
}
return dom;
}
// 参数验证
validate(options: IPaginationOptions): boolean | undefined {
if (!options) throw new Error('options of null');
if (typeof options !== 'object') throw new Error('options not an object');
if (!document.querySelector(options.element)) throw new Error('element of null');
if (!options.layout) throw new Error('layout of null');
['type', 'pageIndex', 'pageSize', 'pageCount', 'total'].forEach( v => {
if (options[v]) {
if (isNaN(options[v])) throw new Error(`${v} not an number`);
if (v === 'pageCount' && options[v] % 2 === 0) throw new Error(`${v} not an odd number`);
if (v === 'pageCount' && options[v] < 4) throw new Error(`${v} must be greater than four`);
}
});
return true;
}
// 参数赋值
setOptions(options: IPaginationOptions): void {
// 容器
this.element = document.querySelector(options.element) as never;
for (let name in options) {
if (options[name] !== void 0) {
this.options[name] = isNaN(options[name]) ? options[name] : +options[name];
}
}
}
}