Added table pagination
This commit is contained in:
1 parent
440b6ef817
commit
d9e1cb1c6f
1 file changed
+92
-5
+92
-5
@@ -1,4 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { computed, ref, watch } from "vue"
|
||||||
import { GET_AUDIO_FILE, GET_FILE } from "@/utils/file"
|
import { GET_AUDIO_FILE, GET_FILE } from "@/utils/file"
|
||||||
|
|
||||||
export type Row = {
|
export type Row = {
|
||||||
@@ -12,9 +13,30 @@ export type Row = {
|
|||||||
type TableProps = {
|
type TableProps = {
|
||||||
rows: Row[]
|
rows: Row[]
|
||||||
data: any
|
data: any
|
||||||
|
pagination?: boolean
|
||||||
|
pageSize?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
const { rows, data } = defineProps<TableProps>()
|
const props = withDefaults(defineProps<TableProps>(), {
|
||||||
|
pagination: false,
|
||||||
|
pageSize: 10,
|
||||||
|
})
|
||||||
|
|
||||||
|
const currentPage = ref(1)
|
||||||
|
const pageSize = computed(() => Math.max(1, Math.floor(props.pageSize)))
|
||||||
|
const pageCount = computed(() =>
|
||||||
|
Math.max(1, Math.ceil(props.data.length / pageSize.value)),
|
||||||
|
)
|
||||||
|
const visibleData = computed(() => {
|
||||||
|
if (!props.pagination) return props.data
|
||||||
|
|
||||||
|
const start = (currentPage.value - 1) * pageSize.value
|
||||||
|
return props.data.slice(start, start + pageSize.value)
|
||||||
|
})
|
||||||
|
|
||||||
|
watch(pageCount, (count) => {
|
||||||
|
if (currentPage.value > count) currentPage.value = count
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -22,7 +44,7 @@ const { rows, data } = defineProps<TableProps>()
|
|||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th
|
<th
|
||||||
v-for="row in rows"
|
v-for="row in props.rows"
|
||||||
:key="row.name"
|
:key="row.name"
|
||||||
:class="row.class"
|
:class="row.class"
|
||||||
:style="{ width: row.width }"
|
:style="{ width: row.width }"
|
||||||
@@ -32,9 +54,16 @@ const { rows, data } = defineProps<TableProps>()
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr v-for="(item, i) in data" :key="i">
|
<tr
|
||||||
<td v-for="(row, j) in rows" :key="j">
|
v-for="(item, i) in visibleData"
|
||||||
<slot :name="row.name" :item="item" :index="i">
|
:key="(currentPage - 1) * pageSize + i"
|
||||||
|
>
|
||||||
|
<td v-for="(row, j) in props.rows" :key="j">
|
||||||
|
<slot
|
||||||
|
:name="row.name"
|
||||||
|
:item="item"
|
||||||
|
:index="props.pagination ? (currentPage - 1) * pageSize + i : i"
|
||||||
|
>
|
||||||
<nuxt-img
|
<nuxt-img
|
||||||
v-if="row.type == 'image'"
|
v-if="row.type == 'image'"
|
||||||
:src="GET_FILE(item[row.name])"
|
:src="GET_FILE(item[row.name])"
|
||||||
@@ -50,6 +79,39 @@ const { rows, data } = defineProps<TableProps>()
|
|||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
<nav
|
||||||
|
v-if="props.pagination && pageCount > 1"
|
||||||
|
class="pagination"
|
||||||
|
aria-label="Table pagination"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
:disabled="currentPage === 1"
|
||||||
|
aria-label="Previous page"
|
||||||
|
@click="currentPage--"
|
||||||
|
>
|
||||||
|
Previous
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
v-for="page in pageCount"
|
||||||
|
:key="page"
|
||||||
|
type="button"
|
||||||
|
:aria-label="`Page ${page}`"
|
||||||
|
:aria-current="currentPage === page ? 'page' : undefined"
|
||||||
|
:class="{ active: currentPage === page }"
|
||||||
|
@click="currentPage = page"
|
||||||
|
>
|
||||||
|
{{ page }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
:disabled="currentPage === pageCount"
|
||||||
|
aria-label="Next page"
|
||||||
|
@click="currentPage++"
|
||||||
|
>
|
||||||
|
Next
|
||||||
|
</button>
|
||||||
|
</nav>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@@ -85,4 +147,29 @@ const { rows, data } = defineProps<TableProps>()
|
|||||||
padding: 5px 0;
|
padding: 5px 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.pagination {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.4rem;
|
||||||
|
margin-top: 0.75rem;
|
||||||
|
|
||||||
|
& button {
|
||||||
|
padding: 0.4rem 0.65rem;
|
||||||
|
border-radius: $border-radius-standard;
|
||||||
|
background-color: $color-button;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
& button:hover:not(:disabled),
|
||||||
|
& button.active {
|
||||||
|
background-color: $color-button-hover;
|
||||||
|
}
|
||||||
|
|
||||||
|
& button:disabled {
|
||||||
|
cursor: default;
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
Reference in new issue
Block a user