| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { FriendRequest } from '../../types' |
| | | |
| | | import { computed, ref } from 'vue' |
| | | |
| | | import { DICT_TYPE } from '#/packages/constants/src' |
| | | import { getDictLabel } from '#/packages/effects/hooks/src' |
| | | import { IconifyIcon as Icon } from '#/packages/icons/src' |
| | | |
| | | import { Badge } from 'ant-design-vue' |
| | | |
| | | import { getCurrentUserId } from '#/views/im/utils/auth' |
| | | |
| | | import { UserAvatar } from '../../components/user' |
| | | import { useFriendStore } from '../../store/friendStore' |
| | | |
| | | defineOptions({ name: 'ImContactFriendRequestList' }) |
| | | |
| | | const props = defineProps<{ |
| | | activeId?: number |
| | | requests: FriendRequest[] |
| | | }>() |
| | | |
| | | const emit = defineEmits<{ |
| | | select: [request: FriendRequest] |
| | | }>() |
| | | |
| | | const friendStore = useFriendStore() |
| | | const expanded = ref(true) |
| | | /** å½åç»å½ç¨æ·ç¼å·ï¼ç¨ computed å
ä¸å±ï¼åè´¦å·åé wsCache éåï¼é¿å
顶屿±å¼å¨ keep-alive å®ä¾éæææ§ id */ |
| | | const currentUserId = computed(() => getCurrentUserId()) |
| | | |
| | | /** å表项å±ç¤ºå¯¹ç«¯ï¼fromUserId == æ â 对端 = toUserï¼å¦å对端 = fromUser */ |
| | | function getPeer(request: FriendRequest) { |
| | | const sentByMe = request.fromUserId === currentUserId.value |
| | | return { |
| | | id: sentByMe ? request.toUserId : request.fromUserId, |
| | | nickname: sentByMe |
| | | ? request.toNickname || String(request.toUserId) |
| | | : request.fromNickname || String(request.fromUserId), |
| | | avatar: sentByMe ? request.toAvatar : request.fromAvatar |
| | | } |
| | | } |
| | | |
| | | /** å表项é¢å
é peer åæ®µï¼æ¨¡æ¿éç´æ¥ {{ peer.xxx }} 䏿¬¡æåï¼ç 3 次 helper è°ç¨ */ |
| | | const enrichedRequests = computed(() => |
| | | props.requests.map((request) => ({ request, peer: getPeer(request) })) |
| | | ) |
| | | |
| | | const loadingMore = ref(false) // ç¹å»ãå è½½æ´å¤ãæä¸ä¸é¡µï¼store å
鍿 maxId 游æ å页 + pending å»é |
| | | async function handleLoadMore() { |
| | | if (loadingMore.value) { |
| | | return |
| | | } |
| | | loadingMore.value = true |
| | | try { |
| | | await friendStore.loadMoreFriendRequestList() |
| | | } finally { |
| | | loadingMore.value = false |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <template> |
| | | <!-- |
| | | éè®¯å½ - æ°çæååç» |
| | | - èªæ²»ï¼æå ç¶æç±ç»ä»¶å
ref 管çï¼é»è®¤å±å¼ |
| | | - å表项å±ç¤ºï¼å¤´å + æµç§° + ç³è¯·çç± + ç¶ææ ç¾ï¼çå¾
éªè¯ / 已添å / å·²æç»ï¼ |
| | | - é䏿ç±ç¶çº§ activeId éä¼ ï¼ç¹å»è§¦å select äºä»¶ |
| | | --> |
| | | <div> |
| | | <!-- æå åç»å¤´ --> |
| | | <div |
| | | class="flex gap-2 items-center px-3.5 py-2.5 text-15px text-[var(--ant-color-text)] cursor-pointer select-none hover:bg-[var(--ant-color-fill-secondary)]" |
| | | @click="expanded = !expanded" |
| | | > |
| | | <Icon :icon="expanded ? 'ep:caret-bottom' : 'ep:caret-right'" :size="14" /> |
| | | <span class="flex-1">æ°çæå</span> |
| | | <!-- 红ç¹ï¼æªå¤çä¸å«äººå æçï¼ç»ä¸èµ° store getterï¼é¿å
æ¬å° computed è· store åå£å¾ï¼ --> |
| | | <Badge |
| | | v-if="friendStore.getUnhandledRequestCount > 0" |
| | | :count="friendStore.getUnhandledRequestCount" |
| | | :max="99" |
| | | class="mr-2" |
| | | /> |
| | | <span class="text-sm text-[var(--ant-color-text-secondary)]">{{ requests.length }}</span> |
| | | </div> |
| | | <div v-show="expanded"> |
| | | <div |
| | | v-for="{ request, peer } in enrichedRequests" |
| | | :key="request.id" |
| | | class="flex gap-3 items-start px-3.5 py-2.5 cursor-pointer transition-colors hover:bg-[var(--ant-color-fill-secondary)]" |
| | | :class="{ |
| | | 'bg-[var(--ant-color-fill)]': activeId === request.id |
| | | }" |
| | | @click="emit('select', request)" |
| | | > |
| | | <UserAvatar |
| | | :id="peer.id" |
| | | :url="peer.avatar" |
| | | :name="peer.nickname" |
| | | :size="36" |
| | | :clickable="false" |
| | | /> |
| | | <div class="flex-1 min-w-0 overflow-hidden"> |
| | | <div class="flex justify-between gap-2 items-center"> |
| | | <span class="flex-1 text-sm font-medium truncate text-[var(--ant-color-text)]"> |
| | | {{ peer.nickname }} |
| | | </span> |
| | | <span class="flex-shrink-0 text-12px text-[var(--ant-color-text-secondary)]"> |
| | | {{ getDictLabel(DICT_TYPE.IM_FRIEND_REQUEST_HANDLE_RESULT, request.handleResult) }} |
| | | </span> |
| | | </div> |
| | | <div |
| | | v-if="request.applyContent" |
| | | class="mt-0.5 text-xs truncate text-[var(--ant-color-text-secondary)]" |
| | | > |
| | | {{ request.applyContent }} |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <div |
| | | v-if="requests.length === 0" |
| | | class="py-3 text-12px text-center text-[var(--ant-color-text-disabled)]" |
| | | > |
| | | ææ æ°çæå |
| | | </div> |
| | | <!-- å è½½æ´å¤ï¼ææ¬å°ææ§ requestId 游æ å页æä¸ä¸æ¹ï¼hasMore=false ä¸å±ç¤º --> |
| | | <div |
| | | v-else-if="friendStore.hasMoreFriendRequests" |
| | | class="py-2 text-12px text-center cursor-pointer text-[var(--ant-color-text-secondary)] hover:bg-[var(--ant-color-fill-secondary)]" |
| | | @click="handleLoadMore" |
| | | > |
| | | {{ loadingMore ? 'å è½½ä¸â¦' : 'å è½½æ´å¤' }} |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </template> |