✨ feat(im): 增加群管理的完善
parent
8564788b11
commit
7ed6fa5579
|
|
@ -20,7 +20,12 @@ export interface ImManagerGroupMemberVO {
|
|||
userId: number
|
||||
nickname?: string
|
||||
avatar?: string
|
||||
displayUserName?: string
|
||||
displayGroupName?: string
|
||||
muted?: boolean
|
||||
status: number
|
||||
joinTime?: Date
|
||||
quitTime?: Date
|
||||
}
|
||||
|
||||
// 获得群分页
|
||||
|
|
@ -43,7 +48,7 @@ export const unbanManagerGroup = (id: number) => {
|
|||
return request.put({ url: '/im/manager/group/unban?id=' + id })
|
||||
}
|
||||
|
||||
// 获得群成员列表
|
||||
// 获得群成员列表(含已退群成员,由前端按需过滤)
|
||||
export const getManagerGroupMemberList = (groupId: number) => {
|
||||
return request.get({ url: '/im/manager/group/member/list?groupId=' + groupId })
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,63 @@
|
|||
<template>
|
||||
<el-dialog v-model="dialogVisible" title="封禁群" width="500" :close-on-click-modal="false">
|
||||
<el-form :model="formData" :rules="formRules" ref="formRef" label-width="80px">
|
||||
<el-form-item label="群名称">
|
||||
<span>{{ formData.groupName }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="封禁原因" prop="reason">
|
||||
<el-input
|
||||
v-model="formData.reason"
|
||||
type="textarea"
|
||||
:rows="3"
|
||||
maxlength="200"
|
||||
show-word-limit
|
||||
placeholder="请输入封禁原因"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button :loading="formLoading" type="primary" @click="submitForm">确 定</el-button>
|
||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import * as ManagerGroupApi from '@/api/im/manager/group'
|
||||
|
||||
defineOptions({ name: 'ImGroupBanForm' })
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
|
||||
const dialogVisible = ref(false) // 弹窗的显示
|
||||
const formLoading = ref(false) // 提交的加载中
|
||||
const formData = reactive({ id: 0, groupName: '', reason: '' }) // 封禁表单
|
||||
const formRef = ref() // 表单 Ref
|
||||
const formRules = {
|
||||
reason: [{ required: true, message: '封禁原因不能为空', trigger: 'blur' }]
|
||||
}
|
||||
|
||||
/** 打开弹窗 */
|
||||
const open = (row: ManagerGroupApi.ImManagerGroupVO) => {
|
||||
formData.id = row.id
|
||||
formData.groupName = row.name
|
||||
formData.reason = ''
|
||||
dialogVisible.value = true
|
||||
}
|
||||
defineExpose({ open })
|
||||
|
||||
/** 提交封禁 */
|
||||
const emit = defineEmits(['success']) // 操作成功后的回调
|
||||
const submitForm = async () => {
|
||||
await formRef.value.validate()
|
||||
formLoading.value = true
|
||||
try {
|
||||
await ManagerGroupApi.banManagerGroup({ id: formData.id, reason: formData.reason })
|
||||
message.success('封禁成功')
|
||||
dialogVisible.value = false
|
||||
emit('success')
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
@ -0,0 +1,125 @@
|
|||
<template>
|
||||
<el-drawer v-model="drawerVisible" title="群详情" size="900px" :destroy-on-close="true">
|
||||
<!-- 群基本信息 -->
|
||||
<el-descriptions :column="2" border>
|
||||
<el-descriptions-item label="群编号">{{ detail.id }}</el-descriptions-item>
|
||||
<el-descriptions-item label="群名称">{{ detail.name }}</el-descriptions-item>
|
||||
<el-descriptions-item label="头像">
|
||||
<el-avatar :src="detail.avatar" :size="36">
|
||||
{{ detail.name?.charAt(0) ?? '?' }}
|
||||
</el-avatar>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="群主">
|
||||
{{ detail.ownerNickname || '-' }} ({{ detail.ownerUserId }})
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="成员数">{{ detail.memberCount ?? 0 }}</el-descriptions-item>
|
||||
<el-descriptions-item label="群状态">
|
||||
<dict-tag :type="DICT_TYPE.IM_GROUP_STATUS" :value="detail.status" />
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="封禁状态" :span="2">
|
||||
<dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="detail.banned" />
|
||||
<span v-if="detail.banned" class="ml-5px text-gray-400">{{ detail.bannedReason }}</span>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="群公告" :span="2">{{ detail.notice || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="创建时间" :span="2">
|
||||
{{ formatDate(detail.createTime) }}
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
|
||||
<!-- 群成员列表 -->
|
||||
<div class="mt-20px mb-15px flex items-center justify-between">
|
||||
<span class="font-bold">群成员</span>
|
||||
<el-checkbox v-model="activeOnly">仅展示当前群内的成员</el-checkbox>
|
||||
</div>
|
||||
<el-table v-loading="loading" :data="filteredMembers" border>
|
||||
<el-table-column label="头像" width="80" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-avatar :src="row.avatar" :size="40">
|
||||
{{ row.nickname?.charAt(0) ?? '?' }}
|
||||
</el-avatar>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="用户编号" prop="userId" width="100" align="center" />
|
||||
<el-table-column label="昵称" prop="nickname" min-width="120" show-overflow-tooltip />
|
||||
<el-table-column
|
||||
label="组内显示名"
|
||||
prop="displayUserName"
|
||||
min-width="120"
|
||||
show-overflow-tooltip
|
||||
>
|
||||
<template #default="{ row }">{{ row.displayUserName || '-' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
label="群显示备注"
|
||||
prop="displayGroupName"
|
||||
min-width="120"
|
||||
show-overflow-tooltip
|
||||
>
|
||||
<template #default="{ row }">{{ row.displayGroupName || '-' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="免打扰" prop="muted" width="80" align="center">
|
||||
<template #default="{ row }">
|
||||
<dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="row.muted" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="状态" prop="status" width="100" align="center">
|
||||
<template #default="{ row }">
|
||||
<dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="row.status" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
label="入群时间"
|
||||
prop="joinTime"
|
||||
width="170"
|
||||
align="center"
|
||||
:formatter="dateFormatter"
|
||||
/>
|
||||
<el-table-column
|
||||
label="退群时间"
|
||||
prop="quitTime"
|
||||
width="170"
|
||||
align="center"
|
||||
:formatter="dateFormatter"
|
||||
/>
|
||||
</el-table>
|
||||
</el-drawer>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { dateFormatter, formatDate } from '@/utils/formatTime'
|
||||
import { DICT_TYPE } from '@/utils/dict'
|
||||
import { CommonStatusEnum } from '@/utils/constants'
|
||||
import * as ManagerGroupApi from '@/api/im/manager/group'
|
||||
|
||||
defineOptions({ name: 'ImGroupDetail' })
|
||||
|
||||
const drawerVisible = ref(false) // 抽屉的显示
|
||||
const detail = ref<ManagerGroupApi.ImManagerGroupVO>(
|
||||
{} as ManagerGroupApi.ImManagerGroupVO
|
||||
) // 群详情数据
|
||||
const loading = ref(false) // 成员列表的加载中
|
||||
const memberList = ref<ManagerGroupApi.ImManagerGroupMemberVO[]>([]) // 群成员列表(含已退群)
|
||||
const activeOnly = ref(true) // 仅展示当前群内的成员
|
||||
|
||||
/** 当前展示的群成员(按 activeOnly 前端过滤) */
|
||||
const filteredMembers = computed(() =>
|
||||
activeOnly.value
|
||||
? memberList.value.filter((m) => m.status === CommonStatusEnum.ENABLE)
|
||||
: memberList.value
|
||||
)
|
||||
|
||||
/** 打开详情,加载群成员 */
|
||||
const open = async (row: ManagerGroupApi.ImManagerGroupVO) => {
|
||||
detail.value = row
|
||||
drawerVisible.value = true
|
||||
activeOnly.value = true
|
||||
loading.value = true
|
||||
try {
|
||||
memberList.value = await ManagerGroupApi.getManagerGroupMemberList(row.id)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({ open })
|
||||
</script>
|
||||
|
|
@ -1,51 +0,0 @@
|
|||
<!-- TODO @AI git mv 到 /Users/yunai/Java/yudao-all-in-im/yudao-ui-admin-vue3/src/views/im/manager/group -->
|
||||
<!-- TODO @AI:需要增加状态;然后有个筛选:仅展示当前群内的成员;和不选中,就是全部; -->
|
||||
<!-- TODO @AI:displayUserName、displayGroupName、muted;群成员状态;退群时间;需要也展示; -->
|
||||
<template>
|
||||
<el-drawer v-model="drawerVisible" :title="drawerTitle" size="600px" :destroy-on-close="true">
|
||||
<el-table v-loading="loading" :data="memberList" border>
|
||||
<el-table-column label="头像" width="80" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-avatar :src="row.avatar" :size="40">
|
||||
{{ row.nickname?.charAt(0) ?? '?' }}
|
||||
</el-avatar>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="用户编号" prop="userId" width="120" align="center" />
|
||||
<el-table-column label="昵称" prop="nickname" min-width="120" />
|
||||
<el-table-column
|
||||
label="入群时间"
|
||||
prop="joinTime"
|
||||
width="180"
|
||||
align="center"
|
||||
:formatter="dateFormatter"
|
||||
/>
|
||||
</el-table>
|
||||
</el-drawer>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { dateFormatter } from '@/utils/formatTime'
|
||||
import * as ManagerGroupApi from '@/api/im/manager/group'
|
||||
|
||||
defineOptions({ name: 'ImGroupMemberDrawer' })
|
||||
|
||||
const drawerVisible = ref(false) // 抽屉的显示
|
||||
const drawerTitle = ref('群成员列表') // 抽屉的标题
|
||||
const loading = ref(false) // 列表的加载中
|
||||
const memberList = ref<ManagerGroupApi.ImManagerGroupMemberVO[]>([]) // 群成员列表
|
||||
|
||||
/** 打开抽屉,加载指定群的成员 */
|
||||
const open = async (groupId: number, groupName?: string) => {
|
||||
drawerVisible.value = true
|
||||
drawerTitle.value = groupName ? `群成员 - ${groupName}` : '群成员列表'
|
||||
loading.value = true
|
||||
try {
|
||||
memberList.value = await ManagerGroupApi.getManagerGroupMemberList(groupId)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({ open })
|
||||
</script>
|
||||
|
|
@ -125,10 +125,10 @@
|
|||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
@click="openMemberDrawer(row)"
|
||||
@click="openDetail(row)"
|
||||
v-hasPermi="['im:manager:group:query']"
|
||||
>
|
||||
成员
|
||||
详情
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="!row.banned"
|
||||
|
|
@ -159,45 +159,22 @@
|
|||
/>
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 群成员抽屉 -->
|
||||
<!-- TODO @AI:DrawerRef 简化下 -->
|
||||
<GroupMemberDrawer ref="memberDrawerRef" />
|
||||
|
||||
<!-- 封禁原因弹窗 -->
|
||||
<!-- TODO @AI:独立出来 -->
|
||||
<el-dialog v-model="banDialogVisible" title="封禁群" width="500" :close-on-click-modal="false">
|
||||
<el-form :model="banForm" :rules="banFormRules" ref="banFormRef" label-width="80px">
|
||||
<el-form-item label="群名称">
|
||||
<span>{{ banForm.groupName }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="封禁原因" prop="reason">
|
||||
<el-input
|
||||
v-model="banForm.reason"
|
||||
type="textarea"
|
||||
:rows="3"
|
||||
maxlength="200"
|
||||
show-word-limit
|
||||
placeholder="请输入封禁原因"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button :loading="banSubmitting" type="primary" @click="submitBan">确 定</el-button>
|
||||
<el-button @click="banDialogVisible = false">取 消</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
<!-- 群详情 -->
|
||||
<GroupDetail ref="detailRef" />
|
||||
<!-- 群封禁因弹窗 -->
|
||||
<GroupBanForm ref="banFormRef" @success="getList" />
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { DICT_TYPE, getIntDictOptions, getBoolDictOptions } from '@/utils/dict'
|
||||
import { dateFormatter } from '@/utils/formatTime'
|
||||
import * as ManagerGroupApi from '@/api/im/manager/group'
|
||||
import GroupMemberDrawer from './components/GroupMemberDrawer.vue'
|
||||
import GroupDetail from './GroupDetail.vue'
|
||||
import GroupBanForm from './GroupBanForm.vue'
|
||||
|
||||
defineOptions({ name: 'ImGroup' })
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
const { t } = useI18n() // 国际化 TODO @AI:这里 linter 报错;
|
||||
|
||||
const loading = ref(true) // 列表的加载中
|
||||
const total = ref(0) // 列表的总页数
|
||||
|
|
@ -237,59 +214,29 @@ const resetQuery = () => {
|
|||
handleQuery()
|
||||
}
|
||||
|
||||
const memberDrawerRef = ref<InstanceType<typeof GroupMemberDrawer>>() // 群成员抽屉 Ref
|
||||
|
||||
/** 打开群成员抽屉 */
|
||||
const openMemberDrawer = (row: ManagerGroupApi.ImManagerGroupVO) => {
|
||||
memberDrawerRef.value?.open(row.id, row.name)
|
||||
}
|
||||
|
||||
const banDialogVisible = ref(false) // 封禁弹窗的显示
|
||||
const banSubmitting = ref(false) // 封禁提交的加载中
|
||||
const banForm = reactive({ id: 0, groupName: '', reason: '' }) // 封禁表单
|
||||
const banFormRef = ref() // 封禁表单 Ref
|
||||
const banFormRules = {
|
||||
reason: [{ required: true, message: '封禁原因不能为空', trigger: 'blur' }]
|
||||
/** 打开群详情 */
|
||||
const detailRef = ref()
|
||||
const openDetail = (row: ManagerGroupApi.ImManagerGroupVO) => {
|
||||
detailRef.value.open(row)
|
||||
}
|
||||
|
||||
/** 打开封禁弹窗 */
|
||||
const banFormRef = ref()
|
||||
const openBanDialog = (row: ManagerGroupApi.ImManagerGroupVO) => {
|
||||
banForm.id = row.id
|
||||
banForm.groupName = row.name
|
||||
banForm.reason = ''
|
||||
banDialogVisible.value = true
|
||||
}
|
||||
|
||||
/** 提交封禁 */
|
||||
const submitBan = async () => {
|
||||
await banFormRef.value.validate()
|
||||
banSubmitting.value = true
|
||||
try {
|
||||
// 发起封禁
|
||||
await ManagerGroupApi.banManagerGroup({ id: banForm.id, reason: banForm.reason })
|
||||
message.success('封禁成功')
|
||||
banDialogVisible.value = false
|
||||
// 刷新列表
|
||||
await getList()
|
||||
} finally {
|
||||
banSubmitting.value = false
|
||||
}
|
||||
banFormRef.value.open(row)
|
||||
}
|
||||
|
||||
/** 解封按钮操作 */
|
||||
const handleUnban = async (row: ManagerGroupApi.ImManagerGroupVO) => {
|
||||
// TODO @AI:对齐 system user info 写的习惯;manager 下其他写的,也检查下;
|
||||
try {
|
||||
// 解封的二次确认
|
||||
await message.confirm(`确认解封群「${row.name}」吗?`)
|
||||
} catch {
|
||||
return
|
||||
}
|
||||
// 发起解封
|
||||
await ManagerGroupApi.unbanManagerGroup(row.id)
|
||||
message.success('解封成功')
|
||||
// 刷新列表
|
||||
await getList()
|
||||
// 发起解封
|
||||
await ManagerGroupApi.unbanManagerGroup(row.id)
|
||||
message.success('解封成功')
|
||||
// 刷新列表
|
||||
await getList()
|
||||
} catch {}
|
||||
}
|
||||
|
||||
/** 初始化 */
|
||||
|
|
|
|||
Loading…
Reference in New Issue