Browse Source

feat: 增加 pagination 分页组件(#692)

* fix: pagination

* feat: pagination-taro适配

* fix: 分页样式提取

Co-authored-by: yushuang-d <yush9210@163.com>
yushuang-d 4 years ago
parent
commit
daa95889bc

+ 1 - 1
package.json

@@ -129,4 +129,4 @@
     "type": "git",
     "url": "https://github.com/jdf2e/nutui.git"
   }
-}
+}

+ 19 - 8
src/config.json

@@ -134,7 +134,7 @@
           "cName": "单元格组件",
           "type": "component",
           "show": false,
-          "taro":true,
+          "taro": true,
           "exportEmpty": true,
           "desc": "展示分组列表",
           "author": "richard1015"
@@ -207,7 +207,7 @@
           "cName": "布局-Col",
           "type": "component",
           "show": false,
-          "taro":true,
+          "taro": true,
           "exportEmpty": true,
           "desc": "布局组件Col",
           "author": "undo"
@@ -219,7 +219,7 @@
           "cName": "布局-Row",
           "type": "component",
           "show": false,
-          "taro":true,
+          "taro": true,
           "exportEmpty": true,
           "desc": "布局组件Row",
           "author": "undo"
@@ -397,7 +397,7 @@
           "cName": "步骤条子组件",
           "type": "component",
           "show": false,
-          "taro":true,
+          "taro": true,
           "exportEmpty": true,
           "desc": "步骤条子组件",
           "author": "ailululu"
@@ -421,7 +421,7 @@
           "cName": "轮播图子组件",
           "type": "component",
           "show": false,
-          "taro":true,
+          "taro": true,
           "exportEmpty": true,
           "desc": "轮播图子组件",
           "author": "suzigang"
@@ -527,7 +527,7 @@
           "cName": "标签组件",
           "type": "component",
           "show": false,
-          "taro":true,
+          "taro": true,
           "exportEmpty": true,
           "desc": "标签组件",
           "author": "zhenyulei"
@@ -548,7 +548,7 @@
           "sort": 2,
           "cName": "菜单组件",
           "type": "component",
-          "taro":true,
+          "taro": true,
           "exportEmpty": true,
           "show": false,
           "desc": "下拉菜单组件",
@@ -572,7 +572,7 @@
           "cName": "标签栏子组件",
           "type": "component",
           "show": false,
-          "taro":true,
+          "taro": true,
           "exportEmpty": true,
           "desc": "标签栏子组件",
           "author": "Drjingfubo"
@@ -587,6 +587,17 @@
           "show": true,
           "taro": true,
           "author": "szg2008"
+        },
+        {
+          "version": "3.0.0",
+          "name": "Pagination",
+          "type": "component",
+          "cName": "分页",
+          "desc": "当数据量较多时,采用分页的形式分隔长列表。",
+          "sort": 10,
+          "show": true,
+          "taro": true,
+          "author": "yushuang24"
         }
       ]
     },

+ 61 - 0
src/packages/__VUE/pagination/demo.vue

@@ -0,0 +1,61 @@
+<template>
+  <div class="demo">
+    <h2>基础用法</h2>
+    <nut-pagination
+      v-model="currentPage"
+      :total-items="25"
+      :items-per-page="5"
+    />
+    <h2>简单模式</h2>
+    <nut-pagination v-model="currentPage1" :page-count="12" mode="simple" />
+    <h2>显示省略号</h2>
+    <nut-pagination
+      v-model="currentPage2"
+      :total-items="125"
+      :show-page-size="3"
+      force-ellipses
+    />
+    <h2>自定义按钮</h2>
+    <nut-pagination
+      v-model="currentPage3"
+      :total-items="500"
+      :show-page-size="5"
+    >
+      <template #prev-text>
+        <nut-icon name="left" size="10px" />
+      </template>
+      <template #next-text>
+        <nut-icon name="right" size="10px" />
+      </template>
+      <template #page="{ item }">
+        {{ item.number == 3 ? 'hot' : item.text }}
+      </template>
+    </nut-pagination>
+  </div>
+</template>
+
+<script lang="ts">
+import { ref, reactive, toRefs } from 'vue';
+import { createComponent } from '../../utils/create';
+const { createDemo } = createComponent('pagination');
+export default createDemo({
+  setup() {
+    const state = reactive({
+      currentPage: 1,
+      currentPage1: 1,
+      currentPage2: 1,
+      currentPage3: 1
+    });
+    const pageChange = (value) => {
+      console.log(value, 444);
+    };
+
+    return {
+      ...toRefs(state),
+      pageChange
+    };
+  }
+});
+</script>
+
+<style lang="scss" scoped></style>

