| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { User } from '../../types' |
| | | |
| | | import { computed, h, nextTick, ref, watch } from 'vue' |
| | | |
| | | import { confirm } from '#/packages/effects/common-ui/src' |
| | | import { DICT_TYPE } from '#/packages/constants/src' |
| | | import { getDictLabel } from '#/packages/effects/hooks/src' |
| | | import { IconifyIcon as Icon } from '#/packages/icons/src' |
| | | |
| | | import { Button, Checkbox, Dropdown, Input, Menu, message } from 'ant-design-vue' |
| | | |
| | | import { getSimpleUser, type SystemUserApi } from '#/api/system/user' |
| | | import { formatDate } from '#/views/im/utils/time' |
| | | |
| | | import { ImFriendAddSource } from '../../../utils/constants' |
| | | import { toUserCardTarget } from '../../../utils/message' |
| | | import { getGenderColor, getGenderIcon } from '../../../utils/user' |
| | | import { useFriendStore } from '../../store/friendStore' |
| | | import { FriendAddDialog } from '../friend' |
| | | import RecommendCardDialog from './recommend-card-dialog.vue' |
| | | import UserAvatar from './user-avatar.vue' |
| | | |
| | | defineOptions({ name: 'ImUserInfo' }) |
| | | |
| | | const props = withDefaults( |
| | | defineProps<{ |
| | | /** å 好忥æºï¼é»è®¤ SEARCHï¼åè§ ImFriendAddSource */ |
| | | addSource?: number |
| | | /** æ¥æºé带信æ¯ï¼addSource=GROUPï¼ç¾¤èï¼æ¶ä¼ 群åï¼ç¨äºãææ¯ XX 群ç YYãé¢å¡«è¯æ¯ */ |
| | | addSourceExtra?: string |
| | | /** 夿³¨ï¼ä»
relation=friend æ¶ææï¼ç©ºä¸² â æ¾ç¤º"æ·»å 夿³¨"å ä½ */ |
| | | displayName?: string |
| | | /** UserAvatar é¢è§å± z-indexï¼æ¾å¨é« z-index æµ®å±ï¼å¦ UserInfoCardï¼ééæå¨æ¬é« */ |
| | | previewZIndex?: number |
| | | relation?: UserInfoRelation |
| | | /** èµ·æç¨æ·ï¼è³å°è¦æ idï¼æ§å« / é¨é¨ç±ç»ä»¶æ id ææåå¹¶è¡¥é½ */ |
| | | user: null | User |
| | | }>(), |
| | | { |
| | | addSource: ImFriendAddSource.SEARCH, |
| | | addSourceExtra: '', |
| | | displayName: '', |
| | | previewZIndex: 2000, |
| | | relation: 'readonly' |
| | | } |
| | | ) |
| | | |
| | | const emit = defineEmits<{ |
| | | /** ç¨æ·ç¹"åæ¶æ¯"ï¼å¯¼èª / å
³æµ®å±çåºæ¯ç¸å
³å¨ä½ç±ç¶çº§æ¿æ
ï¼æ¯å¦ UserInfoCard è¦ closeï¼ */ |
| | | chat: [user: User] |
| | | /** å é¤è系人æååéç¥ç¶çº§ï¼confirm + è° store é½å¨æ¬ç»ä»¶å
åå®ï¼ï¼ç¶çº§å
³æµ®å± / æ¸
éä¸ç */ |
| | | deleted: [user: User] |
| | | /** 夿³¨è½åºæååéç¥ç¶çº§ï¼ç¨äºåæ¥ç¶çº§ææçæ¬å° FriendLite / Friend 坿¬ï¼å¦ contact 页ç selectionï¼ */ |
| | | saved: [value: string] |
| | | }>() |
| | | |
| | | /** |
| | | * å
³ç³»æï¼å³å®å¤æ³¨è¡ / å³ä¸ "..." èå / åºé¨å¨ä½åºçå
容 |
| | | * - friend: 夿³¨ï¼å¯ç¼è¾ï¼+ "..." å é¤èå + 3 徿 å¨ä½ |
| | | * - stranger: åæé®"å 为好å" |
| | | * - self: åæé®"ä¸è½åèªå·±è天" disabled |
| | | * - readonly: 䏿¸²æå¤æ³¨ / èå / å¨ä½åºï¼ä»
å±ç¤ºå¤´é¨ä¿¡æ¯ |
| | | */ |
| | | export type UserInfoRelation = 'friend' | 'readonly' | 'self' | 'stranger' |
| | | |
| | | const friendStore = useFriendStore() |
| | | |
| | | const full = ref<null | User>(props.user) // èµ·æ user + getSimpleUser åå¹¶åç宿´å¯¹è±¡ï¼æ§å« / é¨é¨è¡¥é½ç¨ï¼ |
| | | |
| | | /** 主æ é¢ï¼å¤æ³¨ä¼å
ï¼å¥½ååºæ¯ï¼ï¼å
¶æ¬¡åæµç§° */ |
| | | const headerName = computed(() => props.displayName || full.value?.nickname || '') |
| | | |
| | | const deptText = computed(() => full.value?.deptName || '-') |
| | | |
| | | const genderIcon = computed(() => getGenderIcon(full.value?.sex)) |
| | | const genderColor = computed(() => getGenderColor(full.value?.sex)) |
| | | |
| | | /** 好åå
³ç³»è®°å½ï¼æ¥æº / æ·»å æ¶é´ / æ¯å¦æé»ä»è¿éåï¼ä»
friend æä¸æææä¹ï¼ */ |
| | | const friendInfo = computed(() => |
| | | props.user?.id ? friendStore.getFriend(props.user.id) : undefined |
| | | ) |
| | | |
| | | /** æ¯å¦å·²æé»ï¼èå项ãå å
¥é»åå / ç§»åºé»ååãæè¿ä¸ªåæ¢ */ |
| | | const isBlocked = computed(() => !!friendInfo.value?.blocked) |
| | | |
| | | const editingRemark = ref(false) // 夿³¨å
èç¼è¾ï¼editingRemark æ§å¶è¾å
¥æï¼user 忢æ¶ç±ä¸é¢ç watch å¤ä½é¿å
èææ³æ¼ |
| | | const remarkInput = ref('') |
| | | const remarkInputRef = ref<null | { focus: () => void; select?: () => void }>(null) |
| | | |
| | | /** |
| | | * user.id ååçç»ä¸å¤çï¼ |
| | | * 1. èµ·æç¨ prop å
åºé¦å±ï¼full = props.userï¼ï¼å getSimpleUser å½ä¸ååå¹¶æ¿æ¢ |
| | | * 2. 顺便å¤ä½å¤æ³¨ç¼è¾æï¼é¿å
ä¸ä¸ä¸ªç¨æ·çèè¾å
¥æ³æ¼å°ä¸ä¸ä¸ª |
| | | * 3. ç«æç¨ id æ¯å¯¹ä¸¢å¼éæ§ååº |
| | | */ |
| | | watch( |
| | | () => props.user?.id, |
| | | async (id) => { |
| | | full.value = props.user |
| | | editingRemark.value = false |
| | | if (!id) { |
| | | return |
| | | } |
| | | const data = (await getSimpleUser(id)) as User |
| | | if (props.user?.id !== id) { |
| | | return |
| | | } |
| | | full.value = { ...props.user, ...data } |
| | | }, |
| | | { immediate: true } |
| | | ) |
| | | |
| | | /** 夿³¨è¡ç¹å»ï¼è¿ç¼è¾æ + æå½å夿³¨çè¿è¾å
¥æ¡ï¼ä¸ä¸å¸§æç¦ç¹ / å
¨éäº¤ç» el-input */ |
| | | async function handleRowClick() { |
| | | if (editingRemark.value) { |
| | | return |
| | | } |
| | | remarkInput.value = props.displayName || '' |
| | | editingRemark.value = true |
| | | await nextTick() |
| | | remarkInputRef.value?.focus() |
| | | remarkInputRef.value?.select?.() |
| | | } |
| | | |
| | | /** |
| | | * ä¿å夿³¨ï¼ |
| | | * 1. éå
¥ä¿æ¤ââblur + Enter åæ¶è§¦ååªèµ°ä¸æ¬¡ï¼ç¼è¾æå
å¤ä½ï¼ |
| | | * 2. æ ååè·³è¿å端è°ç¨ + 䏿 savedï¼é¿å
ç¶çº§è¯¯åæ¥ |
| | | * 3. è½åºæå / 失败é½å¨ç»ä»¶å
èªéï¼æåæ saved 让ç¶çº§åæ¥æ¬å°å¯æ¬ï¼å¤±è´¥çç¼è¾æå¤çå±ç¤ºå¼ä¸å¨ |
| | | */ |
| | | async function saveRemark() { |
| | | if (!editingRemark.value) { |
| | | return |
| | | } |
| | | const userId = props.user?.id |
| | | if (!userId) { |
| | | editingRemark.value = false |
| | | return |
| | | } |
| | | const next = remarkInput.value.trim() |
| | | editingRemark.value = false |
| | | if (next === (props.displayName || '')) { |
| | | return |
| | | } |
| | | await friendStore.setFriendDisplayName(userId, next) |
| | | message.success('å·²æ´æ°å¤æ³¨') |
| | | emit('saved', next) |
| | | } |
| | | |
| | | function cancelEditRemark() { |
| | | editingRemark.value = false |
| | | } |
| | | |
| | | /** åæ¶æ¯ï¼å¯¼èª / å
³æµ®å±è¿äº"ä¸å¡ä¾§"å¨ä½äº¤ç»ç¶çº§ï¼æ¬ç»ä»¶åªè´è´£éç¥ */ |
| | | function handleChat() { |
| | | if (!props.user) { |
| | | return |
| | | } |
| | | emit('chat', props.user) |
| | | } |
| | | |
| | | /** å ä½æç¤ºï¼è¯é³ / è§é¢è天è½åå°æªæ¥å
¥ï¼å
以"å¼åä¸"å好æç¤º */ |
| | | function handleComingSoon(featureName: string) { |
| | | message.info(`${featureName} åè½å¼åä¸`) |
| | | } |
| | | |
| | | // ==================== æ·»å 好å / å é¤å¥½å ==================== |
| | | |
| | | const friendAddDialogRef = ref<InstanceType<typeof FriendAddDialog>>() // å 好åå¼¹çª refï¼handleAddFriend è° open({ presetUser, addSource, addSourceExtra }) 触å |
| | | |
| | | const recommendDialogRef = ref<InstanceType<typeof RecommendCardDialog>>() // æ¨èåçå¼¹çª refï¼handleRecommend è°ç¨ open({ target }) æå¼ |
| | | /** æä»æ¨èç»æåï¼å¼¹ RecommendCardDialog éç®æ ä¼è¯ */ |
| | | function handleRecommend() { |
| | | if (!props.user?.id) { |
| | | return |
| | | } |
| | | const target = toUserCardTarget(full.value) |
| | | if (!target) { |
| | | return |
| | | } |
| | | recommendDialogRef.value?.open({ target }) |
| | | } |
| | | |
| | | /** å 为好åï¼å¼¹ FriendAddDialogï¼å¸¦é¢å¡«ç¨æ·ï¼ï¼è®©ç¨æ·å¡«ç³è¯·çç± + 夿³¨åååç³è¯· */ |
| | | function handleAddFriend() { |
| | | if (!props.user?.id) { |
| | | return |
| | | } |
| | | const presetUser: SystemUserApi.UserSimple = { |
| | | id: props.user.id, |
| | | nickname: props.user.nickname, |
| | | avatar: props.user.avatar, |
| | | sex: props.user.sex, |
| | | deptId: props.user.deptId, |
| | | deptName: props.user.deptName |
| | | } as SystemUserApi.UserSimple |
| | | friendAddDialogRef.value?.open({ |
| | | presetUser, |
| | | addSource: props.addSource, |
| | | addSourceExtra: props.addSourceExtra |
| | | }) |
| | | } |
| | | |
| | | /** å å
¥é»ååï¼confirm â friendStore.blockFriendï¼å端 FRIEND_BLOCK æ¨å°æ¶ç± dispatcher 忥å¤ç«¯ */ |
| | | async function handleBlock() { |
| | | if (!props.user?.id) { |
| | | return |
| | | } |
| | | const target = props.user |
| | | try { |
| | | await confirm(`ç¡®å®å°ã${target.nickname || ''}ãå å
¥é»åååï¼`, 'å å
¥é»åå') |
| | | } catch { |
| | | return |
| | | } |
| | | await friendStore.blockFriend(target.id) |
| | | message.success('å·²å å
¥é»åå') |
| | | } |
| | | |
| | | /** ç§»åºé»ååï¼æä½æ¸©åä¸å¼¹ confirmï¼å端 FRIEND_UNBLOCK æ¨å°æ¶ç± dispatcher 忥å¤ç«¯ */ |
| | | async function handleUnblock() { |
| | | if (!props.user?.id) { |
| | | return |
| | | } |
| | | await friendStore.unblockFriend(props.user.id) |
| | | message.success('已移åºé»åå') |
| | | } |
| | | |
| | | /** å é¤è系人ï¼å¼¹èªå®ä¹ç¡®è®¤ï¼å«ãåæ¶æ¸
空è天记å½ãé项ï¼â friendStore.deleteFriend â éç¥ç¶çº§å
³æµ®å± / æ¸
éä¸ */ |
| | | async function handleDeleteFriend() { |
| | | if (!props.user?.id) { |
| | | return |
| | | } |
| | | const target = props.user |
| | | const clearConversation = ref(true) |
| | | try { |
| | | await confirm({ |
| | | cancelText: 'åæ¶', |
| | | confirmText: 'å é¤', |
| | | content: h('div', { class: 'flex flex-col gap-3 text-sm' }, [ |
| | | h('div', `ç¡®å®å é¤å¥½åã${target.nickname || ''}ã?`), |
| | | h( |
| | | Checkbox, |
| | | { |
| | | checked: clearConversation.value, |
| | | 'onUpdate:checked': (value: boolean) => { |
| | | clearConversation.value = value |
| | | } |
| | | }, |
| | | () => 'åæ¶æ¸
空è天记å½' |
| | | ) |
| | | ]), |
| | | icon: 'warning', |
| | | title: 'å é¤è系人' |
| | | }) |
| | | } catch { |
| | | return |
| | | } |
| | | await friendStore.deleteFriend(target.id, clearConversation.value) |
| | | message.success('å·²å é¤å¥½å') |
| | | emit('deleted', target) |
| | | } |
| | | </script> |
| | | |
| | | <template> |
| | | <!-- |
| | | ç¨æ·åçèªå
å«ç»ä»¶ï¼æµ®å± / è¡å
éç¨ï¼ |
| | | - ç± UserInfoCardï¼æµ®å±ï¼å contact/index.vueï¼è¡å
ï¼å
±ç¨ï¼UserInfoCard æå®æ¾è¿ teleport æµ®å±ï¼contact ç´æ¥ mount å°å³æ |
| | | - å
³ç³»æç± relation prop å³å®ï¼friend / stranger / self / readonlyï¼ï¼å¯¹åºå³ä¸ "..." èå + åºé¨å¨ä½åºä¸¤åé½å
å卿¬ç»ä»¶ |
| | | - 夿³¨ / å é¤è系人 / å 为好åç store æä½é½å¨æ¬ç»ä»¶å
éç¯ï¼ç¶çº§ä»
çå¬ chat / deleted / saved çéç¥åå¯¼èª / å
³æµ®å± / 忥坿¬ |
| | | --> |
| | | <div> |
| | | <!-- é¡¶é¨ï¼å¤´åï¼å·¦ï¼ + åå + æ§å«å¾æ + æµç§° / é¨é¨ï¼å³ï¼ + å³ä¸ "..." èåï¼ä»
friend æï¼ --> |
| | | <div class="flex gap-3 items-start"> |
| | | <UserAvatar |
| | | :id="full?.id" |
| | | :url="full?.avatar" |
| | | :name="full?.nickname" |
| | | :size="56" |
| | | :clickable="false" |
| | | previewable |
| | | :preview-z-index="previewZIndex" |
| | | /> |
| | | <div class="flex-1 min-w-0"> |
| | | <div class="flex items-center gap-1.5"> |
| | | <span |
| | | class="text-lg font-semibold leading-snug truncate text-[var(--ant-color-text)]" |
| | | > |
| | | {{ headerName }} |
| | | </span> |
| | | <!-- æ§å«å°å¾æ ï¼ç·èã女ç²ï¼æªç¥ / 0 ä¸å±ç¤ºï¼å¯¹é½å¾®ä¿¡çç½åæ³ --> |
| | | <Icon |
| | | v-if="genderIcon" |
| | | :icon="genderIcon" |
| | | :size="16" |
| | | :color="genderColor" |
| | | class="flex-shrink-0" |
| | | /> |
| | | </div> |
| | | <div class="mt-2 space-y-1 text-13px text-[var(--ant-color-text-secondary)]"> |
| | | <!-- ä»
å½å¤æ³¨å·²è®¾æ¶å±ç¤ºæµç§°å¯è¡ï¼æªè®¾ç½®æ¶ä¸»æ é¢å°±æ¯ nicknameï¼é¿å
éå¤ --> |
| | | <div v-if="displayName" class="truncate">æµç§°ï¼{{ full?.nickname }}</div> |
| | | <div class="truncate">é¨é¨ï¼{{ deptText }}</div> |
| | | </div> |
| | | </div> |
| | | <!-- å³ä¸ "..." èåï¼ä»
friend æå±ç¤ºï¼å«ãå å
¥/ç§»åºé»ååã + ãå é¤è系人ã --> |
| | | <div v-if="relation === 'friend'" class="flex-shrink-0"> |
| | | <Dropdown :trigger="['click']" placement="bottomRight" overlay-class-name="im-user-info__more-menu"> |
| | | <div |
| | | class="flex items-center justify-center w-7 h-7 rounded cursor-pointer hover:bg-[var(--ant-color-fill-secondary)]" |
| | | > |
| | | <Icon icon="ep:more-filled" :size="18" class="text-[var(--ant-color-text-secondary)]" /> |
| | | </div> |
| | | <template #overlay> |
| | | <Menu> |
| | | <!-- æä»æ¨èç»æåï¼ä»¥ä¸ªäººåçæ¶æ¯å½¢å¼åå°éä¸çä¼è¯ --> |
| | | <Menu.Item @click="handleRecommend">æä»æ¨èç»æå</Menu.Item> |
| | | <!-- æé» / ç§»åºé»ååï¼æ friendInfo.blocked åæ¢ææ¡ --> |
| | | <Menu.Item v-if="!isBlocked" divided @click="handleBlock"> |
| | | å å
¥é»åå |
| | | </Menu.Item> |
| | | <Menu.Item v-else divided @click="handleUnblock">ç§»åºé»åå</Menu.Item> |
| | | <Menu.Item divided @click="handleDeleteFriend"> |
| | | <span class="text-[var(--ant-color-error)]">å é¤è系人</span> |
| | | </Menu.Item> |
| | | </Menu> |
| | | </template> |
| | | </Dropdown> |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- 夿³¨è¡ï¼ä»
friend æå±ç¤ºï¼æªç¼è¾æ¶æ´è¡å¯ç¹ï¼ç¼è¾æç¨ el-input è¡å
æ¿æ¢ value å --> |
| | | <template v-if="relation === 'friend'"> |
| | | <div class="my-4 h-px bg-[var(--ant-color-border-secondary)]"></div> |
| | | <div |
| | | class="flex gap-5 items-center px-1.5 py-1.5 text-sm" |
| | | :class=" |
| | | !editingRemark |
| | | ? 'group cursor-pointer rounded transition-colors hover:bg-[var(--ant-color-fill-tertiary)]' |
| | | : '' |
| | | " |
| | | @click="handleRowClick" |
| | | > |
| | | <span class="flex-shrink-0 w-16 whitespace-nowrap text-[var(--ant-color-text-secondary)]">夿³¨</span> |
| | | <Input |
| | | v-if="editingRemark" |
| | | ref="remarkInputRef" |
| | | v-model:value="remarkInput" |
| | | size="small" |
| | | :maxlength="20" |
| | | placeholder="æ·»å 夿³¨" |
| | | class="flex-1" |
| | | @click.stop |
| | | @blur="saveRemark" |
| | | @keyup.enter="saveRemark" |
| | | @keyup.esc="cancelEditRemark" |
| | | /> |
| | | <template v-else> |
| | | <span |
| | | class="flex-1 min-w-0 truncate" |
| | | :class=" |
| | | displayName |
| | | ? 'text-[var(--ant-color-text)]' |
| | | : 'text-[var(--ant-color-text-placeholder)]' |
| | | " |
| | | > |
| | | {{ displayName || 'æ·»å 夿³¨' }} |
| | | </span> |
| | | <!-- é»è®¤ opacity:0 å ä½é¿å
hover æ¶å
¶å®åä½ç§»ï¼ä»
ç¶è¡ hover æ¶æµ®ç° --> |
| | | <Icon |
| | | icon="ant-design:edit-outlined" |
| | | :size="14" |
| | | class="flex-shrink-0 text-[var(--ant-color-text-secondary)] opacity-0 group-hover:opacity-100 transition-opacity" |
| | | /> |
| | | </template> |
| | | </div> |
| | | |
| | | <!-- ==================== æ´å¤ä¿¡æ¯ï¼æ¥æº / æ·»å æ¶é´ï¼å¯¹é½å¾®ä¿¡ãæåèµæãåå ==================== --> |
| | | <template v-if="friendInfo?.addSource || friendInfo?.addTime"> |
| | | <div class="my-4 h-px bg-[var(--ant-color-border-secondary)]"></div> |
| | | <div v-if="friendInfo?.addSource" class="flex gap-5 items-center px-1.5 py-1.5 text-sm"> |
| | | <span class="flex-shrink-0 w-16 whitespace-nowrap text-[var(--ant-color-text-secondary)]">æ¥æº</span> |
| | | <span class="flex-1 min-w-0 truncate text-[var(--ant-color-text)]"> |
| | | {{ getDictLabel(DICT_TYPE.IM_FRIEND_ADD_SOURCE, friendInfo.addSource) }} |
| | | </span> |
| | | </div> |
| | | <div v-if="friendInfo?.addTime" class="flex gap-5 items-center px-1.5 py-1.5 text-sm"> |
| | | <span class="flex-shrink-0 w-16 whitespace-nowrap text-[var(--ant-color-text-secondary)]">æ·»å æ¶é´</span> |
| | | <span class="flex-1 min-w-0 truncate text-[var(--ant-color-text)]"> |
| | | {{ formatDate(new Date(friendInfo.addTime), 'YYYY-MM-DD') }} |
| | | </span> |
| | | </div> |
| | | </template> |
| | | </template> |
| | | |
| | | <!-- å¨ä½åºï¼å¥½å = 3 徿 ï¼éç人 = å 为好åæé®ï¼èªå·± = disabledï¼readonly 䏿¸²æ --> |
| | | <template v-if="relation !== 'readonly'"> |
| | | <div class="my-4 h-px bg-[var(--ant-color-border-secondary)]"></div> |
| | | <div v-if="relation === 'friend'" class="flex justify-around"> |
| | | <!-- ä¸è¿å¾æ æé®ï¼å¾ä¸åä¸ã主è²ä½ä¸ºå¯ç¹æç¤ºï¼hover éä¸éæåº¦ååé¦ --> |
| | | <div |
| | | class="flex flex-col gap-1.5 items-center cursor-pointer text-13px text-[var(--ant-color-primary)] transition-opacity hover:opacity-75" |
| | | @click="handleChat" |
| | | > |
| | | <Icon icon="ant-design:message-outlined" :size="22" /> |
| | | <span>åæ¶æ¯</span> |
| | | </div> |
| | | <div |
| | | class="flex flex-col gap-1.5 items-center cursor-pointer text-13px text-[var(--ant-color-primary)] transition-opacity hover:opacity-75" |
| | | @click="handleComingSoon('è¯é³è天')" |
| | | > |
| | | <Icon icon="ant-design:phone-outlined" :size="22" /> |
| | | <span>è¯é³è天</span> |
| | | </div> |
| | | <div |
| | | class="flex flex-col gap-1.5 items-center cursor-pointer text-13px text-[var(--ant-color-primary)] transition-opacity hover:opacity-75" |
| | | @click="handleComingSoon('è§é¢è天')" |
| | | > |
| | | <Icon icon="ant-design:video-camera-outlined" :size="22" /> |
| | | <span>è§é¢è天</span> |
| | | </div> |
| | | </div> |
| | | <div v-else-if="relation === 'self'" class="flex justify-center"> |
| | | <Button type="primary" disabled>ä¸è½åèªå·±è天</Button> |
| | | </div> |
| | | <div v-else class="flex justify-center"> |
| | | <Button type="primary" @click="handleAddFriend">å 为好å</Button> |
| | | </div> |
| | | </template> |
| | | |
| | | <!-- å 好åå¼¹çªï¼æºå¸¦é¢å¡«ç¨æ·è·³è¿æç´¢æ¥éª¤ï¼ç´æ¥è¿ç³è¯·è¡¨åï¼çç±æ addSource åºåè¯æ¯ï¼ --> |
| | | <FriendAddDialog ref="friendAddDialogRef" /> |
| | | |
| | | <!-- æä»æ¨èç»æåå¼¹çªï¼ä»
friend æä¸åºç°å
¥å£ --> |
| | | <RecommendCardDialog ref="recommendDialogRef" /> |
| | | </div> |
| | | </template> |
| | | |
| | | <style> |
| | | /* |
| | | é scopedï¼el-dropdown ç䏿èåèµ° teleport å° bodyï¼scoped éä¸å°ã |
| | | UserInfoCard æµ®å±ç¨ z-9998ï¼è¦æè¿åæ¬å°æ´é«ï¼é»è®¤ --ant-z-index-popup-base ~2050 ä¼è¢«é®ç½©åä½ï¼ã |
| | | */ |
| | | .im-user-info__more-menu { |
| | | z-index: 10000 !important; |
| | | } |
| | | </style> |