admin-vue3/src/views/im/home/pages/contact/FriendRequestDetail.vue

174 lines
6.0 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<template>
<!--
新的朋友详情面板
- 头像 + 昵称
- 申请理由块
- 来源行
- 操作按钮我发起 / 别人加我 + 未处理 / 同意 / 拒绝状态切换
-->
<div class="flex flex-col items-center px-6 pt-12">
<UserAvatar
:id="peerUserId"
:url="peerAvatar"
:name="peerNickname"
:size="64"
:clickable="false"
/>
<div class="mt-3 text-base font-semibold text-[var(--el-text-color-primary)]">
{{ peerNickname }}
</div>
<!-- 申请理由块 -->
<div
v-if="request.applyContent"
class="w-full max-w-[420px] mt-6 px-3.5 py-3 rounded-md bg-[var(--el-fill-color-light)] text-13px text-[var(--el-text-color-primary)]"
>
<span v-if="iSentIt">我:</span>
<span class="ml-1">{{ request.applyContent }}</span>
</div>
<!-- 来源行 -->
<div
v-if="addSourceLabel"
class="w-full max-w-[420px] mt-3 flex items-center text-13px text-[var(--el-text-color-secondary)]"
>
<span class="w-12 flex-shrink-0">来源</span>
<span class="text-[var(--el-text-color-primary)]">{{ addSourceLabel }}</span>
</div>
<!-- 拒绝理由(拒绝状态展示):长文本走 break-words 自动折行,避免横向溢出 -->
<!-- TODO @AI会折行拒绝理由 -->
<!-- TODO @AI我指的是 -->
<!-- TODO @AI尽量对齐下微信的样式 /Users/yunai/Downloads/iShot_2026-05-04_10.42.58.png
/Users/yunai/Downloads/iShot_2026-05-04_10.42.46.png -->
<div
v-if="request.handleResult === ImFriendRequestHandleResult.REFUSED && request.handleContent"
class="w-full max-w-[420px] mt-3 flex items-start text-13px text-[var(--el-text-color-secondary)]"
>
<span class="w-12 flex-shrink-0">拒绝理由</span>
<span class="flex-1 min-w-0 break-words text-[var(--el-text-color-primary)]">
{{ request.handleContent }}
</span>
</div>
<!-- 操作按钮 -->
<div class="w-full max-w-[420px] mt-8 flex justify-center">
<!-- 我发起 + 等待中:禁用「等待对方验证」 -->
<el-button
v-if="iSentIt && request.handleResult === ImFriendRequestHandleResult.UNHANDLED"
disabled
>
等待对方验证
</el-button>
<!-- 别人加我 + 等待中:同意 / 拒绝 -->
<template v-if="!iSentIt && request.handleResult === ImFriendRequestHandleResult.UNHANDLED">
<el-button @click="handleRefuse" :loading="refusing">拒绝</el-button>
<el-button type="primary" @click="handleAgree" :loading="agreeing"> </el-button>
</template>
<!-- 已同意发消息 -->
<el-button
v-if="request.handleResult === ImFriendRequestHandleResult.AGREED"
type="primary"
@click="emit('chat', peerUserId)"
>
发消息
</el-button>
<!-- 已拒绝占位禁用按钮 -->
<el-button v-if="request.handleResult === ImFriendRequestHandleResult.REFUSED" disabled>
已拒绝
</el-button>
</div>
</div>
</template>
<script lang="ts" setup>
import { computed, ref } from 'vue'
import { useMessage } from '@/hooks/web/useMessage'
import { ElMessageBox } from 'element-plus'
import UserAvatar from '../../components/user/UserAvatar.vue'
import { useFriendStore } from '../../store/friendStore'
import { getCurrentUserId } from '../../../utils/storage'
import { ImFriendRequestHandleResult } from '../../../utils/constants'
import { DICT_TYPE, getDictLabel } from '@/utils/dict'
import type { FriendRequest } from '../../types'
defineOptions({ name: 'ImContactFriendRequestDetail' })
const props = defineProps<{
request: FriendRequest
}>()
const emit = defineEmits<{
chat: [peerUserId: number]
}>()
const friendStore = useFriendStore()
const message = useMessage()
const currentUserId = Number(getCurrentUserId() || 0)
/** 是不是我发起的fromUserId === me */
const iSentIt = computed(() => props.request.fromUserId === currentUserId)
/** 对端的用户编号 / 昵称 / 头像 */
const peerUserId = computed(() =>
iSentIt.value ? props.request.toUserId : props.request.fromUserId
)
const peerNickname = computed(() =>
iSentIt.value
? props.request.toNickname || String(props.request.toUserId)
: props.request.fromNickname || String(props.request.fromUserId)
)
const peerAvatar = computed(() =>
iSentIt.value ? props.request.toAvatar : props.request.fromAvatar
)
/** 添加来源文案:走字典,对齐后端 ImFriendAddSourceEnum */
// TODO @AI通过 html 里处理掉。不用抽个方法;
const addSourceLabel = computed(() =>
props.request.addSource
? getDictLabel(DICT_TYPE.IM_FRIEND_ADD_SOURCE, props.request.addSource)
: ''
)
const agreeing = ref(false)
const refusing = ref(false)
/** 同意申请 */
async function handleAgree() {
agreeing.value = true
try {
await friendStore.agreeFriendRequest(props.request.id)
message.success('已同意好友申请')
} finally {
agreeing.value = false
}
}
/** 拒绝申请:弹 prompt 收集可选拒绝理由(点取消则中止),随后调 store 落库 + 提示 */
async function handleRefuse() {
// 1. 弹 prompt 收集拒绝理由(最多 255 字);用户点「取消」会 reject中止后续流程
let handleContent: string | undefined
try {
const result = await ElMessageBox.prompt('可填写拒绝理由(选填)', '拒绝好友申请', {
confirmButtonText: '拒绝',
cancelButtonText: '取消',
inputType: 'textarea',
inputValue: '',
inputPlaceholder: '不填则不告知对方原因',
inputValidator: (value: string) => (value || '').length <= 255 || '最多 255 个字符'
})
handleContent = (result as { value?: string }).value || undefined
} catch {
return
}
// 2. 调 store 拒绝申请;按钮 loading 期间不允许重复点击
refusing.value = true
try {
await friendStore.refuseFriendRequest(props.request.id, handleContent)
message.success('已拒绝好友申请')
} finally {
refusing.value = false
}
}
</script>