gaoluyang
2026-06-29 27cd042df9aca0383a49f3514bc21958dd890912
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
<script lang="ts" setup>
import type { GroupMemberLite } from '../group'
 
import { computed, ref } from 'vue'
 
import { Button, Modal } from 'ant-design-vue'
 
import { getCurrentUserId } from '#/views/im/utils/auth'
 
import { useGroupStore } from '../../store/groupStore'
import { GroupMemberPickerPanel } from '../picker'
 
defineOptions({ name: 'ImRtcCallMemberPickerDialog' })
 
const emit = defineEmits<{
  /** 选完点完成;携带选中的 userId 列表 */
  success: [selectedIds: number[]]
}>()
 
type PickerMode = 'add' | 'invite'
 
const groupStore = useGroupStore()
 
const visible = ref(false) // 弹窗显隐
const groupId = ref(0) // 当前群编号;open 时由调用方传入
const mode = ref<PickerMode>('invite') // 弹窗用途;invite=发起群通话选邀请人 / add=通话中追加成员
const excludeUserIds = ref<number[]>([]) // 置灰的 userId 列表;add 场景把已在通话内的人禁用
const selectedIds = ref<number[]>([]) // 当前选中的 userId 列表;GroupMemberPickerPanel v-model 绑过来
 
/** 标题;按用途切换 */
const title = computed(() => (mode.value === 'add' ? '添加成员' : '选择成员'))
 
/** 群成员列表;从 groupStore 现取,map 成 GroupMemberLite */
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
  }))
})
 
/** 自己不出现在选项里 */
const hideIds = computed<number[]>(() => {
  const myId = getCurrentUserId()
  return myId ? [myId] : []
})
 
/** 已在通话内的成员置灰 */
const disabledIds = computed<number[]>(() => excludeUserIds.value)
 
/** 是否可提交:至少选 1 个 */
const canSubmit = computed(() => selectedIds.value.length > 0)
 
/** 打开弹窗;excludeUserIds 用于「添加成员」时把已在通话内的人置灰 */
function open(opts: { excludeUserIds?: number[]; groupId: number; mode?: PickerMode; }) {
  groupId.value = opts.groupId
  mode.value = opts.mode || 'invite'
  excludeUserIds.value = opts.excludeUserIds || []
  selectedIds.value = []
  visible.value = true
}
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
 
/** 点完成:emit 选中 ID 列表给父级 + 关闭弹窗;提交按钮 disabled 已保证 selectedIds 非空 */
function handleOk() {
  emit('success', [...selectedIds.value])
  visible.value = false
}
</script>
 
<template>
  <!--
    通话成员选择弹窗:发起群通话时选邀请人 / 通话中添加成员;
    选择 UI 委托 GroupMemberPickerPanel;自己 hide、已在通话内的成员 disabled
  -->
  <Modal
    v-model:open="visible"
    :title="title"
    width="720px"
    :mask-closable="false"
    class="im-picker-dialog"
  >
    <div class="h-[480px]">
      <GroupMemberPickerPanel
        v-model:selected-ids="selectedIds"
        :members="members"
        :disabled-ids="disabledIds"
        :hide-ids="hideIds"
      />
    </div>
    <template #footer>
      <Button @click="visible = false">取消</Button>
      <Button type="primary" :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>