laravel +vite+ vue+ tailwindcss paginator分页

创建TS文件 UrlWindow.ts

export class Paginator {
    currentPage: number
    lastPage: number
    query: Object = {}
    path: string = '/'
    fragment: string = ''
    onEachSide: number = 3
    pageName: string = 'page'
    perPage: number = 15
    options: object = {}
    total: number = 0
    items = []

    constructor(paginate: object, options: object = {}) {
        console.log(paginate)
        let total = paginate['total']
        let perPage = paginate['per_page']
        let items = paginate['data']
        this.query = this.getUrlParams(window.location.href)
        this.options = options;
        Object.keys(options).forEach(k => {
            this[k] = options[k]
        })
        this.total = total;
        this.perPage = perPage;
        this.lastPage = paginate['last_page'];//Math.max(Math.ceil(total / perPage), 1);
        this.path = paginate['path'];//this.path !== '/' ? this.path.trim().replace(/^\/|\/$/g, '') : this.path;
        this.currentPage = paginate['current_page']
        this.items = items;
    }

    getUrlParams(url: string) {
        console.log(url)
        // 通过 ? 分割获取后面的参数字符串
        const urlStr = url.split('?')[1]
        if (urlStr) {
            // 创建空对象存储参数
            let obj = {};
            // 再通过 & 将每一个参数单独分割出来
            let paramsArr = urlStr.split('&')
            for (let i = 0, len = paramsArr.length; i < len; i++) {
                // 再通过 = 将每一个参数分割为 key:value 的形式
                let arr = paramsArr[i].split('=')
                obj[arr[0]] = arr[1];
            }
            return obj
        }
        return {}
    }

    hasPages() {
        return this.currentPage != 1 || this.hasMorePages();
    }

    hasMorePages() {
        return this.currentPage < this.lastPage
    }

    getUrlRange(start:number, end:number) {
        const ret = {}
        for (var i = start; i < end + 1; i++) {
            ret[i] = this.url(i)
        }
        return ret
    }
    firstItem() {
        return this.items.length > 0 ? (this.currentPage - 1) * this.perPage + 1 : null;
    }
    arrQuery(parameters: Object) {
        let strs = []
        Object.keys(parameters).forEach(k => {
            strs.push(k + '=' + parameters[k])
        })

        console.log('page ', strs)

        return strs.join('&')
    }

    url(page: any) {
        if (page <= 0) {
            page = 1;
        }
        let parameters = this.query
        parameters['page'] = page;
        return this.path + (this.path.indexOf('?') > -1 ? '&' : '?')
            + this.arrQuery(parameters) + this.buildFragment()
    }


    buildFragment() {
        return this.fragment ? ('#' + this.fragment) : '';
    }

    previousPageUrl() {
        if (this.currentPage > 1) {
            return this.url(this.currentPage - 1);
        }
    }

    nextPageUrl() {
        if (this.hasMorePages()) {
            let url= this.url(this.currentPage + 1);
            return url
        }
    }

    count() {
        return this.items.length;
    }

    onFirstPage()
    {
        return this.currentPage <= 1;
    }

    lastItem()
    {
        return this.items.length > 0 ? this.firstItem() + this.count() - 1 : null;
    }

}
export class UrlWindow {
    paginator: Paginator
    constructor(paginator: Paginator) {
        this.paginator = paginator
    }

    objectAssign(obj1, obj2) {
        const obj3 = {};
        Object.keys(obj1).forEach(key => {
            obj3[key] = obj1[key];
        });
        Object.keys(obj2).forEach(key => {
            obj3[key] = obj2[key];
        });
        return obj3
    }

    hasPages() {
        return this.paginator.lastPage > 1
    }

    currentPage() {
        return this.paginator.currentPage
    }
    lastPage() {
        return this.paginator.lastPage
    }
    getSliderTooCloseToBeginning($window, $onEachSide) {
        return {
            'first': this.paginator.getUrlRange(1, $window + $onEachSide),
            'slider': null,
            'last': this.getFinish(),
        };
    }
    getFinish() {
        return this.paginator.getUrlRange(
            this.lastPage() - 1,
            this.lastPage()
        );
    }

