fix: convert apps/website from submodule to regular directory
This commit is contained in:
@@ -0,0 +1,249 @@
|
||||
<script setup lang="ts">
|
||||
const props = defineProps<{
|
||||
total: number;
|
||||
page: number;
|
||||
pageSize: number;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
'update:page': [page: number];
|
||||
'update:pageSize': [size: number];
|
||||
}>();
|
||||
|
||||
const totalPages = computed(() => {
|
||||
if (props.total <= 0) return 1;
|
||||
return Math.max(1, Math.ceil(props.total / props.pageSize));
|
||||
});
|
||||
|
||||
const pageNumbers = computed<(number | string)[]>(() => {
|
||||
const total = totalPages.value;
|
||||
const current = props.page;
|
||||
if (total <= 7) {
|
||||
return Array.from({ length: total }, (_, i) => i + 1);
|
||||
}
|
||||
const pages: (number | string)[] = [1];
|
||||
const start = Math.max(2, current - 1);
|
||||
const end = Math.min(total - 1, current + 1);
|
||||
if (start > 2) pages.push('...');
|
||||
for (let i = start; i <= end; i++) pages.push(i);
|
||||
if (end < total - 1) pages.push('...');
|
||||
pages.push(total);
|
||||
return pages;
|
||||
});
|
||||
|
||||
const canPrev = computed(() => props.page > 1);
|
||||
const canNext = computed(() => props.page < totalPages.value);
|
||||
|
||||
const gotoInput = ref('');
|
||||
|
||||
function goPrev(): void {
|
||||
if (canPrev.value) emit('update:page', props.page - 1);
|
||||
}
|
||||
|
||||
function goNext(): void {
|
||||
if (canNext.value) emit('update:page', props.page + 1);
|
||||
}
|
||||
|
||||
function goToPage(page: number): void {
|
||||
if (page < 1 || page > totalPages.value || page === props.page) return;
|
||||
emit('update:page', page);
|
||||
}
|
||||
|
||||
function onPageSizeChange(event: Event): void {
|
||||
const target = event.target as HTMLSelectElement;
|
||||
const size = Number(target.value);
|
||||
if (!Number.isFinite(size) || size <= 0) return;
|
||||
emit('update:pageSize', size);
|
||||
}
|
||||
|
||||
function onGoto(): void {
|
||||
const value = Number(gotoInput.value);
|
||||
if (!Number.isFinite(value)) return;
|
||||
const clamped = Math.min(totalPages.value, Math.max(1, Math.floor(value)));
|
||||
gotoInput.value = '';
|
||||
goToPage(clamped);
|
||||
}
|
||||
|
||||
function onGotoKeydown(event: KeyboardEvent): void {
|
||||
if (event.key === 'Enter') {
|
||||
event.preventDefault();
|
||||
onGoto();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="pagination">
|
||||
<div class="total-text">
|
||||
总计 <span>{{ total }}</span> 个产品
|
||||
</div>
|
||||
|
||||
<div class="page-buttons">
|
||||
<button
|
||||
type="button"
|
||||
class="nav-btn"
|
||||
:disabled="!canPrev"
|
||||
@click="goPrev"
|
||||
>
|
||||
<i class="fa-solid fa-chevron-left"></i>
|
||||
</button>
|
||||
<button
|
||||
v-for="(item, idx) in pageNumbers"
|
||||
:key="`page-${idx}-${item}`"
|
||||
type="button"
|
||||
class="page-btn"
|
||||
:class="{
|
||||
current: item === page,
|
||||
dots: item === '...',
|
||||
}"
|
||||
:disabled="item === '...'"
|
||||
@click="typeof item === 'number' && goToPage(item)"
|
||||
>
|
||||
{{ item }}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="nav-btn"
|
||||
:disabled="!canNext"
|
||||
@click="goNext"
|
||||
>
|
||||
<i class="fa-solid fa-chevron-right"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="right-controls">
|
||||
<select :value="pageSize" class="size-select" @change="onPageSizeChange">
|
||||
<option :value="12">12 / 页</option>
|
||||
<option :value="20">20 / 页</option>
|
||||
<option :value="40">40 / 页</option>
|
||||
</select>
|
||||
<span class="goto-label">导航至</span>
|
||||
<input
|
||||
v-model="gotoInput"
|
||||
type="number"
|
||||
min="1"
|
||||
:max="totalPages"
|
||||
class="goto-input"
|
||||
@keydown="onGotoKeydown"
|
||||
/>
|
||||
<button type="button" class="goto-btn" @click="onGoto">Go</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.pagination {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
flex-wrap: wrap;
|
||||
gap: 16px;
|
||||
padding: 16px 24px;
|
||||
font-size: 14px;
|
||||
color: #777;
|
||||
}
|
||||
|
||||
.total-text span {
|
||||
font-weight: 600;
|
||||
color: #222;
|
||||
}
|
||||
|
||||
.page-buttons {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.nav-btn,
|
||||
.page-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 6px;
|
||||
border: 1px solid #eee;
|
||||
background: #fff;
|
||||
color: #555;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.nav-btn:hover:not(:disabled),
|
||||
.page-btn:hover:not(.dots):not(.current) {
|
||||
border-color: #ff6a00;
|
||||
color: #ff6a00;
|
||||
}
|
||||
|
||||
.nav-btn:disabled {
|
||||
opacity: 0.4;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.page-btn.current {
|
||||
background: #ff6a00;
|
||||
border-color: #ff6a00;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.page-btn.dots {
|
||||
border-color: transparent;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.right-controls {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.size-select {
|
||||
height: 32px;
|
||||
padding: 0 8px;
|
||||
border: 1px solid #eee;
|
||||
border-radius: 6px;
|
||||
background: #fff;
|
||||
font-size: 14px;
|
||||
color: #555;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.goto-label {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.goto-input {
|
||||
width: 48px;
|
||||
height: 32px;
|
||||
padding: 0 8px;
|
||||
border: 1px solid #eee;
|
||||
border-radius: 6px;
|
||||
background: #fff;
|
||||
font-size: 14px;
|
||||
color: #222;
|
||||
outline: none;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.goto-input:focus {
|
||||
border-color: #ff6a00;
|
||||
}
|
||||
|
||||
.goto-btn {
|
||||
height: 32px;
|
||||
padding: 0 12px;
|
||||
border: 1px solid #ff6a00;
|
||||
border-radius: 6px;
|
||||
background: transparent;
|
||||
color: #ff6a00;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.goto-btn:hover {
|
||||
background: #ff6a00;
|
||||
color: #fff;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user