gaoluyang
2026-06-29 27cd042df9aca0383a49f3514bc21958dd890912
src/views/im/home/components/group/group-info-card.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,116 @@
<script lang="ts" setup>
import type { GroupLite } from '../../types'
import { computed, onMounted, onUnmounted } from 'vue'
import { useRouter } from 'vue-router'
import { prompt } from '#/packages/effects/common-ui/src'
import { Input, message } from 'ant-design-vue'
import { applyJoinGroup } from '#/api/im/group/request'
import { ImConversationType, ImGroupAddSource } from '../../../utils/constants'
import { getGroupDisplayName } from '../../../utils/user'
import { useConversationStore } from '../../store/conversationStore'
import { useGroupStore } from '../../store/groupStore'
import { useImUiStore } from '../../store/uiStore'
import GroupInfo from './group-info.vue'
defineOptions({ name: 'ImGroupInfoCard' })
const uiStore = useImUiStore()
const conversationStore = useConversationStore()
const groupStore = useGroupStore()
const router = useRouter()
const card = computed(() => uiStore.groupInfoCard)
/** å…³é—­æµ®å±‚ */
function handleClose() {
  uiStore.closeGroupInfoCard()
}
/** Esc å…³é—­ */
function handleKeydown(e: KeyboardEvent) {
  if (e.key === 'Escape' && card.value.show) {
    handleClose()
  }
}
onMounted(() => window.addEventListener('keydown', handleKeydown))
onUnmounted(() => window.removeEventListener('keydown', handleKeydown))
/** è¿›å…¥ç¾¤èŠï¼šå–本地最新群信息(含 silent / ç¾¤å¤‡æ³¨ï¼‰ï¼Œæ–°å»ºæˆ–激活会话 + è·³è·¯ç”± */
function handleChat(group: GroupLite) {
  const cached = groupStore.getGroup(group.id)
  // cached å‘½ä¸­èµ° getGroupDisplayName è®©ç¾¤å¤‡æ³¨ä¼˜å…ˆï¼ˆä¸Ž contact / ä¼šè¯åˆ—表的展示名一致);缺 cached æ—¶å›žè½ showGroupName / åŽŸç¾¤å
  const displayName = cached
    ? getGroupDisplayName(cached)
    : group.showGroupName || group.name || ''
  // æ‰“开或新建会话
  conversationStore.openConversation(
    group.id,
    ImConversationType.GROUP,
    displayName,
    cached?.avatar || group.showImage || '',
    { silent: !!cached?.silent }
  )
  // å¦‚果不在会话页,先跳过去(如果在了,MessagePanel ä¼šè‡ªå·±æ„ŸçŸ¥ä¼šè¯å˜åŒ–刷新)
  if (router.currentRoute.value.name !== 'ImHomeConversation') {
    router.push({ name: 'ImHomeConversation' })
  }
  handleClose()
}
/** åŠ å…¥ç¾¤èŠï¼šå…ˆå…³æµ®å±‚ï¼ˆé¿å…ä¸Ž prompt çš„ mask äº’相遮挡)→ å¼¹ç”³è¯·ç†ç”±ï¼ˆå¯é€‰ï¼‰â†’ applyJoinGroup */
async function handleApply(group: GroupLite) {
  handleClose()
  let applyContent: string
  try {
    const result = await prompt<string>({
      cancelText: '取消',
      component: Input,
      componentProps: {
        allowClear: true,
        placeholder: '请填写验证消息(可选)'
      },
      content: '',
      defaultValue: '',
      confirmText: '发送申请',
      modelPropName: 'value',
      title: `申请加入「${group.name || ''}」`
    })
    applyContent = (result || '').trim()
  } catch {
    return
  }
  await applyJoinGroup({
    groupId: group.id,
    applyContent: applyContent || undefined,
    addSource: ImGroupAddSource.SHARE_LINK
  })
  message.success('加群申请已发送')
}
</script>
<template>
  <!--
    ç¾¤ä¿¡æ¯æµ®å±‚(与 UserInfoCard å¯¹ä½ï¼‰
    - ä»…承担「浮层定位 + å…³é—­é€»è¾‘(点遮罩 / Esc)」,群信息视觉走 <GroupInfo>,与通讯录详情共用一份组件
    - è§¦å‘:useImUiStore.openGroupInfoCardAtEvent(group, e)
    - GroupInfo å†…部按 groupStore ç¼“存推导 member / stranger,浮层只负责接 chat / apply äº‹ä»¶åšä¸šåŠ¡
  -->
  <teleport to="body">
    <div v-if="card.show" class="fixed inset-0 z-9998" @click.self="handleClose">
      <div
        class="fixed w-80 p-4 bg-[var(--ant-color-bg-elevated)] rounded-md shadow-xl"
        :style="{ left: `${card.position.x }px`, top: `${card.position.y }px` }"
        @click.stop
      >
        <GroupInfo v-if="card.group" :group="card.group" @chat="handleChat" @apply="handleApply" />
      </div>
    </div>
  </teleport>
</template>