wujingjing
2024-12-05 d257f90aecbd2ae44de5bf19489b3e82943cb712
src/components/chat/hooks/useScrollToBottom.ts
@@ -1,54 +1,59 @@
import type { ComputedRef, Ref } from 'vue';
import { nextTick, onActivated, onUnmounted, ref, watch } from 'vue';
import { nextTick, onActivated, onMounted, onUnmounted, ref, watch } from 'vue';
import type { ChatMessage } from '../model/types';
import emitter from '/@/utils/mitt';
import { debounce } from '/@/utils/util';
export type UseScrollToBottomOption = {
   chatListDom: Ref<HTMLDivElement>;
   displayMessageList: ComputedRef<ChatMessage[]>;
};
export const useScrollToBottom = (option: UseScrollToBottomOption) => {
   const { chatListDom, displayMessageList } = option;
   const { chatListDom } = option;
   const scrollToBottom = () => {
      if (!chatListDom.value) return;
      const parent = chatListDom.value.parentElement;
      if (!parent) return;
      if (parent.scrollHeight > parent.clientHeight) {
         parent.scrollTop = parent.scrollHeight - parent.clientHeight;
      }
      nextTick(() => {
         if (chatListDom.value.scrollHeight > chatListDom.value.clientHeight) {
            chatListDom.value.scrollTop = chatListDom.value.scrollHeight - chatListDom.value.clientHeight;
         }
      });
   };
   const scrollToTop = () => {
      nextTick(() => {
         chatListDom.value.scrollTop = 0;
      });
   };
   const debounceAmisScroll = debounce(({ instance }) => {
      nextTick(() => {
         scrollToBottom();
      });
      scrollToBottom();
   }, 500);
   emitter.on('amis.page.ready', debounceAmisScroll);
   const checkIsBottom = () => {
      // 误差 2像素
      isBottom.value = Math.abs(chatListDom.value.scrollTop + chatListDom.value.clientHeight - chatListDom.value.scrollHeight) < 2;
   };
   const isBottom = ref(false);
   onMounted(() => {
      chatListDom.value.addEventListener('scrollend', checkIsBottom);
   });
   onUnmounted(()=>{
      emitter.off('amis.page.ready');
   })
   const forbidScroll = ref(false);
   watch(
      displayMessageList,
      () => {
         if (forbidScroll.value) return;
         nextTick(() => scrollToBottom());
      },
      {
         deep: true,
      }
   );
   onUnmounted(() => {
      chatListDom.value.removeEventListener('scrollend', checkIsBottom);
   });
   // emitter.on('amis.page.ready', debounceAmisScroll);
   // onUnmounted(() => {
   //    emitter.off('amis.page.ready');
   // });
   onActivated(() => {
      if (forbidScroll.value) return;
      nextTick(() => scrollToBottom());
      scrollToBottom();
   });
   return {
      forbidScroll,
      scrollToBottom,
      scrollToTop,
      isBottom,
   };
};