From 27cd042df9aca0383a49f3514bc21958dd890912 Mon Sep 17 00:00:00 2001
From: gaoluyang <2820782392@qq.com>
Date: 星期一, 29 六月 2026 15:42:23 +0800
Subject: [PATCH] 银川 1.联调产品维护页面 2.添加IM即时通讯模块
---
src/views/im/home/components/paged-scroller.vue | 109 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 109 insertions(+), 0 deletions(-)
diff --git a/src/views/im/home/components/paged-scroller.vue b/src/views/im/home/components/paged-scroller.vue
new file mode 100644
index 0000000..ed1842a
--- /dev/null
+++ b/src/views/im/home/components/paged-scroller.vue
@@ -0,0 +1,109 @@
+<script lang="ts" setup generic="T">
+import { computed, onBeforeUnmount, onMounted, ref, useTemplateRef, watch } from 'vue'
+
+defineOptions({ name: 'ImPagedScroller' })
+
+const props = withDefaults(
+ defineProps<{
+ itemKey?: string // 涓氬姟 id 瀛楁鍚嶏紙濡� 'userId' / 'id'锛夛紱涓嶄紶 / 瀛楁鍊奸潪 string|number 鏃跺洖閫� idx
+ items: T[] // 鍏ㄩ噺鏁版嵁
+ pageSize?: number // 姣忛〉娓叉煋鏉℃暟
+ threshold?: number // 璺濆簳澶氬皯 px 瑙﹀彂涓嬩竴椤�
+ }>(),
+ {
+ itemKey: undefined,
+ pageSize: 30,
+ threshold: 30
+ }
+)
+
+/** 瑙f瀽姣忔潯 item 鐨� :key锛歝aller 浼� itemKey 鍒欐寜瀛楁鍙栵紝鏃犳晥 / 缂哄け鍥為��绱㈠紩锛岄伩鍏嶄紶閿欏瓧娈垫椂鍏ㄨ〃 undefined key */
+function resolveItemKey(item: T, idx: number): number | string {
+ if (!props.itemKey || item == null || typeof item !== 'object') {
+ return idx
+ }
+ const value = (item as Record<string, unknown>)[props.itemKey]
+ return typeof value === 'string' || typeof value === 'number' ? value : idx
+}
+
+const scrollbarRef = useTemplateRef<HTMLDivElement>('scrollbarRef')
+const page = ref(1)
+
+const displayItems = computed(() => {
+ const limit = Math.min(page.value * props.pageSize, props.items.length)
+ return props.items.slice(0, limit)
+})
+
+const allLoaded = computed(() => displayItems.value.length >= props.items.length)
+
+/** 浠呭綋瓒呰繃涓�椤垫椂鎵嶆樉绀恒�屽凡鍒板簳閮ㄣ�嶏紝閬垮厤鐭垪琛ㄤ篃鍑虹幇杩欐潯鎻愮ず */
+const showFooter = computed(() => allLoaded.value && props.items.length > props.pageSize)
+
+let wrapEl: HTMLElement | null = null
+
+onMounted(() => {
+ wrapEl = scrollbarRef.value
+ wrapEl?.addEventListener('scroll', onScroll)
+})
+
+onBeforeUnmount(() => {
+ wrapEl?.removeEventListener('scroll', onScroll)
+})
+
+/** 鍒囨崲鏁版嵁婧愶紙濡傚垏浼氳瘽锛夋椂閲嶇疆鍒嗛〉锛氶伩鍏嶆柊鍒楄〃娌跨敤鏃� page锛岄灞忓嚭鐜扮┖娈� */
+watch(
+ () => props.items,
+ () => {
+ page.value = 1
+ }
+)
+
+/** 婊氬埌璺濆簳 threshold 鍐呮椂鑷 page锛屾墿鍑轰笅涓�娈靛垏鐗� */
+function onScroll(e: Event) {
+ const el = e.target as HTMLElement
+ if (el.scrollTop + el.clientHeight < el.scrollHeight - props.threshold) {
+ return
+ }
+ if (allLoaded.value) {
+ return
+ }
+ page.value++
+}
+
+defineExpose({
+ /** 鎵嬪姩婊氬埌椤堕儴 */
+ scrollTop: () => {
+ if (wrapEl) {
+ wrapEl.scrollTop = 0
+ }
+ },
+ /** 鎵嬪姩婊氬埌搴曢儴 */
+ scrollBottom: () => {
+ if (wrapEl) {
+ wrapEl.scrollTop = wrapEl.scrollHeight
+ }
+ }
+})
+</script>
+
+<template>
+ <!--
+ 鍒嗛〉澧為噺婊氬姩瀹瑰櫒
+ - 婊氬埌搴曢儴鑷姩 page++锛岀洿鍒板叏閮ㄦ覆鏌撳畬
+ - 閫氳繃 slot 鏆撮湶姣忎竴椤癸紝璁╄皟鐢ㄦ柟鑷繁鍐冲畾娓叉煋
+ -->
+ <div ref="scrollbarRef" class="w-full h-full overflow-y-auto">
+ <slot
+ v-for="(item, idx) in displayItems"
+ :item="item"
+ :index="idx"
+ :key="resolveItemKey(item, idx)"
+ ></slot>
+ <div
+ v-if="showFooter"
+ class="py-3 text-xs text-center text-[var(--ant-color-text-secondary)]"
+ >
+ 宸插埌搴曢儴
+ </div>
+ </div>
+</template>
--
Gitblit v1.9.3