admin-vben/apps/web-antd/src/views/im/home/components/group/group-create-dialog.vue

145 lines
4.2 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<script lang="ts" setup>
import type { FriendLite } from '../../types'
import { computed, ref } from 'vue'
import { Button, message, Modal } from 'ant-design-vue'
import { createGroup } from '#/api/im/group'
import { buildDefaultGroupName } from '../../../utils/group'
import { useFriendStore } from '../../store/friendStore'
import { useGroupStore } from '../../store/groupStore'
import { FriendPickerPanel } from '../picker'
defineOptions({ name: 'ImGroupCreateDialog' })
const emit = defineEmits<{
/** 创建成功,携带新群编号;父侧通常用来跳转到新群会话 */
created: [groupId: number]
}>()
const friendStore = useFriendStore()
const groupStore = useGroupStore()
const visible = ref(false)
const submitting = ref(false)
const lockedIds = ref<number[]>([])
const selectedIds = ref<number[]>([])
defineExpose({
/** 打开发起群聊弹窗reset → 灌参 → visible=true */
open(opts?: { lockedIds?: number[] }) {
lockedIds.value = opts?.lockedIds ? [...opts.lockedIds] : []
selectedIds.value = []
submitting.value = false
visible.value = true
}
})
/** 全量好友:直接复用 friendStore Lite 视图(带拼音字段供分桶用) */
const friends = computed<FriendLite[]>(() => friendStore.getActiveFriendLiteList)
/** 完成按钮可点:至少有 1 个非 locked 勾选locked 是入口锁定项,不算"用户主动选择" */
const canSubmit = computed(() => selectedIds.value.length > 0)
/** 拿到所有要进群的好友locked + selected建群默认群名按这批人生成 */
function resolveMembersToInvite(): FriendLite[] {
const seen = new Set<number>()
const result: FriendLite[] = []
const byId = new Map(friends.value.map((f) => [f.id, f]))
for (const id of lockedIds.value) {
if (seen.has(id)) {
continue
}
const friend = byId.get(id)
if (friend) {
seen.add(id)
result.push(friend)
}
}
for (const id of selectedIds.value) {
if (seen.has(id)) {
continue
}
const friend = byId.get(id)
if (friend) {
seen.add(id)
result.push(friend)
}
}
return result
}
/** 创建群聊:建群(同时邀请初始成员)→ upsert groupStore → emit created 让父页跳转新会话 */
async function handleOk() {
const members = resolveMembersToInvite()
if (members.length === 0) {
return
}
submitting.value = true
try {
const memberUserIds = members.map((m) => m.id)
const name = buildDefaultGroupName(members)
const group = await createGroup({ name, memberUserIds, joinApproval: false })
if (!group?.id) {
throw new Error('创建群失败:未返回群编号')
}
// 直接 upsert 进 groupStore省一次 fetchGroupList —— 服务端返回 VO 已经够建会话了
groupStore.upsertGroup({
id: group.id,
name: group.name,
avatar: group.avatar,
notice: group.notice,
ownerUserId: group.ownerUserId
})
message.success('群聊创建成功')
emit('created', group.id)
visible.value = false
} finally {
submitting.value = false
}
}
</script>
<template>
<!--
发起群聊选好友 默认按所选成员名生成群名 createGroup
- dialog 壳本组件持有选择 UI 委托 FriendPickerPanel
- lockedIds 由调用方通过 open({ lockedIds }) 传入私聊侧 +建群锁定对方
- 不再要求先输入群名 / 不再展示进群审批开关对齐微信 PC
- 对外接口ref + open({ lockedIds }) + emit created(groupId)
-->
<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"
:locked-ids="lockedIds"
/>
</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>