From 27cd042df9aca0383a49f3514bc21958dd890912 Mon Sep 17 00:00:00 2001
From: gaoluyang <2820782392@qq.com>
Date: 星期一, 29 六月 2026 15:42:23 +0800
Subject: [PATCH] 银川 1.联调产品维护页面 2.添加IM即时通讯模块
---
src/views/im/home/components/user/user-info.vue | 447 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 447 insertions(+), 0 deletions(-)
diff --git a/src/views/im/home/components/user/user-info.vue b/src/views/im/home/components/user/user-info.vue
new file mode 100644
index 0000000..4c09cf1
--- /dev/null
+++ b/src/views/im/home/components/user/user-info.vue
@@ -0,0 +1,447 @@
+<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
+ /** 鏉ユ簮闄勫甫淇℃伅锛歛ddSource=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]
+ /** 鍒犻櫎鑱旂郴浜烘垚鍔熷悗閫氱煡鐖剁骇锛坈onfirm + 璋� 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) // 澶囨敞鍐呰仈缂栬緫锛歟ditingRemark 鎺у埗杈撳叆鎬侊紱user 鍒囨崲鏃剁敱涓嬮潰鐨� watch 澶嶄綅閬垮厤鑴忔�佹硠婕�
+const remarkInput = ref('')
+const remarkInputRef = ref<null | { focus: () => void; select?: () => void }>(null)
+
+/**
+ * user.id 鍙樺寲鐨勭粺涓�澶勭悊锛�
+ * 1. 璧锋墜鐢� prop 鍏滃簳棣栧睆锛坒ull = 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. 閲嶅叆淇濇姢鈥斺�攂lur + 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
+}
+
+/** 鍙戞秷鎭細瀵艰埅 / 鍏虫诞灞傝繖浜�"涓氬姟渚�"鍔ㄤ綔浜ょ粰鐖剁骇锛屾湰缁勪欢鍙礋璐i�氱煡 */
+function handleChat() {
+ if (!props.user) {
+ return
+ }
+ emit('chat', props.user)
+}
+
+/** 鍗犱綅鎻愮ず锛氳闊� / 瑙嗛鑱婂ぉ鑳藉姏灏氭湭鎺ュ叆锛屽厛浠�"寮�鍙戜腑"鍙嬪ソ鎻愮ず */
+function handleComingSoon(featureName: string) {
+ message.info(`${featureName} 鍔熻兘寮�鍙戜腑`)
+}
+
+// ==================== 娣诲姞濂藉弸 / 鍒犻櫎濂藉弸 ====================
+
+const friendAddDialogRef = ref<InstanceType<typeof FriendAddDialog>>() // 鍔犲ソ鍙嬪脊绐� ref锛歨andleAddFriend 璋� open({ presetUser, addSource, addSourceExtra }) 瑙﹀彂
+
+const recommendDialogRef = ref<InstanceType<typeof RecommendCardDialog>>() // 鎺ㄨ崘鍚嶇墖寮圭獥 ref锛歨andleRecommend 璋冪敤 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锛堣鍐咃級鍏辩敤锛歎serInfoCard 鎶婂畠鏀捐繘 teleport 娴眰锛宑ontact 鐩存帴 mount 鍒板彸鏍�
+ - 鍏崇郴鎬佺敱 relation prop 鍐冲畾锛坒riend / 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锛況eadonly 涓嶆覆鏌� -->
+ <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锛歟l-dropdown 鐨勪笅鎷夎彍鍗曡蛋 teleport 鍒� body锛宻coped 閫変笉鍒般��
+ UserInfoCard 娴眰鐢� z-9998锛岃鎶婅繖鍧楁姮鍒版洿楂橈紙榛樿 --ant-z-index-popup-base ~2050 浼氳閬僵鍘嬩綇锛夈��
+*/
+.im-user-info__more-menu {
+ z-index: 10000 !important;
+}
+</style>
--
Gitblit v1.9.3