Files
inquiry_robot/inquiry-backend/src/views/inquiry/components/AdminPagination.vue
T

178 lines
4.3 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<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>