forked from opentiny/tiny-vue
refactor(pager): [pager] pager component refactor (#1198)
* refactor(pager): [pager] pager component refactor * refactor(pager): [pager] pager component refactor * refactor(pager): [pager] pager component refactor * refactor(pager): [pager] pager component refactor
This commit is contained in:
parent
2ce68c0edb
commit
9de15b4536
|
@ -18,7 +18,7 @@ const currentPage = ref(5)
|
||||||
|
|
||||||
function onBeforePageChange(param) {
|
function onBeforePageChange(param) {
|
||||||
const { callback, rollback } = param
|
const { callback, rollback } = param
|
||||||
Modal.confirm('您确定要放弃当前页的修改吗?').then((res) => {
|
Modal.confirm('您确定要继续变更操作吗?').then((res) => {
|
||||||
if (res === 'confirm') {
|
if (res === 'confirm') {
|
||||||
callback && callback()
|
callback && callback()
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -25,7 +25,7 @@ export default {
|
||||||
methods: {
|
methods: {
|
||||||
onBeforePageChange(param) {
|
onBeforePageChange(param) {
|
||||||
const { callback, rollback } = param
|
const { callback, rollback } = param
|
||||||
Modal.confirm('您确定要放弃当前页的修改吗?').then((res) => {
|
Modal.confirm('您确定要继续变更操作吗?').then((res) => {
|
||||||
if (res === 'confirm') {
|
if (res === 'confirm') {
|
||||||
callback && callback()
|
callback && callback()
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
<template>
|
<template>
|
||||||
<tiny-pager layout="total, sizes, prev, pager, next, slot, jumper" :total="1000">
|
<div>
|
||||||
<template #default>
|
<tiny-pager layout="total, sizes, prev, pager, next, slot, jumper" :total="1000">
|
||||||
<span>默认插槽</span>
|
<template #default>
|
||||||
</template>
|
<span>默认插槽</span>
|
||||||
</tiny-pager>
|
</template>
|
||||||
<tiny-pager layout="sizes, prev, current, next, total" :total="1000"></tiny-pager>
|
</tiny-pager>
|
||||||
|
<tiny-pager layout="sizes, prev, current, next, total" :total="1000"></tiny-pager>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
<template>
|
<template>
|
||||||
<tiny-pager layout="total, sizes, prev, pager, next, slot, jumper" :total="1000">
|
<div>
|
||||||
<template #default>
|
<tiny-pager layout="total, sizes, prev, pager, next, slot, jumper" :total="1000">
|
||||||
<span>默认插槽</span>
|
<template #default>
|
||||||
</template>
|
<span>默认插槽</span>
|
||||||
</tiny-pager>
|
</template>
|
||||||
<tiny-pager layout="sizes, prev, current, next, total" :total="1000"></tiny-pager>
|
</tiny-pager>
|
||||||
|
<tiny-pager layout="sizes, prev, current, next, total" :total="1000"></tiny-pager>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
|
@ -1,22 +1,24 @@
|
||||||
<template>
|
<template>
|
||||||
<tiny-pager
|
<div>
|
||||||
:current-page="currentPage"
|
<tiny-pager
|
||||||
@update:current-page="currentPage = $event"
|
:current-page="currentPage"
|
||||||
:page-size="100"
|
@update:current-page="currentPage = $event"
|
||||||
layout="total, sizes, prev, pager, next"
|
:page-size="100"
|
||||||
:total="5000000"
|
layout="total, sizes, prev, pager, next"
|
||||||
:custom-total="true"
|
:total="5000000"
|
||||||
>
|
:custom-total="true"
|
||||||
</tiny-pager>
|
>
|
||||||
<tiny-pager
|
</tiny-pager>
|
||||||
:current-page="currentPage"
|
<tiny-pager
|
||||||
@update:current-page="currentPage = $event"
|
:current-page="currentPage"
|
||||||
:page-size="100"
|
@update:current-page="currentPage = $event"
|
||||||
layout="total, sizes, prev, pager, next"
|
:page-size="100"
|
||||||
:total="5000000"
|
layout="total, sizes, prev, pager, next"
|
||||||
:custom-total="'条数超出百万'"
|
:total="5000000"
|
||||||
>
|
custom-total="条数超出百万"
|
||||||
</tiny-pager>
|
>
|
||||||
|
</tiny-pager>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
|
|
@ -1,22 +1,24 @@
|
||||||
<template>
|
<template>
|
||||||
<tiny-pager
|
<div>
|
||||||
:current-page="currentPage"
|
<tiny-pager
|
||||||
@update:current-page="currentPage = $event"
|
:current-page="currentPage"
|
||||||
:page-size="100"
|
@update:current-page="currentPage = $event"
|
||||||
layout="total, sizes, prev, pager, next"
|
:page-size="100"
|
||||||
:total="5000000"
|
layout="total, sizes, prev, pager, next"
|
||||||
:custom-total="true"
|
:total="5000000"
|
||||||
>
|
:custom-total="true"
|
||||||
</tiny-pager>
|
>
|
||||||
<tiny-pager
|
</tiny-pager>
|
||||||
:current-page="currentPage"
|
<tiny-pager
|
||||||
@update:current-page="currentPage = $event"
|
:current-page="currentPage"
|
||||||
:page-size="100"
|
@update:current-page="currentPage = $event"
|
||||||
layout="total, sizes, prev, pager, next"
|
:page-size="100"
|
||||||
:total="5000000"
|
layout="total, sizes, prev, pager, next"
|
||||||
:custom-total="'条数超出百万'"
|
:total="5000000"
|
||||||
>
|
custom-total="条数超出百万"
|
||||||
</tiny-pager>
|
>
|
||||||
|
</tiny-pager>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
是否隐藏:<tiny-switch v-model="isHide"></tiny-switch>
|
是否隐藏:<tiny-switch v-model="isHide"></tiny-switch>
|
||||||
<tiny-pager :hide-on-single-page="isHide" layout="prev, pager, next" :total="1"></tiny-pager>
|
<tiny-pager :hide-on-single-page="isHide" :total="1"></tiny-pager>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { test, expect } from '@playwright/test'
|
import { test, expect } from '@playwright/test'
|
||||||
|
|
||||||
test('只有一页时隐藏分页', async ({ page }) => {
|
test('单页时隐藏', async ({ page }) => {
|
||||||
page.on('pageerror', (exception) => expect(exception).toBeNull())
|
page.on('pageerror', (exception) => expect(exception).toBeNull())
|
||||||
await page.goto('pager#hide-on-single-page')
|
await page.goto('pager#hide-on-single-page')
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
是否隐藏:<tiny-switch v-model="isHide"></tiny-switch>
|
是否隐藏:<tiny-switch v-model="isHide"></tiny-switch>
|
||||||
<tiny-pager :hide-on-single-page="isHide" layout="prev, pager, next" :total="1"></tiny-pager>
|
<tiny-pager :hide-on-single-page="isHide" :total="1"></tiny-pager>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
|
@ -29,30 +29,35 @@ function debounce(fn, delay = 50) {
|
||||||
|
|
||||||
function handleCurrentChange(val) {
|
function handleCurrentChange(val) {
|
||||||
Modal.message({
|
Modal.message({
|
||||||
message: `current-change 事件,当前页: ${val}`
|
message: `current-change 事件,当前页: ${val}`,
|
||||||
|
status: 'info'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleSizeChange(val) {
|
function handleSizeChange(val) {
|
||||||
Modal.message({
|
Modal.message({
|
||||||
message: `size-change 事件,每页条目数: ${val}`
|
message: `size-change 事件,每页条目数: ${val}`,
|
||||||
|
status: 'info'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function prevClick(val) {
|
function prevClick(val) {
|
||||||
Modal.message({
|
Modal.message({
|
||||||
message: `prev-click 事件,当前页: ${val}`
|
message: `prev-click 事件,当前页: ${val}`,
|
||||||
|
status: 'info'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function nextClick(val) {
|
function nextClick(val) {
|
||||||
Modal.message({
|
Modal.message({
|
||||||
message: `next-click 事件,当前页: ${val}`
|
message: `next-click 事件,当前页: ${val}`,
|
||||||
|
status: 'info'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
const fetchData = debounce(() => {
|
const fetchData = debounce(() => {
|
||||||
Modal.message({
|
Modal.message({
|
||||||
message: '模拟后台拉取数据'
|
message: '模拟后台拉取数据',
|
||||||
|
status: 'info'
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -34,27 +34,32 @@ export default {
|
||||||
methods: {
|
methods: {
|
||||||
handleCurrentChange(val) {
|
handleCurrentChange(val) {
|
||||||
Modal.message({
|
Modal.message({
|
||||||
message: `current-change 事件,当前页: ${val}`
|
message: `current-change 事件,当前页: ${val}`,
|
||||||
|
status: 'info'
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
handleSizeChange(val) {
|
handleSizeChange(val) {
|
||||||
Modal.message({
|
Modal.message({
|
||||||
message: `size-change 事件,每页条目数: ${val}`
|
message: `size-change 事件,每页条目数: ${val}`,
|
||||||
|
status: 'info'
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
prevClick(val) {
|
prevClick(val) {
|
||||||
Modal.message({
|
Modal.message({
|
||||||
message: `prev-click 事件,当前页: ${val}`
|
message: `prev-click 事件,当前页: ${val}`,
|
||||||
|
status: 'info'
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
nextClick(val) {
|
nextClick(val) {
|
||||||
Modal.message({
|
Modal.message({
|
||||||
message: `next-click 事件,当前页: ${val}`
|
message: `next-click 事件,当前页: ${val}`,
|
||||||
|
status: 'info'
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
fetchData: debounce(() => {
|
fetchData: debounce(() => {
|
||||||
Modal.message({
|
Modal.message({
|
||||||
message: '模拟后台拉取数据'
|
message: '模拟后台拉取数据',
|
||||||
|
status: 'info'
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -131,9 +131,9 @@ export default {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'demoId': 'hide-on-single-page',
|
'demoId': 'hide-on-single-page',
|
||||||
'name': { 'zh-CN': '只有一页时隐藏分页', 'en-US': 'Grid Table Pagination' },
|
'name': { 'zh-CN': '单页时隐藏', 'en-US': 'Grid Table Pagination' },
|
||||||
'desc': {
|
'desc': {
|
||||||
'zh-CN': '<p>当 <code>hide-on-single-page</code> 为 <code>true</code> 时,只有一页时会隐藏分页。</p>\n',
|
'zh-CN': '<p>通过 <code>hide-on-single-page</code> 设置当仅有一页时是否隐藏分页组件。</p>\n',
|
||||||
'en-US': '<p>When there is only one page, the pagination will be hidden.</p>\n'
|
'en-US': '<p>When there is only one page, the pagination will be hidden.</p>\n'
|
||||||
},
|
},
|
||||||
'codeFiles': ['hide-on-single-page.vue']
|
'codeFiles': ['hide-on-single-page.vue']
|
||||||
|
@ -164,7 +164,7 @@ export default {
|
||||||
'name': { 'zh-CN': '分页变更前置处理', 'en-US': 'Pre processing of pagination changes' },
|
'name': { 'zh-CN': '分页变更前置处理', 'en-US': 'Pre processing of pagination changes' },
|
||||||
'desc': {
|
'desc': {
|
||||||
'zh-CN':
|
'zh-CN':
|
||||||
'<p>通过 <code>is-before-page-change</code> 开启前置处理特性,翻页或者改变页大小时会触发 <code>before-page-change</code> 事件。事件函数类型为 <a href="#IBeforeChangeEvent">IBeforeChangeEvent</a> ,调用传参中的 <code>callback</code> 继续变更,调用 <code>rollback</code> 中止变更。</p>\n',
|
'<p>通过 <code>is-before-page-change</code> 开启前置处理特性,翻页或者改变页大小时会触发 <code>before-page-change</code> 事件。调用传参中的 <code>callback</code> 继续变更,调用 <code>rollback</code> 中止变更。</p>\n',
|
||||||
'en-US':
|
'en-US':
|
||||||
'<p>By enabling the pre processing feature through <code>is before page change</code> , the <code>before page change</code> event is triggered when flipping or changing page size. The event function type is <a="#IBeforeChangeEvent">IBeforeChangeEvent</a> , call <code>callback</code> in the passed parameters to continue the change, and call <code>rollback</code> to abort the change.</p>\n'
|
'<p>By enabling the pre processing feature through <code>is before page change</code> , the <code>before page change</code> event is triggered when flipping or changing page size. The event function type is <a="#IBeforeChangeEvent">IBeforeChangeEvent</a> , call <code>callback</code> in the passed parameters to continue the change, and call <code>rollback</code> to abort the change.</p>\n'
|
||||||
},
|
},
|
||||||
|
|
|
@ -0,0 +1,453 @@
|
||||||
|
import type { IPagerRenderlessParams } from '@/types'
|
||||||
|
import { emitEvent } from '../common/event'
|
||||||
|
|
||||||
|
export const computedShowPager =
|
||||||
|
({ props, state }: Pick<IPagerRenderlessParams, 'props' | 'state'>) =>
|
||||||
|
(): boolean => {
|
||||||
|
const hidePager = props.hideOnSinglePage && (!state.internalPageCount || state.internalPageCount === 1)
|
||||||
|
return state.internalLayout.length > 0 && !hidePager
|
||||||
|
}
|
||||||
|
|
||||||
|
export const computedInternalLayout =
|
||||||
|
({ props }: Pick<IPagerRenderlessParams, 'props'>) =>
|
||||||
|
(): string[] => {
|
||||||
|
let layout = ''
|
||||||
|
|
||||||
|
if (props.mode && !props.layout) {
|
||||||
|
props.mode === 'number' && (layout = 'total, sizes, prev, pager, next, jumper')
|
||||||
|
props.mode === 'simple' && (layout = 'sizes, total, prev, current, next')
|
||||||
|
props.mode === 'complete' && (layout = 'sizes, total, prev, pager, next, jumper')
|
||||||
|
props.mode === 'fixed' && (layout = 'prev,pager,next')
|
||||||
|
} else if ((!props.mode && props.layout) || (props.mode && props.layout)) {
|
||||||
|
layout = props.layout
|
||||||
|
} else {
|
||||||
|
layout = 'total, prev, pager, next, jumper'
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!layout) {
|
||||||
|
return []
|
||||||
|
} else {
|
||||||
|
const components = layout.split(',').map((item) => item.trim())
|
||||||
|
return components
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const computedTotalText =
|
||||||
|
({ props, t }: Pick<IPagerRenderlessParams, 'props' | 't'>) =>
|
||||||
|
(): string => {
|
||||||
|
if (typeof props.customTotal === 'string') return props.customTotal
|
||||||
|
|
||||||
|
const totals = Number(props.total)
|
||||||
|
|
||||||
|
if (isNaN(totals)) return '0'
|
||||||
|
|
||||||
|
const HUNDRED_THOUSAND = 100000
|
||||||
|
const MILLION = 1000000
|
||||||
|
const TEN_MILLION = 10000000
|
||||||
|
if (totals <= HUNDRED_THOUSAND) {
|
||||||
|
return String(totals)
|
||||||
|
} else if (totals <= MILLION) {
|
||||||
|
return t('ui.page.hundredThousand')
|
||||||
|
} else if (totals <= TEN_MILLION) {
|
||||||
|
return t('ui.page.million')
|
||||||
|
} else {
|
||||||
|
return t('ui.page.tenMillion')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const computedInternalPageCount =
|
||||||
|
({ props, state }: Pick<IPagerRenderlessParams, 'props' | 'state'>) =>
|
||||||
|
(): number | null => {
|
||||||
|
if (typeof props.total === 'number') {
|
||||||
|
return Math.max(1, Math.ceil(props.total / state.internalPageSize))
|
||||||
|
} else if (typeof props.pageCount === 'number') {
|
||||||
|
return Math.max(1, props.pageCount)
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
export const handleJumperFocus =
|
||||||
|
({ state }: Pick<IPagerRenderlessParams, 'state'>) =>
|
||||||
|
(e: Event): void => {
|
||||||
|
state.jumperBackup = (e.target as HTMLInputElement)?.value
|
||||||
|
}
|
||||||
|
|
||||||
|
export const watchInternalCurrentPage =
|
||||||
|
({ state, emit }: Pick<IPagerRenderlessParams, 'state' | 'emit'>) =>
|
||||||
|
(currentPage: number): void => {
|
||||||
|
const value = String(currentPage)
|
||||||
|
|
||||||
|
if (state.jumperValue !== value) {
|
||||||
|
state.jumperValue = value
|
||||||
|
}
|
||||||
|
emit('update:currentPage', currentPage)
|
||||||
|
emit('current-change', currentPage)
|
||||||
|
state.lastEmittedPage = -1
|
||||||
|
}
|
||||||
|
|
||||||
|
export const watchPageSizes =
|
||||||
|
({ state, props }: Pick<IPagerRenderlessParams, 'props' | 'state'>) =>
|
||||||
|
(newVal: number[]): void => {
|
||||||
|
if (Array.isArray(newVal)) {
|
||||||
|
state.internalPageSize = newVal.includes(props.pageSize) ? props.pageSize : newVal[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const watchCurrentPage =
|
||||||
|
({ state, api }: Pick<IPagerRenderlessParams, 'api' | 'state'>) =>
|
||||||
|
(curPage: number): void => {
|
||||||
|
state.internalCurrentPage = api.getValidCurrentPage(curPage)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const watchInternalPageCount =
|
||||||
|
({ state, api }: Pick<IPagerRenderlessParams, 'api' | 'state'>) =>
|
||||||
|
(pageCount: number | null): void => {
|
||||||
|
const oldCurPage = state.internalCurrentPage
|
||||||
|
|
||||||
|
if (pageCount && pageCount > 0 && oldCurPage === 0) {
|
||||||
|
state.internalCurrentPage = 1
|
||||||
|
} else if (oldCurPage > Number(pageCount)) {
|
||||||
|
state.internalCurrentPage = pageCount || 1
|
||||||
|
state.userChangePageSize && api.emitChange()
|
||||||
|
}
|
||||||
|
|
||||||
|
state.userChangePageSize = false
|
||||||
|
}
|
||||||
|
|
||||||
|
export const watchPageSize =
|
||||||
|
({ state }: Pick<IPagerRenderlessParams, 'state'>) =>
|
||||||
|
(pageSize: number): void => {
|
||||||
|
state.internalPageSize = isNaN(pageSize) ? 10 : pageSize
|
||||||
|
}
|
||||||
|
|
||||||
|
export const watchTotal =
|
||||||
|
({ state }: Pick<IPagerRenderlessParams, 'state'>) =>
|
||||||
|
(total: number | undefined): void => {
|
||||||
|
state.internalTotal = total
|
||||||
|
}
|
||||||
|
|
||||||
|
export const handleSizeChange =
|
||||||
|
({ props, state, api, emit, vm }: Pick<IPagerRenderlessParams, 'props' | 'state' | 'api' | 'emit' | 'vm'>) =>
|
||||||
|
(val: number): void => {
|
||||||
|
// 防止用户pagerSizes传入字符串数组导致bug
|
||||||
|
val = Number(val)
|
||||||
|
if (val !== state.internalPageSize) {
|
||||||
|
const callback = () => {
|
||||||
|
if (!api.beforeChangeHandler()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
state.internalPageSize = val
|
||||||
|
state.userChangePageSize = true
|
||||||
|
state.showSizes = false
|
||||||
|
emit('update:pageSize', val)
|
||||||
|
emit('size-change', val)
|
||||||
|
emit('page-change', {
|
||||||
|
currentPage: state.internalCurrentPage,
|
||||||
|
pageSize: val,
|
||||||
|
total: state.internalTotal
|
||||||
|
})
|
||||||
|
vm.$refs.sizesList[0].state.showPopper = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (props.isBeforePageChange) {
|
||||||
|
let newPageSize = val
|
||||||
|
let currentPageSize = state.internalPageSize
|
||||||
|
let params = { newPageSize, currentPageSize, callback }
|
||||||
|
|
||||||
|
api.beforeSizeChangeHandler(params)
|
||||||
|
} else {
|
||||||
|
callback()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const handleJumperInput =
|
||||||
|
({ state }: Pick<IPagerRenderlessParams, 'state'>) =>
|
||||||
|
(e: Event): void => {
|
||||||
|
const target = e.target as HTMLInputElement
|
||||||
|
if (!target.value) {
|
||||||
|
state.jumperValue = ''
|
||||||
|
} else if (/^\d+$/.test(target.value)) {
|
||||||
|
state.jumperValue = target.value || '1'
|
||||||
|
}
|
||||||
|
target.value = state.jumperValue
|
||||||
|
}
|
||||||
|
|
||||||
|
export const handleJumperChange =
|
||||||
|
({ props, state, api }: Pick<IPagerRenderlessParams, 'props' | 'state' | 'api'>) =>
|
||||||
|
(): void => {
|
||||||
|
api.parseValueNumber()
|
||||||
|
|
||||||
|
const callback = () => {
|
||||||
|
api.handleJumperClick()
|
||||||
|
}
|
||||||
|
const rollback = () => {
|
||||||
|
state.jumperValue = String(state.jumperBackup)
|
||||||
|
}
|
||||||
|
const newPage = state.jumperValue
|
||||||
|
const currentPage = state.jumperBackup
|
||||||
|
|
||||||
|
if (props.isBeforePageChange && newPage !== currentPage) {
|
||||||
|
const params = { newPage, currentPage, callback, rollback }
|
||||||
|
|
||||||
|
api.beforePagerChangeHandler(params)
|
||||||
|
} else {
|
||||||
|
callback()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const handleJumperClick =
|
||||||
|
({ props, state, api }: Pick<IPagerRenderlessParams, 'props' | 'state' | 'api'>) =>
|
||||||
|
(): void => {
|
||||||
|
if (!api.canJumperGo() || props.disabled) return
|
||||||
|
|
||||||
|
state.internalCurrentPage = api.getValidCurrentPage(state.jumperValue)
|
||||||
|
api.emitChange()
|
||||||
|
}
|
||||||
|
|
||||||
|
export const isValueNumber =
|
||||||
|
({ state }: Pick<IPagerRenderlessParams, 'state'>) =>
|
||||||
|
(): boolean => {
|
||||||
|
return !isNaN(Number(state.jumperValue))
|
||||||
|
}
|
||||||
|
|
||||||
|
export const parseValueNumber =
|
||||||
|
({ state }: Pick<IPagerRenderlessParams, 'state'>) =>
|
||||||
|
(): void => {
|
||||||
|
let value = Number(
|
||||||
|
String(state.jumperValue)
|
||||||
|
.split(/[^0-9-+.]/)
|
||||||
|
.join('')
|
||||||
|
)
|
||||||
|
|
||||||
|
if (isNaN(value)) {
|
||||||
|
value = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
value = Number(value.toFixed(0))
|
||||||
|
|
||||||
|
const min = 1
|
||||||
|
const max = state.internalPageCount || 1
|
||||||
|
|
||||||
|
if (value >= max) {
|
||||||
|
state.jumperValue = String(max)
|
||||||
|
} else if (value <= min) {
|
||||||
|
state.jumperValue = String(min)
|
||||||
|
} else {
|
||||||
|
state.jumperValue = String(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const handleSizeShowPopover =
|
||||||
|
({ state, props }: Pick<IPagerRenderlessParams, 'props' | 'state'>) =>
|
||||||
|
(): void => {
|
||||||
|
if (props.disabled) {
|
||||||
|
state.showSizes = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
state.showSizes = true
|
||||||
|
}
|
||||||
|
|
||||||
|
export const handleSizeHidePopover =
|
||||||
|
({ state }: Pick<IPagerRenderlessParams, 'state'>) =>
|
||||||
|
(): void => {
|
||||||
|
state.showSizes = false
|
||||||
|
}
|
||||||
|
|
||||||
|
export const canJumperGo =
|
||||||
|
({ props, state, vm }: Pick<IPagerRenderlessParams, 'props' | 'state' | 'vm'>) =>
|
||||||
|
(): boolean => {
|
||||||
|
const inputValue = Number(vm.$refs.jumperInput[0].value || 0)
|
||||||
|
const currentPage = Number(state.internalCurrentPage || 0)
|
||||||
|
return props.accurateJumper ? inputValue !== currentPage : true
|
||||||
|
}
|
||||||
|
export const beforeSizeChangeHandler =
|
||||||
|
({ state, emit }: Pick<IPagerRenderlessParams, 'emit' | 'state'>) =>
|
||||||
|
(params): void => {
|
||||||
|
const { newPageSize, currentPageSize, callback } = params
|
||||||
|
const newPage = 1
|
||||||
|
const currentPage = state.internalCurrentPage
|
||||||
|
const temp = {
|
||||||
|
newPage,
|
||||||
|
newPageSize,
|
||||||
|
currentPage,
|
||||||
|
currentPageSize,
|
||||||
|
callback
|
||||||
|
}
|
||||||
|
|
||||||
|
emit('before-page-change', temp)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const beforePagerChangeHandler =
|
||||||
|
({ state, emit }: Pick<IPagerRenderlessParams, 'emit' | 'state'>) =>
|
||||||
|
(params): void => {
|
||||||
|
const { newPage, currentPage, callback, rollback } = params
|
||||||
|
const newPageSize = state.internalPageSize
|
||||||
|
const currentPageSize = state.internalPageSize
|
||||||
|
const temp = {
|
||||||
|
newPage,
|
||||||
|
newPageSize,
|
||||||
|
currentPage,
|
||||||
|
currentPageSize,
|
||||||
|
callback,
|
||||||
|
rollback
|
||||||
|
}
|
||||||
|
|
||||||
|
emit('before-page-change', temp)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const beforeJumperChangeHandler =
|
||||||
|
({ state, emit }: Pick<IPagerRenderlessParams, 'emit' | 'state'>) =>
|
||||||
|
(params): void => {
|
||||||
|
const { newPage, currentPage, callback, rollback } = params
|
||||||
|
const newPageSize = state.internalPageSize
|
||||||
|
const currentPageSize = state.internalPageSize
|
||||||
|
const temp = {
|
||||||
|
newPage,
|
||||||
|
newPageSize,
|
||||||
|
currentPage,
|
||||||
|
currentPageSize,
|
||||||
|
callback,
|
||||||
|
rollback
|
||||||
|
}
|
||||||
|
|
||||||
|
emit('before-page-change', temp)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const copyEmit =
|
||||||
|
({ emit }: Pick<IPagerRenderlessParams, 'emit'>) =>
|
||||||
|
(...args): void => {
|
||||||
|
emit(args[0], ...args.slice(1))
|
||||||
|
}
|
||||||
|
|
||||||
|
export const beforeChangeHandler =
|
||||||
|
({ state, api }: Pick<IPagerRenderlessParams, 'api' | 'state'>) =>
|
||||||
|
(val: number = -1) => {
|
||||||
|
return emitEvent(api.copyEmit, 'before-change', state.internalCurrentPage, this, val)
|
||||||
|
}
|
||||||
|
export const handleCurrentChange =
|
||||||
|
({ state, api }: Pick<IPagerRenderlessParams, 'api' | 'state'>) =>
|
||||||
|
(val: number): void => {
|
||||||
|
if (!api.beforeChangeHandler(val)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
state.internalCurrentPage = api.getValidCurrentPage(val)
|
||||||
|
state.userChangePageSize = true
|
||||||
|
api.emitChange()
|
||||||
|
}
|
||||||
|
|
||||||
|
export const prev =
|
||||||
|
({ state, props, api, emit }: Pick<IPagerRenderlessParams, 'props' | 'state' | 'api' | 'emit'>) =>
|
||||||
|
(): void => {
|
||||||
|
const callback = () => {
|
||||||
|
if (props.disabled || !api.beforeChangeHandler(state.internalCurrentPage - 1)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const newVal = state.internalCurrentPage - 1
|
||||||
|
|
||||||
|
state.internalCurrentPage = api.getValidCurrentPage(newVal)
|
||||||
|
emit('prev-click', state.internalCurrentPage)
|
||||||
|
api.emitChange()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (props.isBeforePageChange) {
|
||||||
|
const newPage = state.internalCurrentPage - 1
|
||||||
|
const temp = api.buildBeforePageChangeParam({ newPage, callback })
|
||||||
|
|
||||||
|
emit('before-page-change', temp)
|
||||||
|
} else {
|
||||||
|
callback()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const next =
|
||||||
|
({ props, state, api, emit }: Pick<IPagerRenderlessParams, 'props' | 'state' | 'api' | 'emit'>) =>
|
||||||
|
(): void => {
|
||||||
|
const callback = () => {
|
||||||
|
if (props.disabled || !api.beforeChangeHandler(state.internalCurrentPage + 1)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const newVal = state.internalCurrentPage + 1
|
||||||
|
|
||||||
|
state.internalCurrentPage = api.getValidCurrentPage(newVal)
|
||||||
|
emit('next-click', state.internalCurrentPage)
|
||||||
|
api.emitChange()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (props.isBeforePageChange) {
|
||||||
|
const newPage = state.internalCurrentPage + 1
|
||||||
|
const temp = api.buildBeforePageChangeParam({ newPage, callback })
|
||||||
|
|
||||||
|
emit('before-page-change', temp)
|
||||||
|
} else {
|
||||||
|
callback()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const buildBeforePageChangeParam =
|
||||||
|
({ state }: Pick<IPagerRenderlessParams, 'state'>) =>
|
||||||
|
(param) => {
|
||||||
|
const currentPage = state.internalCurrentPage
|
||||||
|
const newPageSize = state.internalPageSize
|
||||||
|
const currentPageSize = state.internalPageSize
|
||||||
|
|
||||||
|
return { currentPage, newPageSize, currentPageSize, ...param }
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getValidCurrentPage =
|
||||||
|
({ state }: Pick<IPagerRenderlessParams, 'state'>) =>
|
||||||
|
(val: string | number) => {
|
||||||
|
const parseVal = Number(val)
|
||||||
|
|
||||||
|
const hasPageCount = typeof state.internalPageCount === 'number'
|
||||||
|
|
||||||
|
let resetVal
|
||||||
|
|
||||||
|
if (hasPageCount) {
|
||||||
|
if (parseVal < 1) {
|
||||||
|
resetVal = 1
|
||||||
|
} else if (parseVal > (state.internalPageCount || 0)) {
|
||||||
|
resetVal = state.internalPageCount
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (isNaN(parseVal) || parseVal < 1) {
|
||||||
|
resetVal = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resetVal === undefined && isNaN(parseVal)) {
|
||||||
|
resetVal = 1
|
||||||
|
} else if (resetVal === 0) {
|
||||||
|
resetVal = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
return resetVal === undefined ? parseVal : resetVal
|
||||||
|
}
|
||||||
|
|
||||||
|
export const emitChange =
|
||||||
|
({ state, nextTick, emit }: Pick<IPagerRenderlessParams, 'emit' | 'state' | 'nextTick'>) =>
|
||||||
|
(): void => {
|
||||||
|
nextTick(() => {
|
||||||
|
if (state.internalCurrentPage !== state.lastEmittedPage || state.userChangePageSize) {
|
||||||
|
emit('update:current-page', state.internalCurrentPage)
|
||||||
|
emit('page-change', {
|
||||||
|
currentPage: state.internalCurrentPage,
|
||||||
|
pageSize: state.internalPageSize,
|
||||||
|
total: state.internalTotal
|
||||||
|
})
|
||||||
|
state.lastEmittedPage = state.internalCurrentPage
|
||||||
|
state.userChangePageSize = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const setTotal =
|
||||||
|
({ state }: Pick<IPagerRenderlessParams, 'state'>) =>
|
||||||
|
(val: number): void => {
|
||||||
|
state.internalTotal = val
|
||||||
|
}
|
|
@ -1,7 +1,135 @@
|
||||||
export const api = []
|
import type {
|
||||||
|
IPagerApi,
|
||||||
|
IPagerProps,
|
||||||
|
IPagerState,
|
||||||
|
ISharedRenderlessParamHooks,
|
||||||
|
IPagerRenderlessParamUtils
|
||||||
|
} from '@/types'
|
||||||
|
import {
|
||||||
|
computedShowPager,
|
||||||
|
computedInternalLayout,
|
||||||
|
computedTotalText,
|
||||||
|
computedInternalPageCount,
|
||||||
|
handleJumperFocus,
|
||||||
|
handleSizeChange,
|
||||||
|
handleJumperInput,
|
||||||
|
handleJumperChange,
|
||||||
|
handleJumperClick,
|
||||||
|
isValueNumber,
|
||||||
|
parseValueNumber,
|
||||||
|
handleSizeShowPopover,
|
||||||
|
handleSizeHidePopover,
|
||||||
|
canJumperGo,
|
||||||
|
beforeSizeChangeHandler,
|
||||||
|
beforePagerChangeHandler,
|
||||||
|
copyEmit,
|
||||||
|
beforeChangeHandler,
|
||||||
|
handleCurrentChange,
|
||||||
|
prev,
|
||||||
|
next,
|
||||||
|
buildBeforePageChangeParam,
|
||||||
|
getValidCurrentPage,
|
||||||
|
emitChange,
|
||||||
|
setTotal,
|
||||||
|
watchInternalCurrentPage,
|
||||||
|
watchPageSizes,
|
||||||
|
watchCurrentPage,
|
||||||
|
watchInternalPageCount,
|
||||||
|
watchPageSize,
|
||||||
|
watchTotal
|
||||||
|
} from './index'
|
||||||
|
|
||||||
export const renderless = () => {
|
export const api = [
|
||||||
const api = {}
|
'state',
|
||||||
|
'handleJumperFocus',
|
||||||
|
'handleSizeChange',
|
||||||
|
'handleJumperInput',
|
||||||
|
'handleJumperChange',
|
||||||
|
'handleJumperClick',
|
||||||
|
'isValueNumber',
|
||||||
|
'parseValueNumber',
|
||||||
|
'handleSizeShowPopover',
|
||||||
|
'handleSizeHidePopover',
|
||||||
|
'canJumperGo',
|
||||||
|
'beforeSizeChangeHandler',
|
||||||
|
'beforePagerChangeHandler',
|
||||||
|
'beforeJumperChangeHandler',
|
||||||
|
'beforeChangeHandler',
|
||||||
|
'handleCurrentChange',
|
||||||
|
'prev',
|
||||||
|
'next',
|
||||||
|
'buildBeforePageChangeParam',
|
||||||
|
'getValidCurrentPage',
|
||||||
|
'emitChange',
|
||||||
|
'setTotal'
|
||||||
|
]
|
||||||
|
|
||||||
|
export const renderless = (
|
||||||
|
props: IPagerProps,
|
||||||
|
{ reactive, computed, watch }: ISharedRenderlessParamHooks,
|
||||||
|
{ emit, vm, nextTick, t }: IPagerRenderlessParamUtils
|
||||||
|
): IPagerApi => {
|
||||||
|
const api = {} as IPagerApi
|
||||||
|
|
||||||
|
const state: IPagerState = reactive({
|
||||||
|
showSizes: false,
|
||||||
|
internalCurrentPage: 1,
|
||||||
|
internalPageSize: props.pageSize,
|
||||||
|
lastEmittedPage: -1,
|
||||||
|
userChangePageSize: false,
|
||||||
|
internalTotal: props.total,
|
||||||
|
jumperValue: '1',
|
||||||
|
jumperBackup: '1',
|
||||||
|
showPager: computed(() => api.computedShowPager()),
|
||||||
|
internalLayout: computed(() => api.computedInternalLayout()),
|
||||||
|
totalText: computed(() => api.computedTotalText()),
|
||||||
|
internalPageCount: computed(() => api.computedInternalPageCount())
|
||||||
|
})
|
||||||
|
|
||||||
|
Object.assign(api, {
|
||||||
|
state,
|
||||||
|
computedShowPager: computedShowPager({ props, state }),
|
||||||
|
computedInternalLayout: computedInternalLayout({ props }),
|
||||||
|
computedTotalText: computedTotalText({ props, t }),
|
||||||
|
computedInternalPageCount: computedInternalPageCount({ props, state }),
|
||||||
|
getValidCurrentPage: getValidCurrentPage({ state }),
|
||||||
|
handleJumperFocus: handleJumperFocus({ state }),
|
||||||
|
handleSizeChange: handleSizeChange({ props, state, api, emit, vm }),
|
||||||
|
handleJumperInput: handleJumperInput({ state }),
|
||||||
|
handleJumperChange: handleJumperChange({ props, state, api }),
|
||||||
|
handleJumperClick: handleJumperClick({ props, state, api }),
|
||||||
|
isValueNumber: isValueNumber({ state }),
|
||||||
|
parseValueNumber: parseValueNumber({ state }),
|
||||||
|
handleSizeShowPopover: handleSizeShowPopover({ state, props }),
|
||||||
|
handleSizeHidePopover: handleSizeHidePopover({ state }),
|
||||||
|
canJumperGo: canJumperGo({ props, state, vm }),
|
||||||
|
beforeSizeChangeHandler: beforeSizeChangeHandler({ state, emit }),
|
||||||
|
beforePagerChangeHandler: beforePagerChangeHandler({ state, emit }),
|
||||||
|
copyEmit: copyEmit({ emit }),
|
||||||
|
beforeChangeHandler: beforeChangeHandler({ state, api }),
|
||||||
|
handleCurrentChange: handleCurrentChange({ state, api }),
|
||||||
|
prev: prev({ state, props, api, emit }),
|
||||||
|
next: next({ props, state, api, emit }),
|
||||||
|
buildBeforePageChangeParam: buildBeforePageChangeParam({ state }),
|
||||||
|
emitChange: emitChange({ state, nextTick, emit }),
|
||||||
|
setTotal: setTotal({ state }),
|
||||||
|
// watch
|
||||||
|
watchInternalCurrentPage: watchInternalCurrentPage({ state, emit }),
|
||||||
|
watchPageSizes: watchPageSizes({ state, props }),
|
||||||
|
watchCurrentPage: watchCurrentPage({ state, api }),
|
||||||
|
watchInternalPageCount: watchInternalPageCount({ state, api }),
|
||||||
|
watchPageSize: watchPageSize({ state }),
|
||||||
|
watchTotal: watchTotal({ state })
|
||||||
|
})
|
||||||
|
|
||||||
|
state.internalCurrentPage = api.getValidCurrentPage(props.currentPage)
|
||||||
|
|
||||||
|
watch(() => state.internalCurrentPage, api.watchInternalCurrentPage)
|
||||||
|
watch(() => props.pageSizes, api.watchPageSizes, { immediate: true })
|
||||||
|
watch(() => props.currentPage, api.watchCurrentPage)
|
||||||
|
watch(() => state.internalPageCount, api.watchInternalPageCount)
|
||||||
|
watch(() => props.pageSize, api.watchPageSize, { immediate: true })
|
||||||
|
watch(() => props.total, api.watchTotal)
|
||||||
|
|
||||||
return api
|
return api
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,96 @@
|
||||||
|
import type { ExtractPropTypes } from 'vue'
|
||||||
|
import type { pagerProps } from '@/pager/src'
|
||||||
|
import type { ISharedRenderlessFunctionParams, ISharedRenderlessParamUtils } from './shared.type'
|
||||||
|
import type {
|
||||||
|
computedShowPager,
|
||||||
|
computedInternalLayout,
|
||||||
|
computedTotalText,
|
||||||
|
computedInternalPageCount,
|
||||||
|
handleJumperFocus,
|
||||||
|
handleSizeChange,
|
||||||
|
handleJumperInput,
|
||||||
|
handleJumperChange,
|
||||||
|
handleJumperClick,
|
||||||
|
isValueNumber,
|
||||||
|
parseValueNumber,
|
||||||
|
handleSizeShowPopover,
|
||||||
|
handleSizeHidePopover,
|
||||||
|
canJumperGo,
|
||||||
|
beforeSizeChangeHandler,
|
||||||
|
beforePagerChangeHandler,
|
||||||
|
copyEmit,
|
||||||
|
beforeChangeHandler,
|
||||||
|
handleCurrentChange,
|
||||||
|
prev,
|
||||||
|
next,
|
||||||
|
buildBeforePageChangeParam,
|
||||||
|
getValidCurrentPage,
|
||||||
|
emitChange,
|
||||||
|
setTotal,
|
||||||
|
watchInternalCurrentPage,
|
||||||
|
watchPageSizes,
|
||||||
|
watchCurrentPage,
|
||||||
|
watchInternalPageCount,
|
||||||
|
watchPageSize,
|
||||||
|
watchTotal
|
||||||
|
} from '../src/pager'
|
||||||
|
|
||||||
|
export type IPagerProps = ExtractPropTypes<typeof pagerProps>
|
||||||
|
|
||||||
|
export interface IPagerState {
|
||||||
|
showPager: boolean
|
||||||
|
showSizes: boolean
|
||||||
|
internalCurrentPage: number
|
||||||
|
internalPageSize: number
|
||||||
|
lastEmittedPage: number
|
||||||
|
userChangePageSize: boolean
|
||||||
|
internalTotal: number | undefined
|
||||||
|
jumperValue: string
|
||||||
|
jumperBackup: string
|
||||||
|
internalLayout: string[]
|
||||||
|
totalText: string
|
||||||
|
internalPageCount: number | null
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IPagerApi {
|
||||||
|
state: IPagerState
|
||||||
|
t: IPagerRenderlessParamUtils['t']
|
||||||
|
computedShowPager: ReturnType<typeof computedShowPager>
|
||||||
|
computedInternalLayout: ReturnType<typeof computedInternalLayout>
|
||||||
|
computedTotalText: ReturnType<typeof computedTotalText>
|
||||||
|
computedInternalPageCount: ReturnType<typeof computedInternalPageCount>
|
||||||
|
handleJumperFocus: ReturnType<typeof handleJumperFocus>
|
||||||
|
handleSizeChange: ReturnType<typeof handleSizeChange>
|
||||||
|
handleJumperInput: ReturnType<typeof handleJumperInput>
|
||||||
|
handleJumperChange: ReturnType<typeof handleJumperChange>
|
||||||
|
handleJumperClick: ReturnType<typeof handleJumperClick>
|
||||||
|
isValueNumber: ReturnType<typeof isValueNumber>
|
||||||
|
parseValueNumber: ReturnType<typeof parseValueNumber>
|
||||||
|
handleSizeShowPopover: ReturnType<typeof handleSizeShowPopover>
|
||||||
|
handleSizeHidePopover: ReturnType<typeof handleSizeHidePopover>
|
||||||
|
canJumperGo: ReturnType<typeof canJumperGo>
|
||||||
|
beforeSizeChangeHandler: ReturnType<typeof beforeSizeChangeHandler>
|
||||||
|
beforePagerChangeHandler: ReturnType<typeof beforePagerChangeHandler>
|
||||||
|
copyEmit: ReturnType<typeof copyEmit>
|
||||||
|
beforeChangeHandler: ReturnType<typeof beforeChangeHandler>
|
||||||
|
handleCurrentChange: ReturnType<typeof handleCurrentChange>
|
||||||
|
prev: ReturnType<typeof prev>
|
||||||
|
next: ReturnType<typeof next>
|
||||||
|
buildBeforePageChangeParam: ReturnType<typeof buildBeforePageChangeParam>
|
||||||
|
getValidCurrentPage: ReturnType<typeof getValidCurrentPage>
|
||||||
|
emitChange: ReturnType<typeof emitChange>
|
||||||
|
setTotal: ReturnType<typeof setTotal>
|
||||||
|
watchInternalCurrentPage: ReturnType<typeof watchInternalCurrentPage>
|
||||||
|
watchPageSizes: ReturnType<typeof watchPageSizes>
|
||||||
|
watchCurrentPage: ReturnType<typeof watchCurrentPage>
|
||||||
|
watchInternalPageCount: ReturnType<typeof watchInternalPageCount>
|
||||||
|
watchPageSize: ReturnType<typeof watchPageSize>
|
||||||
|
watchTotal: ReturnType<typeof watchTotal>
|
||||||
|
}
|
||||||
|
export type IPagerRenderlessParams = ISharedRenderlessFunctionParams<never> & {
|
||||||
|
api: IPagerApi
|
||||||
|
state: IPagerState
|
||||||
|
props: IPagerProps
|
||||||
|
}
|
||||||
|
|
||||||
|
export type IPagerRenderlessParamUtils = ISharedRenderlessParamUtils<never>
|
|
@ -1,70 +1,73 @@
|
||||||
|
import type { PropType } from 'vue'
|
||||||
import { $props, $prefix, $setup, defineComponent } from '@opentiny/vue-common'
|
import { $props, $prefix, $setup, defineComponent } from '@opentiny/vue-common'
|
||||||
import template from 'virtual-template?pc|mobile-first'
|
import template from 'virtual-template?pc|mobile-first'
|
||||||
|
|
||||||
|
export const pagerProps = {
|
||||||
|
...$props,
|
||||||
|
accurateJumper: {
|
||||||
|
type: Boolean,
|
||||||
|
default: () => true
|
||||||
|
},
|
||||||
|
appendToBody: {
|
||||||
|
type: Boolean,
|
||||||
|
default: () => true
|
||||||
|
},
|
||||||
|
currentPage: {
|
||||||
|
type: Number,
|
||||||
|
default: () => 1
|
||||||
|
},
|
||||||
|
disabled: {
|
||||||
|
type: Boolean,
|
||||||
|
default: () => false
|
||||||
|
},
|
||||||
|
hideOnSinglePage: Boolean,
|
||||||
|
isBeforePageChange: Boolean,
|
||||||
|
layout: String,
|
||||||
|
mode: String,
|
||||||
|
nextText: String,
|
||||||
|
pageCount: Number,
|
||||||
|
pageSize: {
|
||||||
|
type: Number,
|
||||||
|
default: () => 10
|
||||||
|
},
|
||||||
|
pageSizes: {
|
||||||
|
type: Array as PropType<number[]>,
|
||||||
|
default: () => [10, 20, 30, 40, 50, 100]
|
||||||
|
},
|
||||||
|
pagerCount: {
|
||||||
|
type: Number,
|
||||||
|
validator: (value) => (value | 0) === value && value > 2 && value < 22 && value % 2 === 1,
|
||||||
|
default: () => 7
|
||||||
|
},
|
||||||
|
popperAppendToBody: {
|
||||||
|
type: Boolean,
|
||||||
|
default: () => true
|
||||||
|
},
|
||||||
|
showTotalLoading: {
|
||||||
|
type: Boolean,
|
||||||
|
default: () => false
|
||||||
|
},
|
||||||
|
customTotal: {
|
||||||
|
type: [Boolean, String],
|
||||||
|
default: () => false
|
||||||
|
},
|
||||||
|
popperClass: String,
|
||||||
|
prevText: String,
|
||||||
|
total: Number,
|
||||||
|
size: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
align: {
|
||||||
|
// 分页对齐方式 【left,center,right】
|
||||||
|
type: String,
|
||||||
|
validator: (value) => ['left', 'center', 'right'].includes(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: $prefix + 'Pager',
|
name: $prefix + 'Pager',
|
||||||
props: {
|
props: pagerProps,
|
||||||
...$props,
|
|
||||||
accurateJumper: {
|
|
||||||
type: Boolean,
|
|
||||||
default: () => true
|
|
||||||
},
|
|
||||||
appendToBody: {
|
|
||||||
type: Boolean,
|
|
||||||
default: () => true
|
|
||||||
},
|
|
||||||
currentPage: {
|
|
||||||
type: Number,
|
|
||||||
default: () => 1
|
|
||||||
},
|
|
||||||
disabled: {
|
|
||||||
type: Boolean,
|
|
||||||
default: () => false
|
|
||||||
},
|
|
||||||
hideOnSinglePage: Boolean,
|
|
||||||
isBeforePageChange: Boolean,
|
|
||||||
layout: String,
|
|
||||||
mode: String,
|
|
||||||
nextText: String,
|
|
||||||
pageCount: Number,
|
|
||||||
pageSize: {
|
|
||||||
type: Number,
|
|
||||||
default: () => 10
|
|
||||||
},
|
|
||||||
pageSizes: {
|
|
||||||
type: Array,
|
|
||||||
default: () => [10, 20, 30, 40, 50, 100]
|
|
||||||
},
|
|
||||||
pagerCount: {
|
|
||||||
type: Number,
|
|
||||||
validator: (value) => (value | 0) === value && value > 2 && value < 22 && value % 2 === 1,
|
|
||||||
default: () => 7
|
|
||||||
},
|
|
||||||
popperAppendToBody: {
|
|
||||||
type: Boolean,
|
|
||||||
default: () => true
|
|
||||||
},
|
|
||||||
showTotalLoading: {
|
|
||||||
type: Boolean,
|
|
||||||
default: () => false
|
|
||||||
},
|
|
||||||
customTotal: {
|
|
||||||
type: [Boolean, String],
|
|
||||||
default: () => false
|
|
||||||
},
|
|
||||||
popperClass: String,
|
|
||||||
prevText: String,
|
|
||||||
total: Number,
|
|
||||||
size: {
|
|
||||||
type: String,
|
|
||||||
default: ''
|
|
||||||
},
|
|
||||||
align: {
|
|
||||||
// 分页对齐方式 【left,center,right】
|
|
||||||
type: String,
|
|
||||||
validator: (value) => ['left', 'center', 'right'].includes(value)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
setup(props, context) {
|
setup(props, context) {
|
||||||
return $setup({ props, context, template })
|
return $setup({ props, context, template })
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,752 +9,191 @@
|
||||||
* A PARTICULAR PURPOSE. SEE THE APPLICABLE LICENSES FOR MORE DETAILS.
|
* A PARTICULAR PURPOSE. SEE THE APPLICABLE LICENSES FOR MORE DETAILS.
|
||||||
*
|
*
|
||||||
-->
|
-->
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
v-if="state.showPager"
|
||||||
|
:class="['tiny-pager tiny-pager__number', size ? 'tiny-pager--' + size : '', disabled ? 'is-disabled' : '']"
|
||||||
|
:style="{ textAlign: align }"
|
||||||
|
>
|
||||||
|
<template v-for="(item, index) in state.internalLayout">
|
||||||
|
<!-- prev -->
|
||||||
|
<button
|
||||||
|
v-if="item === 'prev'"
|
||||||
|
:key="'prev' + index"
|
||||||
|
type="button"
|
||||||
|
class="tiny-pager__btn-prev"
|
||||||
|
:disabled="disabled || state.internalCurrentPage <= 1"
|
||||||
|
@click="prev"
|
||||||
|
>
|
||||||
|
<span v-if="prevText">{{ prevText }}</span>
|
||||||
|
<chevron-left v-else class="tiny-svg-size" />
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<!-- jumper -->
|
||||||
|
<div v-else-if="item === 'jumper'" :key="'jumper' + index" class="tiny-pager__group">
|
||||||
|
<div class="tiny-pager__goto">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
ref="jumperInput"
|
||||||
|
:value="state.jumperValue"
|
||||||
|
:disabled="disabled"
|
||||||
|
@focus="handleJumperFocus"
|
||||||
|
@input="handleJumperInput"
|
||||||
|
@change="handleJumperChange"
|
||||||
|
/>
|
||||||
|
<button :class="['tiny-btn', disabled ? 'is-disabled' : '']" type="button" @click="handleJumperClick">
|
||||||
|
{{ t('ui.page.goto') }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- current -->
|
||||||
|
<div v-else-if="item === 'current'" :key="'current' + index" class="tiny-pager__group tiny-unselect">
|
||||||
|
<ul class="tiny-pager__pages">
|
||||||
|
<li class="is-active">{{ state.internalCurrentPage }}</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- pager-item -->
|
||||||
|
<pager
|
||||||
|
v-else-if="item === 'pager'"
|
||||||
|
:key="'pager' + index"
|
||||||
|
:is-before-page-change="isBeforePageChange"
|
||||||
|
:current-page="state.internalCurrentPage"
|
||||||
|
:page-count="state.internalPageCount"
|
||||||
|
:pager-count="pagerCount"
|
||||||
|
:disabled="disabled"
|
||||||
|
@change="handleCurrentChange"
|
||||||
|
@before-page-change="beforePagerChangeHandler"
|
||||||
|
></pager>
|
||||||
|
|
||||||
|
<!-- next -->
|
||||||
|
<button
|
||||||
|
v-else-if="item === 'next'"
|
||||||
|
:key="'next' + index"
|
||||||
|
type="button"
|
||||||
|
class="tiny-pager__btn-next"
|
||||||
|
:disabled="disabled || state.internalCurrentPage === state.internalPageCount || state.internalPageCount === 0"
|
||||||
|
@click="next"
|
||||||
|
>
|
||||||
|
<span v-if="nextText">{{ nextText }}</span>
|
||||||
|
<chevron-right v-else class="tiny-svg-size" />
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<!-- sizes -->
|
||||||
|
<div v-else-if="item === 'sizes'" :key="'sizes' + index" class="tiny-pager__group tiny-pager__sizes">
|
||||||
|
<tiny-popover
|
||||||
|
ref="sizesList"
|
||||||
|
placement="bottom-start"
|
||||||
|
:append-to-body="popperAppendToBody === false ? false : appendToBody"
|
||||||
|
trigger="click"
|
||||||
|
:popper-class="'tiny-pager__selector ' + (popperClass ? '' + popperClass : '')"
|
||||||
|
:visible-arrow="false"
|
||||||
|
:disabled="disabled"
|
||||||
|
@show="handleSizeShowPopover"
|
||||||
|
@hide="handleSizeHidePopover"
|
||||||
|
>
|
||||||
|
<template #reference>
|
||||||
|
<div class="tiny-pager__popover">
|
||||||
|
<div class="tiny-pager__page-size">
|
||||||
|
<span class="sizes">{{ state.internalPageSize }}</span>
|
||||||
|
<div class="tiny-pager__page-size-btn">
|
||||||
|
<triangle-down :class="['tiny-svg-size', state.showSizes ? 'tiny-svg-size__reverse-180' : '']" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div class="tiny-pager tiny-pager__selector-body">
|
||||||
|
<ul class="tiny-pager__selector-poplist">
|
||||||
|
<li
|
||||||
|
v-for="(sizeItem, sizeIndex) in pageSizes"
|
||||||
|
:key="sizeIndex"
|
||||||
|
:class="['list-item', sizeItem === state.internalPageSize ? 'is-selected select-pre' : '']"
|
||||||
|
:title="String(sizeItem)"
|
||||||
|
@click="handleSizeChange(sizeItem)"
|
||||||
|
>
|
||||||
|
{{ sizeItem }}
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</tiny-popover>
|
||||||
|
</div>
|
||||||
|
<slot v-else-if="item === 'slot'"></slot>
|
||||||
|
|
||||||
|
<!-- total -->
|
||||||
|
<div
|
||||||
|
v-else-if="item === 'total' && typeof state.internalTotal === 'number'"
|
||||||
|
:key="'total' + index"
|
||||||
|
class="tiny-pager__group tiny-pager__pull-left"
|
||||||
|
:class="{
|
||||||
|
'is-disabled': disabled,
|
||||||
|
'tiny-pager__loading': showTotalLoading
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<div :class="['tiny-pager__total', size ? 'tiny-pager--' + size : '']">
|
||||||
|
<template v-if="showTotalLoading">
|
||||||
|
<div v-loading="showTotalLoading" class="tiny-pager__total-loading"></div>
|
||||||
|
<span class="tiny-pager__loading-text">{{ t('ui.page.loadingTotals') }}</span>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<span>{{ t('ui.page.total') }}:</span>
|
||||||
|
<span class="tiny-pager__total-allpage"> {{ customTotal ? state.totalText : state.internalTotal }} </span>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<script lang="tsx">
|
<script lang="tsx">
|
||||||
import Pager from '@opentiny/vue-pager-item'
|
import Pager from '@opentiny/vue-pager-item'
|
||||||
import Popover from '@opentiny/vue-popover'
|
import Popover from '@opentiny/vue-popover'
|
||||||
import Loading from '@opentiny/vue-loading'
|
import Loading from '@opentiny/vue-loading'
|
||||||
import { t } from '@opentiny/vue-locale'
|
import { $prefix, setup, defineComponent, props } from '@opentiny/vue-common'
|
||||||
import { $prefix, h, setup, defineComponent, $props } from '@opentiny/vue-common'
|
|
||||||
import { renderless, api } from '@opentiny/vue-renderless/pager/vue'
|
import { renderless, api } from '@opentiny/vue-renderless/pager/vue'
|
||||||
import { iconTriangleDown, iconChevronLeft, iconChevronRight } from '@opentiny/vue-icon'
|
import { iconTriangleDown, iconChevronLeft, iconChevronRight } from '@opentiny/vue-icon'
|
||||||
import { emitEvent } from '@opentiny/vue-renderless/common/event'
|
import type { IPagerApi } from '@opentiny/vue-renderless/types/pager.type'
|
||||||
import '@opentiny/vue-theme/pager/index.less'
|
import '@opentiny/vue-theme/pager/index.less'
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: $prefix + 'Pager',
|
name: $prefix + 'Pager',
|
||||||
props: {
|
props: [
|
||||||
...$props,
|
...props,
|
||||||
accurateJumper: {
|
'accurateJumper',
|
||||||
type: Boolean,
|
'appendToBody',
|
||||||
default: () => true
|
'currentPage',
|
||||||
},
|
'disabled',
|
||||||
appendToBody: {
|
'hideOnSinglePage',
|
||||||
type: Boolean,
|
'isBeforePageChange',
|
||||||
default: () => true
|
'layout',
|
||||||
},
|
'mode',
|
||||||
currentPage: {
|
'nextText',
|
||||||
type: Number,
|
'pageCount',
|
||||||
default: () => 1
|
'pageSize',
|
||||||
},
|
'pageSizes',
|
||||||
disabled: {
|
'pagerCount',
|
||||||
type: Boolean,
|
'popperAppendToBody',
|
||||||
default: () => false
|
'showTotalLoading',
|
||||||
},
|
'customTotal',
|
||||||
hideOnSinglePage: Boolean,
|
'popperClass',
|
||||||
isBeforePageChange: Boolean,
|
'prevText',
|
||||||
layout: String,
|
'total',
|
||||||
mode: String,
|
'size',
|
||||||
nextText: String,
|
'align'
|
||||||
pageCount: Number,
|
],
|
||||||
pageSize: {
|
directives: {
|
||||||
type: Number,
|
loading: Loading.directive
|
||||||
default: () => 10
|
|
||||||
},
|
|
||||||
pageSizes: {
|
|
||||||
type: Array,
|
|
||||||
default: () => [10, 20, 30, 40, 50, 100]
|
|
||||||
},
|
|
||||||
pagerCount: {
|
|
||||||
type: Number,
|
|
||||||
validator: (value) => (value | 0) === value && value > 2 && value < 22 && value % 2 === 1,
|
|
||||||
default: () => 7
|
|
||||||
},
|
|
||||||
popperAppendToBody: {
|
|
||||||
type: Boolean,
|
|
||||||
default: () => true
|
|
||||||
},
|
|
||||||
showTotalLoading: {
|
|
||||||
type: Boolean,
|
|
||||||
default: () => false
|
|
||||||
},
|
|
||||||
customTotal: {
|
|
||||||
type: [Boolean, String],
|
|
||||||
default: () => false
|
|
||||||
},
|
|
||||||
popperClass: String,
|
|
||||||
prevText: String,
|
|
||||||
total: Number,
|
|
||||||
size: {
|
|
||||||
type: String,
|
|
||||||
default: ''
|
|
||||||
},
|
|
||||||
align: String
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
internalCurrentPage: 1,
|
|
||||||
internalPageSize: 0,
|
|
||||||
lastEmittedPage: -1,
|
|
||||||
userChangePageSize: false,
|
|
||||||
internalTotal: this.total
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
setup(props, context) {
|
setup(props, context) {
|
||||||
return setup({ props, context, renderless, api })
|
return setup({ props, context, renderless, api }) as unknown as IPagerApi
|
||||||
},
|
|
||||||
render() {
|
|
||||||
const layout = this.internalLayout
|
|
||||||
|
|
||||||
if (!layout) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.hideOnSinglePage && (!this.internalPageCount || this.internalPageCount === 1)) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
const TEMPLATE_MAP = {
|
|
||||||
prev: <prev></prev>,
|
|
||||||
jumper: (
|
|
||||||
<jumper
|
|
||||||
ref="jumper"
|
|
||||||
isBeforePageChange={this.isBeforePageChange}
|
|
||||||
onBeforePageChange={this.beforeJumperChangeHandler}
|
|
||||||
disabled={this.disabled}
|
|
||||||
max={this.internalPageCount}></jumper>
|
|
||||||
),
|
|
||||||
current: <current></current>,
|
|
||||||
pager: (
|
|
||||||
<pager
|
|
||||||
isBeforePageChange={this.isBeforePageChange}
|
|
||||||
onBeforePageChange={this.beforePagerChangeHandler}
|
|
||||||
currentPage={this.internalCurrentPage}
|
|
||||||
pageCount={this.internalPageCount}
|
|
||||||
pagerCount={this.pagerCount}
|
|
||||||
onChange={this.handleCurrentChange}
|
|
||||||
disabled={this.disabled}></pager>
|
|
||||||
),
|
|
||||||
next: <next></next>,
|
|
||||||
sizes: (
|
|
||||||
<sizes
|
|
||||||
ref="sizes"
|
|
||||||
isBeforePageChange={this.isBeforePageChange}
|
|
||||||
onBeforePageChange={this.beforeSizeChangeHandler}
|
|
||||||
popperAppendToBody={this.popperAppendToBody === false ? false : this.appendToBody}
|
|
||||||
popperClass={this.popperClass}
|
|
||||||
pageSizes={this.pageSizes}></sizes>
|
|
||||||
),
|
|
||||||
slot: typeof this.slots.default === 'function' ? this.slots.default() : this.slots.default,
|
|
||||||
total: <total></total>
|
|
||||||
}
|
|
||||||
|
|
||||||
const components = layout.split(',').map((item) => item.trim())
|
|
||||||
const templateChildren = components.map((compo) => {
|
|
||||||
return TEMPLATE_MAP[compo]
|
|
||||||
})
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
class={[
|
|
||||||
'tiny-pager tiny-pager__number',
|
|
||||||
this.size ? 'tiny-pager--' + this.size : '',
|
|
||||||
this.disabled ? 'is-disabled' : ''
|
|
||||||
]}
|
|
||||||
style={{ textAlign: this.align }}>
|
|
||||||
{templateChildren}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
Prev: {
|
TinyPopover: Popover,
|
||||||
render() {
|
ChevronLeft: iconChevronLeft(),
|
||||||
const ChevronLeft = iconChevronLeft()
|
ChevronRight: iconChevronRight(),
|
||||||
|
TriangleDown: iconTriangleDown(),
|
||||||
return (
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class="tiny-pager__btn-prev"
|
|
||||||
disabled={this.$parent.disabled || this.$parent.internalCurrentPage <= 1}
|
|
||||||
onClick={this.$parent.prev}>
|
|
||||||
{this.$parent.prevText ? <span>{this.$parent.prevText}</span> : <ChevronLeft class="tiny-svg-size" />}
|
|
||||||
</button>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Next: {
|
|
||||||
render() {
|
|
||||||
const ChevronRight = iconChevronRight()
|
|
||||||
|
|
||||||
return (
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class="tiny-pager__btn-next"
|
|
||||||
disabled={
|
|
||||||
this.$parent.disabled ||
|
|
||||||
this.$parent.internalCurrentPage === this.$parent.internalPageCount ||
|
|
||||||
this.$parent.internalPageCount === 0
|
|
||||||
}
|
|
||||||
onClick={this.$parent.next}>
|
|
||||||
{this.$parent.nextText ? <span>{this.$parent.nextText}</span> : <ChevronRight class="tiny-svg-size" />}
|
|
||||||
</button>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Current: {
|
|
||||||
render() {
|
|
||||||
const { internalCurrentPage } = this.$parent
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div class="tiny-pager__group tiny-unselect">
|
|
||||||
<ul class="tiny-pager__pages">
|
|
||||||
<li class="is-active" v-text={internalCurrentPage}></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Sizes: {
|
|
||||||
props: {
|
|
||||||
pageSizes: Array,
|
|
||||||
appendToBody: Boolean,
|
|
||||||
isBeforePageChange: Boolean,
|
|
||||||
popperClass: String,
|
|
||||||
popperAppendToBody: {
|
|
||||||
type: Boolean,
|
|
||||||
default: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
showSizes: false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
pageSizes: {
|
|
||||||
immediate: true,
|
|
||||||
handler(newVal) {
|
|
||||||
if (Array.isArray(newVal)) {
|
|
||||||
this.$parent.internalPageSize = newVal.includes(this.$parent.pageSize)
|
|
||||||
? this.$parent.pageSize
|
|
||||||
: this.pageSizes[0]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
render() {
|
|
||||||
const TriangleDown = iconTriangleDown()
|
|
||||||
const scopedSlots = {
|
|
||||||
reference: () => (
|
|
||||||
<div slot="reference" class="tiny-pager__popover">
|
|
||||||
<div class="tiny-pager__page-size" ref="pageSize">
|
|
||||||
<span class="sizes">{this.$parent.internalPageSize}</span>
|
|
||||||
<div class="tiny-pager__page-size-btn">
|
|
||||||
<TriangleDown class={['tiny-svg-size', this.showSizes ? 'tiny-svg-size__reverse-180' : '']} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
default: () => (
|
|
||||||
<div class="tiny-pager tiny-pager__selector-body">
|
|
||||||
<ul class="tiny-pager__selector-poplist">
|
|
||||||
{this.pageSizes.map((item) => (
|
|
||||||
<li
|
|
||||||
class={['list-item', item === this.$parent.internalPageSize ? 'is-selected select-pre' : '']}
|
|
||||||
val={item}
|
|
||||||
title={item}
|
|
||||||
onClick={() => this.handleChange(item)}>
|
|
||||||
{item}
|
|
||||||
</li>
|
|
||||||
))}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div class="tiny-pager__group tiny-pager__sizes">
|
|
||||||
{h(Popover, {
|
|
||||||
props: {
|
|
||||||
placement: 'bottom-start',
|
|
||||||
appendToBody: this.popperAppendToBody,
|
|
||||||
trigger: 'click',
|
|
||||||
popperClass: 'tiny-pager__selector' + (this.popperClass ? ' ' + this.popperClass : ''),
|
|
||||||
visibleArrow: false,
|
|
||||||
disabled: this.$parent.disabled
|
|
||||||
},
|
|
||||||
on: {
|
|
||||||
show: this.handleShowPopover,
|
|
||||||
hide: this.handleHidePopover
|
|
||||||
},
|
|
||||||
scopedSlots,
|
|
||||||
ref: 'sizesList'
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
handleChange(val) {
|
|
||||||
if (val !== this.$parent.internalPageSize) {
|
|
||||||
const callback = () => {
|
|
||||||
if (!this.$parent.beforeChangeHandler()) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
this.$parent.internalPageSize = val = parseInt(val, 10)
|
|
||||||
this.$parent.userChangePageSize = true
|
|
||||||
this.showSizes = false
|
|
||||||
this.$parent.$emit('update:pageSize', val)
|
|
||||||
this.$parent.$emit('size-change', val)
|
|
||||||
this.$parent.$emit('page-change', {
|
|
||||||
currentPage: this.$parent.internalCurrentPage,
|
|
||||||
pageSize: val,
|
|
||||||
total: this.$parent.internalTotal
|
|
||||||
})
|
|
||||||
this.$refs.sizesList.state.showPopper = false
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.isBeforePageChange) {
|
|
||||||
let newPageSize = val
|
|
||||||
let currentPageSize = this.$parent.internalPageSize
|
|
||||||
let params = { newPageSize, currentPageSize, callback }
|
|
||||||
|
|
||||||
this.$parent.beforeSizeChangeHandler(params)
|
|
||||||
} else {
|
|
||||||
callback()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
handleShowPopover() {
|
|
||||||
if (this.$parent.disabled) return (this.showSizes = false)
|
|
||||||
this.showSizes = true
|
|
||||||
},
|
|
||||||
handleHidePopover() {
|
|
||||||
this.showSizes = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Jumper: {
|
|
||||||
props: {
|
|
||||||
isBeforePageChange: Boolean,
|
|
||||||
disabled: Boolean,
|
|
||||||
min: {
|
|
||||||
type: Number,
|
|
||||||
default: 1
|
|
||||||
},
|
|
||||||
max: {
|
|
||||||
type: Number,
|
|
||||||
default: 10
|
|
||||||
},
|
|
||||||
initValue: {
|
|
||||||
type: Number,
|
|
||||||
default: 1
|
|
||||||
}
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
backupValue: String(this.initValue),
|
|
||||||
value: String(this.initValue)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
'$parent.internalCurrentPage': {
|
|
||||||
handler(currentPage) {
|
|
||||||
const value = String(currentPage)
|
|
||||||
|
|
||||||
if (this.value !== value) {
|
|
||||||
this.value = value
|
|
||||||
}
|
|
||||||
},
|
|
||||||
immediate: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
handleFocus(e) {
|
|
||||||
this.backupValue = e.target.value
|
|
||||||
},
|
|
||||||
handleInput(e) {
|
|
||||||
if (!e.target.value) {
|
|
||||||
this.value = ''
|
|
||||||
} else if (/^\d+$/.test(e.target.value)) {
|
|
||||||
this.value = Number(e.target.value) || 1
|
|
||||||
}
|
|
||||||
e.target.value = this.value
|
|
||||||
},
|
|
||||||
handleChange() {
|
|
||||||
this.parseValueNumber()
|
|
||||||
|
|
||||||
const callback = () => {
|
|
||||||
this.handleClick()
|
|
||||||
}
|
|
||||||
const rollback = () => {
|
|
||||||
this.value = String(this.backupValue)
|
|
||||||
}
|
|
||||||
const newPage = this.value
|
|
||||||
const currentPage = this.backupValue
|
|
||||||
|
|
||||||
if (this.isBeforePageChange && newPage !== currentPage) {
|
|
||||||
const params = { newPage, currentPage, callback, rollback }
|
|
||||||
|
|
||||||
this.$parent.beforePagerChangeHandler(params)
|
|
||||||
} else {
|
|
||||||
callback()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
handleClick() {
|
|
||||||
if (!this.$parent.canJumperGo() || this.disabled) return
|
|
||||||
|
|
||||||
this.$parent.internalCurrentPage = this.$parent.getValidCurrentPage(this.value)
|
|
||||||
this.$parent.emitChange()
|
|
||||||
},
|
|
||||||
isValueNumber() {
|
|
||||||
return !isNaN(Number(this.value))
|
|
||||||
},
|
|
||||||
parseValueNumber() {
|
|
||||||
let value = Number(
|
|
||||||
String(this.value)
|
|
||||||
.split(/[^0-9-+.]/)
|
|
||||||
.join('')
|
|
||||||
)
|
|
||||||
|
|
||||||
if (isNaN(value)) {
|
|
||||||
value = this.min
|
|
||||||
}
|
|
||||||
|
|
||||||
value = value.toFixed(0)
|
|
||||||
|
|
||||||
const min = this.min
|
|
||||||
const max = this.max
|
|
||||||
|
|
||||||
if (value >= max) {
|
|
||||||
this.value = String(max)
|
|
||||||
} else if (value <= min) {
|
|
||||||
this.value = String(min)
|
|
||||||
} else {
|
|
||||||
this.value = String(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
render() {
|
|
||||||
return h(
|
|
||||||
'div',
|
|
||||||
{
|
|
||||||
class: ['tiny-pager__group']
|
|
||||||
},
|
|
||||||
[
|
|
||||||
h('div', { class: ['tiny-pager__goto'] }, [
|
|
||||||
h('input', {
|
|
||||||
domProps: {
|
|
||||||
value: this.value
|
|
||||||
},
|
|
||||||
attrs: {
|
|
||||||
type: 'text',
|
|
||||||
disabled: this.disabled
|
|
||||||
},
|
|
||||||
on: {
|
|
||||||
focus: this.handleFocus,
|
|
||||||
input: this.handleInput,
|
|
||||||
change: this.handleChange
|
|
||||||
},
|
|
||||||
ref: 'input'
|
|
||||||
}),
|
|
||||||
h(
|
|
||||||
'button',
|
|
||||||
{
|
|
||||||
class: ['tiny-btn', this.disabled ? 'is-disabled' : ''],
|
|
||||||
attrs: { type: 'button' },
|
|
||||||
on: { click: this.handleClick }
|
|
||||||
},
|
|
||||||
[t('ui.page.goto')]
|
|
||||||
)
|
|
||||||
])
|
|
||||||
]
|
|
||||||
)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Total: {
|
|
||||||
watch: {
|
|
||||||
'$parent.showTotalLoading': function () {
|
|
||||||
this.$nextTick(() => {
|
|
||||||
this.serviceLoading()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
serviceLoading() {
|
|
||||||
if (document.querySelector('.tiny-pager__total-loading')) {
|
|
||||||
Loading.service({
|
|
||||||
target: document.querySelector('.tiny-pager__total-loading')
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
this.serviceLoading()
|
|
||||||
},
|
|
||||||
render() {
|
|
||||||
let tempalte = ''
|
|
||||||
|
|
||||||
if (typeof this.$parent.internalTotal === 'number') {
|
|
||||||
const loadingTotalTemplate = (
|
|
||||||
<div class="tiny-pager__group tiny-pager__pull-left tiny-pager__loading">
|
|
||||||
<div class="tiny-pager__total">
|
|
||||||
<div class="tiny-pager__total-loading"></div>
|
|
||||||
<span class="tiny-pager__loading-text">{t('ui.page.loadingTotals')}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
const totalTemplate = (
|
|
||||||
<div class={['tiny-pager__group tiny-pager__pull-left', this.$parent.disabled ? 'is-disabled' : '']}>
|
|
||||||
{' '}
|
|
||||||
<div class={['tiny-pager__total', this.$parent.size ? 'tiny-pager--' + this.$parent.size : '']}>
|
|
||||||
<span>{t('ui.page.total')}:</span>
|
|
||||||
<span class="tiny-pager__total-allpage">
|
|
||||||
{this.$parent.customTotal ? this.$parent.totalText : this.$parent.internalTotal}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
tempalte = this.$parent.showTotalLoading ? loadingTotalTemplate : totalTemplate
|
|
||||||
}
|
|
||||||
|
|
||||||
return tempalte
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Pager
|
Pager
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
canJumperGo() {
|
|
||||||
const inputValue = Number(this.$refs.jumper.$refs.input.value || 0)
|
|
||||||
const currentPage = Number(this.internalCurrentPage || 0)
|
|
||||||
return this.accurateJumper ? inputValue !== currentPage : true
|
|
||||||
},
|
|
||||||
beforeSizeChangeHandler(params) {
|
|
||||||
const { newPageSize, currentPageSize, callback } = params
|
|
||||||
const newPage = 1
|
|
||||||
const currentPage = this.internalCurrentPage
|
|
||||||
const temp = {
|
|
||||||
newPage,
|
|
||||||
newPageSize,
|
|
||||||
currentPage,
|
|
||||||
currentPageSize,
|
|
||||||
callback
|
|
||||||
}
|
|
||||||
|
|
||||||
this.$emit('before-page-change', temp)
|
|
||||||
},
|
|
||||||
beforePagerChangeHandler(params) {
|
|
||||||
const { newPage, currentPage, callback, rollback } = params
|
|
||||||
const newPageSize = this.internalPageSize
|
|
||||||
const currentPageSize = this.internalPageSize
|
|
||||||
const temp = {
|
|
||||||
newPage,
|
|
||||||
newPageSize,
|
|
||||||
currentPage,
|
|
||||||
currentPageSize,
|
|
||||||
callback,
|
|
||||||
rollback
|
|
||||||
}
|
|
||||||
|
|
||||||
this.$emit('before-page-change', temp)
|
|
||||||
},
|
|
||||||
beforeJumperChangeHandler(params) {
|
|
||||||
const { newPage, currentPage, callback, rollback } = params
|
|
||||||
const newPageSize = this.internalPageSize
|
|
||||||
const currentPageSize = this.internalPageSize
|
|
||||||
const temp = {
|
|
||||||
newPage,
|
|
||||||
newPageSize,
|
|
||||||
currentPage,
|
|
||||||
currentPageSize,
|
|
||||||
callback,
|
|
||||||
rollback
|
|
||||||
}
|
|
||||||
|
|
||||||
this.$emit('before-page-change', temp)
|
|
||||||
},
|
|
||||||
copyEmit(...args) {
|
|
||||||
this.$emit.apply(this, args)
|
|
||||||
},
|
|
||||||
beforeChangeHandler(val = -1) {
|
|
||||||
return emitEvent(this.copyEmit, 'before-change', this.internalCurrentPage, this, val)
|
|
||||||
},
|
|
||||||
handleCurrentChange(val) {
|
|
||||||
if (!this.beforeChangeHandler(val)) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
this.internalCurrentPage = this.getValidCurrentPage(val)
|
|
||||||
this.userChangePageSize = true
|
|
||||||
this.emitChange()
|
|
||||||
},
|
|
||||||
prev() {
|
|
||||||
const callback = () => {
|
|
||||||
if (this.disabled || !this.beforeChangeHandler(this.internalCurrentPage - 1)) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
const newVal = this.internalCurrentPage - 1
|
|
||||||
|
|
||||||
this.internalCurrentPage = this.getValidCurrentPage(newVal)
|
|
||||||
this.$emit('prev-click', this.internalCurrentPage)
|
|
||||||
this.emitChange()
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.isBeforePageChange) {
|
|
||||||
const newPage = this.internalCurrentPage - 1
|
|
||||||
const temp = this.buildBeforePageChangeParam({ newPage, callback })
|
|
||||||
|
|
||||||
this.$emit('before-page-change', temp)
|
|
||||||
} else {
|
|
||||||
callback()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
next() {
|
|
||||||
const callback = () => {
|
|
||||||
if (this.disabled || !this.beforeChangeHandler(this.internalCurrentPage + 1)) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
const newVal = this.internalCurrentPage + 1
|
|
||||||
|
|
||||||
this.internalCurrentPage = this.getValidCurrentPage(newVal)
|
|
||||||
this.$emit('next-click', this.internalCurrentPage)
|
|
||||||
this.emitChange()
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.isBeforePageChange) {
|
|
||||||
const newPage = this.internalCurrentPage + 1
|
|
||||||
const temp = this.buildBeforePageChangeParam({ newPage, callback })
|
|
||||||
|
|
||||||
this.$emit('before-page-change', temp)
|
|
||||||
} else {
|
|
||||||
callback()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
buildBeforePageChangeParam(param) {
|
|
||||||
const currentPage = this.internalCurrentPage
|
|
||||||
const newPageSize = this.internalPageSize
|
|
||||||
const currentPageSize = this.internalPageSize
|
|
||||||
|
|
||||||
return { currentPage, newPageSize, currentPageSize, ...param }
|
|
||||||
},
|
|
||||||
getValidCurrentPage(val) {
|
|
||||||
val = parseInt(val, 10)
|
|
||||||
|
|
||||||
const hasPageCount = typeof this.internalPageCount === 'number'
|
|
||||||
let resetVal
|
|
||||||
|
|
||||||
if (hasPageCount) {
|
|
||||||
if (val < 1) {
|
|
||||||
resetVal = 1
|
|
||||||
} else if (val > this.internalPageCount) {
|
|
||||||
resetVal = this.internalPageCount
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (isNaN(val) || val < 1) {
|
|
||||||
resetVal = 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (resetVal === undefined && isNaN(val)) {
|
|
||||||
resetVal = 1
|
|
||||||
} else if (resetVal === 0) {
|
|
||||||
resetVal = 1
|
|
||||||
}
|
|
||||||
|
|
||||||
return resetVal === undefined ? val : resetVal
|
|
||||||
},
|
|
||||||
emitChange() {
|
|
||||||
this.$nextTick(() => {
|
|
||||||
if (this.internalCurrentPage !== this.lastEmittedPage || this.userChangePageSize) {
|
|
||||||
this.$emit('update:current-page', this.internalCurrentPage)
|
|
||||||
this.$emit('page-change', {
|
|
||||||
currentPage: this.internalCurrentPage,
|
|
||||||
pageSize: this.internalPageSize,
|
|
||||||
total: this.internalTotal
|
|
||||||
})
|
|
||||||
this.lastEmittedPage = this.internalCurrentPage
|
|
||||||
this.userChangePageSize = false
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
setTotal(val) {
|
|
||||||
this.internalTotal = val
|
|
||||||
}
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
totalText() {
|
|
||||||
if (typeof this.customTotal === 'string') return this.customTotal
|
|
||||||
|
|
||||||
const totals = parseInt(this.total)
|
|
||||||
|
|
||||||
if (isNaN(totals)) return 0
|
|
||||||
|
|
||||||
const HUNDRED_THOUSAND = 100000
|
|
||||||
const MILLION = 1000000
|
|
||||||
const TEN_MILLION = 10000000
|
|
||||||
if (totals <= HUNDRED_THOUSAND) {
|
|
||||||
return totals
|
|
||||||
} else if (totals <= MILLION) {
|
|
||||||
return t('ui.page.hundredThousand')
|
|
||||||
} else if (totals <= TEN_MILLION) {
|
|
||||||
return t('ui.page.million')
|
|
||||||
} else {
|
|
||||||
return t('ui.page.tenMillion')
|
|
||||||
}
|
|
||||||
},
|
|
||||||
internalPageCount() {
|
|
||||||
if (typeof this.total === 'number') {
|
|
||||||
return Math.max(1, Math.ceil(this.total / this.internalPageSize))
|
|
||||||
} else if (typeof this.pageCount === 'number') {
|
|
||||||
return Math.max(1, this.pageCount)
|
|
||||||
}
|
|
||||||
|
|
||||||
return null
|
|
||||||
},
|
|
||||||
internalLayout() {
|
|
||||||
let layout = ''
|
|
||||||
|
|
||||||
if (this.mode && !this.layout) {
|
|
||||||
this.mode === 'number' && (layout = 'total, sizes, prev, pager, next, jumper')
|
|
||||||
this.mode === 'simple' && (layout = 'sizes, total, prev, current, next')
|
|
||||||
this.mode === 'complete' && (layout = 'sizes, total, prev, pager, next, jumper')
|
|
||||||
this.mode === 'fixed' && (layout = 'prev,pager,next')
|
|
||||||
} else if ((!this.mode && this.layout) || (this.mode && this.layout)) {
|
|
||||||
layout = this.layout
|
|
||||||
} else {
|
|
||||||
layout = 'total, prev, pager, next, jumper'
|
|
||||||
}
|
|
||||||
|
|
||||||
return layout
|
|
||||||
}
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
currentPage: {
|
|
||||||
handler(curPage) {
|
|
||||||
this.internalCurrentPage = this.getValidCurrentPage(curPage)
|
|
||||||
},
|
|
||||||
immediate: true
|
|
||||||
},
|
|
||||||
internalPageCount(pageCount) {
|
|
||||||
/* istanbul ignore if */
|
|
||||||
const oldCurPage = this.internalCurrentPage
|
|
||||||
|
|
||||||
if (pageCount > 0 && oldCurPage === 0) {
|
|
||||||
this.internalCurrentPage = 1
|
|
||||||
} else if (oldCurPage > pageCount) {
|
|
||||||
this.internalCurrentPage = pageCount || 1
|
|
||||||
this.userChangePageSize && this.emitChange()
|
|
||||||
}
|
|
||||||
|
|
||||||
this.userChangePageSize = false
|
|
||||||
},
|
|
||||||
internalCurrentPage: {
|
|
||||||
handler(newVal) {
|
|
||||||
this.$emit('update:currentPage', newVal)
|
|
||||||
this.$emit('current-change', newVal)
|
|
||||||
this.lastEmittedPage = -1
|
|
||||||
}
|
|
||||||
},
|
|
||||||
pageSize: {
|
|
||||||
handler(pageSize) {
|
|
||||||
this.internalPageSize = isNaN(pageSize) ? 10 : pageSize
|
|
||||||
},
|
|
||||||
immediate: true
|
|
||||||
},
|
|
||||||
total(total) {
|
|
||||||
this.internalTotal = total
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
Loading…
Reference in New Issue