178 lines
4.3 KiB
Vue
178 lines
4.3 KiB
Vue
<script setup lang="ts">
|
||
/**
|
||
* 询价后台列表分页条:整组靠右,总数紧挨在「每页」左边,页码在后。
|
||
* 样式对齐 admin-fidelity.css 的 .pagination;翻页只发事件,不请求接口。
|
||
*/
|
||
import { computed } from "vue";
|
||
|
||
const props = withDefaults(
|
||
defineProps<{
|
||
total: number;
|
||
pageNo: number;
|
||
pageSize: number;
|
||
/** 文案:共 N {unit} */
|
||
unit?: string;
|
||
pageSizeOptions?: number[];
|
||
disabled?: boolean;
|
||
}>(),
|
||
{
|
||
unit: "条",
|
||
pageSizeOptions: () => [10, 20, 50, 100],
|
||
disabled: false,
|
||
},
|
||
);
|
||
|
||
const emit = defineEmits<{
|
||
(e: "update:pageNo", v: number): void;
|
||
(e: "update:pageSize", v: number): void;
|
||
(e: "change"): void;
|
||
}>();
|
||
|
||
const totalPages = computed(() => Math.max(1, Math.ceil(Math.max(0, props.total) / Math.max(1, props.pageSize))));
|
||
|
||
/**
|
||
* 页码按钮:两端保留,中间省略。
|
||
* 例:1 … 4 5 6 … 20
|
||
*/
|
||
const pageItems = computed(() => {
|
||
const total = totalPages.value;
|
||
const cur = Math.min(Math.max(1, props.pageNo), total);
|
||
if (total <= 7) {
|
||
return Array.from({ length: total }, (_, i) => i + 1) as Array<number | "ellipsis">;
|
||
}
|
||
const set = new Set<number>();
|
||
set.add(1);
|
||
set.add(total);
|
||
for (let p = cur - 1; p <= cur + 1; p++) {
|
||
if (p >= 1 && p <= total) set.add(p);
|
||
}
|
||
if (cur <= 3) {
|
||
set.add(2);
|
||
set.add(3);
|
||
set.add(4);
|
||
}
|
||
if (cur >= total - 2) {
|
||
set.add(total - 1);
|
||
set.add(total - 2);
|
||
set.add(total - 3);
|
||
}
|
||
const sorted = [...set].sort((a, b) => a - b);
|
||
const out: Array<number | "ellipsis"> = [];
|
||
let prev = 0;
|
||
for (const p of sorted) {
|
||
if (prev && p - prev > 1) out.push("ellipsis");
|
||
out.push(p);
|
||
prev = p;
|
||
}
|
||
return out;
|
||
});
|
||
|
||
function go(page: number) {
|
||
if (props.disabled) return;
|
||
const next = Math.min(Math.max(1, page), totalPages.value);
|
||
if (next === props.pageNo) return;
|
||
emit("update:pageNo", next);
|
||
emit("change");
|
||
}
|
||
|
||
function onSizeChange(e: Event) {
|
||
if (props.disabled) return;
|
||
const v = Number((e.target as HTMLSelectElement).value);
|
||
if (!v || v === props.pageSize) return;
|
||
emit("update:pageSize", v);
|
||
emit("update:pageNo", 1);
|
||
emit("change");
|
||
}
|
||
</script>
|
||
|
||
<template>
|
||
<div class="pagination admin-pagination">
|
||
<div class="admin-pagination__right">
|
||
<span class="admin-pagination__total">
|
||
共 <b>{{ total }}</b> {{ unit }}
|
||
<span v-if="total > 0" class="admin-pagination__range">
|
||
(第 {{ pageNo }} / {{ totalPages }} 页)
|
||
</span>
|
||
</span>
|
||
<label class="admin-pagination__size">
|
||
每页
|
||
<select :value="pageSize" :disabled="disabled" @change="onSizeChange">
|
||
<option v-for="n in pageSizeOptions" :key="n" :value="n">{{ n }}</option>
|
||
</select>
|
||
</label>
|
||
<div class="pages">
|
||
<button type="button" :disabled="disabled || pageNo <= 1" @click="go(pageNo - 1)">上一页</button>
|
||
<template v-for="(item, idx) in pageItems" :key="idx + '-' + item">
|
||
<span v-if="item === 'ellipsis'" class="admin-pagination__ellipsis">…</span>
|
||
<button
|
||
v-else
|
||
type="button"
|
||
:class="{ 'is-active': item === pageNo }"
|
||
:disabled="disabled"
|
||
@click="go(item)"
|
||
>
|
||
{{ item }}
|
||
</button>
|
||
</template>
|
||
<button type="button" :disabled="disabled || pageNo >= totalPages" @click="go(pageNo + 1)">下一页</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<style scoped>
|
||
.admin-pagination__right {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 10px;
|
||
flex-wrap: wrap;
|
||
justify-content: flex-end;
|
||
margin-left: auto;
|
||
}
|
||
|
||
.admin-pagination__total {
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.admin-pagination__size {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 6px;
|
||
color: #475467;
|
||
font-size: 11px;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.admin-pagination__size select {
|
||
height: 28px;
|
||
border: 1px solid var(--line, #e4e7ec);
|
||
border-radius: 6px;
|
||
padding: 0 8px;
|
||
background: #fff;
|
||
font-size: 11px;
|
||
font-weight: 700;
|
||
color: #344054;
|
||
cursor: pointer;
|
||
}
|
||
|
||
.admin-pagination__range {
|
||
color: #98a2b3;
|
||
font-weight: 600;
|
||
margin-left: 4px;
|
||
}
|
||
|
||
.admin-pagination__ellipsis {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
min-width: 20px;
|
||
color: #98a2b3;
|
||
font-weight: 700;
|
||
}
|
||
|
||
.pages button:disabled {
|
||
opacity: 0.45;
|
||
cursor: not-allowed;
|
||
}
|
||
</style>
|