diff --git a/packages/element3/src/components/Pagination/index.js b/packages/element3/src/components/Pagination/index.js
new file mode 100644
index 000000000..10b050909
--- /dev/null
+++ b/packages/element3/src/components/Pagination/index.js
@@ -0,0 +1,7 @@
+import ElPagination from './src/Pagination.vue'
+
+ElPagination.install = function (app) {
+ app.component(ElPagination.name, ElPagination)
+}
+
+export { ElPagination }
diff --git a/packages/element3/src/components/Pagination/src/Layout.vue b/packages/element3/src/components/Pagination/src/Layout.vue
new file mode 100644
index 000000000..a814ab1af
--- /dev/null
+++ b/packages/element3/src/components/Pagination/src/Layout.vue
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/element3/src/components/Pagination/src/Pagination.vue b/packages/element3/src/components/Pagination/src/Pagination.vue
new file mode 100644
index 000000000..2042c6bd8
--- /dev/null
+++ b/packages/element3/src/components/Pagination/src/Pagination.vue
@@ -0,0 +1,205 @@
+
+
+
+
+
diff --git a/packages/element3/src/components/Pagination/src/entity/Pager.ts b/packages/element3/src/components/Pagination/src/entity/Pager.ts
new file mode 100644
index 000000000..3f46f0517
--- /dev/null
+++ b/packages/element3/src/components/Pagination/src/entity/Pager.ts
@@ -0,0 +1,176 @@
+import mitt from 'mitt'
+import { ElPaginationProps, Keep } from '../../types'
+import { makerArray } from '../tools/makerArray'
+
+export enum PagerEventType {
+ CHANGE = 'change',
+ PREV = 'prev',
+ NEXT = 'next',
+ SIZE_CHANGE = 'size_change'
+}
+
+export type PagerEventCb = (pageNumber: number) => void
+
+export interface PagerParam {
+ size: number
+ total: number
+ current: number
+ viewCount: number
+}
+
+export type PagerStyle = Keep<
+ 'prevText' | 'nextText' | 'popperClass' | 'disabled'
+>
+
+export class Pager {
+ private _current: number
+ private _total: number
+ private _size: number
+ private _viewCount: number
+ private _event = mitt()
+ private _sizes = []
+ private _style: PagerStyle = {
+ popperClass: '',
+ prevText: '',
+ nextText: '',
+ disabled: false
+ }
+
+ get style(): PagerStyle {
+ return this._style
+ }
+
+ set style(v: PagerStyle) {
+ this._style = v
+ }
+
+ get sizes(): number[] {
+ return this._sizes
+ }
+
+ set sizes(v: number[]) {
+ this._sizes = v
+ }
+
+ get size(): number {
+ return this._size
+ }
+
+ set size(v: number) {
+ this.changeSize(v)
+ }
+
+ get total(): number {
+ return this._total
+ }
+
+ set total(v: number) {
+ this._total = v
+ }
+
+ get pagerCountNotSelf(): number {
+ return this._viewCount - 1
+ }
+
+ get halfPager(): number {
+ return Math.floor(this.pagerCountNotSelf / 2)
+ }
+
+ get leftCount(): number {
+ return Math.max(this._current - 1, 0)
+ }
+
+ get rightCount(): number {
+ return Math.max(this.count - this._current, 0)
+ }
+
+ get current(): number {
+ return this._current
+ }
+
+ set current(v: number) {
+ this.jump(v)
+ }
+
+ get count(): number {
+ return Math.ceil(this._total / this._size)
+ }
+
+ get viewCount(): number {
+ return this._viewCount
+ }
+
+ set viewCount(v: number) {
+ this._viewCount = v
+ }
+
+ get pages(): number[] {
+ return makerArray(1, this.count)
+ }
+
+ get viewPages(): number[] {
+ let leftViewCount = Math.min(this.leftCount, this.halfPager)
+ let rightViewCount = Math.min(this.rightCount, this.halfPager)
+
+ if (leftViewCount > rightViewCount) {
+ leftViewCount =
+ this.pagerCountNotSelf - Math.min(rightViewCount, this.halfPager)
+ } else {
+ rightViewCount =
+ this.pagerCountNotSelf - Math.min(leftViewCount, this.halfPager)
+ }
+
+ return this.catOut(
+ this._current - leftViewCount,
+ this._current + rightViewCount
+ )
+ }
+
+ get midViewPages(): number[] {
+ const result = this.viewPages
+ result.shift()
+ result.pop()
+ return result
+ }
+
+ constructor(pagerParam: PagerParam) {
+ this._size = pagerParam.size
+ this._total = pagerParam.total
+ this._current = pagerParam.current
+ this._viewCount = pagerParam.viewCount
+ }
+
+ catOut(start: number, end: number): number[] {
+ return this.pages.slice(Math.max(start - 1, 0), Math.min(end, this.count))
+ }
+
+ jump(v: number): void {
+ if (this._style.disabled) {
+ return
+ }
+ v = Math.min(this.count, v)
+ v = Math.max(1, v)
+ this._current = v
+ this._event.emit(PagerEventType.CHANGE, v)
+ }
+
+ prev(step = 1): void {
+ this.jump(this._current - step)
+ this._event.emit(PagerEventType.PREV, this._current)
+ }
+
+ next(step = 1): void {
+ this.jump(this._current + step)
+ this._event.emit(PagerEventType.NEXT, this._current)
+ }
+
+ on(type: PagerEventType, cb: PagerEventCb): void {
+ this._event.on(type, cb)
+ }
+
+ changeSize(v: number): void {
+ this._size = v
+ this._event.emit(PagerEventType.SIZE_CHANGE, v)
+ this.jump(this._current)
+ }
+}
diff --git a/packages/element3/src/components/Pagination/src/parts/Jumper.vue b/packages/element3/src/components/Pagination/src/parts/Jumper.vue
new file mode 100644
index 000000000..aa615829c
--- /dev/null
+++ b/packages/element3/src/components/Pagination/src/parts/Jumper.vue
@@ -0,0 +1,33 @@
+
+
+
+
diff --git a/packages/element3/src/components/Pagination/src/parts/Next.vue b/packages/element3/src/components/Pagination/src/parts/Next.vue
new file mode 100644
index 000000000..a3c40b4a6
--- /dev/null
+++ b/packages/element3/src/components/Pagination/src/parts/Next.vue
@@ -0,0 +1,22 @@
+
+
+
+
+
diff --git a/packages/element3/src/components/Pagination/src/parts/Pager.vue b/packages/element3/src/components/Pagination/src/parts/Pager.vue
new file mode 100644
index 000000000..d51f20582
--- /dev/null
+++ b/packages/element3/src/components/Pagination/src/parts/Pager.vue
@@ -0,0 +1,66 @@
+
+
+
+
+
diff --git a/packages/element3/src/components/Pagination/src/parts/Prev.vue b/packages/element3/src/components/Pagination/src/parts/Prev.vue
new file mode 100644
index 000000000..0b1b4a304
--- /dev/null
+++ b/packages/element3/src/components/Pagination/src/parts/Prev.vue
@@ -0,0 +1,22 @@
+
+
+
+
+
diff --git a/packages/element3/src/components/Pagination/src/parts/Sizes.vue b/packages/element3/src/components/Pagination/src/parts/Sizes.vue
new file mode 100644
index 000000000..912c5ea06
--- /dev/null
+++ b/packages/element3/src/components/Pagination/src/parts/Sizes.vue
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
diff --git a/packages/element3/src/components/Pagination/src/parts/Slot.vue b/packages/element3/src/components/Pagination/src/parts/Slot.vue
new file mode 100644
index 000000000..85aec3ecc
--- /dev/null
+++ b/packages/element3/src/components/Pagination/src/parts/Slot.vue
@@ -0,0 +1,15 @@
+
diff --git a/packages/element3/src/components/Pagination/src/parts/Total.vue b/packages/element3/src/components/Pagination/src/parts/Total.vue
new file mode 100644
index 000000000..e5034cccd
--- /dev/null
+++ b/packages/element3/src/components/Pagination/src/parts/Total.vue
@@ -0,0 +1,18 @@
+
+
+
+
+
diff --git a/packages/element3/src/components/Pagination/src/tools/element.ts b/packages/element3/src/components/Pagination/src/tools/element.ts
new file mode 100644
index 000000000..c1b2c08d4
--- /dev/null
+++ b/packages/element3/src/components/Pagination/src/tools/element.ts
@@ -0,0 +1,12 @@
+type SwitchFun = (el: Element | EventTarget) => void
+export function switchClass([a, b]: [string, string]): SwitchFun {
+ return (el: Element) => {
+ if (!el.className.includes(a)) {
+ el.classList.add(a)
+ el.classList.remove(b)
+ } else {
+ el.classList.remove(a)
+ el.classList.add(b)
+ }
+ }
+}
diff --git a/packages/element3/src/components/Pagination/src/tools/makerArray.ts b/packages/element3/src/components/Pagination/src/tools/makerArray.ts
new file mode 100644
index 000000000..68ff86429
--- /dev/null
+++ b/packages/element3/src/components/Pagination/src/tools/makerArray.ts
@@ -0,0 +1,5 @@
+export function makerArray(start: number, end: number): number[] {
+ const arr = []
+ for (let i = start; i <= end; i++) arr.push(i)
+ return arr
+}
diff --git a/packages/element3/src/components/Pagination/src/tools/parseLayout.ts b/packages/element3/src/components/Pagination/src/tools/parseLayout.ts
new file mode 100644
index 000000000..dc1b4778c
--- /dev/null
+++ b/packages/element3/src/components/Pagination/src/tools/parseLayout.ts
@@ -0,0 +1,18 @@
+export function parseLayout(layout: string): string[] {
+ const recursion = (arr: string[], index: number): string[] => {
+ const result = []
+ for (let i = index; i < arr.length; i++) {
+ const item = arr[i]
+ if (item === '->') {
+ result.push(recursion(arr, i + 1))
+ break
+ }
+ result.push(item)
+ }
+ return result
+ }
+ return recursion(
+ layout.split(',').map((item) => item.trim()),
+ 0
+ )
+}
diff --git a/packages/element3/src/components/Pagination/tests/Pagination.spec.ts b/packages/element3/src/components/Pagination/tests/Pagination.spec.ts
new file mode 100644
index 000000000..2c8f9d105
--- /dev/null
+++ b/packages/element3/src/components/Pagination/tests/Pagination.spec.ts
@@ -0,0 +1,325 @@
+import { mount } from '@vue/test-utils'
+import Pagination from '../src/Pagination.vue'
+import Prev from '../src/parts/Prev.vue'
+import Next from '../src/parts/Next.vue'
+import Pager from '../src/parts/Pager.vue'
+import Total from '../src/parts/Total.vue'
+import { h, nextTick, ref } from 'vue'
+
+describe('Pagination.vue', () => {
+ it('Realize layout prev,pager,next', () => {
+ const wrapper = mount(Pagination, {
+ props: {
+ layout: 'prev, pager, next, total',
+ currentPage: 2,
+ pageCount: 5
+ }
+ })
+
+ expect(wrapper.findComponent(Prev).exists()).toBeTruthy()
+ expect(wrapper.findComponent(Next).exists()).toBeTruthy()
+ expect(wrapper.findComponent(Pager).exists()).toBeTruthy()
+ expect(wrapper.findComponent(Total).exists()).toBeTruthy()
+ })
+ it('Realize simple paging display', () => {
+ const wrapper = mount(Pagination, {
+ props: {
+ layout: 'prev, pager, next',
+ currentPage: 2,
+ pageCount: 5
+ }
+ })
+
+ const pager = wrapper.vm.pager
+
+ expect(wrapper.findComponent(Pager).vm.pager).toBe(pager)
+ expect(pager.current).toBe(2)
+ expect(pager.count).toBe(5)
+ })
+ it('Calculate page numbers by Total and pageSize', () => {
+ const wrapper = mount(Pagination, {
+ props: {
+ layout: 'prev, pager, next, total',
+ currentPage: 2,
+ pageSize: 10,
+ total: 100
+ }
+ })
+
+ const pager = wrapper.vm.pager
+
+ expect(wrapper.findComponent(Total).vm.pager).toBe(pager)
+ expect(pager.current).toBe(2)
+ expect(pager.count).toBe(10)
+ })
+
+ it('Hide when there is only one page', () => {
+ const wrapper = mount(Pagination, {
+ props: {
+ layout: 'pager',
+ currentPage: 1,
+ pageSize: 1,
+ total: 1,
+ hideOnSinglePage: true
+ }
+ })
+
+ expect(wrapper.findComponent(Pager).exists()).toBeFalsy()
+ })
+
+ it('click prev/next button currentPage', async () => {
+ const currentPage = ref(2)
+ const wrapper = mount(Pagination, {
+ props: {
+ layout: 'prev, next',
+ currentPage: (currentPage as unknown) as number,
+ pageCount: 10,
+ 'onUpdate:currentPage': (v) => (currentPage.value = v)
+ }
+ })
+
+ await wrapper.findComponent(Prev).trigger('click')
+ expect(currentPage.value).toBe(1)
+ await wrapper.findComponent(Next).trigger('click')
+ expect(currentPage.value).toBe(2)
+ })
+
+ it('vModel currentPage', async () => {
+ const currentPage = ref(2)
+ const wrapper = mount(Pagination, {
+ props: {
+ layout: 'pager',
+ currentPage: (currentPage as unknown) as number,
+ pageCount: 10,
+ 'onUpdate:currentPage': (v) => (currentPage.value = v)
+ }
+ })
+
+ currentPage.value = 3
+ await nextTick()
+ expect(wrapper.find('.active').text()).toBe('3')
+ })
+
+ it('change size by ref', async () => {
+ const currentPage = ref(2)
+ const total = ref(100)
+ const size = ref(10)
+ const wrapper = mount(Pagination, {
+ props: {
+ layout: 'pager',
+ currentPage: (currentPage as unknown) as number,
+ total: (total as unknown) as number,
+ pageSize: (size as unknown) as number,
+ 'onUpdate:currentPage': (v) => (currentPage.value = v)
+ }
+ })
+
+ expect(wrapper.vm.pager.count).toBe(10)
+ size.value = 20
+ await nextTick()
+ expect(wrapper.vm.pager.count).toBe(5)
+ })
+
+ it('click pager button currentPage', async () => {
+ const currentPage = ref(2)
+ const wrapper = mount(Pagination, {
+ props: {
+ layout: 'pager',
+ currentPage: (currentPage as unknown) as number,
+ pageCount: 100,
+ 'onUpdate:currentPage': (v) => (currentPage.value = v)
+ }
+ })
+ const pages = wrapper.findAll('.number')
+
+ await pages[0].trigger('click')
+ expect(currentPage.value).toBe(1)
+
+ await wrapper.find('.btn-quicknext').trigger('click')
+ expect(currentPage.value).toBe(4)
+ })
+
+ it('click test sizes change pageSize', () => {
+ const currentPage = ref(2)
+ const total = ref(100)
+ const size = ref(10)
+
+ const wrapper = mount(Pagination, {
+ props: {
+ layout: 'sizes',
+ currentPage: (currentPage as unknown) as number,
+ total: (total as unknown) as number,
+ pageSize: (size as unknown) as number,
+ pageSizes: [10, 20, 30],
+ 'onUpdate:currentPage': (v) => (currentPage.value = v),
+ 'onUpdate:pageSize': (v) => (size.value = v)
+ }
+ })
+
+ wrapper.vm.pager.size = 20
+
+ expect(wrapper.vm.pager.count).toBe(5)
+ })
+
+ it('sizes part popperClass', () => {
+ const currentPage = ref(2)
+ const total = ref(100)
+ const size = ref(10)
+
+ const wrapper = mount(Pagination, {
+ props: {
+ layout: 'sizes',
+ currentPage: (currentPage as unknown) as number,
+ total: (total as unknown) as number,
+ pageSize: (size as unknown) as number,
+ pageSizes: [10, 20, 30],
+ popperClass: 'test',
+ 'onUpdate:currentPage': (v) => (currentPage.value = v),
+ 'onUpdate:pageSize': (v) => (size.value = v)
+ }
+ })
+
+ expect(wrapper.find('.test').exists()).toBeTruthy()
+ })
+
+ it('Achieve right-aligned layout', () => {
+ const wrapper = mount(Pagination, {
+ props: {
+ layout: 'prev, pager, next, ->, total',
+ currentPage: 2,
+ pageCount: 5
+ }
+ })
+
+ expect(wrapper.find('.btn-prev').exists()).toBeTruthy()
+ expect(wrapper.findAll('.el-pager .number')).toHaveLength(5)
+ expect(wrapper.find('.el-pager .number.active').text()).toBe('2')
+ expect(wrapper.find('.btn-next').exists()).toBeTruthy()
+ expect(wrapper.find('.el-pagination__rightwrapper').exists()).toBeTruthy()
+ expect(wrapper.find('.el-pagination__total').exists()).toBeTruthy()
+ })
+
+ it('When layout is set to slot, customize slots based on #default', () => {
+ const wrapper = mount(Pagination, {
+ props: {
+ layout: 'slot',
+ currentPage: 2,
+ pageCount: 5
+ },
+ slots: {
+ default(pager) {
+ return h(
+ 'div',
+ {
+ class: 'slot'
+ },
+ 'Current: ' + pager.current
+ )
+ }
+ }
+ })
+
+ expect(wrapper.find('.slot').exists()).toBeTruthy()
+ expect(wrapper.find('.slot').text()).toBe('Current: 2')
+ })
+
+ it('Customize Prev and Next content', () => {
+ const wrapper = mount(Pagination, {
+ props: {
+ layout: 'prev, next',
+ currentPage: 2,
+ pageCount: 5,
+ prevText: 'PrevPage',
+ nextText: 'NextPage'
+ }
+ })
+
+ expect(wrapper.findComponent(Prev).text()).toBe('PrevPage')
+ expect(wrapper.findComponent(Next).text()).toBe('NextPage')
+ })
+
+ it('jump page number', async () => {
+ const currentPage = ref(1)
+ const wrapper = mount(Pagination, {
+ props: {
+ layout: 'jumper',
+ currentPage: (currentPage as unknown) as number,
+ 'onUpdate:currentPage': (v) => (currentPage.value = v),
+ pageCount: 5
+ }
+ })
+
+ const jumperInput = wrapper.find('input')
+ await jumperInput.setValue('5')
+
+ expect(currentPage.value).toBe(5)
+ })
+
+ it('test disable effect', async () => {
+ const disabled = ref(false)
+ const currentPage = ref(1)
+ const wrapper = mount(Pagination, {
+ props: {
+ currentPage: (currentPage as unknown) as number,
+ pageCount: 5,
+ disabled: (disabled as unknown) as boolean,
+ 'onUpdate:currentPage': (v) => (currentPage.value = v)
+ }
+ })
+
+ const [leftBtn, rightBtn] = wrapper.findAll('button')
+
+ expect(leftBtn.element.disabled).toBeTruthy()
+
+ currentPage.value = 5
+ await nextTick()
+ expect(rightBtn.element.disabled).toBeTruthy()
+
+ disabled.value = true
+ await nextTick()
+ expect(wrapper.vm.pager.style.disabled).toBeTruthy()
+ })
+
+ it('small pager', () => {
+ const currentPage = ref(1)
+ const wrapper = mount(Pagination, {
+ props: {
+ layout: 'pager',
+ currentPage: (currentPage as unknown) as number,
+ 'onUpdate:currentPage': (v) => (currentPage.value = v),
+ pageCount: 5,
+ small: true
+ }
+ })
+ expect(wrapper.find('.el-pagination--small').exists()).toBeTruthy()
+ })
+
+ it('Pager with background', () => {
+ const currentPage = ref(1)
+ const wrapper = mount(Pagination, {
+ props: {
+ layout: 'pager',
+ currentPage: (currentPage as unknown) as number,
+ 'onUpdate:currentPage': (v) => (currentPage.value = v),
+ pageCount: 5,
+ background: true
+ }
+ })
+ expect(wrapper.find('.is-background').exists()).toBeTruthy()
+ })
+
+ it('When the page number changes, so does the Jumper', async () => {
+ const currentPage = ref(1)
+ const wrapper = mount(Pagination, {
+ props: {
+ layout: 'jumper, next',
+ currentPage: (currentPage as unknown) as number,
+ 'onUpdate:currentPage': (v) => (currentPage.value = v),
+ pageCount: 5,
+ background: true
+ }
+ })
+ await wrapper.find('button').trigger('click')
+ expect(wrapper.find('input').element.value).toBe('2')
+ })
+})
diff --git a/packages/element3/src/components/Pagination/tests/entity/Pager.spec.ts b/packages/element3/src/components/Pagination/tests/entity/Pager.spec.ts
new file mode 100644
index 000000000..29828c62c
--- /dev/null
+++ b/packages/element3/src/components/Pagination/tests/entity/Pager.spec.ts
@@ -0,0 +1,246 @@
+import { Pager, PagerEventType } from '../../src/entity/Pager'
+
+describe('Pager.ts', () => {
+ it('instance Pager', () => {
+ const pageCount = 6
+ const currentPage = 2
+ const pagerCount = 7
+ const pager = new Pager({
+ total: pageCount,
+ size: 1,
+ current: currentPage,
+ viewCount: pagerCount
+ })
+
+ expect(pager.pages).toHaveLength(6)
+ })
+
+ it('view pages', () => {
+ const pageCount = 10
+ const currentPage = 1
+ const pagerCount = 7
+ const pager = new Pager({
+ total: pageCount,
+ size: 1,
+ current: currentPage,
+ viewCount: pagerCount
+ })
+
+ expect(pager.midViewPages).toEqual([2, 3, 4, 5, 6])
+
+ pager.current = 2
+ expect(pager.midViewPages).toEqual([2, 3, 4, 5, 6])
+
+ pager.current = 3
+ expect(pager.midViewPages).toEqual([2, 3, 4, 5, 6])
+
+ pager.current = 5
+ expect(pager.midViewPages).toEqual([3, 4, 5, 6, 7])
+
+ pager.current = 8
+ expect(pager.midViewPages).toEqual([5, 6, 7, 8, 9])
+
+ pager.current = 9
+ expect(pager.midViewPages).toEqual([5, 6, 7, 8, 9])
+
+ pager.current = 10
+ expect(pager.midViewPages).toEqual([5, 6, 7, 8, 9])
+
+ expect(pager.leftCount).toBe(9)
+ expect(pager.rightCount).toBe(0)
+ })
+
+ it('test catOut method', () => {
+ const pageCount = 10
+ const currentPage = 2
+ const pagerCount = 7
+ const pager = new Pager({
+ total: pageCount,
+ size: 1,
+ current: currentPage,
+ viewCount: pagerCount
+ })
+
+ expect(pager.catOut(1, 2)).toEqual([1, 2])
+ expect(pager.catOut(4, 7)).toEqual([4, 5, 6, 7])
+ expect(pager.catOut(0, 11)).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
+ })
+
+ it('test pageCount = pagerCount', () => {
+ const pageCount = 10
+ const currentPage = 2
+ const pagerCount = 10
+ const pager = new Pager({
+ total: pageCount,
+ size: 1,
+ current: currentPage,
+ viewCount: pagerCount
+ })
+
+ expect(pager.viewPages).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
+ expect(pager.leftCount).toBe(1)
+ expect(pager.rightCount).toBe(8)
+ })
+
+ it('test viewPages', () => {
+ const pageCount = 10
+ const currentPage = 1
+ const pagerCount = 5
+ const pager = new Pager({
+ total: pageCount,
+ size: 1,
+ current: currentPage,
+ viewCount: pagerCount
+ })
+
+ expect(pager.viewPages).toEqual([1, 2, 3, 4, 5])
+
+ pager.current = 2
+ expect(pager.viewPages).toEqual([1, 2, 3, 4, 5])
+
+ pager.current = 3
+ expect(pager.viewPages).toEqual([1, 2, 3, 4, 5])
+
+ pager.current = 4
+ expect(pager.viewPages).toEqual([2, 3, 4, 5, 6])
+
+ pager.current = 5
+ expect(pager.viewPages).toEqual([3, 4, 5, 6, 7])
+
+ pager.current = 6
+ expect(pager.viewPages).toEqual([4, 5, 6, 7, 8])
+
+ pager.current = 7
+ expect(pager.viewPages).toEqual([5, 6, 7, 8, 9])
+
+ pager.current = 8
+ expect(pager.viewPages).toEqual([6, 7, 8, 9, 10])
+
+ pager.current = 9
+ expect(pager.viewPages).toEqual([6, 7, 8, 9, 10])
+
+ pager.current = 10
+ expect(pager.viewPages).toEqual([6, 7, 8, 9, 10])
+
+ pager.current = 0
+ expect(pager.viewPages).toEqual([1, 2, 3, 4, 5])
+
+ pager.current = 11
+ expect(pager.viewPages).toEqual([6, 7, 8, 9, 10])
+ })
+ it('test total', () => {
+ const total = 100
+ const currentPage = 2
+ const pagerCount = 7
+ const pageSize = 10
+ const pager = new Pager({
+ total: total,
+ size: pageSize,
+ current: currentPage,
+ viewCount: pagerCount
+ })
+
+ expect(pager.count).toBe(10)
+ })
+
+ it('jump page', () => {
+ const pageCount = 6
+ const currentPage = 2
+ const pagerCount = 7
+ const pager = new Pager({
+ total: pageCount,
+ size: 1,
+ current: currentPage,
+ viewCount: pagerCount
+ })
+
+ pager.jump(5)
+ expect(pager.current).toBe(5)
+ })
+
+ it('jump page when currentPage Crossing the line', () => {
+ const pageCount = 6
+ const currentPage = 2
+ const pagerCount = 7
+ const pager = new Pager({
+ total: pageCount,
+ size: 1,
+ current: currentPage,
+ viewCount: pagerCount
+ })
+
+ pager.jump(7)
+ expect(pager.current).toBe(6)
+ pager.jump(0)
+ expect(pager.current).toBe(1)
+ })
+
+ it('change prev/next page', () => {
+ const pageCount = 6
+ const currentPage = 2
+ const pagerCount = 7
+ const pager = new Pager({
+ total: pageCount,
+ size: 1,
+ current: currentPage,
+ viewCount: pagerCount
+ })
+
+ pager.prev()
+ expect(pager.current).toBe(1)
+ pager.next()
+ expect(pager.current).toBe(2)
+ })
+
+ it('when current change, trigger event', () => {
+ const changeHandler = jest.fn()
+ const prevHandler = jest.fn()
+ const nextHandler = jest.fn()
+
+ const pageCount = 6
+ const currentPage = 2
+ const pagerCount = 7
+ const pager = new Pager({
+ total: pageCount,
+ size: 1,
+ current: currentPage,
+ viewCount: pagerCount
+ })
+
+ pager.on(PagerEventType.CHANGE, changeHandler)
+ pager.on(PagerEventType.PREV, prevHandler)
+ pager.on(PagerEventType.NEXT, nextHandler)
+
+ pager.jump(5)
+ pager.prev()
+ pager.next()
+
+ expect(changeHandler).toBeCalledTimes(3)
+ expect(changeHandler).toHaveBeenNthCalledWith(1, 5)
+ expect(changeHandler).toHaveBeenNthCalledWith(2, 4)
+ expect(changeHandler).toHaveBeenNthCalledWith(3, 5)
+
+ expect(prevHandler).toBeCalledTimes(1)
+ expect(prevHandler).toHaveBeenNthCalledWith(1, 4)
+
+ expect(nextHandler).toBeCalledTimes(1)
+ expect(nextHandler).toHaveBeenNthCalledWith(1, 5)
+ })
+
+ it('update size', () => {
+ const sizeHandler = jest.fn()
+
+ const pager = new Pager({
+ total: 100,
+ size: 1,
+ current: 90,
+ viewCount: 7
+ })
+
+ pager.on(PagerEventType.SIZE_CHANGE, sizeHandler)
+ pager.size = 10
+ expect(sizeHandler).toBeCalledTimes(1)
+ expect(sizeHandler).toHaveBeenNthCalledWith(1, 10)
+ expect(pager.current).toBe(10)
+ })
+})
diff --git a/packages/element3/src/components/Pagination/tests/parts/Pager.spec.ts b/packages/element3/src/components/Pagination/tests/parts/Pager.spec.ts
new file mode 100644
index 000000000..ca4fda822
--- /dev/null
+++ b/packages/element3/src/components/Pagination/tests/parts/Pager.spec.ts
@@ -0,0 +1,106 @@
+import { mount } from '@vue/test-utils'
+import { Pager } from '../../src/entity/Pager'
+import ElPager from '../../src/parts/Pager.vue'
+
+describe('Pager.vue', () => {
+ it('show page pageCount <= pagerCount', () => {
+ const pager = new Pager({ total: 5, size: 1, current: 3, viewCount: 7 })
+ const wrapper = mount(ElPager, {
+ props: {
+ pager
+ }
+ })
+
+ expect(wrapper.findAll('.number')).toHaveLength(5)
+ expect(wrapper.findAll('.number').map((item) => item.text())).toEqual([
+ '1',
+ '2',
+ '3',
+ '4',
+ '5'
+ ])
+ expect(wrapper.find('.number.active').text()).toBe('3')
+ expect(wrapper.findAll('.el-icon.more')).toHaveLength(0)
+ })
+ it('show page pageCount > pagerCount currentPage is 4', () => {
+ const pager = new Pager({ total: 30, size: 1, current: 4, viewCount: 7 })
+
+ const wrapper = mount(ElPager, {
+ props: {
+ pager
+ }
+ })
+
+ expect(wrapper.findAll('.number')).toHaveLength(7)
+ expect(wrapper.find('.el-icon.more.btn-quickprev').exists()).toBeFalsy()
+ expect(wrapper.find('.el-icon.more.btn-quicknext').exists()).toBeTruthy()
+ })
+ it('show page pageCount > pagerCount currentPage is 27', () => {
+ const pager = new Pager({ total: 30, size: 1, current: 27, viewCount: 7 })
+
+ const wrapper = mount(ElPager, {
+ props: {
+ pager
+ }
+ })
+
+ expect(wrapper.findAll('.number')).toHaveLength(7)
+ expect(wrapper.find('.el-icon.more.btn-quickprev').exists()).toBeTruthy()
+ expect(wrapper.find('.el-icon.more.btn-quicknext').exists()).toBeFalsy()
+ })
+ it('show page pageCount > pagerCount', () => {
+ const pager = new Pager({ total: 30, size: 1, current: 10, viewCount: 7 })
+
+ const wrapper = mount(ElPager, {
+ props: {
+ pager
+ }
+ })
+
+ expect(wrapper.findAll('.number')).toHaveLength(7)
+ expect(wrapper.findAll('.el-icon.more')).toHaveLength(2)
+ expect(wrapper.find('.number.active').text()).toBe('10')
+ })
+
+ it('when pageCount less', () => {
+ const pager = new Pager({ total: 2, size: 1, current: 1, viewCount: 7 })
+
+ const wrapper = mount(ElPager, {
+ props: {
+ pager
+ }
+ })
+
+ expect(wrapper.findAll('.number')).toHaveLength(2)
+ expect(wrapper.findAll('.number').map((item) => item.text())).toEqual([
+ '1',
+ '2'
+ ])
+ expect(wrapper.find('.number.active').text()).toBe('1')
+ })
+
+ it('more btn mouse enter and leave', async () => {
+ const pager = new Pager({ total: 100, size: 1, current: 50, viewCount: 7 })
+
+ const wrapper = mount(ElPager, {
+ props: {
+ pager
+ }
+ })
+
+ const quickPrev = wrapper.find('.btn-quickprev')
+ const quickNext = wrapper.find('.btn-quicknext')
+
+ await quickPrev.trigger('mouseenter')
+ await quickNext.trigger('mouseenter')
+
+ expect(quickPrev.classes()).toContain('el-icon-d-arrow-left')
+ expect(quickNext.classes()).toContain('el-icon-d-arrow-right')
+
+ await quickPrev.trigger('mouseleave')
+ await quickNext.trigger('mouseleave')
+
+ expect(quickPrev.classes()).toContain('el-icon-more')
+ expect(quickNext.classes()).toContain('el-icon-more')
+ })
+})
diff --git a/packages/element3/src/components/Pagination/tests/parts/Total.spec.ts b/packages/element3/src/components/Pagination/tests/parts/Total.spec.ts
new file mode 100644
index 000000000..1e28db18e
--- /dev/null
+++ b/packages/element3/src/components/Pagination/tests/parts/Total.spec.ts
@@ -0,0 +1,20 @@
+import { mount } from '@vue/test-utils'
+import { Pager } from '../../src/entity/Pager'
+import Total from '../../src/parts/Total.vue'
+
+describe('Total.vue', () => {
+ it('show total', () => {
+ const wrapper = mount(Total, {
+ props: {
+ pager: new Pager({
+ total: 10,
+ size: 1,
+ current: 2,
+ viewCount: 7
+ })
+ }
+ })
+
+ expect(wrapper.text()).toBe(`共 ${10} 条`)
+ })
+})
diff --git a/packages/element3/src/components/Pagination/tests/tools/element.spec.ts b/packages/element3/src/components/Pagination/tests/tools/element.spec.ts
new file mode 100644
index 000000000..61decbdc1
--- /dev/null
+++ b/packages/element3/src/components/Pagination/tests/tools/element.spec.ts
@@ -0,0 +1,14 @@
+import { switchClass } from '../../src/tools/element'
+
+describe('element.ts', () => {
+ it('switch tow class state', () => {
+ const element = document.createElement('div')
+ const switchTrigger = switchClass(['a', 'b'])
+
+ switchTrigger(element)
+ expect(element.className).toBe('a')
+
+ switchTrigger(element)
+ expect(element.className).toBe('b')
+ })
+})
diff --git a/packages/element3/src/components/Pagination/tests/tools/makerArray.spec.ts b/packages/element3/src/components/Pagination/tests/tools/makerArray.spec.ts
new file mode 100644
index 000000000..053e1cd7a
--- /dev/null
+++ b/packages/element3/src/components/Pagination/tests/tools/makerArray.spec.ts
@@ -0,0 +1,7 @@
+import { makerArray } from '../../src/tools/makerArray'
+
+describe('makerArray.ts', () => {
+ it('test create 2-6 array', () => {
+ expect(makerArray(2, 6)).toEqual([2, 3, 4, 5, 6])
+ })
+})
diff --git a/packages/element3/src/components/Pagination/tests/tools/parseLayout.spec.ts b/packages/element3/src/components/Pagination/tests/tools/parseLayout.spec.ts
new file mode 100644
index 000000000..5f9782bb9
--- /dev/null
+++ b/packages/element3/src/components/Pagination/tests/tools/parseLayout.spec.ts
@@ -0,0 +1,13 @@
+import { parseLayout } from '../../src/tools/parseLayout'
+
+describe('parseLayout.ts', () => {
+ it('parse a layout string', () => {
+ expect(parseLayout('prev, pager, next ')).toEqual(['prev', 'pager', 'next'])
+ })
+
+ it('parse -> flag', () => {
+ const layoutStr = 'prev, pager, next, -> , jumper, ->, total'
+ const result = parseLayout(layoutStr)
+ expect(result).toEqual(['prev', 'pager', 'next', ['jumper', ['total']]])
+ })
+})
diff --git a/packages/element3/src/components/Pagination/types.ts b/packages/element3/src/components/Pagination/types.ts
new file mode 100644
index 000000000..8f4d0791a
--- /dev/null
+++ b/packages/element3/src/components/Pagination/types.ts
@@ -0,0 +1,26 @@
+export interface ElPaginationProps {
+ layout?: string
+ pagerCount?: number
+ currentPage?: number
+ pageCount?: number
+ total?: number
+ pageSize?: number
+ hideOnSinglePage?: boolean
+ pageSizes?: number[]
+ popperClass?: string
+ nextText?: string
+ prevText?: string
+ disabled?: boolean
+ small?: boolean
+ background?: boolean
+}
+
+declare class ElPagination {
+ $props: ElPaginationProps
+}
+
+export type Keep = {
+ [key in keyof ElPaginationProps & S]?: ElPaginationProps[key]
+}
+
+export default ElPagination
diff --git a/packages/element3/src/index.js b/packages/element3/src/index.js
index cb7532c22..35b3bc7b1 100644
--- a/packages/element3/src/index.js
+++ b/packages/element3/src/index.js
@@ -47,7 +47,7 @@ import { ElNewTableColumn } from '../src/components/TableColumn'
import { ElTag } from './components/Tag'
import { ElProgress } from './components/Progress'
import ElTree from '../packages/tree'
-import ElPagination from '../packages/pagination'
+import { ElPagination } from './components/Pagination'
import { ElBadge } from './components/Badge'
import { ElAvatar } from './components/Avatar'
// Notice