    getSliderTooCloseToEnding($window, $onEachSide) {
        let $last = this.paginator.getUrlRange(
            this.lastPage() - ($window + ($onEachSide - 1)),
            this.lastPage()
        );

        return {
            'first': this.getStart(),
            'slider': null,
            'last': $last,
        };
    }
    getStart() {
        return this.paginator.getUrlRange(1, 2);
    }
    getFullSlider($onEachSide) {
        return {
            'first': this.getStart(),
            'slider': this.getAdjacentUrlRange($onEachSide),
            'last': this.getFinish(),
        };
    }
    getAdjacentUrlRange($onEachSide) {
        return this.paginator.getUrlRange(
            this.currentPage() - $onEachSide,
            this.currentPage() + $onEachSide
        );
    }
    getUrlSlider($onEachSide: number) {
        let $window = $onEachSide + 4;

        if (!this.hasPages()) {
            return { 'first': null, 'slider': null, 'last': null };
        }

        // If the current page is very close to the beginning of the page range, we will
        // just render the beginning of the page range, followed by the last 2 of the
        // links in this list, since we will not have room to create a full slider.
        if (this.currentPage() <= $window) {
            return this.getSliderTooCloseToBeginning($window, $onEachSide);
        }

        // If the current page is close to the ending of the page range we will just get
        // this first couple pages, followed by a larger window of these ending pages
        // since we're too close to the end of the list to create a full on slider.
        else if (this.currentPage() > (this.lastPage() - $window)) {
            return this.getSliderTooCloseToEnding($window, $onEachSide);
        }

        // If we have enough room on both sides of the current page to build a slider we
        // will surround it with both the beginning and ending caps, with this window
        // of pages in the middle providing a Google style sliding paginator setup.
        return this.getFullSlider($onEachSide);
    }



    getSmallSlider() {
        return {
            'first': this.paginator.getUrlRange(1, this.lastPage()),
            'slider': null,
            'last': null,
        };
    }

    elements() {
        let $window = this.get();

        let ret = [
            $window['first'],
            Array.isArray($window['slider']) ? '...' : null,
            $window['slider'],
            Array.isArray($window['last']) ? '...' : null,
            $window['last'],
        ];
        let ret2 = [];
        console.log('ret ', ret)
        ret.forEach(v => {
            if (v !== null) {
                ret2.push(v)
            }
        })
        return ret2
    }

    get() {
        let $onEachSide = this.paginator.onEachSide;

        if (this.paginator.lastPage < ($onEachSide * 2) + 8) {
            console.log('get small slider')
            return this.getSmallSlider();
        }

        return this.getUrlSlider($onEachSide);
    }
}

vue分页组件Paginate.vue

<script setup>
import { computed } from 'vue';
import { Link } from '@inertiajs/vue3';
import { stringify } from 'qs';

import { UrlWindow, Paginator } from '@/Components/UrlWindow'
import { isObject, isString } from '@vue/shared';


const props = defineProps({
    paginate: Object
});

const paginator = new Paginator(props.paginate)
const UW = new UrlWindow(paginator)

const elements = UW.elements()
console.log('elements:', elements)
</script>

