From 2eeed85b8fc5902f2272bac379993769b6930b0a Mon Sep 17 00:00:00 2001 From: dong Date: Mon, 9 Aug 2021 20:44:57 +0800 Subject: [PATCH 1/6] =?UTF-8?q?feat:=E6=96=B0=E5=A2=9E=E5=88=86=E9=A1=B5?= =?UTF-8?q?=E5=99=A8=E7=BB=84=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- devui/pagination/index.ts | 3 +++ devui/vue-devui.ts | 11 ++++++----- sites/.vitepress/config/sidebar.ts | 1 + sites/components/pagination/index.md | 7 +++++++ 4 files changed, 17 insertions(+), 5 deletions(-) create mode 100644 devui/pagination/index.ts create mode 100644 sites/components/pagination/index.md diff --git a/devui/pagination/index.ts b/devui/pagination/index.ts new file mode 100644 index 00000000..8784eff8 --- /dev/null +++ b/devui/pagination/index.ts @@ -0,0 +1,3 @@ +import Pagination from './pagination' + +export default Pagination \ No newline at end of file diff --git a/devui/vue-devui.ts b/devui/vue-devui.ts index 2a0a2d82..fdb05ed3 100644 --- a/devui/vue-devui.ts +++ b/devui/vue-devui.ts @@ -6,11 +6,12 @@ import Icon from './icon'; import Panel from './panel'; // 导航 +import Pagination from './pagination'; import Tabs from './tabs'; // 反馈 -import Alert from './alert/alert'; -import DLoading, { LoadingService, Loading } from './loading'; +import Alert from './alert'; +import Loading from './loading'; // 数据录入 import Checkbox from './checkbox'; @@ -24,7 +25,7 @@ import Avatar from './avatar'; import Carousel from './carousel'; function install(app: App): void { - const packages = [ Button, Icon, Panel, Tabs, Alert, DLoading, Checkbox, Radio, Switch, TagsInput, TextInput, Avatar, Carousel ]; + const packages = [ Button, Icon, Panel, Pagination, Tabs, Alert, Loading, Checkbox, Radio, Switch, TagsInput, TextInput, Avatar, Carousel ]; packages.forEach((item: any) => { if (item.install) { app.use(item); @@ -34,5 +35,5 @@ function install(app: App): void { }); } -export { Button, Icon, Panel, Tabs, Alert, LoadingService, Loading, Checkbox, Radio, Switch, TagsInput, TextInput, Avatar, Carousel }; -export default { install, version: '0.0.1' }; +export { Button, Icon, Panel, Pagination, Tabs, Alert, Loading, Checkbox, Radio, Switch, TagsInput, TextInput, Avatar, Carousel }; +export default { install, version: '0.0.1' }; \ No newline at end of file diff --git a/sites/.vitepress/config/sidebar.ts b/sites/.vitepress/config/sidebar.ts index c49dde11..9aed9022 100644 --- a/sites/.vitepress/config/sidebar.ts +++ b/sites/.vitepress/config/sidebar.ts @@ -12,6 +12,7 @@ const sidebar = { { text: '导航', children: [ + { text: 'Pagination 分页', link: '/components/pagination/' }, { text: 'Tabs 选项卡切换', link: '/components/tabs/' }, ] }, diff --git a/sites/components/pagination/index.md b/sites/components/pagination/index.md new file mode 100644 index 00000000..5b2a108d --- /dev/null +++ b/sites/components/pagination/index.md @@ -0,0 +1,7 @@ +# Pagination 分页 + +分页器。 + +### 何时使用 + +当加载/渲染所有数据将花费很多时间时,可以切换页码浏览数据。 \ No newline at end of file -- Gitee From 725af359ab1c1092276e52aa81a72ea070a3670a Mon Sep 17 00:00:00 2001 From: dong Date: Wed, 11 Aug 2021 11:20:00 +0800 Subject: [PATCH 2/6] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- devui/loading/__tests__/loading.spec.ts | 23 ++-- devui/pagination/index.ts | 2 +- devui/pagination/pagination.tsx | 12 -- devui/pagination/src/pagination.scss | 53 ++++++++ devui/pagination/src/pagination.tsx | 101 +++++++++++++++ devui/pagination/src/types.ts | 106 +++++++++++++++ sites/components/pagination/index.md | 163 +++++++++++++++++++++++- 7 files changed, 436 insertions(+), 24 deletions(-) delete mode 100644 devui/pagination/pagination.tsx create mode 100644 devui/pagination/src/pagination.scss create mode 100644 devui/pagination/src/pagination.tsx create mode 100644 devui/pagination/src/types.ts diff --git a/devui/loading/__tests__/loading.spec.ts b/devui/loading/__tests__/loading.spec.ts index b2fb756c..25e0550c 100644 --- a/devui/loading/__tests__/loading.spec.ts +++ b/devui/loading/__tests__/loading.spec.ts @@ -1,14 +1,11 @@ import { mount } from '@vue/test-utils'; import { ref, Ref, nextTick, h, shallowReactive } from 'vue'; -import loading from '../index'; - -// 服务方式 -const LoadingService = loading.LoadingService +import { LoadingService, Loading } from '../index'; // 全局属性 const globalOption = { directives: { - dLoading: loading.dLoading + dLoading: Loading } } @@ -126,7 +123,6 @@ describe('Loading as directive', () => { }) - // TODO Promise 的单元测试, 需完善 it('loading test Promise', async () => { const wrapper = mount( { @@ -137,7 +133,7 @@ describe('Loading as directive', () => { `, setup() { - const loading: Ref | undefined | boolean> = ref(undefined) + const loading: Ref | undefined | boolean> = ref(false) const click = () => { loading.value = new Promise((res: any) => { @@ -160,10 +156,13 @@ describe('Loading as directive', () => { expect(btn.exists()).toBeTruthy() await btn.trigger('click') - expect(wrapper.find('.devui-loading-wrapper').exists()).toBeFalsy() + expect(wrapper.find('.devui-loading-wrapper').exists()).toBeTruthy() + // TODO 组件移除是在finally内部移除,在微任务队列末尾,这里好像检测不到 + setTimeout(() => { + expect(wrapper.find('.devui-loading-wrapper').exists()).toBeFalsy() + }) }) - // TODO 多个 Promise 的单元测试, 需完善 it('loading test mutiple Promise', async () => { const wrapper = mount( { @@ -203,7 +202,11 @@ describe('Loading as directive', () => { expect(btn.exists()).toBeTruthy() await btn.trigger('click') - expect(wrapper.find('.devui-loading-wrapper').exists()).toBeFalsy() + expect(wrapper.find('.devui-loading-wrapper').exists()).toBeTruthy() + // TODO 组件移除是在finally内部移除,在微任务队列末尾,这里好像检测不到 + setTimeout(() => { + expect(wrapper.find('.devui-loading-wrapper').exists()).toBeFalsy() + }) }) }) diff --git a/devui/pagination/index.ts b/devui/pagination/index.ts index 8784eff8..d29ede63 100644 --- a/devui/pagination/index.ts +++ b/devui/pagination/index.ts @@ -1,3 +1,3 @@ -import Pagination from './pagination' +import Pagination from './src/pagination' export default Pagination \ No newline at end of file diff --git a/devui/pagination/pagination.tsx b/devui/pagination/pagination.tsx deleted file mode 100644 index b144b0ba..00000000 --- a/devui/pagination/pagination.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import { defineComponent } from 'vue' - -export default defineComponent({ - name: 'd-pagination', - props: { - }, - setup(props, ctx) { - return () => { - return
devui-pagination
- } - } -}) \ No newline at end of file diff --git a/devui/pagination/src/pagination.scss b/devui/pagination/src/pagination.scss new file mode 100644 index 00000000..cbe35b70 --- /dev/null +++ b/devui/pagination/src/pagination.scss @@ -0,0 +1,53 @@ +.devui-pagination { + font-size: var(--devui-font-size,12px); + ul, li { + padding: 0; + margin: 0; + list-style: none; + } + a { + text-decoration: none; + color: #3eaf7c; + } + .devui-pagination-list { + vertical-align: middle; + display: inline-flex; + align-items: center; + li { + cursor: pointer; + &.active { + a.devui-pagination-link { + text-decoration: none; + background-color: var(--devui-list-item-active-bg,#5e7ce0); + color: var(--devui-list-item-active-text,#fff); + } + } + .devui-pagination-link { + margin-left: 5px; + padding: 3px 7px; + line-height: 1.5; + border-radius: var(--devui-border-radius,2px); + color: var(--devui-text-weak,#575d6c); + display: flex; + align-items: center; + &:hover { + text-decoration: none; + background-color: var(--devui-list-item-hover-bg,#f2f5fc); + color: var(--devui-list-item-hover-text,#526ecc); + } + } + } + } + .devui-jump-container { + display: inline-block; + position: relative; + margin: 0 10px; + vertical-align: middle; + .devui-input { + display: inline-block; + width: 3.5em; + vertical-align: middle; + margin: 0 3px; + } + } +} \ No newline at end of file diff --git a/devui/pagination/src/pagination.tsx b/devui/pagination/src/pagination.tsx new file mode 100644 index 00000000..9962994c --- /dev/null +++ b/devui/pagination/src/pagination.tsx @@ -0,0 +1,101 @@ +import { defineComponent, ref } from 'vue' +import { ComponentProps, componentProps } from './types' + +import './pagination.scss' + +export default defineComponent({ + name: 'd-pagination', + props: componentProps, + emits: ['pageIndexChange', 'pageSizeChange'], + setup(props: ComponentProps, { emit }) { + + console.log(props, 'props') + + let jumpPage = '1' + const jumpPageChange = (val: string) => { + console.log('jumpPageChange跳转' + val) + } + + return { + jumpPage, + jumpPageChange + } + + }, + render() { + + const { + pageSize, + total, + pageSizeOptions, + pageSizeDirection, + pageIndex, + maxItems, + preLink, + nextLink, + size, + canJumpPage, + canChangePageSize, + canViewTotal, + totalItemText, + goToText, + showJumpButton, + showTruePageIndex, + lite, + showPageSelector, + haveConfigMenu, + autoFixPageIndex, + autoHide, + + jumpPage, + jumpPageChange + } = this + + return ( +
+ + {totalItemText}: {total} +
    +
  • + { + preLink + ? + : < + } +
  • +
  • + 1 +
  • +
  • + 2 +
  • +
  • + 3 +
  • +
  • + 4 +
  • + +
  • + { + nextLink + ? + : > + } +
  • +
+
+ {goToText} + + +
+
+ ) + } +}) \ No newline at end of file diff --git a/devui/pagination/src/types.ts b/devui/pagination/src/types.ts new file mode 100644 index 00000000..2bc82ee5 --- /dev/null +++ b/devui/pagination/src/types.ts @@ -0,0 +1,106 @@ +import { PropType, ExtractPropTypes } from "vue"; + +type AppendToBodyDirection = 'rightDown' | 'rightUp' | 'leftUp' | 'leftDown' | 'centerDown' | 'centerUp'; + +interface ConnectedPosition { + originX : 'start' | 'center' | 'end'; + originY : 'top' | 'center' | 'bottom'; + + overlayX : 'start' | 'center' | 'end'; + overlayY : 'top' | 'center' | 'bottom'; + + weight ?: number; + offsetX ?: number; + offsetY ?: number; + panelClass ?: string | string[]; +} + +type Size = 'lg' | '' | 'sm' + +export const componentProps = { + pageSize: { + type: Number, + default: 10 + }, + total: { + type: Number, + default: 0 + }, + pageSizeOptions: { + type: Array as PropType, + default: () => [5, 10, 20, 50] + }, + pageSizeDirection: { + type: Array as PropType>, + default: () => ['centerDown', 'centerUp'] + }, + pageIndex: { + type: Number, + default: 1 + }, + maxItems: { + type: Number, + default: 10 + }, + preLink: { + type: String + }, + nextLink: { + type: String + }, + size: { + type: String as PropType, + default: '' + }, + canJumpPage: { + type: Boolean, + default: false + }, + canChangePageSize: { + type: Boolean, + default: false + }, + canViewTotal: { + type: Boolean, + default: false + }, + totalItemText: { + type: String, + default: '所有条目' + }, + goToText: { + type: String, + default: '跳至' + }, + showJumpButton: { + type: Boolean, + default: false + }, + showTruePageIndex: { + type: Boolean, + default: false + }, + lite: { + type: Boolean, + default: false + }, + showPageSelector: { + type: Boolean, + default: true + }, + haveConfigMenu: { + type: Boolean, + default: false + }, + autoFixPageIndex: { + type: Boolean, + default: true + }, + autoHide: { + type: Boolean, + default: false + } +} + +// 组件props +export type ComponentProps = ExtractPropTypes diff --git a/sites/components/pagination/index.md b/sites/components/pagination/index.md index 5b2a108d..5e4e3977 100644 --- a/sites/components/pagination/index.md +++ b/sites/components/pagination/index.md @@ -4,4 +4,165 @@ ### 何时使用 -当加载/渲染所有数据将花费很多时间时,可以切换页码浏览数据。 \ No newline at end of file +当加载/渲染所有数据将花费很多时间时,可以切换页码浏览数据。 + + +### 基本用法 + +**size = 'sm'** + + +**size = 'md'** + + +**size = 'lg'** + + +**Custom Style** + + +```html + + + +``` + + +### 极简模式 +极简模式适用于一些有大量信息的页面,可以简化页面的复杂度。 + + +### 多种配置 +支持设置输入跳转、显示跳转按钮;设置pageSize等功能。 + + + +### 特殊情况 +特殊场景下分页器的显示。 + + + \ No newline at end of file -- Gitee From 8ac77ebca1902d3903fc6cda0eaae3ee319cb2b2 Mon Sep 17 00:00:00 2001 From: dong Date: Mon, 16 Aug 2021 10:06:15 +0800 Subject: [PATCH 3/6] =?UTF-8?q?feat:=20=E5=88=86=E9=A1=B5=E5=99=A8?= =?UTF-8?q?=E7=BB=84=E4=BB=B6=E5=9F=BA=E6=9C=AC=E5=AE=8C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- devui/pagination/index.ts | 7 +- devui/pagination/src/pagination.scss | 190 ++++++++- devui/pagination/src/pagination.tsx | 360 +++++++++++++++--- .../src/{types.ts => use-pagination.ts} | 38 +- devui/pagination/src/utils.ts | 42 ++ devui/vue-devui.ts | 51 +-- sites/.vitepress/config/sidebar.ts | 4 - sites/components/pagination/index.md | 335 ++++++++++++---- 8 files changed, 827 insertions(+), 200 deletions(-) rename devui/pagination/src/{types.ts => use-pagination.ts} (68%) create mode 100644 devui/pagination/src/utils.ts diff --git a/devui/pagination/index.ts b/devui/pagination/index.ts index d29ede63..26a84814 100644 --- a/devui/pagination/index.ts +++ b/devui/pagination/index.ts @@ -1,3 +1,8 @@ +import { App } from 'vue' import Pagination from './src/pagination' -export default Pagination \ No newline at end of file +Pagination.install = (app: App): void => { + app.component(Pagination.name, Pagination) +} + +export default Pagination diff --git a/devui/pagination/src/pagination.scss b/devui/pagination/src/pagination.scss index cbe35b70..f8d7ae0c 100644 --- a/devui/pagination/src/pagination.scss +++ b/devui/pagination/src/pagination.scss @@ -1,53 +1,219 @@ +@import '../../style/theme/color'; +@import '../../style/core/_font'; + .devui-pagination { - font-size: var(--devui-font-size,12px); - ul, li { + font-size: var(--devui-font-size, 12px); + + .devui-page-size { + display: inline-block; + max-width: 100px; + line-height: 24px; + margin: 0 12px 0 0; + position: relative; + vertical-align: middle; + } + + .devui-select-input { + height: 24px; + } + + .devui-total-size { + display: inline-block; + position: relative; + vertical-align: middle; + margin: 0 12px 0 0; + color: var(--devui-text-weak, #575d6c); + } + + .devui-pagination-list, + .devui-pagination-item { padding: 0; margin: 0; list-style: none; } + a { text-decoration: none; color: #3eaf7c; } + .devui-pagination-list { vertical-align: middle; display: inline-flex; align-items: center; - li { + + li.devui-pagination-item { cursor: pointer; + &.active { a.devui-pagination-link { text-decoration: none; - background-color: var(--devui-list-item-active-bg,#5e7ce0); - color: var(--devui-list-item-active-text,#fff); + background-color: var(--devui-list-item-active-bg, #5e7ce0); + color: var(--devui-list-item-active-text, #ffffff); } } + + &.disabled { + a.devui-pagination-link { + cursor: not-allowed; + opacity: 0.5; + background-color: #ffffff; + color: var(--devui-text-weak, #575d6c); + } + } + .devui-pagination-link { margin-left: 5px; padding: 3px 7px; line-height: 1.5; - border-radius: var(--devui-border-radius,2px); - color: var(--devui-text-weak,#575d6c); + border-radius: var(--devui-border-radius, 2px); + color: var(--devui-text-weak, #575d6c); display: flex; align-items: center; + // transition: background-color var(--devui-animation-duration-slow, .3s) var(--devui-animation-ease-in-out-smooth, cubic-bezier(.645,.045,.355,1)); &:hover { text-decoration: none; - background-color: var(--devui-list-item-hover-bg,#f2f5fc); - color: var(--devui-list-item-hover-text,#526ecc); + background-color: var(--devui-list-item-hover-bg, #f2f5fc); + color: var(--devui-list-item-hover-text, #526ecc); } } } } + .devui-jump-container { - display: inline-block; + display: inline-flex; position: relative; - margin: 0 10px; + margin: 0 12px; vertical-align: middle; + align-items: center; + .devui-input { display: inline-block; width: 3.5em; + height: 24px; vertical-align: middle; margin: 0 3px; } } -} \ No newline at end of file + + .devui-jump-button { + display: inline-flex; + vertical-align: middle; + width: 24px; + height: 24px; + border-radius: var(--devui-border-radius, 2px); + border: 1px solid var(--devui-line, #adb0b8); + cursor: pointer; + margin-left: 4px; + align-items: center; + justify-content: center; + + .devui-pagination-go { + width: 0; + height: 0; + border-top: 6px solid transparent; + border-left: 6px solid #252b3a; + border-left: 6px solid var(--devui-icon-text, #252b3a); + border-bottom: 6px solid transparent; + } + } + + .devui-pagination-config { + color: var(--devui-text, #252b3a); + position: relative; + display: inline-block; + vertical-align: middle; + margin: 0 4px; + } + + .devui-setup-icon { + line-height: 30px; + cursor: pointer; + display: flex; + } + + .devui-config-container { + padding: 4px 0; + box-shadow: 0 2px 8px 0 rgb(0 0 0 / 20%); + box-shadow: var(--devui-shadow-connected-overlay, 0 2px 8px 0) var(--devui-shadow, rgba(0, 0, 0, 0.2)); + border-radius: 2px; + border-radius: var(--devui-border-radius, 2px); + width: 150px; + background-color: #ffffff; + background-color: var(--devui-connected-overlay-bg, #ffffff); + line-height: 26px; + position: absolute; + left: -136px; + top: 28px; + cursor: auto; + z-index: 1052; + z-index: var(--devui-z-index-dropdown, 1052); + -moz-user-select: none; + -webkit-user-select: none; + -ms-user-select: none; + user-select: none; + } + + /* 配置中的每一项,自定义项建议应用此样式或在此基础上修改 */ + .pagination-config-item { + padding-bottom: 8px; + padding-top: 4px; + border-bottom: 1px solid $devui-line; + + &:last-child { + border-bottom: none; + } + } + + /* 配置中每一项的标题样式,自定义项建议应用此样式或在此基础上修改 */ + .config-item-title { + color: $devui-line; + padding-left: 8px; + font-size: $devui-font-size; + line-height: 1.5; + } + + .devui-page-number { + padding-left: 8px; + margin-top: 4px; + display: flex; + + div { + color: var(--devui-text, #252b3a); + cursor: pointer; + border-top: 1px solid var(--devui-line, #adb0b8); + border-bottom: 1px solid var(--devui-line, #adb0b8); + border-right: 1px solid var(--devui-line, #adb0b8); + text-align: center; + height: 26px; + width: 26px; + + &:first-child { + border-left: 1px solid var(--devui-line, #adb0b8); + } + + &:hover { + background-color: var(--devui-list-item-hover-bg, #f2f5fc); + color: var(--devui-list-item-hover-text, #526ecc); + } + + &.choosed { + color: var(--devui-list-item-active-text, #ffffff); + background-color: var(--devui-list-item-active-bg, #5e7ce0) !important; + cursor: auto !important; + } + } + } + + .config-item-words { + color: $devui-text; + padding-left: 8px; + font-size: $devui-font-size; + margin-top: 4px; + } + + .config-item-words:hover { + background-color: $devui-area; + cursor: pointer; + } +} diff --git a/devui/pagination/src/pagination.tsx b/devui/pagination/src/pagination.tsx index 9962994c..35387d36 100644 --- a/devui/pagination/src/pagination.tsx +++ b/devui/pagination/src/pagination.tsx @@ -1,36 +1,164 @@ -import { defineComponent, ref } from 'vue' -import { ComponentProps, componentProps } from './types' +import { defineComponent, computed, ref, onMounted, onUnmounted, nextTick } from 'vue' +import { ComponentProps, componentProps } from './use-pagination' +import { handlePages, liteSelectOptions } from './utils' import './pagination.scss' export default defineComponent({ - name: 'd-pagination', + name: 'DPagination', props: componentProps, - emits: ['pageIndexChange', 'pageSizeChange'], + emits: ['pageIndexChange', 'pageSizeChange', 'update:pageSize', 'update:pageIndex'], setup(props: ComponentProps, { emit }) { - console.log(props, 'props') + const isShowConfig = ref(false) + const paginationConfig = ref(null) + const changeConfigDispaly = () => { + isShowConfig.value = !isShowConfig.value + } - let jumpPage = '1' - const jumpPageChange = (val: string) => { - console.log('jumpPageChange跳转' + val) + const clickCallback = (e: Event) => { + if (isShowConfig.value && paginationConfig.value && paginationConfig.value !== e.target) { + isShowConfig.value = false + } } + onMounted(() => { + if (props.lite && props.haveConfigMenu) { + document.addEventListener('click', clickCallback) + } + }) + onUnmounted(() => { + if (props.lite && props.haveConfigMenu) { + document.removeEventListener('click', clickCallback) + } + }) + + // 页码较多时,计算中间的显示页码的起始页码数 + const showPageNum = computed(() => handlePages(cursor.value, props.maxItems, totalPages.value)) - return { - jumpPage, - jumpPageChange + // 极简模式下,可选的下拉选择页码 + const litePageOptions = computed(() => { + return liteSelectOptions(totalPages.value) + }) + + // 当前页码 + const cursor = computed({ + get() { + if (!props.showTruePageIndex && props.pageIndex > totalPages.value) { + return totalPages.value || 1 + } + return props.pageIndex || 1 + }, + set(val: number) { + emit('update:pageIndex', val) + } + }) + const changePageNo = ref(props.pageSize) + // 输入框显示的页码 + const inputPageNum = computed({ + get() { + return props.pageIndex + }, + set(val: number) { + changePageNo.value = val + } + }) + // 每页显示最大条目数量 + const currentPageSize = computed({ + get() { + return props.pageSize + }, + set(val: number) { + emit('update:pageSize', val) + } + }) + // 总页数 + const totalPages = computed(() => { + return Math.ceil(props.total / props.pageSize) + }) + + const changeCursorEmit = (val: number) => { + cursor.value = val + emit('pageIndexChange', val) + } + // 输入跳转页码 + const jumpPageChange = (currentPage: string) => { + const curPage = +currentPage + if (isNaN(curPage) || curPage < 1 || curPage > totalPages.value) { + inputPageNum.value = props.pageIndex + return + } + inputPageNum.value = curPage } + const jump = (e: KeyboardEvent | 'btn') => { + if (e === 'btn' || e.key === 'Enter') { + cursor.value = changePageNo.value + } + } + // 点击页码 + const changeCursor = (pageSize: number) => { + if (isNaN(pageSize)) return + const page = pageSize < 1 ? 1 : pageSize > totalPages.value ? totalPages.value : pageSize | 0 + changeCursorEmit(page) + } + // 上一页 + const prevChange = (page: number) => { + if (cursor.value > 1) { + const toPage = page === -1 ? cursor.value - 1 : page + changeCursorEmit(toPage) + } + } + // 下一页 + const nextChange = (page: number) => { + if (cursor.value < totalPages.value) { + const toPage = page === -1 ? cursor.value + 1 : page + changeCursorEmit(toPage) + } + } + // 每页条数改变 + const pageSizeChange = (value: number) => { + currentPageSize.value = value + // 页数改变后,如果当前页码超出最大页码时修正 + if (props.autoFixPageIndex) { + nextTick(() => { + if (cursor.value > totalPages.value) { + changeCursorEmit(totalPages.value) + } + }) + } + emit('pageSizeChange', value) + } + // 极简模式下的跳转页码 + const litePageIndexChange = (page: {name: string; value: number;}) => { + cursor.value = page.value + emit('pageIndexChange', page.value) + } + + return { + cursor, + totalPages, + prevChange, + nextChange, + jump, + inputPageNum, + jumpPageChange, + currentPageSize, + pageSizeChange, + changeCursor, + showPageNum, + litePageOptions, + litePageIndexChange, + isShowConfig, + paginationConfig, + changeConfigDispaly + } }, render() { const { - pageSize, total, pageSizeOptions, - pageSizeDirection, - pageIndex, - maxItems, + // pageSizeDirection, preLink, nextLink, size, @@ -44,57 +172,179 @@ export default defineComponent({ lite, showPageSelector, haveConfigMenu, - autoFixPageIndex, autoHide, + $slots, - jumpPage, - jumpPageChange + cursor, + totalPages, + prevChange, + nextChange, + jump, + inputPageNum, + jumpPageChange, + currentPageSize, + pageSizeChange, + changeCursor, + showPageNum, + litePageOptions, + litePageIndexChange, + isShowConfig, + changeConfigDispaly } = this return ( -
- - {totalItemText}: {total} + // autoHide为 true 并且 pageSizeOptions最小值 > total 不展示分页 + autoHide && Math.min(...pageSizeOptions) > total + ? null + :
+ { + canChangePageSize && !lite && +
+ +
+ } + { + ((!lite || (lite && showPageSelector)) && canViewTotal) && +
{totalItemText}: {total}
+ } + { + // 极简模式下的选择页码下拉框 + lite && showPageSelector && +
+ +
+ } +
    -
  • - { - preLink - ? - : < - } -
  • -
  • - 1 -
  • -
  • - 2 -
  • -
  • - 3 + {/* 左侧上一页按钮 */} +
  • +
  • -
  • - 4 + { + !lite && + <> + {/* 页码展示 */} + {/* 单独展示第一页 */} +
  • + {1} +
  • + { + // 是否展示第一个 ... + showPageNum[0] > 2 && ( +
  • + ... +
  • + ) + } + { + // 中间显示页码 + (() => { + const list = [] + for(let i = showPageNum[0]; i <= showPageNum[1]; i++) { + list.push( +
  • + {i} +
  • + ) + } + return list + })() + } + { + // 是否展示第二个 ... + showPageNum[1] < totalPages - 1 && ( +
  • + ... +
  • + ) + } + { + // 是否单独展示最后一页 + showPageNum[1] < totalPages && ( +
  • + {totalPages} +
  • + ) + } + { + // 在默认页码超出总页码的时候 + showTruePageIndex && cursor > totalPages && totalPages > 0 && + <> + { + cursor > totalPages + 1 && +
  • + ... +
  • + } +
  • + { cursor } +
  • + + } + + } + {/* 右侧下一页按钮 */} +
  • = totalPages}}> +
  • +
