| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { Message } from '../../../../types' |
| | | |
| | | import { computed, ref } from 'vue' |
| | | |
| | | import { CommonStatusEnum } from '#/packages/constants/src' |
| | | |
| | | import { Popover, TabPane, Tabs } from 'ant-design-vue' |
| | | |
| | | import { getGroupReadUsers as apiGetGroupReadUsers } from '#/api/im/message/group' |
| | | |
| | | import { ImConversationType, ImMessageReceiptStatus } from '../../../../../utils/constants' |
| | | import { PagedScroller } from '../../../../components' |
| | | import { GroupMember, type GroupMemberLite } from '../../../../components/group' |
| | | import { useMessageStore } from '../../../../store/messageStore' |
| | | |
| | | defineOptions({ name: 'ImMessageReadStatus' }) |
| | | |
| | | const props = defineProps<{ |
| | | // å½å群ç¼å·ï¼ä¾ loadReadUsers ä½ä¸º /im/message/group/get-read-users çå
¥å |
| | | groupId: number |
| | | // å½åç¾¤æææåï¼å¤é¨ MessagePanel.groupMembers ä¼ å
¥ï¼æ²¡æå°±ä¼ 空æ°ç»ï¼æªè¯»å表ä¼å空ä½ä¸æ¥éï¼ |
| | | groupMembers: GroupMemberLite[] |
| | | message: Message |
| | | }>() |
| | | |
| | | const messageStore = useMessageStore() |
| | | |
| | | // popover å¼å
³ï¼show æ¶æå·²è¯»ååï¼å
³éåä¿ç readUserIds ç¼åï¼éå¼å䏿¡æ¶æ¯ä¸å请æ±ï¼ |
| | | const popVisible = ref(false) |
| | | const activeTab = ref<'read' | 'unread'>('read') |
| | | // æå¡ç«¯è¿åç"å·²è¯»è¿æ¡æ¶æ¯ç userId å表"ï¼æªè¯»é visibleMembers åå»è¿ä»½å¾å° |
| | | const readUserIds = ref<number[]>([]) |
| | | |
| | | /** |
| | | * æ ç¾ææ¡ï¼ |
| | | * - receiptStatus === DONEï¼æå¡ç«¯ç¡®è®¤å
¨å已读 â ç´æ¥æ¾ç¤º"å
¨é¨å·²è¯»"ï¼readCount ä¸åä½¿ç¨ |
| | | * - readCount > 0ï¼æ¾ç¤º"N 人已读" |
| | | * - å
¶ä»ï¼readCount = 0 æ undefinedï¼ä¸æªå° DONEï¼ï¼æ¾ç¤º"æªè¯»" |
| | | */ |
| | | const label = computed(() => { |
| | | if (props.message.receiptStatus === ImMessageReceiptStatus.DONE) { |
| | | return 'å
¨é¨å·²è¯»' |
| | | } |
| | | const readCount = props.message.readCount || 0 |
| | | return readCount > 0 ? `${readCount} 人已读` : 'æªè¯»' |
| | | }) |
| | | |
| | | /** |
| | | * è¿æ¡æ¶æ¯"åºè¯¥è¢«è°çå°"çå¯è§æåéåï¼å·²è¯» / æªè¯»ä¸¤ä¸ª tab å
±ç¨åºåºï¼ |
| | | * |
| | | * ä¸å±è¿æ»¤ï¼ |
| | | * 1. å®åç¾¤æ¶æ¯ï¼receiverUserIds é空æ¶ï¼åªç®ååå
çæåãå°äºè¿å±ï¼æªå¨æéèå´éç人 |
| | | * ä¼è¢«éç®æ"æªè¯»"ï¼getGroupReadUsers å端已æå¯è§æ§è¿æ»¤ï¼å端å¿
é¡»é
å¥ï¼ |
| | | * 2. åéè
æ¬äººï¼èªå·±åçæ¶æ¯ç®ä¸ç®"èªå·±å·²è¯»"è¯ä¹æ¨¡ç³ï¼ç´¢æ§ä¸¤ä¸ª tab é½ä¸å±ç¤º |
| | | * 3. å·²é群ï¼status === DISABLEï¼ï¼ä»è¯»æ²¡è¯»å·²ç»ä¸å
³å¿ï¼UI ä¸å±ç¤º |
| | | */ |
| | | const visibleMembers = computed<GroupMemberLite[]>(() => { |
| | | const receiverUserIds = props.message.receiverUserIds |
| | | const isDirected = !!receiverUserIds && receiverUserIds.length > 0 |
| | | return props.groupMembers.filter( |
| | | (member) => |
| | | member.status !== CommonStatusEnum.DISABLE && |
| | | member.userId !== props.message.senderId && |
| | | (!isDirected || receiverUserIds.includes(member.userId)) |
| | | ) |
| | | }) |
| | | |
| | | /** 已读 = å¯è§æå â© readUserIds */ |
| | | const readMembers = computed(() => |
| | | visibleMembers.value.filter((member) => readUserIds.value.includes(member.userId)) |
| | | ) |
| | | |
| | | /** æªè¯» = å¯è§æå â readUserIds */ |
| | | const unreadMembers = computed(() => |
| | | visibleMembers.value.filter((member) => !readUserIds.value.includes(member.userId)) |
| | | ) |
| | | |
| | | /** |
| | | * æåå·²è¯»ç¨æ· id å表 |
| | | * |
| | | * 触åï¼el-popover @showï¼æéæå è½½ï¼é¿å
æ¯æ¡ç¾¤æ¶æ¯é½é¢æï¼ |
| | | * è·³è¿ï¼æ¬å°å 使¶æ¯ï¼id = 0ï¼è¿æ²¡æ¿å°æå¡ç«¯ idï¼ï¼åç«¯æ²¡æ³æ messageId æ¥ |
| | | * 失败ï¼ä»
æ§å¶å°åè¦ï¼readUserIds ä¿æç©ºæ°ç» â label èµ° readCount å
åºï¼ä¸é»å¡ UI |
| | | * |
| | | * æå°åååé¡ºææ readCount / receiptStatus ååå° messageStoreï¼è®© popover å¤é¢ç |
| | | * label ä¹è·çèµ°ææ°æ°ï¼ç¦»çº¿ / æ¼æ¶ RECEIPT äºä»¶æ¶æ¬å° readCount ä¼åæ§ï¼å¼¹å±éçå°"已读 5" |
| | | * ä½å¤é¢ä»æ¯"æªè¯»"ææ§äººæ°ï¼è¿é以æå¡ç«¯è¿å为åç«æ£åå» |
| | | */ |
| | | async function loadReadUsers() { |
| | | if (!props.message.id) { |
| | | return |
| | | } |
| | | try { |
| | | const userIds = await apiGetGroupReadUsers({ |
| | | groupId: props.groupId, |
| | | messageId: props.message.id |
| | | }) |
| | | readUserIds.value = userIds || [] |
| | | const readCount = readUserIds.value.length |
| | | // å
¨å¯è§æåé½å·²è¯» â æ´æ°ä¸º DONEï¼è®©å¤é¢ label ç´æ¥å½ä¸ãå
¨é¨å·²è¯»ãåæ¯ï¼ |
| | | // å¦ååªæ´æ° readCountï¼receiptStatus ç»´æä¸åï¼PENDINGï¼ |
| | | const allRead = readCount > 0 && readCount >= visibleMembers.value.length |
| | | messageStore.applyMessageReadReceipt({ |
| | | conversationType: ImConversationType.GROUP, |
| | | targetId: props.groupId, |
| | | groupMessageId: props.message.id, |
| | | readCount, |
| | | receiptStatus: allRead ? ImMessageReceiptStatus.DONE : undefined |
| | | }) |
| | | } catch (error) { |
| | | console.error('[IM] æå群已读å表失败:', error) |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <template> |
| | | <!-- |
| | | ç¾¤æ¶æ¯å·²è¯»ç¶æ |
| | | - æ ç¾å½¢æï¼å±ç¤ºãN 已读ãæãå
¨é¨å·²è¯»ãï¼ç¹å»å¼¹åº popover ç¨ tab ååºå·²è¯» / æªè¯»æå |
| | | - æ¸²ææ¡ä»¶ï¼ä»
å¨ç¾¤èãèªå·±åéãreceiptStatus !== NO_RECEIPT æ¶æåºï¼ç± MessageItem æå
³ï¼ |
| | | - 已读ååæéæï¼popover 触å show ææ /im/message/group/get-read-usersï¼é¿å
æ¯æ¡ç¾¤æ¶æ¯é½é¢æ |
| | | - æªè¯»åå = å½å群æå â èªå·± â å·²é群 â 已读éåï¼å端èåï¼å端åªå已读 userIdï¼ |
| | | --> |
| | | <Popover |
| | | v-model:open="popVisible" |
| | | placement="left" |
| | | trigger="click" |
| | | :overlay-style="{ width: '320px' }" |
| | | @open-change="(open) => open && loadReadUsers()" |
| | | > |
| | | <span |
| | | class="text-12px whitespace-nowrap cursor-pointer text-[var(--ant-color-text-secondary)] hover:text-[#409eff]" |
| | | > |
| | | {{ label }} |
| | | </span> |
| | | |
| | | <template #content> |
| | | <Tabs v-model:active-key="activeTab" centered> |
| | | <TabPane :tab="`已读(${readMembers.length})`" key="read"> |
| | | <PagedScroller :items="readMembers" :page-size="20" item-key="userId" class="h-75"> |
| | | <template #default="{ item }"> |
| | | <GroupMember :member="item as GroupMemberLite" :height="40" :clickable="false" /> |
| | | </template> |
| | | </PagedScroller> |
| | | <div |
| | | v-if="readMembers.length === 0" |
| | | class="py-5 text-12px text-center text-[var(--ant-color-text-disabled)]" |
| | | > |
| | | {{ groupMembers.length === 0 ? '群æåæªå è½½' : 'ææ å·²è¯»' }} |
| | | </div> |
| | | </TabPane> |
| | | <TabPane :tab="`æªè¯»(${unreadMembers.length})`" key="unread"> |
| | | <PagedScroller :items="unreadMembers" :page-size="20" item-key="userId" class="h-75"> |
| | | <template #default="{ item }"> |
| | | <GroupMember :member="item as GroupMemberLite" :height="40" :clickable="false" /> |
| | | </template> |
| | | </PagedScroller> |
| | | <div |
| | | v-if="unreadMembers.length === 0" |
| | | class="py-5 text-12px text-center text-[var(--ant-color-text-disabled)]" |
| | | > |
| | | {{ groupMembers.length === 0 ? '群æåæªå è½½' : 'å
¨é¨å·²è¯»' }} |
| | | </div> |
| | | </TabPane> |
| | | </Tabs> |
| | | </template> |
| | | </Popover> |
| | | </template> |