<template>
    <nav v-if="paginator.hasPages()" role="navigation" aria-label="分页导航" class="flex items-center justify-between">
        <div class="flex justify-between flex-1 sm:hidden">
            <span v-if="paginator.onFirstPage()"
                class="relative inline-flex items-center px-4 py-2 text-sm font-medium text-gray-500 bg-white border border-gray-300 cursor-default leading-5 rounded-md">
                上一页
            </span>
            <a v-else :href="paginator.previousPageUrl()"
                class="relative inline-flex items-center px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 leading-5 rounded-md hover:text-gray-500 focus:outline-none focus:ring ring-gray-300 focus:border-blue-300 active:bg-gray-100 active:text-gray-700 transition ease-in-out duration-150">
                上一页
            </a>

            <a v-if="paginator.hasMorePages()" :href="paginator.nextPageUrl()"
                class="relative inline-flex items-center px-4 py-2 ml-3 text-sm font-medium text-gray-700 bg-white border border-gray-300 leading-5 rounded-md hover:text-gray-500 focus:outline-none focus:ring ring-gray-300 focus:border-blue-300 active:bg-gray-100 active:text-gray-700 transition ease-in-out duration-150">
                下一页
            </a>
            <span v-else
                class="relative inline-flex items-center px-4 py-2 ml-3 text-sm font-medium text-gray-500 bg-white border border-gray-300 cursor-default leading-5 rounded-md">
                下一页
            </span>
        </div>

        <div class="hidden sm:flex-1 sm:flex sm:items-center sm:justify-between">
            <div>
                <p class="text-sm text-gray-700 leading-5">
                    显示
                    <span v-if="paginator.firstItem()">
                        <span class="font-medium">{{ paginator.firstItem() }}</span>
                        到
                        <span class="font-medium">{{ paginator.lastItem() }}</span>
                    </span>

                    <span v-else>
                        {{ paginator.count() }}
                    </span>
                    of
                    <span class="font-medium">{{ paginator.total }}</span>
                    结果
                </p>
            </div>

            <div>
                <span class="relative z-0 inline-flex shadow-sm rounded-md">
                    <span v-if="paginator.onFirstPage()" aria-disabled="true" aria-label="上一页">
                        <span
                            class="relative inline-flex items-center px-2 py-2 text-sm font-medium text-gray-500 bg-white border border-gray-300 cursor-default rounded-l-md leading-5"
                            aria-hidden="true">
                            <svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
                                <path fill-rule="evenodd"
                                    d="M12.707 5.293a1 1 0 010 1.414L9.414 10l3.293 3.293a1 1 0 01-1.414 1.414l-4-4a1 1 0 010-1.414l4-4a1 1 0 011.414 0z"
                                    clip-rule="evenodd" />
                            </svg>
                        </span>
                    </span>
                    <a v-else :href="paginator.previousPageUrl()" rel="prev"
                        class="relative inline-flex items-center px-2 py-2 text-sm font-medium text-gray-500 bg-white border border-gray-300 rounded-l-md leading-5 hover:text-gray-400 focus:z-10 focus:outline-none focus:ring ring-gray-300 focus:border-blue-300 active:bg-gray-100 active:text-gray-500 transition ease-in-out duration-150"
                        aria-label="{{ __('pagination.previous') }}">
                        <svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
                            <path fill-rule="evenodd"
                                d="M12.707 5.293a1 1 0 010 1.414L9.414 10l3.293 3.293a1 1 0 01-1.414 1.414l-4-4a1 1 0 010-1.414l4-4a1 1 0 011.414 0z"
                                clip-rule="evenodd" />
                        </svg>
                    </a>

                    <template v-for="element in elements">
                        <template v-if="isString(element)">
                            <span aria-disabled="true">
                                <span
                                    class="relative inline-flex items-center px-4 py-2 -ml-px text-sm font-medium text-gray-700 bg-white border border-gray-300 cursor-default leading-5">
                                    {{ element }}
                                </span>
                            </span>
                        </template>

                        <template v-if="isObject(element)">
                            <template v-for="(url, page) in element">
                                <span v-if="paginator.currentPage == page" aria-current="page">
                                    <span
                                        class="relative inline-flex items-center px-4 py-2 -ml-px text-sm font-medium text-gray-500 bg-white border border-gray-300 cursor-default leading-5">
                                        {{ page }}</span>
                                </span>
                                <a v-else :href="url"
                                    class="relative inline-flex items-center px-4 py-2 -ml-px text-sm font-medium text-gray-700 bg-white border border-gray-300 leading-5 hover:text-gray-500 focus:z-10 focus:outline-none focus:ring ring-gray-300 focus:border-blue-300 active:bg-gray-100 active:text-gray-700 transition ease-in-out duration-150"
                                    aria-label="">
                                    {{ page }}
                                </a>
                            </template>
                        </template>
                    </template>
                    <a v-if="paginator.hasMorePages" :href="paginator.nextPageUrl()" rel="next"
                        class="relative inline-flex items-center px-2 py-2 -ml-px text-sm font-medium text-gray-500 bg-white border border-gray-300 rounded-r-md leading-5 hover:text-gray-400 focus:z-10 focus:outline-none focus:ring ring-gray-300 focus:border-blue-300 active:bg-gray-100 active:text-gray-500 transition ease-in-out duration-150"
                        aria-label="下一页">
                        <svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
                            <path fill-rule="evenodd"
                                d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z"
                                clip-rule="evenodd" />
                        </svg>
                    </a>
                    <span v-else aria-disabled="true" aria-label="下一页">
                        <span
                            class="relative inline-flex items-center px-2 py-2 -ml-px text-sm font-medium text-gray-500 bg-white border border-gray-300 cursor-default rounded-r-md leading-5"
                            aria-hidden="true">
                            <svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
                                <path fill-rule="evenodd"
                                    d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z"
                                    clip-rule="evenodd" />
                            </svg>
                        </span>
                    </span>
                </span>
            </div>
        </div>
    </nav>
</template>

控制器ArticleController.php

<?php

namespace App\Http\Controllers;

use Inertia\Inertia;
use App\Models\Article;
use Illuminate\Http\Request;
use Inertia\Response;
use Illuminate\Http\RedirectResponse;
use Illuminate\Support\Facades\Redirect;

class ArticleController extends Controller
{
    /**
     * Display the user's profile form.
     */
    public function index(): Response
    {
        $paginate = Article::orderBy('updated_at', 'desc')->paginate(1)->toArray();
        return Inertia::render('Article/Index', [
            'paginate' => $paginate,
        ]);
    }
}


   引用

<script setup>

import Paginate from '@/Components/Paginate.vue';

const props = defineProps(['paginate']);

</script>

<template>
    ......
    <paginate :paginate="paginate"></paginate>
    ......
</template>

效果

------------------------------------------------------

已经封装成组件vue-laravel-ui发布到npm,使用更方便

npm i vue-laravel-ui

// 页面代码

<script setup>

import {Pagination} from 'vue-laravel-ui'
const props = defineProps(['paginate']);

</script>

<template>
     // .....
     <Pagination :paginate="paginate"></Pagination>
    //.....
</template>
// 控制器传分页参数

    public function index(): Response
    {
        $paginate = Article::orderBy('updated_at', 'desc')->paginate(1)->toArray();
        return Inertia::render('Article/Index', [
            'paginate' => $paginate, // 这里传参
        ]);
    }

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值