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