+ 76 - 0
src/packages/__VUE/pagination/doc.md

@@ -0,0 +1,76 @@
+#  pagination组件
+
+### 介绍
+    
+当数据量较多时,采用分页的形式分隔长列表。
+    
+### 安装
+``` javascript
+import { createApp } from 'vue';
+import { Pagination,Icon } from '@nutui/nutui';
+
+const app = createApp();
+app.use(Pagination).use(Icon);
+```    
+## 代码演示
+    
+### 基础用法
+通过 v-model 来绑定当前页码。
+``` javascript
+<nut-pagination v-model="currentPage" :total-items="25" :items-per-page="5" />
+```  
+### 简单模式
+将 mode 设置为 simple 来切换到简单模式,此时分页器不会展示具体的页码按钮。
+``` javascript
+<nut-pagination v-model="currentPage1" :page-count="12" mode="simple"/>
+```  
+### 显示省略号
+设置 force-ellipses 后会展示省略号按钮,点击后可以快速跳转。
+``` javascript
+<nut-pagination v-model="currentPage2" :total-items="125" :show-page-size="3" force-ellipses/>
+``` 
+### 自定义按钮
+设置 force-ellipses 后会展示省略号按钮,点击后可以快速跳转。
+``` javascript
+<nut-pagination v-model="currentPage3" :total-items="500" :show-page-size="5" >
+    <template #prev-text >
+        <nut-icon name="left" size="10px"/>
+    </template>
+    <template #next-text >
+        <nut-icon name="right" size="10px"/>
+    </template>
+    <template #page="{item}" >{ {item} }</template>
+</nut-pagination>
+``` 
+
+
+    
+## API
+    
+### Props
+    
+| 参数         | 说明                             | 类型   | 默认值           |
+|--------------|-------------------------------- |--------|------------------|
+| v-model      | 当前页码                         | Number | 1                |
+| mode         | 显示模式,可选值为 simple           | String | multi                |
+| prev-text    | 上一页按钮文字                     | String | 上一页               |
+| next-text    | 下一页按钮文字                     | String | 下一页               |
+| page-count   | 总页数                           | String,Number | 传入/根据页数计算 |
+| total-items   | 总记录数                       | String,Number | 0             |
+| items-per-page   | 每页记录数                      | String,Number | 10           |
+| show-page-size   | 显示的页码个数                    | String,Number | 5           |
+| force-ellipses  | 是否显示省略号                  | Boolean | false          |
+    
+### Events
+    
+| 事件名 | 说明           | 回调参数     |
+|--------|----------------|--------------|
+| change  | 页码改变时触发  | value |
+
+### Slots
+    
+| 名称 | 描述          | 参数     |
+|--------|----------------|--------------|
+| prev-text   | 自定义上一页按钮内容 | - |
+| next-text   | 自定义下一页按钮内容 | - |
+| page   | 自定义页码 | - |

+ 51 - 0
src/packages/__VUE/pagination/index.scss

@@ -0,0 +1,51 @@
+.nut-pagination {
+  display: flex;
+  font-size: $pagination-font-size;
+  color: $pagination-color;
+  &-contain {
+    display: flex;
+  }
+  &-simple {
+    height: 39px;
+    width: 124px;
+    line-height: 39px;
+    text-align: center;
+  }
+  &-prev,
+  &-item,
+  &-next {
+    height: 39px;
+    min-width: 39px;
+    flex-shrink: 0;
+    box-sizing: border-box;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    background: $white;
+    border-radius: $pagination-item-border-radius;
+    border: $pagination-item-border-width solid $pagination-item-border-color;
+    cursor: pointer;
+  }
+  &-prev,
+  &-item {
+    border-right: none;
+  }
+  &-prev,
+  &-next {
+    padding: $pagination-prev-next-padding;
+  }
+  .simple-border {
+    border-right: $pagination-item-border-width solid
+      $pagination-item-border-color;
+  }
+  .active {
+    color: $white;
+    border: none;
+    background: $pagination-active-background-color;
+  }
+  .disabled {
+    color: $pagination-disable-color;
+    background-color: $pagination-disable-background-color;
+    cursor: not-allowed;
+  }
+}

