257 lines
4.6 KiB
Plaintext
257 lines
4.6 KiB
Plaintext
|
|
<template>
|
|||
|
|
<view class="cl-pagination">
|
|||
|
|
<view
|
|||
|
|
class="cl-pagination__prev"
|
|||
|
|
:class="[
|
|||
|
|
{
|
|||
|
|
'is-disabled': value == 1,
|
|||
|
|
'is-dark': isDark
|
|||
|
|
},
|
|||
|
|
pt.item?.className,
|
|||
|
|
pt.prev?.className
|
|||
|
|
]"
|
|||
|
|
@tap="prev"
|
|||
|
|
>
|
|||
|
|
<slot name="prev" :disabled="value == 1">
|
|||
|
|
<cl-icon
|
|||
|
|
:name="pt.prevIcon?.name ?? 'arrow-left-s-line'"
|
|||
|
|
:size="pt.prevIcon?.size"
|
|||
|
|
:color="pt.prevIcon?.color"
|
|||
|
|
:pt="{
|
|||
|
|
className: pt.prevIcon?.className
|
|||
|
|
}"
|
|||
|
|
></cl-icon>
|
|||
|
|
</slot>
|
|||
|
|
</view>
|
|||
|
|
|
|||
|
|
<view
|
|||
|
|
v-for="(item, index) in list"
|
|||
|
|
:key="index"
|
|||
|
|
class="cl-pagination__item"
|
|||
|
|
:class="[
|
|||
|
|
{
|
|||
|
|
'is-active': item == value,
|
|||
|
|
'is-dark': isDark
|
|||
|
|
},
|
|||
|
|
pt.item?.className
|
|||
|
|
]"
|
|||
|
|
@tap="toPage(item)"
|
|||
|
|
>
|
|||
|
|
<cl-text
|
|||
|
|
:pt="{
|
|||
|
|
className: parseClass([
|
|||
|
|
'cl-pagination__item-text',
|
|||
|
|
{
|
|||
|
|
'text-white': item == value
|
|||
|
|
},
|
|||
|
|
pt.itemText?.className
|
|||
|
|
])
|
|||
|
|
}"
|
|||
|
|
>{{ item }}</cl-text
|
|||
|
|
>
|
|||
|
|
</view>
|
|||
|
|
|
|||
|
|
<view
|
|||
|
|
class="cl-pagination__next"
|
|||
|
|
:class="[
|
|||
|
|
{
|
|||
|
|
'is-disabled': value == totalPage,
|
|||
|
|
'is-dark': isDark
|
|||
|
|
},
|
|||
|
|
pt.item?.className,
|
|||
|
|
pt.next?.className
|
|||
|
|
]"
|
|||
|
|
@tap="next"
|
|||
|
|
>
|
|||
|
|
<slot name="next" :disabled="value == totalPage">
|
|||
|
|
<cl-icon
|
|||
|
|
:name="pt.nextIcon?.name ?? 'arrow-right-s-line'"
|
|||
|
|
:size="pt.nextIcon?.size"
|
|||
|
|
:color="pt.nextIcon?.color"
|
|||
|
|
:pt="{
|
|||
|
|
className: pt.nextIcon?.className
|
|||
|
|
}"
|
|||
|
|
></cl-icon>
|
|||
|
|
</slot>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
</template>
|
|||
|
|
|
|||
|
|
<script setup lang="ts">
|
|||
|
|
import type { PassThroughProps } from "../../types";
|
|||
|
|
import { isDark, parseClass, parsePt } from "@/cool";
|
|||
|
|
import { computed, ref, watch } from "vue";
|
|||
|
|
import type { ClIconProps } from "../cl-icon/props";
|
|||
|
|
|
|||
|
|
defineOptions({
|
|||
|
|
name: "cl-pagination"
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
const props = defineProps({
|
|||
|
|
pt: {
|
|||
|
|
type: Object,
|
|||
|
|
default: () => ({})
|
|||
|
|
},
|
|||
|
|
modelValue: {
|
|||
|
|
type: Number,
|
|||
|
|
default: 1
|
|||
|
|
},
|
|||
|
|
total: {
|
|||
|
|
type: Number,
|
|||
|
|
default: 0
|
|||
|
|
},
|
|||
|
|
size: {
|
|||
|
|
type: Number,
|
|||
|
|
default: 10
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
const emit = defineEmits(["update:modelValue", "change"]);
|
|||
|
|
|
|||
|
|
// 透传样式类型定义
|
|||
|
|
type PassThrough = {
|
|||
|
|
className?: string;
|
|||
|
|
item?: PassThroughProps;
|
|||
|
|
itemText?: PassThroughProps;
|
|||
|
|
prev?: PassThroughProps;
|
|||
|
|
prevIcon?: ClIconProps;
|
|||
|
|
next?: PassThroughProps;
|
|||
|
|
nextIcon?: ClIconProps;
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 解析透传样式配置
|
|||
|
|
const pt = computed(() => parsePt<PassThrough>(props.pt));
|
|||
|
|
|
|||
|
|
// 计算总页数,根据总数和每页大小向上取整
|
|||
|
|
const totalPage = computed(() => {
|
|||
|
|
if (props.total == 0) return 1;
|
|||
|
|
|
|||
|
|
return Math.ceil(props.total / props.size);
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 绑定值
|
|||
|
|
const value = ref(props.modelValue);
|
|||
|
|
|
|||
|
|
// 计算分页列表,根据当前页码和总页数生成显示的分页按钮
|
|||
|
|
const list = computed(() => {
|
|||
|
|
const total = totalPage.value;
|
|||
|
|
const list: (number | string)[] = [];
|
|||
|
|
|
|||
|
|
if (total <= 7) {
|
|||
|
|
// 总页数小于等于7,显示所有页码
|
|||
|
|
for (let i = 1; i <= total; i++) {
|
|||
|
|
list.push(i);
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
// 总是显示第一页
|
|||
|
|
list.push(1);
|
|||
|
|
|
|||
|
|
if (value.value <= 4) {
|
|||
|
|
// 当前页在前面: 1 2 3 4 5 ... 100
|
|||
|
|
for (let i = 2; i <= 5; i++) {
|
|||
|
|
list.push(i);
|
|||
|
|
}
|
|||
|
|
list.push("...");
|
|||
|
|
list.push(total);
|
|||
|
|
} else if (value.value >= total - 3) {
|
|||
|
|
// 当前页在后面: 1 ... 96 97 98 99 100
|
|||
|
|
list.push("...");
|
|||
|
|
for (let i = total - 4; i <= total; i++) {
|
|||
|
|
list.push(i);
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
// 当前页在中间: 1 ... 4 5 6 ... 100
|
|||
|
|
list.push("...");
|
|||
|
|
for (let i = value.value - 1; i <= value.value + 1; i++) {
|
|||
|
|
list.push(i);
|
|||
|
|
}
|
|||
|
|
list.push("...");
|
|||
|
|
list.push(total);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return list;
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 跳转到指定页面,处理页码点击事件
|
|||
|
|
function toPage(item: number | string) {
|
|||
|
|
// 忽略省略号点击
|
|||
|
|
if (item == "..." || typeof item !== "number") return;
|
|||
|
|
|
|||
|
|
// 边界检查,确保页码在有效范围内
|
|||
|
|
if (typeof item == "number") {
|
|||
|
|
if (item > totalPage.value) return;
|
|||
|
|
if ((item as number) < 1) return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
value.value = item;
|
|||
|
|
|
|||
|
|
// 触发双向绑定更新和变化事件
|
|||
|
|
emit("update:modelValue", item);
|
|||
|
|
emit("change", item);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 跳转到上一页
|
|||
|
|
function prev() {
|
|||
|
|
toPage(value.value - 1);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 跳转到下一页
|
|||
|
|
function next() {
|
|||
|
|
toPage(value.value + 1);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
watch(
|
|||
|
|
computed(() => props.modelValue),
|
|||
|
|
(val: number) => {
|
|||
|
|
value.value = val;
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
immediate: true
|
|||
|
|
}
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
defineExpose({
|
|||
|
|
prev,
|
|||
|
|
next
|
|||
|
|
});
|
|||
|
|
</script>
|
|||
|
|
|
|||
|
|
<style lang="scss" scoped>
|
|||
|
|
.cl-pagination {
|
|||
|
|
@apply flex flex-row justify-center w-full;
|
|||
|
|
|
|||
|
|
&__prev,
|
|||
|
|
&__next,
|
|||
|
|
&__item {
|
|||
|
|
@apply flex flex-row items-center justify-center bg-surface-100 rounded-lg;
|
|||
|
|
height: 60rpx;
|
|||
|
|
width: 60rpx;
|
|||
|
|
|
|||
|
|
&.is-disabled {
|
|||
|
|
@apply opacity-50;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
&.is-dark {
|
|||
|
|
@apply bg-surface-700;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
&__item {
|
|||
|
|
margin: 0 5rpx;
|
|||
|
|
|
|||
|
|
&.is-active {
|
|||
|
|
@apply bg-primary-500;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
&__prev {
|
|||
|
|
margin-right: 5rpx;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
&__next {
|
|||
|
|
margin-left: 5rpx;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
</style>
|