feat: add vue use and pagination

This commit is contained in:
2025-09-13 00:31:33 +08:00
parent 3f3fa302ca
commit 74b5bca9c6
9 changed files with 510 additions and 551 deletions

0
src/apis/.gitkeep Normal file
View File

View File

@@ -1,13 +0,0 @@
import http from "@/utils/http";
const url = "";
const data = {};
export const exampleApi = () => {
return http({
url,
method: "POST",
data,
});
};

0
src/components/.gitkeep Normal file
View File

View File

@@ -1,10 +0,0 @@
<script lang="ts" setup>
</script>
<template>
<div>
</div>
</template>
<style scoped>
</style>

86
src/pages/HelpPage.vue Normal file
View File

@@ -0,0 +1,86 @@
<script lang="ts" setup>
import { onMounted, ref } from "vue";
import { range } from "radash";
import { usePagination } from "@/utils/pagination";
const merchandise = ref([
{
id: 1,
name: "商品1",
},
]);
const {
currentPage,
pageSize,
nextPage,
prevPage,
changePage,
pageNumbers,
totalPages,
getPaginatedData,
} = usePagination(() => merchandise.value);
const handlePageChange = (page: number) => {
changePage(page);
};
onMounted(async () => {
await new Promise(async (resolve, reject) => {
setTimeout(() => {
merchandise.value = Array.from(range(1, 50)).map((item) => ({
id: item,
name: `商品${item}`,
}));
}, 1000);
});
});
</script>
<template>
<div class="hero min-h-screen bg-base-200">
<div class="hero-content text-center">
<div class="max-w-md">
<h1 class="text-5xl font-bold">帮助</h1>
</div>
<div>
<ul>
<li v-for="item in getPaginatedData" :key="item.id">
{{ item.name }}
</li>
</ul>
</div>
<button
class="join-item btn btn-sm"
:disabled="currentPage === 1"
@click="handlePageChange(currentPage - 1)"
>
«
</button>
<button
v-for="page in pageNumbers"
:key="page"
class="join-item btn btn-sm"
:class="{
'btn-active': page === currentPage,
'btn-disabled': page === '...',
}"
@click="page !== '...' && handlePageChange(Number(page))"
>
{{ page }}
</button>
<button
class="join-item btn btn-sm"
:disabled="currentPage === totalPages"
@click="handlePageChange(currentPage + 1)"
>
»
</button>
</div>
</div>
</template>
<style scoped>
/* Component styles */
</style>

View File

@@ -1,6 +1,6 @@
import {
createRouter,
createWebHistory,
createWebHashHistory,
type RouteRecordRaw,
} from "vue-router";
@@ -65,7 +65,7 @@ function generateRoutesFromPages(): RouteRecordRaw[] {
const routes: RouteRecordRaw[] = generateRoutesFromPages();
const router = createRouter({
history: createWebHistory(),
history: createWebHashHistory(),
routes,
});

158
src/utils/pagination.ts Normal file
View File

@@ -0,0 +1,158 @@
import { ref, computed, ComputedRef, Ref } from "vue";
export interface PaginationOptions {
pageSize?: number;
initialPage?: number;
maxVisiblePages?: number;
}
export interface PaginationResult<T> {
// Pagination state
currentPage: Ref<number>;
pageSize: Ref<number>;
totalPages: ComputedRef<number>;
pageNumbers: ComputedRef<(number | string)[]>;
// Pagination methods
changePage: (page: number) => void;
nextPage: () => void;
prevPage: () => void;
// Data methods
getPaginatedData: ComputedRef<T[]>;
setTotalItems: (count: number) => void;
}
/**
* Create pagination functionality for a list of items
*
* @param data Ref to the full data array or a function that returns the full data
* @param options Pagination options
* @returns Pagination state and methods
*/
export function usePagination<T>(
data: Ref<T[]> | (() => T[]),
options: PaginationOptions = {},
): PaginationResult<T> {
// Initialize pagination state
const currentPage = ref(options.initialPage || 1);
const pageSize = ref(options.pageSize || 10);
const maxVisiblePages = options.maxVisiblePages || 5;
// Calculate if data is a ref or a function
const isDataRef = "value" in data;
// Calculate total items
const totalItems = computed(() => {
if (isDataRef) {
return (data as Ref<T[]>).value.length;
} else {
return (data as () => T[])().length;
}
});
// Calculate total pages
const totalPages = computed(() => {
return Math.ceil(totalItems.value / pageSize.value);
});
// Get paginated data
const getPaginatedData = computed(() => {
const start = (currentPage.value - 1) * pageSize.value;
const end = start + pageSize.value;
if (isDataRef) {
return (data as Ref<T[]>).value.slice(start, end);
} else {
const fullData = (data as () => T[])();
return fullData.slice(start, end);
}
});
// Generate array of page numbers for pagination
const pageNumbers = computed(() => {
const pages: (number | string)[] = [];
if (totalPages.value <= maxVisiblePages) {
// Show all pages if total pages are less than max visible
for (let i = 1; i <= totalPages.value; i++) {
pages.push(i);
}
} else {
// Always show first page
pages.push(1);
// Calculate start and end of visible page range
let startPage = Math.max(2, currentPage.value - 1);
let endPage = Math.min(totalPages.value - 1, currentPage.value + 1);
// Adjust if we're near the beginning
if (currentPage.value <= 3) {
endPage = Math.min(totalPages.value - 1, 4);
}
// Adjust if we're near the end
if (currentPage.value >= totalPages.value - 2) {
startPage = Math.max(2, totalPages.value - 3);
}
// Add ellipsis if needed before visible pages
if (startPage > 2) {
pages.push("...");
}
// Add visible page numbers
for (let i = startPage; i <= endPage; i++) {
pages.push(i);
}
// Add ellipsis if needed after visible pages
if (endPage < totalPages.value - 1) {
pages.push("...");
}
// Always show last page
pages.push(totalPages.value);
}
return pages;
});
// Change page
const changePage = (page: number) => {
if (page >= 1 && page <= totalPages.value) {
currentPage.value = page;
}
};
// Next page
const nextPage = () => {
if (currentPage.value < totalPages.value) {
currentPage.value++;
}
};
// Previous page
const prevPage = () => {
if (currentPage.value > 1) {
currentPage.value--;
}
};
// Set total items (useful for API pagination)
const setTotalItems = (count: number) => {
totalItems.value = count;
};
return {
currentPage,
pageSize,
totalPages,
pageNumbers,
changePage,
nextPage,
prevPage,
getPaginatedData,
setTotalItems,
};
}