+ 161 - 0
src/packages/__VUE/pagination/index.taro.vue

@@ -0,0 +1,161 @@
+<template>
+  <ul class="nut-pagination">
+    <li
+      :class="[
+        'nut-pagination-prev',
+        mode == 'multi' ? '' : 'simple-border',
+        modelValue == 1 ? 'disabled' : ''
+      ]"
+      @click="select(modelValue - 1, true)"
+    >
+      <slot name="prev-text">
+        {{ prevText }}
+      </slot>
+    </li>
+    <div class="nut-pagination-contain" v-if="mode == 'multi'">
+      <li
+        v-for="(item, index) of pages"
+        :key="index + 'pagination'"
+        :class="['nut-pagination-item', item.active ? 'active' : '']"
+        @click="select(item.number, true)"
+      >
+        <slot name="page" :item="item">
+          {{ item.text }}
+        </slot>
+      </li>
+    </div>
+    <div class="nut-pagination-contain" v-if="mode == 'simple'">
+      <li class="nut-pagination-simple">{{ modelValue }}/{{ countRef }}</li>
+    </div>
+    <li
+      :class="['nut-pagination-next', modelValue >= countRef ? 'disabled' : '']"
+      @click="select(modelValue + 1, true)"
+    >
+      <slot name="next-text">
+        {{ nextText }}
+      </slot>
+    </li>
+  </ul>
+</template>
+<script lang="ts">
+import { toRefs, onMounted, watchEffect, computed } from 'vue';
+import { createComponent } from '../../utils/create';
+const { componentName, create } = createComponent('pagination');
+
+export default create({
+  props: {
+    modelValue: {
+      type: Number,
+      default: 1
+    },
+    mode: {
+      type: String,
+      default: 'multi'
+    },
+    prevText: {
+      type: String,
+      default: '上一页'
+    },
+    nextText: {
+      type: String,
+      default: '下一页'
+    },
+    pageCount: {
+      type: [String, Number],
+      default: ''
+    },
+    totalItems: {
+      type: [String, Number],
+      default: '0'
+    },
+    itemsPerPage: {
+      type: [String, Number],
+      default: '10'
+    },
+    showPageSize: {
+      type: [String, Number],
+      default: '5'
+    },
+    forceEllipses: {
+      type: Boolean,
+      default: false
+    }
+  },
+  components: {},
+  emits: ['change', 'update:modelValue'],
+
+  setup(props, { emit }) {
+    const { modelValue, mode, showPageSize, forceEllipses } = toRefs(props);
+
+    //计算页面的数量
+    const countRef = computed(() => {
+      const { pageCount, totalItems, itemsPerPage } = toRefs(props);
+      const num =
+        +pageCount.value || Math.ceil(+totalItems.value / +itemsPerPage.value);
+      return Math.max(1, num);
+    });
+
+    //点击选择page
+    const select = (curPage, isSelect) => {
+      if (curPage > countRef.value || curPage < 1) return;
+      if (curPage != modelValue.value) emit('update:modelValue', curPage);
+      if (isSelect) emit('change', curPage);
+    };
+    //set page 对象
+    const setPage = (number, text, active) => {
+      return { number, text, active };
+    };
+    //生成pages数组,用来遍历
+    const pages = computed(() => {
+      let items = [];
+      const pageCount = countRef.value; //总的页面数量
+      const pageSize = showPageSize.value; //展示的页面个数
+      let startPage = 1;
+      let endPage = pageCount;
+      if (mode.value == 'simple') return;
+      const partialShow = pageCount > pageSize;
+      if (partialShow) {
+        //选中的page在展示的page中间
+        startPage = Math.max(modelValue.value - Math.floor(pageSize / 2), 1);
+        endPage = startPage + pageSize - 1;
+        if (endPage > pageCount) {
+          endPage = pageCount;
+          startPage = endPage - pageSize + 1;
+        }
+      }
+      //遍历生成数组
+      for (var i = startPage; i <= endPage; i++) {
+        const page = setPage(i, i, modelValue.value == i);
+        items.push(page);
+      }
+      //判断是否有折叠
+      if (partialShow && pageSize > 0 && forceEllipses.value) {
+        if (startPage > 1) {
+          const prevPage = setPage(startPage - 1, '...');
+          items.unshift(prevPage);
+        }
+        if (endPage < pageCount) {
+          const nextPage = setPage(endPage + 1, '...');
+          items.push(nextPage);
+        }
+      }
+
+      return items;
+    });
+
+    //监听选中的page变化
+    watchEffect(() => {
+      select(modelValue.value, false);
+    });
+
+    return {
+      modelValue,
+      select,
+      countRef,
+      mode,
+      pages,
+      forceEllipses
+    };
+  }
+});
+</script>

