+
通过
@@ -225,7 +230,10 @@ watch(
groupId.value && visible.value
? groupRequestStore.unhandledList
.filter((request) => request.groupId === groupId.value)
- .map((request) => `${request.id}:${request.inviterUserId ?? ''}:${request.applyContent ?? ''}`)
+ .map(
+ (request) =>
+ `${request.id}:${request.inviterUserId ?? ''}:${request.applyContent ?? ''}`
+ )
.join(',')
: null,
(current, previous) => {
@@ -241,12 +249,22 @@ watch(
}
)
-async function fetchList(groupId: number) {
+let fetchSeq = 0 // 单调递增请求序号;同群也会因为 WS 1503 推送触发额外 fetch,乱序返回时旧响应不能覆盖新数据
+async function fetchList(targetGroupId: number) {
+ const seq = ++fetchSeq
loading.value = true
try {
- groupList.value = (await getGroupRequestListByGroupId(groupId)) || []
+ const data = (await getGroupRequestListByGroupId(targetGroupId)) || []
+ // 期间切群 / 关弹窗 / 又触发更新 fetch:丢响应
+ if (seq !== fetchSeq || !visible.value || groupId.value !== targetGroupId) {
+ return
+ }
+ groupList.value = data
} finally {
- loading.value = false
+ // 旧请求 finally 命中时新请求仍在跑,跳过避免提前关 loading
+ if (seq === fetchSeq) {
+ loading.value = false
+ }
}
}
diff --git a/src/views/im/home/pages/contact/FriendRequestDetail.vue b/src/views/im/home/pages/contact/FriendRequestDetail.vue
index c470113aa..564879426 100644
--- a/src/views/im/home/pages/contact/FriendRequestDetail.vue
+++ b/src/views/im/home/pages/contact/FriendRequestDetail.vue
@@ -140,22 +140,38 @@ const peerUser = computed(() => ({
avatar: peerAvatar.value
}))
+// 各自的 loading 用于按钮 spinner 显示;processing 是跨按钮互斥锁,避免同意 / 拒绝并发提交同一申请
const agreeing = ref(false)
const refusing = ref(false)
+const processing = ref(false)
-/** 同意申请 */
+/** 同意申请:互斥锁 + 状态二次校验,避免并发 / 服务端已处理后再次提交 */
async function handleAgree() {
+ if (processing.value) {
+ return
+ }
+ if (props.request.handleResult !== ImFriendRequestHandleResult.UNHANDLED) {
+ return
+ }
+ processing.value = true
agreeing.value = true
try {
await friendStore.agreeFriendRequest(props.request.id)
message.success('已同意好友申请')
} finally {
agreeing.value = false
+ processing.value = false
}
}
/** 拒绝申请:弹 prompt 收集可选拒绝理由(点取消则中止),随后调 store 落库 + 提示 */
async function handleRefuse() {
+ if (processing.value) {
+ return
+ }
+ if (props.request.handleResult !== ImFriendRequestHandleResult.UNHANDLED) {
+ return
+ }
// 1. 弹 prompt 收集拒绝理由(最多 255 字);用户点「取消」会 reject,中止后续流程
let handleContent: string | undefined
try {
@@ -171,13 +187,21 @@ async function handleRefuse() {
} catch {
return
}
- // 2. 调 store 拒绝申请;按钮 loading 期间不允许重复点击
+ // 2. prompt 期间状态可能被跨端改成 AGREED / REFUSED,再校验一次避免重复提交
+ if (processing.value) {
+ return
+ }
+ if (props.request.handleResult !== ImFriendRequestHandleResult.UNHANDLED) {
+ return
+ }
+ processing.value = true
refusing.value = true
try {
await friendStore.refuseFriendRequest(props.request.id, handleContent)
message.success('已拒绝好友申请')
} finally {
refusing.value = false
+ processing.value = false
}
}
diff --git a/src/views/im/home/pages/contact/index.vue b/src/views/im/home/pages/contact/index.vue
index 5c9b6c1fc..4261129e8 100644
--- a/src/views/im/home/pages/contact/index.vue
+++ b/src/views/im/home/pages/contact/index.vue
@@ -86,7 +86,7 @@