123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203 |
- <template>
- <div
- v-if="isScrollButtonVisible"
- class="scroll-button"
- @click="scrollToMessageListBottom"
- >
- <Icon width="10px" height="10px" :file="doubleArrowIcon" />
- <div class="scroll-button-text">
- {{ scrollButtonContent }}
- </div>
- </div>
- </template>
- <script lang="ts" setup>
- import {
- ref,
- onMounted,
- onUnmounted,
- computed,
- watch,
- } from "../../../../adapter-vue";
- import {
- TUIStore,
- StoreName,
- IMessageModel,
- IConversationModel,
- TUITranslateService,
- } from "@tencentcloud/chat-uikit-engine";
- import Icon from "../../../common/Icon.vue";
- import doubleArrowIcon from "../../../../assets/icon/double-arrow.svg";
- import { getBoundingClientRect } from "@tencentcloud/universal-api";
- import { JSONToObject } from "../../../../utils";
- interface IEmits {
- (key: "scrollToLatestMessage"): void;
- }
- const emits = defineEmits<IEmits>();
- const messageList = ref<IMessageModel[]>([]);
- const currentConversationID = ref<string>("");
- const currentLastMessageTime = ref<number>(0);
- const newMessageCount = ref<number>(0);
- const isScrollOverOneScreen = ref<boolean>(false);
- const isExistLastMessage = ref<boolean>(false);
- const isScrollButtonVisible = ref<boolean>(false);
- const scrollButtonContent = computed(() =>
- newMessageCount.value
- ? `${newMessageCount.value}${TUITranslateService.t("TUIChat.条新消息")}`
- : TUITranslateService.t("TUIChat.回到最新位置")
- );
- watch(
- () => [isScrollOverOneScreen.value, isExistLastMessage.value],
- () => {
- isScrollButtonVisible.value =
- isScrollOverOneScreen.value || isExistLastMessage.value;
- if (!isScrollButtonVisible.value) {
- resetNewMessageCount();
- }
- },
- { immediate: true }
- );
- onMounted(() => {
- TUIStore.watch(StoreName.CHAT, {
- messageList: onMessageListUpdated,
- newMessageList: onNewMessageListUpdated,
- });
- TUIStore.watch(StoreName.CONV, {
- currentConversation: onCurrentConversationUpdated,
- });
- });
- onUnmounted(() => {
- TUIStore.unwatch(StoreName.CHAT, {
- messageList: onMessageListUpdated,
- newMessageList: onNewMessageListUpdated,
- });
- TUIStore.unwatch(StoreName.CONV, {
- currentConversation: onCurrentConversationUpdated,
- });
- });
- function isTypingMessage(message: IMessageModel): boolean {
- return (
- JSONToObject(message.payload?.data)?.businessID === "user_typing_status"
- );
- }
- function onMessageListUpdated(newMessageList: Array<IMessageModel>) {
- messageList.value = newMessageList || [];
- const lastMessage = messageList.value?.[messageList.value?.length - 1];
- isExistLastMessage.value = !!(
- lastMessage && lastMessage?.time < currentLastMessageTime?.value
- );
- }
- function onNewMessageListUpdated(newMessageList: Array<IMessageModel>) {
- if (Array.isArray(newMessageList) && isScrollButtonVisible.value) {
- newMessageList.forEach((message: IMessageModel) => {
- if (
- message &&
- message.conversationID === currentConversationID.value &&
- !message.isDeleted &&
- !message.isRevoked &&
- !isTypingMessage(message)
- ) {
- newMessageCount.value += 1;
- }
- });
- }
- }
- function onCurrentConversationUpdated(
- conversation: IConversationModel | undefined
- ) {
- if (conversation?.conversationID !== currentConversationID.value) {
- resetNewMessageCount();
- }
- currentConversationID.value = conversation?.conversationID || "";
- currentLastMessageTime.value = conversation?.lastMessage?.lastTime || 0;
- }
- // 消息列表向上的滚动高度大于一屏时,展示滚动到最新
- async function judgeScrollOverOneScreen(e: Event) {
- if (e.target) {
- try {
- const { height } =
- (await getBoundingClientRect(
- `#${(e.target as HTMLElement)?.id}`,
- "messageList"
- )) || {};
- const scrollHeight =
- (e.target as HTMLElement)?.scrollHeight ||
- (e.detail as HTMLElement)?.scrollHeight;
- const scrollTop =
- (e.target as HTMLElement)?.scrollTop ||
- (e.detail as HTMLElement)?.scrollTop ||
- 0;
- // while scroll over one screen show this scroll button.
- if (scrollHeight - scrollTop > 2 * height) {
- isScrollOverOneScreen.value = true;
- return;
- }
- isScrollOverOneScreen.value = false;
- } catch (error) {
- isScrollOverOneScreen.value = false;
- }
- }
- }
- // 载入最新的 messageSource
- function resetMessageSource() {
- if (TUIStore.getData(StoreName.CHAT, "messageSource") !== undefined) {
- TUIStore.update(StoreName.CHAT, "messageSource", undefined);
- }
- }
- // reset newMessageCount
- function resetNewMessageCount() {
- newMessageCount.value = 0;
- }
- // 滚动到消息列表最底部
- function scrollToMessageListBottom() {
- resetMessageSource();
- resetNewMessageCount();
- emits("scrollToLatestMessage");
- }
- defineExpose({
- judgeScrollOverOneScreen,
- isScrollButtonVisible,
- });
- </script>
- <style scoped lang="scss">
- .scroll-button {
- position: absolute;
- bottom: 10px;
- right: 10px;
- width: 92px;
- height: 28px;
- background: #fff;
- border: 1px solid #e0e0e0;
- box-shadow: 0 4px 12px -5px rgba(0, 0, 0, 0.1);
- display: flex;
- flex-direction: row;
- align-items: center;
- justify-content: center;
- border-radius: 3px;
- cursor: pointer;
- -webkit-tap-highlight-color: transparent;
- &-text {
- font-family: PingFangSC-Regular, system-ui;
- font-size: 10px;
- color: #00b693;
- margin-left: 3px;
- }
- }
- </style>
|