+ 162 - 0
src/packages/__VUE/pagination/index.vue

@@ -0,0 +1,162 @@
+<template>
+  <ul class="nut-pagination">
+    <li
+      :class="[
+        'nut-pagination-prev',
+        mode == 'multi' ? '' : 'simple-border',
+        modelValue == 1 ? 'disabled' : ''
+      ]"
+      @click="select(modelValue - 1, true)"
+    >
+      <slot name="prev-text">
+        {{ prevText }}
+      </slot>
+    </li>
+    <div class="nut-pagination-contain" v-if="mode == 'multi'">
+      <li
+        v-for="(item, index) of pages"
+        :key="index + 'pagination'"
+        :class="['nut-pagination-item', item.active ? 'active' : '']"
+        @click="select(item.number, true)"
+      >
+        <slot name="page" :item="item">
+          {{ item.text }}
+        </slot>
+      </li>
+    </div>
+    <div class="nut-pagination-contain" v-if="mode == 'simple'">
+      <li class="nut-pagination-simple">{{ modelValue }}/{{ countRef }}</li>
+    </div>
+    <li
+      :class="['nut-pagination-next', modelValue >= countRef ? 'disabled' : '']"
+      @click="select(modelValue + 1, true)"
+    >
+      <slot name="next-text">
+        {{ nextText }}
+      </slot>
+    </li>
+  </ul>
+</template>
+<script lang="ts">
+import { toRefs, onMounted, watchEffect, computed } from 'vue';
+import { createComponent } from '../../utils/create';
+const { componentName, create } = createComponent('pagination');
+
+export default create({
+  props: {
+    modelValue: {
+      type: Number,
+      default: 1
+    },
+    mode: {
+      type: String,
+      default: 'multi'
+    },
+    prevText: {
+      type: String,
+      default: '上一页'
+    },
+    nextText: {
+      type: String,
+      default: '下一页'
+    },
+    pageCount: {
+      type: [String, Number],
+      default: ''
+    },
+    totalItems: {
+      type: [String, Number],
+      default: '0'
+    },
+    itemsPerPage: {
+      type: [String, Number],
+      default: '10'
+    },
+    showPageSize: {
+      type: [String, Number],
+      default: '5'
+    },
+    forceEllipses: {
+      type: Boolean,
+      default: false
+    }
+  },
+  components: {},
+
+  emits: ['change', 'update:modelValue'],
+
+  setup(props, { emit }) {
+    const { modelValue, mode, showPageSize, forceEllipses } = toRefs(props);
+
+    //计算页面的数量
+    const countRef = computed(() => {
+      const { pageCount, totalItems, itemsPerPage } = toRefs(props);
+      const num =
+        +pageCount.value || Math.ceil(+totalItems.value / +itemsPerPage.value);
+      return Math.max(1, num);
+    });
+
+    //点击选择page
+    const select = (curPage, isSelect) => {
+      if (curPage > countRef.value || curPage < 1) return;
+      if (curPage != modelValue.value) emit('update:modelValue', curPage);
+      if (isSelect) emit('change', curPage);
+    };
+    //set page 对象
+    const setPage = (number, text, active) => {
+      return { number, text, active };
+    };
+    //生成pages数组,用来遍历
+    const pages = computed(() => {
+      if (mode.value == 'simple') return;
+      let items = [];
+      const pageCount = countRef.value; //总的页面数量
+      const pageSize = showPageSize.value; //展示的页面个数
+      let startPage = 1;
+      let endPage = pageCount;
+      const partialShow = pageCount > pageSize;
+      if (partialShow) {
+        //选中的page在展示的page中间
+        startPage = Math.max(modelValue.value - Math.floor(pageSize / 2), 1);
+        endPage = startPage + pageSize - 1;
+        if (endPage > pageCount) {
+          endPage = pageCount;
+          startPage = endPage - pageSize + 1;
+        }
+      }
+      //遍历生成数组
+      for (var i = startPage; i <= endPage; i++) {
+        const page = setPage(i, i, modelValue.value == i);
+        items.push(page);
+      }
+      //判断是否有折叠
+      if (partialShow && pageSize > 0 && forceEllipses.value) {
+        if (startPage > 1) {
+          const prevPage = setPage(startPage - 1, '...');
+          items.unshift(prevPage);
+        }
+        if (endPage < pageCount) {
+          const nextPage = setPage(endPage + 1, '...');
+          items.push(nextPage);
+        }
+      }
+
+      return items;
+    });
+
+    //监听选中的page变化
+    watchEffect(() => {
+      select(modelValue.value, false);
+    });
+
+    return {
+      modelValue,
+      select,
+      countRef,
+      mode,
+      pages,
+      forceEllipses
+    };
+  }
+});
+</script>

