| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <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 弿¥å è½½ççªå£ï¼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 ç"å·²å¾éç°æ" |
| | | - 坹夿¥å£ï¼ref + 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> |