Files
WAI_Project_UNIX/uni_modules/cool-ui/components/cl-pagination/cl-pagination.uvue
2025-10-17 23:23:39 +08:00

257 lines
4.6 KiB
Plaintext
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.

<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>