+ 17 - 0
src/packages/styles/variables.scss

@@ -321,5 +321,22 @@ $popover-border-bottom-color: rgba(229, 229, 229, 1) !default;
 $popover-primary-text-color: rgba(51, 51, 51, 1) !default;
 $popover-disable-color: rgba(154, 155, 157, 1) !default;
 
+//分页
+$pagination-color: $primary-color !default;
+$pagination-font-size: $font-size-2 !default;
+$pagination-item-border-color: #e4e7eb !default;
+$pagination-active-background-color: linear-gradient(
+  135deg,
+  rgba(250, 44, 25, 1) 0%,
+  rgba(250, 63, 25, 1) 44.59259259%,
+  rgba(250, 89, 25, 1) 83.40740741%,
+  rgba(250, 100, 25, 1) 100%
+) !default;
+$pagination-disable-color: rgba(116, 116, 116, 0.31) !default;
+$pagination-disable-background-color: #f7f8fa !default;
+$pagination-item-border-width: 1px !default;
+$pagination-item-border-radius: 2px !default;
+$pagination-prev-next-padding: 0 11px !default;
+
 @import './mixins/index';
 @import './animation/index';

+ 2 - 1
src/sites/mobile-taro/vue/src/app.config.ts

@@ -46,7 +46,8 @@ export default {
         'pages/tabbar/index',
         'pages/tab/index',
         'pages/fixednav/index',
-        'pages/elevator/index'
+        'pages/elevator/index',
+        'pages/pagination/index'
       ]
     },
     {

+ 3 - 0
src/sites/mobile-taro/vue/src/nav/pages/pagination/index.config.ts

@@ -0,0 +1,3 @@
+export default {
+  navigationBarTitleText: 'Pagination'
+};

+ 59 - 0
src/sites/mobile-taro/vue/src/nav/pages/pagination/index.vue

@@ -0,0 +1,59 @@
+<template>
+  <div class="demo">
+    <h2>基础用法</h2>
+    <nut-pagination
+      v-model="currentPage"
+      :total-items="25"
+      :items-per-page="5"
+    />
+    <h2>简单模式</h2>
+    <nut-pagination v-model="currentPage1" :page-count="12" mode="simple" />
+    <h2>显示省略号</h2>
+    <nut-pagination
+      v-model="currentPage2"
+      :total-items="125"
+      :show-page-size="3"
+      force-ellipses
+    />
+    <h2>自定义按钮</h2>
+    <nut-pagination
+      v-model="currentPage3"
+      :total-items="500"
+      :show-page-size="5"
+    >
+      <template #prev-text>
+        <nut-icon name="left" size="10px" />
+      </template>
+      <template #next-text>
+        <nut-icon name="right" size="10px" />
+      </template>
+      <template #page="{ item }">
+        {{ item.number == 3 ? 'hot' : item.text }}
+      </template>
+    </nut-pagination>
+  </div>
+</template>
+
+<script lang="ts">
+import { ref, reactive, toRefs, defineComponent } from 'vue';
+export default defineComponent({
+  setup() {
+    const state = reactive({
+      currentPage: 1,
+      currentPage1: 1,
+      currentPage2: 1,
+      currentPage3: 1
+    });
+    const pageChange = (value) => {
+      console.log(value, 444);
+    };
+
+    return {
+      ...toRefs(state),
+      pageChange
+    };
+  }
+});
+</script>
+
+<style lang="scss"></style>