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/group/group-member-add-dialog.vue |  167 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 167 insertions(+), 0 deletions(-)

diff --git a/src/views/im/home/components/group/group-member-add-dialog.vue b/src/views/im/home/components/group/group-member-add-dialog.vue
new file mode 100644
index 0000000..b0d0e12
--- /dev/null
+++ b/src/views/im/home/components/group/group-member-add-dialog.vue
@@ -0,0 +1,167 @@
+<script lang="ts" setup>
+import type { GroupMemberLite } from './group-member.vue'
+
+import { computed, ref } from 'vue'
+
+import { CommonStatusEnum } from '#/packages/constants/src'
+
+import { Button, message, Modal } from 'ant-design-vue'
+
+import { inviteGroupMember } from '#/api/im/group/member'
+import { getCurrentUserId } from '#/views/im/utils/auth'
+import { GROUP_MAX_MEMBER } from '#/views/im/utils/config'
+import { ImGroupMemberRole } from '#/views/im/utils/constants'
+
+import { useFriendStore } from '../../store/friendStore'
+import { useGroupStore } from '../../store/groupStore'
+import { FriendPickerPanel } from '../picker'
+
+defineOptions({ name: 'ImGroupMemberAddDialog' })
+
+const emit = defineEmits<{
+  /** 閭�璇锋垚鍔燂紝鎼哄甫琚個璇风殑濂藉弸 id 鍒楄〃锛涚埗渚ч�氬父鐢ㄦ潵 reload 缇ゆ垚鍛� */
+  reload: [friendIds: number[]]
+}>()
+
+const friendStore = useFriendStore()
+const groupStore = useGroupStore()
+
+const visible = ref(false)
+const submitting = ref(false)
+const groupId = ref(0)
+const selectedIds = ref<number[]>([])
+
+defineExpose({
+  /** 鎵撳紑娣诲姞缇ゆ垚鍛樺脊绐楋細reset 鈫� 鐏屽弬 鈫� visible=true */
+  open(opts: { groupId: number }) {
+    groupId.value = opts.groupId
+    selectedIds.value = []
+    submitting.value = false
+    visible.value = true
+  }
+})
+
+/** 褰撳墠缇ゆ垚鍛樺垪琛細浠� groupStore 鐜板彇锛岄伩鍏嶉殢 groupId 鍙樺寲鏃剁埗渚� prop 鏇存柊寤惰繜 */
+const members = computed<GroupMemberLite[]>(() => {
+  const group = groupStore.getGroup(groupId.value)
+  return (group?.members || []).map((member) => ({
+    userId: member.userId,
+    nickname: member.nickname,
+    showName: member.displayUserName || member.nickname,
+    avatar: member.avatar,
+    status: member.status,
+    role: member.role
+  }))
+})
+
+/** 鍏ㄩ噺濂藉弸锛氱洿鎺ュ鐢� friendStore Lite 瑙嗗浘 */
+const friends = computed(() => friendStore.getActiveFriendLiteList)
+
+/** 宸插湪缇ら噷鐨勫ソ鍙� id锛氫紶缁� Panel 鐨� disabledIds 缃伆 + 涓嶈鍏ュ凡閫� */
+const disabledIds = computed<number[]>(() =>
+  members.value
+    .filter((member) => member.status !== CommonStatusEnum.DISABLE)
+    .map((member) => member.userId)
+)
+
+/** 鏄惁璧板鎵瑰垎鏀細缇ゅ紑鍚� joinApproval + 褰撳墠鐢ㄦ埛鏄櫘閫氭垚鍛橈紙缇や富 / 绠$悊鍛橀個璇风洿杩涳級 */
+const willGoApproval = computed(() => {
+  const group = groupStore.getGroup(groupId.value)
+  if (!group?.joinApproval) {
+    return false
+  }
+  const myId = getCurrentUserId()
+  if (!myId) {
+    return false
+  }
+  // 缇や富鐩村垽锛岄伩寮� members 寮傛鍔犺浇鐨勭獥鍙o紱admin 浠嶄緷璧� members
+  if (group.ownerUserId === myId) {
+    return false
+  }
+  // members 鏈埌浣嶆椂鏃犳硶鍒ゅ畾 admin锛屼繚瀹堟寜闈炲鎵瑰鐞嗭紝瀹佸彲婕忔姤銆岀瓑寰呭鎵广�嶄篃涓嶈鎶ョ粰鐪熷疄绠$悊鍛�
+  const myRole = members.value.find((member) => member.userId === myId)?.role
+  if (myRole == null) {
+    return false
+  }
+  return myRole !== ImGroupMemberRole.ADMIN
+})
+
+/** 褰撳墠缇ゅ凡鍚敤鎴愬憳鏁帮紙DISABLE 鍗抽��缇� / 琚涪涓嶈鍏ワ級锛岀敤浜庝笂闄愬垽瀹� */
+const activeMemberCount = computed(
+  () => members.value.filter((member) => member.status !== CommonStatusEnum.DISABLE).length
+)
+
+/** 閭�璇峰悗缇ゆ�讳汉鏁拌嫢瓒� GROUP_MAX_MEMBER锛屽墠绔厛鎷︼紱activeMemberCount + selectedIds.length 鍗抽個璇峰悗鐨勬垚鍛樻暟 */
+const willExceedLimit = computed(
+  () => activeMemberCount.value + selectedIds.value.length > GROUP_MAX_MEMBER
+)
+
+/** 娣诲姞鎸夐挳鍙偣锛氳嚦灏戞湁 1 涓柊閭�璇风殑濂藉弸 + 涓嶈秴缇や汉鏁颁笂闄� */
+const canSubmit = computed(() => selectedIds.value.length > 0 && !willExceedLimit.value)
+
+/** 閭�璇峰叆缇わ細璋� /im/group/invite锛屾垚鍔熷悗 emit reload 璁╃埗渚у埛鏂扮兢鎴愬憳 */
+async function handleOk() {
+  if (!groupId.value) {
+    return
+  }
+  const memberUserIds = [...selectedIds.value]
+  if (memberUserIds.length === 0) {
+    return
+  }
+  // 缇や汉鏁颁笂闄愬啑浣欓槻寰★細涓� canSubmit 閲嶅鍒や竴娆★紝闃叉鐘舵�佸湪 await 闂撮殭鍙樺寲鎴栬皟鐢ㄦ柟缁曡繃鎸夐挳鐩存帴璋�
+  if (activeMemberCount.value + memberUserIds.length > GROUP_MAX_MEMBER) {
+    message.warning(`缇ゆ垚鍛樹笂闄愪负 ${GROUP_MAX_MEMBER} 浜篳)
+    return
+  }
+  submitting.value = true
+  try {
+    await inviteGroupMember({ groupId: groupId.value, memberUserIds })
+    // 瀹℃壒鍒嗘敮锛氬悗绔粎钀藉鎵硅褰曪紝鏈叆缇�
+    message.success(willGoApproval.value ? '閭�璇峰凡鍙戣捣锛岀瓑寰呯兢涓� / 绠$悊鍛樺鎵�' : '閭�璇锋垚鍔�')
+    emit('reload', memberUserIds)
+    visible.value = false
+  } finally {
+    submitting.value = false
+  }
+}
+</script>
+
+<template>
+  <!--
+    娣诲姞缇ゆ垚鍛橈細閫夊ソ鍙嬮個璇峰叆缇わ紙宸插湪缇ゆ垚鍛樼疆鐏般�佷笉璁″叆宸查�夛級
+    - dialog 澹虫湰缁勪欢鎸佹湁锛涢�夋嫨 UI 濮旀墭 FriendPickerPanel
+    - 宸插湪缇ゆ垚鍛橀�氳繃 disabledIds 浼犲叆锛屼笉鍐嶈蛋 checked+disabled 鐨�"宸插嬀閫夌伆鎬�"
+    - 瀵瑰鎺ュ彛锛歳ef + open({ groupId }) + emit reload(friendIds)
+  -->
+  <Modal
+    v-model:open="visible"
+    title="娣诲姞缇ゆ垚鍛�"
+    width="720px"
+    :mask-closable="false"
+    class="im-picker-dialog"
+  >
+    <div class="h-[480px]">
+      <FriendPickerPanel
+        v-model:selected-ids="selectedIds"
+        :friends="friends"
+        :disabled-ids="disabledIds"
+      />
+    </div>
+
+    <template #footer>
+      <Button @click="visible = false">鍙栨秷</Button>
+      <Button type="primary" :loading="submitting" :disabled="!canSubmit" @click="handleOk">
+        娣诲姞
+      </Button>
+    </template>
+  </Modal>
+</template>
+
+<style scoped lang="scss">
+@use '../picker/picker-dialog' as picker;
+
+/* :deep 绌块�� el-dialog 鍐呴儴绫伙紱澶嶇敤 picker 鍏叡 mixin */
+.im-picker-dialog {
+  @include picker.styles;
+}
+</style>

--
Gitblit v1.9.3