feat: 首次渲染滚动到底部
This commit is contained in:
@ -23,9 +23,34 @@ import {
|
||||
export default defineComponent({
|
||||
name: 'BubbleList',
|
||||
inheritAttrs: false,
|
||||
props: {},
|
||||
setup(_, { attrs, slots, expose }) {
|
||||
const props = attrs as unknown as BubbleListProps & { class?: any; style?: any };
|
||||
// 正确声明 props,提供默认值,确保 TSX 下默认值生效
|
||||
props: {
|
||||
autoScroll: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
items: {
|
||||
type: Array as () => BubbleListProps['items'],
|
||||
required: true,
|
||||
},
|
||||
roles: {
|
||||
type: Object as () => RolesType,
|
||||
default: () => ({} as RolesType),
|
||||
},
|
||||
rootClassName: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
style: {
|
||||
type: Object as () => Record<string, any>,
|
||||
default: () => ({}),
|
||||
},
|
||||
class: {
|
||||
type: [String, Array, Object] as unknown as () => any,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
setup(props, { attrs, slots, expose }) {
|
||||
const passThroughAttrs = useAttrs();
|
||||
|
||||
const TOLERANCE = 1;
|
||||
@ -63,14 +88,16 @@ export default defineComponent({
|
||||
// scroll
|
||||
const [scrollReachEnd, setScrollReachEnd] = useState(true);
|
||||
const [updateCount, setUpdateCount] = useState(0);
|
||||
// 首次挂载后仅自动滚动一次
|
||||
const didInitialAutoScroll = ref(false);
|
||||
|
||||
const onInternalScroll = (e: Event) => {
|
||||
const target = e.target as HTMLElement;
|
||||
setScrollReachEnd(target.scrollHeight - Math.abs(target.scrollTop) - target.clientHeight <= TOLERANCE);
|
||||
};
|
||||
|
||||
watch(updateCount, () => {
|
||||
if ((props.autoScroll ?? true) && unref(listRef) && unref(scrollReachEnd)) {
|
||||
watch([updateCount, scrollReachEnd, listRef], () => {
|
||||
if (props.autoScroll && unref(listRef) && unref(scrollReachEnd)) {
|
||||
nextTick(() => {
|
||||
unref(listRef)!.scrollTo({ top: unref(listRef)!.scrollHeight });
|
||||
});
|
||||
@ -79,26 +106,24 @@ export default defineComponent({
|
||||
|
||||
watch(
|
||||
() => unref(displayData).length,
|
||||
() => {
|
||||
if (props.autoScroll ?? true) {
|
||||
const lastItemKey = unref(displayData)[unref(displayData).length - 2]?.key;
|
||||
const bubbleInst = unref(bubbleRefs)[lastItemKey!];
|
||||
if (bubbleInst) {
|
||||
const { nativeElement } = bubbleInst;
|
||||
const { top = 0, bottom = 0 } = nativeElement?.getBoundingClientRect() ?? {};
|
||||
const { top: listTop, bottom: listBottom } = unref(listRef)!.getBoundingClientRect();
|
||||
const isVisible = top < listBottom && bottom > listTop;
|
||||
if (isVisible) {
|
||||
setUpdateCount(unref(updateCount) + 1);
|
||||
setScrollReachEnd(true);
|
||||
}
|
||||
}
|
||||
(newLen, oldLen) => {
|
||||
if (!props.autoScroll) return;
|
||||
// 首次渲染:当有内容时滚到底部一次
|
||||
if (!didInitialAutoScroll.value && newLen > 0) {
|
||||
scrollToBottom('auto');
|
||||
didInitialAutoScroll.value = true;
|
||||
return;
|
||||
}
|
||||
// 新增内容且当前在底部:继续粘底
|
||||
if (oldLen !== undefined && newLen > (oldLen ?? 0) && unref(scrollReachEnd)) {
|
||||
scrollToBottom();
|
||||
}
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
|
||||
const onBubbleUpdate = useEventCallback<void>(() => {
|
||||
if (props.autoScroll ?? true) setUpdateCount(unref(updateCount) + 1);
|
||||
if (props.autoScroll) setUpdateCount(unref(updateCount) + 1);
|
||||
});
|
||||
const context = computed(() => ({ onUpdate: onBubbleUpdate }));
|
||||
|
||||
@ -106,6 +131,18 @@ export default defineComponent({
|
||||
const abortTypingByKey = (key: string | number) => {
|
||||
bubbleRefs.value[key]?.abortTyping?.();
|
||||
};
|
||||
// 通用:滚动到底部
|
||||
const scrollToBottom = (behavior: ScrollBehavior = 'smooth') => {
|
||||
nextTick(() => {
|
||||
requestAnimationFrame(() => {
|
||||
const el = unref(listRef);
|
||||
if (el) {
|
||||
el.scrollTo({ top: el.scrollHeight, behavior });
|
||||
setScrollReachEnd(true);
|
||||
}
|
||||
})
|
||||
});
|
||||
};
|
||||
// 对外暴露能力
|
||||
expose({
|
||||
nativeElement: listRef,
|
||||
@ -113,6 +150,7 @@ export default defineComponent({
|
||||
scrollTo: (info: any) => {
|
||||
unref(listRef)?.scrollTo?.(info);
|
||||
},
|
||||
scrollToBottom,
|
||||
});
|
||||
|
||||
return () => (
|
||||
|
||||
Reference in New Issue
Block a user