更新列表刷新组件
This commit is contained in:
@@ -47,14 +47,16 @@
|
||||
height: refresherThreshold + 'px'
|
||||
}"
|
||||
>
|
||||
<cl-loading
|
||||
v-if="refresherStatus === 'refreshing'"
|
||||
:size="28"
|
||||
:pt="{
|
||||
className: 'mr-2'
|
||||
}"
|
||||
></cl-loading>
|
||||
<cl-text> {{ refresherText }} </cl-text>
|
||||
<slot name="refresher" :status="refresherStatus" :text="refresherText">
|
||||
<cl-loading
|
||||
v-if="refresherStatus === 'refreshing'"
|
||||
:size="28"
|
||||
:pt="{
|
||||
className: 'mr-2'
|
||||
}"
|
||||
></cl-loading>
|
||||
<cl-text> {{ refresherText }} </cl-text>
|
||||
</slot>
|
||||
</view>
|
||||
|
||||
<view
|
||||
@@ -122,6 +124,7 @@
|
||||
</view>
|
||||
|
||||
<cl-empty v-if="noData" :fixed="false"></cl-empty>
|
||||
<cl-back-top :top="scrollTop" v-if="showBackTop" @tap="scrollToTop"></cl-back-top>
|
||||
</scroll-view>
|
||||
|
||||
<view
|
||||
@@ -142,30 +145,16 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, getCurrentInstance, onMounted, ref, watch, type PropType } from "vue";
|
||||
import type { ClListViewItem, PassThroughProps } from "../../types";
|
||||
import { computed, getCurrentInstance, nextTick, onMounted, ref, watch, type PropType } from "vue";
|
||||
import type {
|
||||
ClListViewItem,
|
||||
ClListViewGroup,
|
||||
ClListViewVirtualItem,
|
||||
PassThroughProps,
|
||||
ClListViewRefresherStatus
|
||||
} from "../../types";
|
||||
import { isApp, isDark, isEmpty, parseClass, parsePt } from "@/cool";
|
||||
|
||||
// 定义虚拟列表项
|
||||
type VirtualItem = {
|
||||
// 每一项的唯一标识符,用于v-for的key
|
||||
key: string;
|
||||
// 项目类型:header表示分组头部,item表示列表项
|
||||
type: "header" | "item";
|
||||
// 在整个列表中的索引号
|
||||
index: number;
|
||||
// 该项距离列表顶部的像素距离
|
||||
top: number;
|
||||
// 该项的高度,header和item可以不同
|
||||
height: number;
|
||||
// 该项的具体数据
|
||||
data: ClListViewItem;
|
||||
};
|
||||
|
||||
type Group = {
|
||||
index: string;
|
||||
children: ClListViewItem[];
|
||||
};
|
||||
import { t } from "@/locale";
|
||||
|
||||
defineOptions({
|
||||
name: "cl-list-view"
|
||||
@@ -177,11 +166,18 @@ defineSlots<{
|
||||
// 分组头部插槽
|
||||
header(props: { index: string }): any;
|
||||
// 列表项插槽
|
||||
item(props: { data: ClListViewItem; item: VirtualItem; value: any | null; index: number }): any;
|
||||
item(props: {
|
||||
data: ClListViewItem;
|
||||
item: ClListViewVirtualItem;
|
||||
value: any | null;
|
||||
index: number;
|
||||
}): any;
|
||||
// 底部插槽
|
||||
bottom(): any;
|
||||
// 索引插槽
|
||||
index(props: { index: string }): any;
|
||||
// 下拉刷新插槽
|
||||
refresher(props: { status: ClListViewRefresherStatus; text: string }): any;
|
||||
}>();
|
||||
|
||||
const props = defineProps({
|
||||
@@ -248,7 +244,7 @@ const props = defineProps({
|
||||
// 下拉刷新触发距离,相当于下拉内容高度
|
||||
refresherThreshold: {
|
||||
type: Number,
|
||||
default: 45
|
||||
default: 50
|
||||
},
|
||||
// 下拉刷新区域背景色
|
||||
refresherBackground: {
|
||||
@@ -258,17 +254,22 @@ const props = defineProps({
|
||||
// 下拉刷新默认文案
|
||||
refresherDefaultText: {
|
||||
type: String,
|
||||
default: "下拉刷新"
|
||||
default: () => t("下拉刷新")
|
||||
},
|
||||
// 释放刷新文案
|
||||
refresherPullingText: {
|
||||
type: String,
|
||||
default: "释放立即刷新"
|
||||
default: () => t("释放立即刷新")
|
||||
},
|
||||
// 正在刷新文案
|
||||
refresherRefreshingText: {
|
||||
type: String,
|
||||
default: "加载中"
|
||||
default: () => t("加载中")
|
||||
},
|
||||
// 是否显示回到顶部按钮
|
||||
showBackTop: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
});
|
||||
|
||||
@@ -317,9 +318,9 @@ const hasIndex = computed(() => {
|
||||
});
|
||||
|
||||
// 计算属性:将原始数据按索引分组
|
||||
const data = computed<Group[]>(() => {
|
||||
const data = computed<ClListViewGroup[]>(() => {
|
||||
// 初始化分组数组
|
||||
const group: Group[] = [];
|
||||
const group: ClListViewGroup[] = [];
|
||||
|
||||
// 遍历原始数据,按index字段进行分组
|
||||
props.data.forEach((item) => {
|
||||
@@ -334,7 +335,7 @@ const data = computed<Group[]>(() => {
|
||||
group.push({
|
||||
index: item.index ?? "",
|
||||
children: [item]
|
||||
} as Group);
|
||||
} as ClListViewGroup);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -347,9 +348,9 @@ const indexList = computed<string[]>(() => {
|
||||
});
|
||||
|
||||
// 计算属性:将分组数据扁平化为虚拟列表项数组
|
||||
const virtualItems = computed<VirtualItem[]>(() => {
|
||||
const virtualItems = computed<ClListViewVirtualItem[]>(() => {
|
||||
// 初始化虚拟列表数组
|
||||
const items: VirtualItem[] = [];
|
||||
const items: ClListViewVirtualItem[] = [];
|
||||
|
||||
// 初始化顶部位置,考虑预留空间
|
||||
let top = props.topHeight;
|
||||
@@ -415,7 +416,7 @@ const targetScrollTop = ref(0);
|
||||
const scrollerHeight = ref(0);
|
||||
|
||||
// 计算属性:获取当前可见区域的列表项
|
||||
const visibleItems = computed<VirtualItem[]>(() => {
|
||||
const visibleItems = computed<ClListViewVirtualItem[]>(() => {
|
||||
// 如果虚拟列表为空,返回空数组
|
||||
if (isEmpty(virtualItems.value)) {
|
||||
return [];
|
||||
@@ -434,7 +435,7 @@ const visibleItems = computed<VirtualItem[]>(() => {
|
||||
const viewportBottom = scrollTop.value + scrollerHeight.value + bufferHeight;
|
||||
|
||||
// 初始化可见项目数组
|
||||
const visible: VirtualItem[] = [];
|
||||
const visible: ClListViewVirtualItem[] = [];
|
||||
|
||||
// 使用二分查找优化查找起始位置
|
||||
let startIndex = 0;
|
||||
@@ -599,7 +600,7 @@ function onScrollEnd(e: UniScrollEvent) {
|
||||
}
|
||||
|
||||
// 行点击事件处理函数
|
||||
function onItemTap(item: VirtualItem) {
|
||||
function onItemTap(item: ClListViewVirtualItem) {
|
||||
emit("item-tap", item.data);
|
||||
}
|
||||
|
||||
@@ -619,12 +620,13 @@ function onIndexChange(index: number) {
|
||||
|
||||
// 下拉刷新事件处理函数
|
||||
function onRefresherPulling(e: UniRefresherEvent) {
|
||||
if (e.detail.dy > props.refresherThreshold * 1.5) {
|
||||
if (e.detail.dy > props.refresherThreshold) {
|
||||
refresherStatus.value = "pulling";
|
||||
}
|
||||
emit("refresher-pulling", e);
|
||||
}
|
||||
|
||||
// 下拉刷新事件处理函数
|
||||
function onRefresherRefresh(e: UniRefresherEvent) {
|
||||
refresherStatus.value = "refreshing";
|
||||
refreshTriggered.value = true;
|
||||
@@ -632,16 +634,27 @@ function onRefresherRefresh(e: UniRefresherEvent) {
|
||||
emit("pull", e);
|
||||
}
|
||||
|
||||
// 恢复下拉刷新
|
||||
function onRefresherRestore(e: UniRefresherEvent) {
|
||||
refresherStatus.value = "default";
|
||||
emit("refresher-restore", e);
|
||||
}
|
||||
|
||||
// 停止下拉刷新
|
||||
function onRefresherAbort(e: UniRefresherEvent) {
|
||||
refresherStatus.value = "default";
|
||||
emit("refresher-abort", e);
|
||||
}
|
||||
|
||||
// 滚动到顶部
|
||||
function scrollToTop() {
|
||||
targetScrollTop.value = 0.01;
|
||||
|
||||
nextTick(() => {
|
||||
targetScrollTop.value = 0;
|
||||
});
|
||||
}
|
||||
|
||||
// 获取滚动容器的高度
|
||||
function getScrollerHeight() {
|
||||
setTimeout(() => {
|
||||
|
||||
Reference in New Issue
Block a user