Files
HTFX-CRM-Sales/pages/components/baseTree/list.vue

140 lines
3.1 KiB
Vue
Raw Normal View History

2025-07-07 16:05:18 +08:00
<template>
<view class="VirtualizationList virtualization-list" @scroll.passive="update" >
<component
:is="listTag"
:class="listClass"
>
<template v-for="(info, i) in visibleItems" :key="info.item.$id">
<slot
:item="info.item"
:index="info.index"
:renderIndex="i"
:itemStyle="{ marginBottom: gap + 'px' }"
{{ info.item.text }}
></slot>
</template>
</component>
</view>
</template>
<script lang="ts">
import { defineComponent, PropType, nextTick } from "./vue"
import { obj } from "./types"
export default defineComponent({
props: {
items: { type: Array as PropType<obj[]>, default: () => [] },
enabled: { type: Boolean, default: true },
buffer: {
type: Number,
default: 200,
},
minItemHeight: { type: Number, default: 20 },
prerender: { type: Number, default: 20 },
listTag: { type: String, default: "view" },
listClass: { type: String },
itemClass: { type: String, default: "vl-item" },
gap: { type: Number, default: 0 },
afterCalcTop2: { type: Function as PropType<(top2: number) => number> },
isForceVisible: {
type: Function as PropType<(node: obj, index: number) => boolean>,
},
},
data() {
return {
start: 0,
end: -1,
top: 0,
bottom: 0,
totalHeight: 0,
itemsHeight: <number[]>[],
mountedPromise: new Promise((resolve, reject) => {
this._mountedPromise_resolve = resolve;
}),
};
},
computed: {
visibleItems(): { item: obj; index: number }[] {
const r: { item: obj; index: number }[] = [];
console.log(this.items)
this.items.forEach((item: obj, index: number) => {
if (!this.enabled) {
r.push({ item, index });
} else if (
(index >= this.start && index <= this.end) ||
(this.isForceVisible && this.isForceVisible(item, index))
) {
r.push({ item, index });
}
});
console.log(r)
return r;
},
},
watch: {
enabled: {
immediate: true,
handler() {
if (!this.enabled) {
// @ts-ignore
this.totalHeight = undefined;
}
},
},
},
methods: {
getItemElHeight(el: UniApp.NodesRef) {
return new Promise((resolve, reject) => {
el.boundingClientRect(res => {
if(res) {
resolve(res)
} else {
reject(res)
}
}).exec()
})
},
update() {
const task = async () => {
this.start = 0;
this.end = this.items.length;
}
}
},
created() {
this.bottom = this.prerender - 1;
},
mounted() {
// @ts-ignore
this._mountedPromise_resolve!(null);
let updatedOnce = false;
this.$watch(
() => [this.items],
() => {
this.itemsHeight = [];
nextTick(() => {
this.update();
if (!updatedOnce) {
this.update();
}
updatedOnce = true;
});
},
{ immediate: true }
);
this.$watch(
() => [this.buffer],
() => {
this.update();
}
);
}
})
</script>
<style lang="scss">
.vl-items {
overflow: hidden;
box-sizing: border-box;
}
</style>