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
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];
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|