+ { + canJumpPage && !lite && +
+ {goToText} + + -
  • + {/* TODO 加入国际化后,替换为当前语言为中文的时候加上 '页' */} + {goToText === '跳至' && '页'} { - nextLink - ? - : > + showJumpButton && +
    +
    +
    } -
  • - -
    - {goToText} - - -
    +
    + } + { + // 极简模式下是否显示配置 + lite && haveConfigMenu && +
    +
    + +
    + { + isShowConfig && +
    + {$slots.default?.()} + +
    +
    每页条数
    +
    + { + pageSizeOptions.map((v: number) => { + return
    {v}
    + }) + } +
    +
    +
    + } +
    + }
    ) } diff --git a/devui/pagination/src/types.ts b/devui/pagination/src/use-pagination.ts similarity index 68% rename from devui/pagination/src/types.ts rename to devui/pagination/src/use-pagination.ts index 2bc82ee5..c6663598 100644 --- a/devui/pagination/src/types.ts +++ b/devui/pagination/src/use-pagination.ts @@ -1,18 +1,18 @@ -import { PropType, ExtractPropTypes } from "vue"; +import { PropType, ExtractPropTypes } from 'vue'; type AppendToBodyDirection = 'rightDown' | 'rightUp' | 'leftUp' | 'leftDown' | 'centerDown' | 'centerUp'; interface ConnectedPosition { - originX : 'start' | 'center' | 'end'; - originY : 'top' | 'center' | 'bottom'; + originX : 'start' | 'center' | 'end' + originY : 'top' | 'center' | 'bottom' - overlayX : 'start' | 'center' | 'end'; - overlayY : 'top' | 'center' | 'bottom'; + overlayX : 'start' | 'center' | 'end' + overlayY : 'top' | 'center' | 'bottom' - weight ?: number; - offsetX ?: number; - offsetY ?: number; - panelClass ?: string | string[]; + weight ?: number + offsetX ?: number + offsetY ?: number + panelClass ?: string | string[] } type Size = 'lg' | '' | 'sm' @@ -43,10 +43,12 @@ export const componentProps = { default: 10 }, preLink: { - type: String + type: String, + default: '<' }, nextLink: { - type: String + type: String, + default: '>' }, size: { type: String as PropType, @@ -99,8 +101,20 @@ export const componentProps = { autoHide: { type: Boolean, default: false + }, + 'onUpdate:pageIndex': { + type: Function as PropType<(v: number) => void> + }, + 'onUpdate:pageSize': { + type: Function as PropType<(v: number) => void> + }, + 'onPageIndexChange': { + type: Function as PropType<(v: number) => void> + }, + 'onPageSizeChange': { + type: Function as PropType<(v: number) => void> } -} +} as const // 组件props export type ComponentProps = ExtractPropTypes diff --git a/devui/pagination/src/utils.ts b/devui/pagination/src/utils.ts new file mode 100644 index 00000000..095b5a43 --- /dev/null +++ b/devui/pagination/src/utils.ts @@ -0,0 +1,42 @@ +/** + * 功能函数 + */ + +// 处理页码显示 +// 1 ... 5 6 7 8 ... 11 +export const handlePages = (cursor: number, maxItems: number, totalPages: number): number[] => { + const currentPage = cursor // 当前页码 + const maxPages = maxItems // 能显示的最大页码 + if (maxPages >= totalPages) { + return [2, totalPages] + } + + const midPages = maxPages - 2 // 中间显示的页码 + // 1 ... 5 6 7 8 ... 11 + // 获取 5 和 8 + let midStart = currentPage - (midPages >> 1) + let midEnd = currentPage + (midPages - 1 >> 1) + + if (midStart < 2) { + midStart = 2 + midEnd = maxPages - 2 + } + + if (midEnd > totalPages) { + midStart = totalPages - maxPages + 3 + midEnd = totalPages + } + + return [midStart, midEnd] +} + +// 处理极简模式下的页码下拉多选显示 +export function liteSelectOptions(total: number): Array<{name: string; value: number;}> { + return new Array(total || 1).fill(0).map((v: number, index: number) => { + return { + name: `${index + 1}/${total}`, + value: index + 1 + } + }) +} + diff --git a/devui/vue-devui.ts b/devui/vue-devui.ts index fdb05ed3..bbc1f862 100644 --- a/devui/vue-devui.ts +++ b/devui/vue-devui.ts @@ -1,39 +1,22 @@ -import { App } from 'vue'; +import type { App } from 'vue' -// 通用 -import Button from './button'; -import Icon from './icon'; -import Panel from './panel'; +import LoadingInstall, { LoadingService, Loading } from './loading' +import ToastInstall, { ToastService } from './toast' -// 导航 -import Pagination from './pagination'; -import Tabs from './tabs'; +const installs = [ + LoadingInstall, + ToastInstall +] -// 反馈 -import Alert from './alert'; -import Loading from './loading'; - -// 数据录入 -import Checkbox from './checkbox'; -import Radio from './radio'; -import Switch from './switch'; -import TagsInput from './tags-input'; -import TextInput from './text-input'; - -// 数据展示 -import Avatar from './avatar'; -import Carousel from './carousel'; - -function install(app: App): void { - const packages = [ Button, Icon, Panel, Pagination, Tabs, Alert, Loading, Checkbox, Radio, Switch, TagsInput, TextInput, Avatar, Carousel ]; - packages.forEach((item: any) => { - if (item.install) { - app.use(item); - } else if (item.name) { - app.component(item.name, item); - } - }); +export { + LoadingService, + Loading, + ToastService } -export { Button, Icon, Panel, Pagination, Tabs, Alert, Loading, Checkbox, Radio, Switch, TagsInput, TextInput, Avatar, Carousel }; -export default { install, version: '0.0.1' }; \ No newline at end of file +export default { + version: '0.0.1', + install(app: App) { + installs.forEach((p) => app.use(p as any)) + } +} diff --git a/sites/.vitepress/config/sidebar.ts b/sites/.vitepress/config/sidebar.ts index c77ed891..6f31b891 100644 --- a/sites/.vitepress/config/sidebar.ts +++ b/sites/.vitepress/config/sidebar.ts @@ -11,14 +11,10 @@ const sidebar = { }, { text: '导航', -<<<<<<< HEAD children: [ { text: 'Pagination 分页', link: '/components/pagination/' }, { text: 'Tabs 选项卡切换', link: '/components/tabs/' }, ] -======= - children: [{ text: 'Tabs 选项卡切换', link: '/components/tabs/' }] ->>>>>>> dev }, { text: '反馈', diff --git a/sites/components/pagination/index.md b/sites/components/pagination/index.md index 5e4e3977..28e9fe4d 100644 --- a/sites/components/pagination/index.md +++ b/sites/components/pagination/index.md @@ -10,140 +10,249 @@ ### 基本用法 **size = 'sm'** + + **size = 'md'** + + **size = 'lg'** + + **Custom Style** + ```html - - - ``` ### 极简模式 极简模式适用于一些有大量信息的页面,可以简化页面的复杂度。 +**Simple Mode** + + + + +**Super Simple Mode** + + + + +**[haveConfigMenu] = "true"** + + +
    +
    show field
    +
    setting
    +
    +
    +
    display method
    +
    + + +
    +
    +
    + ### 多种配置 支持设置输入跳转、显示跳转按钮;设置pageSize等功能。 +
    + + + +
    + + + +
    + + ### 特殊情况 特殊场景下分页器的显示。 +
    +When the value of pageIndex exceeds the maximum page number, enable showTruePageIndex to display the value of pageIndex +
    + + + + +
    +When the value of pageIndex exceeds the maximum page number, the showTruePageIndex function is disabled and only the maximum page number is displayed. +
    + + + + +
    Default Mode
    + + + + +
    + total = 0 + total = 5 + total = 15 +
    + +
    Simple Mode
    + + + +
    + total = 0 + total = 20 + total = 30000 + total = 100000 + index = 2 + index = 3 +