feat(im): align tenant admin im management
这个提交包含在:
父节点
c84a27f4ec
当前提交
de89297457
@ -177,6 +177,18 @@ export const imAdminApi = {
|
||||
})
|
||||
},
|
||||
|
||||
updateUser(
|
||||
appId: string,
|
||||
userId: string,
|
||||
form: { nickname?: string; avatar?: string; gender?: string; status?: string },
|
||||
) {
|
||||
return imClient.put<{ data: ImUser }>(
|
||||
`/api/im/admin/users/${encodeURIComponent(userId)}`,
|
||||
form,
|
||||
{ params: { appId } },
|
||||
)
|
||||
},
|
||||
|
||||
updateWebhook(appId: string, webhookId: string, form: WebhookConfigForm) {
|
||||
return imClient.put<{ data: WebhookConfig }>(`/api/im/admin/webhooks/${encodeURIComponent(webhookId)}`, form, {
|
||||
params: { appId },
|
||||
@ -301,18 +313,56 @@ export const imAdminApi = {
|
||||
return imClient.delete<{ data: null }>(`/api/im/admin/groups/${encodeURIComponent(groupId)}`, { params: { appId } })
|
||||
},
|
||||
|
||||
registerUser(appId: string, userId: string, nickname?: string, avatar?: string) {
|
||||
registerUser(
|
||||
appId: string,
|
||||
userId: string,
|
||||
nickname?: string,
|
||||
avatar?: string,
|
||||
gender?: string,
|
||||
status?: string,
|
||||
) {
|
||||
return imClient.post<{ data: ImUser }>(
|
||||
'/api/im/admin/users',
|
||||
{ userId, nickname, avatar },
|
||||
{
|
||||
userId,
|
||||
nickname,
|
||||
avatar,
|
||||
...(gender ? { gender } : {}),
|
||||
...(status ? { status } : {}),
|
||||
},
|
||||
{ params: { appId } },
|
||||
)
|
||||
},
|
||||
|
||||
createGroup(appId: string, name: string, creatorId: string, memberIds: string[]) {
|
||||
createGroup(
|
||||
appId: string,
|
||||
name: string,
|
||||
creatorId: string,
|
||||
memberIds: string[],
|
||||
groupType?: string,
|
||||
announcement?: string,
|
||||
) {
|
||||
return imClient.post<{ data: ImGroup }>(
|
||||
'/api/im/admin/groups',
|
||||
{ name, creatorId, memberIds },
|
||||
{
|
||||
name,
|
||||
creatorId,
|
||||
memberIds,
|
||||
...(groupType ? { groupType } : {}),
|
||||
...(announcement ? { announcement } : {}),
|
||||
},
|
||||
{ params: { appId } },
|
||||
)
|
||||
},
|
||||
|
||||
updateGroup(
|
||||
appId: string,
|
||||
groupId: string,
|
||||
form: { name?: string; groupType?: string; announcement?: string },
|
||||
) {
|
||||
return imClient.put<{ data: ImGroup }>(
|
||||
`/api/im/admin/groups/${encodeURIComponent(groupId)}`,
|
||||
form,
|
||||
{ params: { appId } },
|
||||
)
|
||||
},
|
||||
@ -324,6 +374,32 @@ export const imAdminApi = {
|
||||
)
|
||||
},
|
||||
|
||||
searchMessages(
|
||||
appId: string,
|
||||
filters: {
|
||||
keyword?: string
|
||||
chatType?: string
|
||||
msgType?: string
|
||||
startTime?: string
|
||||
endTime?: string
|
||||
page?: number
|
||||
size?: number
|
||||
} = {},
|
||||
) {
|
||||
return imClient.get<{ data: PagedResult<ImMessage> }>('/api/im/admin/messages/search', {
|
||||
params: {
|
||||
appId,
|
||||
...(filters.keyword ? { keyword: filters.keyword } : {}),
|
||||
...(filters.chatType ? { chatType: filters.chatType } : {}),
|
||||
...(filters.msgType ? { msgType: filters.msgType } : {}),
|
||||
...(filters.startTime ? { startTime: filters.startTime } : {}),
|
||||
...(filters.endTime ? { endTime: filters.endTime } : {}),
|
||||
page: filters.page ?? 0,
|
||||
size: filters.size ?? 20,
|
||||
},
|
||||
})
|
||||
},
|
||||
|
||||
getProfile(appId: string, userId: string) {
|
||||
return imClient.get<{ data: ImProfile }>(
|
||||
`/api/im/accounts/${encodeURIComponent(userId)}`,
|
||||
|
||||
@ -17,12 +17,19 @@
|
||||
<el-tabs v-model="activeTab" @tab-change="handleTabChange">
|
||||
<el-tab-pane label="注册用户" name="users">
|
||||
<div class="toolbar">
|
||||
<el-button type="primary" @click="showRegisterUser = true">注册用户</el-button>
|
||||
<el-button type="primary" @click="openCreateUserDialog">新增用户</el-button>
|
||||
<el-button @click="loadUsers" :loading="loadingUsers">刷新</el-button>
|
||||
</div>
|
||||
|
||||
<el-table :data="users" v-loading="loadingUsers" border stripe>
|
||||
<el-table-column prop="userId" label="用户ID" width="180" />
|
||||
<el-table-column label="头像" width="90">
|
||||
<template #default="{ row }">
|
||||
<el-avatar :size="32" :src="row.avatar || undefined">
|
||||
{{ userAvatarFallback(row) }}
|
||||
</el-avatar>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="nickname" label="昵称" width="140" />
|
||||
<el-table-column prop="gender" label="性别" width="90">
|
||||
<template #default="{ row }">
|
||||
@ -41,8 +48,11 @@
|
||||
{{ formatTime(row.createdAt) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="140" fixed="right">
|
||||
<el-table-column label="操作" width="180" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<el-button link type="primary" size="small" @click="openEditUserDialog(row)">
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button
|
||||
link
|
||||
:type="row.status === 'ACTIVE' ? 'danger' : 'success'"
|
||||
@ -68,6 +78,7 @@
|
||||
|
||||
<el-tab-pane label="群组列表" name="groups">
|
||||
<div class="toolbar">
|
||||
<el-button type="primary" @click="openCreateGroupDialog">创建群组</el-button>
|
||||
<el-button @click="loadGroups" :loading="loadingGroups">刷新</el-button>
|
||||
</div>
|
||||
|
||||
@ -90,8 +101,11 @@
|
||||
<el-table-column prop="createdAt" label="创建时间" width="180">
|
||||
<template #default="{ row }">{{ formatTime(row.createdAt) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="120" fixed="right">
|
||||
<el-table-column label="操作" width="180" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<el-button link type="primary" size="small" @click="openEditGroupDialog(row)">
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button link type="danger" size="small" @click="dismissGroup(row)">
|
||||
解散
|
||||
</el-button>
|
||||
@ -103,13 +117,13 @@
|
||||
<el-tab-pane label="消息历史" name="messages">
|
||||
<el-form :inline="true" :model="historyForm" class="toolbar" label-width="0">
|
||||
<el-form-item>
|
||||
<el-input v-model="historyForm.userA" placeholder="用户A" clearable style="width:160px" />
|
||||
<el-input v-model="historyForm.keyword" placeholder="关键词" clearable style="width:200px" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-input v-model="historyForm.userB" placeholder="用户B" clearable style="width:160px" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-input v-model="historyForm.keyword" placeholder="关键词" clearable style="width:180px" />
|
||||
<el-select v-model="historyForm.chatType" placeholder="会话类型" clearable style="width:120px">
|
||||
<el-option label="单聊" value="SINGLE" />
|
||||
<el-option label="群聊" value="GROUP" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-select v-model="historyForm.msgType" placeholder="消息类型" clearable style="width:140px">
|
||||
@ -129,7 +143,7 @@
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" :loading="loadingMessages" @click="searchMessages">
|
||||
查询
|
||||
搜索
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
@ -141,9 +155,9 @@
|
||||
<el-table-column prop="createdAt" label="时间" width="180">
|
||||
<template #default="{ row }">{{ formatTime(row.createdAt) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="chatType" label="会话" width="90" />
|
||||
<el-table-column prop="fromUserId" label="发送者" width="160" />
|
||||
<el-table-column prop="toId" label="会话ID" width="180" />
|
||||
<el-table-column prop="chatType" label="类型" width="90" />
|
||||
<el-table-column prop="msgType" label="消息类型" width="110" />
|
||||
<el-table-column prop="status" label="状态" width="100" />
|
||||
<el-table-column prop="content" label="内容" min-width="260">
|
||||
@ -171,89 +185,6 @@
|
||||
/>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="好友申请" name="friend-requests">
|
||||
<div class="toolbar toolbar-space-between">
|
||||
<div class="toolbar-group">
|
||||
<el-select v-model="friendRequestDirection" style="width:140px" @change="loadFriendRequests">
|
||||
<el-option label="收到的申请" value="incoming" />
|
||||
<el-option label="发出的申请" value="outgoing" />
|
||||
</el-select>
|
||||
<el-button @click="loadFriendRequests" :loading="loadingFriendRequests">刷新</el-button>
|
||||
</div>
|
||||
<el-button type="primary" @click="showSendFriendRequest = true">发送好友申请</el-button>
|
||||
</div>
|
||||
|
||||
<el-table :data="friendRequests" v-loading="loadingFriendRequests" border stripe>
|
||||
<el-table-column prop="createdAt" label="时间" width="180">
|
||||
<template #default="{ row }">{{ formatTime(row.createdAt) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="fromUserId" label="申请人" width="160" />
|
||||
<el-table-column prop="toUserId" label="接收人" width="160" />
|
||||
<el-table-column prop="remark" label="申请信息" min-width="220" show-overflow-tooltip />
|
||||
<el-table-column prop="status" label="状态" width="110">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="friendRequestTagType(row.status)" size="small">{{ friendRequestStatusLabel(row.status) }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="reviewedAt" label="处理时间" width="180">
|
||||
<template #default="{ row }">{{ formatTime(row.reviewedAt) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="180" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<template v-if="friendRequestDirection === 'incoming' && row.status === 'PENDING'">
|
||||
<el-button link type="success" size="small" @click="approveFriend(row)">同意</el-button>
|
||||
<el-button link type="danger" size="small" @click="declineFriend(row)">拒绝</el-button>
|
||||
</template>
|
||||
<span v-else>-</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="入群申请" name="group-requests">
|
||||
<div class="toolbar toolbar-space-between">
|
||||
<div class="toolbar-group">
|
||||
<el-input v-model="groupRequestGroupId" placeholder="群组ID" clearable style="width:260px" />
|
||||
<el-button @click="loadGroupJoinRequests" :loading="loadingGroupRequests">刷新</el-button>
|
||||
</div>
|
||||
<el-button type="primary" @click="showSendGroupJoinRequest = true">发起入群申请</el-button>
|
||||
</div>
|
||||
|
||||
<el-alert
|
||||
v-if="!groupRequestGroupId.trim()"
|
||||
type="info"
|
||||
show-icon
|
||||
title="请输入群组ID后刷新,才能查看该群的入群申请。"
|
||||
style="margin-bottom:12px"
|
||||
/>
|
||||
|
||||
<el-table :data="groupJoinRequests" v-loading="loadingGroupRequests" border stripe>
|
||||
<el-table-column prop="createdAt" label="时间" width="180">
|
||||
<template #default="{ row }">{{ formatTime(row.createdAt) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="requesterId" label="申请人" width="160" />
|
||||
<el-table-column prop="groupId" label="群组ID" width="180" />
|
||||
<el-table-column prop="remark" label="申请信息" min-width="220" show-overflow-tooltip />
|
||||
<el-table-column prop="status" label="状态" width="110">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="groupRequestTagType(row.status)" size="small">{{ groupRequestStatusLabel(row.status) }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="reviewedAt" label="处理时间" width="180">
|
||||
<template #default="{ row }">{{ formatTime(row.reviewedAt) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="180" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<template v-if="row.status === 'PENDING'">
|
||||
<el-button link type="success" size="small" @click="approveGroupJoin(row)">同意</el-button>
|
||||
<el-button link type="danger" size="small" @click="declineGroupJoin(row)">拒绝</el-button>
|
||||
</template>
|
||||
<span v-else>-</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="操作日志" name="logs">
|
||||
<div class="toolbar">
|
||||
<el-button @click="loadOperationLogs" :loading="loadingLogs">刷新</el-button>
|
||||
@ -264,10 +195,20 @@
|
||||
<template #default="{ row }">{{ formatTime(row.createdAt) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="operatorId" label="操作者" width="160" />
|
||||
<el-table-column prop="action" label="动作" width="180" />
|
||||
<el-table-column prop="resourceType" label="资源类型" width="120" />
|
||||
<el-table-column prop="action" label="动作" width="180">
|
||||
<template #default="{ row }">
|
||||
<el-tag size="small" effect="plain">{{ logActionLabel(row.action) }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="resourceType" label="资源类型" width="120">
|
||||
<template #default="{ row }">{{ logResourceLabel(row.resourceType) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="resourceId" label="资源ID" width="220" show-overflow-tooltip />
|
||||
<el-table-column prop="detail" label="详情" min-width="220" show-overflow-tooltip />
|
||||
<el-table-column prop="detail" label="详情" min-width="320">
|
||||
<template #default="{ row }">
|
||||
<span class="log-detail">{{ formatLogDetail(row.detail) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<el-pagination
|
||||
@ -315,22 +256,40 @@
|
||||
</el-tabs>
|
||||
</el-card>
|
||||
|
||||
<el-dialog v-model="showRegisterUser" title="注册用户" width="420px">
|
||||
<el-form :model="registerForm" label-width="72px">
|
||||
<el-dialog v-model="showRegisterUser" :title="editingUserId ? '编辑用户' : '注册用户'" width="520px" @closed="resetUserForm">
|
||||
<el-form :model="registerForm" label-width="84px">
|
||||
<el-form-item label="用户ID">
|
||||
<el-input v-model="registerForm.userId" placeholder="全局唯一标识" />
|
||||
<el-input v-model="registerForm.userId" placeholder="全局唯一标识" :disabled="!!editingUserId" />
|
||||
</el-form-item>
|
||||
<el-form-item label="昵称">
|
||||
<el-input v-model="registerForm.nickname" />
|
||||
</el-form-item>
|
||||
<el-form-item label="头像">
|
||||
<el-input v-model="registerForm.avatar" placeholder="头像 URL" />
|
||||
</el-form-item>
|
||||
<el-form-item label="性别">
|
||||
<el-select v-model="registerForm.gender" style="width:100%">
|
||||
<el-option label="未知" value="UNKNOWN" />
|
||||
<el-option label="男" value="MALE" />
|
||||
<el-option label="女" value="FEMALE" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态">
|
||||
<el-select v-model="registerForm.status" style="width:100%">
|
||||
<el-option label="正常" value="ACTIVE" />
|
||||
<el-option label="封禁" value="BANNED" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="showRegisterUser = false">取消</el-button>
|
||||
<el-button type="primary" :loading="submittingRegister" @click="submitRegisterUser">注册</el-button>
|
||||
<el-button type="primary" :loading="submittingRegister" @click="submitRegisterUser">
|
||||
{{ editingUserId ? '保存' : '注册' }}
|
||||
</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog v-model="showCreateGroup" title="创建群组" width="520px" @closed="resetCreateGroupForm">
|
||||
<el-dialog v-model="showCreateGroup" title="创建群组" width="600px" @closed="resetCreateGroupForm">
|
||||
<el-form :model="createGroupForm" label-width="90px">
|
||||
<el-form-item label="群名称">
|
||||
<el-input v-model="createGroupForm.name" />
|
||||
@ -338,6 +297,15 @@
|
||||
<el-form-item label="创建者ID">
|
||||
<el-input v-model="createGroupForm.creatorId" />
|
||||
</el-form-item>
|
||||
<el-form-item label="群类型">
|
||||
<el-select v-model="createGroupForm.groupType" style="width:100%">
|
||||
<el-option label="工作群" value="WORK" />
|
||||
<el-option label="公开群" value="PUBLIC" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="公告">
|
||||
<el-input v-model="createGroupForm.announcement" type="textarea" :rows="3" />
|
||||
</el-form-item>
|
||||
<el-form-item label="搜索成员">
|
||||
<el-input
|
||||
v-model="memberSearchKeyword"
|
||||
@ -386,33 +354,27 @@
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog v-model="showSendFriendRequest" title="发送好友申请" width="420px" @closed="resetSendFriendRequestForm">
|
||||
<el-form :model="sendFriendRequestForm" label-width="88px">
|
||||
<el-form-item label="接收人ID">
|
||||
<el-input v-model="sendFriendRequestForm.toUserId" placeholder="对方用户ID" />
|
||||
</el-form-item>
|
||||
<el-form-item label="申请信息">
|
||||
<el-input v-model="sendFriendRequestForm.remark" type="textarea" :rows="3" placeholder="例如:我是 XXX,想和你加好友" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="showSendFriendRequest = false">取消</el-button>
|
||||
<el-button type="primary" :loading="submittingSendFriendRequest" @click="submitSendFriendRequest">发送</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog v-model="showSendGroupJoinRequest" title="发起入群申请" width="420px" @closed="resetSendGroupJoinRequestForm">
|
||||
<el-form :model="sendGroupJoinRequestForm" label-width="88px">
|
||||
<el-dialog v-model="showEditGroup" title="编辑群组" width="520px" @closed="resetEditGroupForm">
|
||||
<el-form :model="editGroupForm" label-width="90px">
|
||||
<el-form-item label="群组ID">
|
||||
<el-input v-model="sendGroupJoinRequestForm.groupId" placeholder="目标群组ID" />
|
||||
<el-input :model-value="editingGroup?.id ?? ''" disabled />
|
||||
</el-form-item>
|
||||
<el-form-item label="申请信息">
|
||||
<el-input v-model="sendGroupJoinRequestForm.remark" type="textarea" :rows="3" placeholder="例如:希望加入此群进行沟通" />
|
||||
<el-form-item label="群名称">
|
||||
<el-input v-model="editGroupForm.name" />
|
||||
</el-form-item>
|
||||
<el-form-item label="群类型">
|
||||
<el-select v-model="editGroupForm.groupType" style="width:100%">
|
||||
<el-option label="工作群" value="WORK" />
|
||||
<el-option label="公开群" value="PUBLIC" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="公告">
|
||||
<el-input v-model="editGroupForm.announcement" type="textarea" :rows="4" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="showSendGroupJoinRequest = false">取消</el-button>
|
||||
<el-button type="primary" :loading="submittingSendGroupJoinRequest" @click="submitSendGroupJoinRequest">发送</el-button>
|
||||
<el-button @click="showEditGroup = false">取消</el-button>
|
||||
<el-button type="primary" :loading="submittingEditGroup" @click="submitEditGroup">保存</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
@ -518,20 +480,40 @@ const submittingSendGroupJoinRequest = ref(false)
|
||||
const sendGroupJoinRequestForm = ref({ groupId: '', remark: '' })
|
||||
|
||||
const historyForm = ref({
|
||||
userA: '',
|
||||
userB: '',
|
||||
keyword: '',
|
||||
chatType: '',
|
||||
msgType: '',
|
||||
timeRange: [] as string[] | null,
|
||||
})
|
||||
|
||||
const showRegisterUser = ref(false)
|
||||
const submittingRegister = ref(false)
|
||||
const registerForm = ref({ userId: '', nickname: '' })
|
||||
const editingUserId = ref<string | null>(null)
|
||||
const registerForm = ref({
|
||||
userId: '',
|
||||
nickname: '',
|
||||
avatar: '',
|
||||
gender: 'UNKNOWN' as 'UNKNOWN' | 'MALE' | 'FEMALE',
|
||||
status: 'ACTIVE' as 'ACTIVE' | 'BANNED',
|
||||
})
|
||||
|
||||
const showCreateGroup = ref(false)
|
||||
const submittingCreateGroup = ref(false)
|
||||
const createGroupForm = ref({ name: '', creatorId: '' })
|
||||
const createGroupForm = ref({
|
||||
name: '',
|
||||
creatorId: '',
|
||||
groupType: 'WORK' as 'WORK' | 'PUBLIC',
|
||||
announcement: '',
|
||||
})
|
||||
|
||||
const showEditGroup = ref(false)
|
||||
const submittingEditGroup = ref(false)
|
||||
const editingGroup = ref<ImGroup | null>(null)
|
||||
const editGroupForm = ref({
|
||||
name: '',
|
||||
groupType: 'WORK' as 'WORK' | 'PUBLIC',
|
||||
announcement: '',
|
||||
})
|
||||
|
||||
const memberSearchKeyword = ref('')
|
||||
const memberSearchResults = ref<ImUser[]>([])
|
||||
@ -622,12 +604,71 @@ function removeMember(userId: string) {
|
||||
}
|
||||
|
||||
function resetCreateGroupForm() {
|
||||
createGroupForm.value = { name: '', creatorId: '' }
|
||||
createGroupForm.value = { name: '', creatorId: '', groupType: 'WORK', announcement: '' }
|
||||
memberSearchKeyword.value = ''
|
||||
memberSearchResults.value = []
|
||||
selectedMembers.value = []
|
||||
}
|
||||
|
||||
function openCreateUserDialog() {
|
||||
editingUserId.value = null
|
||||
registerForm.value = {
|
||||
userId: '',
|
||||
nickname: '',
|
||||
avatar: '',
|
||||
gender: 'UNKNOWN',
|
||||
status: 'ACTIVE',
|
||||
}
|
||||
showRegisterUser.value = true
|
||||
}
|
||||
|
||||
function openEditUserDialog(user: ImUser) {
|
||||
editingUserId.value = user.userId
|
||||
registerForm.value = {
|
||||
userId: user.userId,
|
||||
nickname: user.nickname ?? '',
|
||||
avatar: user.avatar ?? '',
|
||||
gender: user.gender ?? 'UNKNOWN',
|
||||
status: user.status ?? 'ACTIVE',
|
||||
}
|
||||
showRegisterUser.value = true
|
||||
}
|
||||
|
||||
function resetUserForm() {
|
||||
editingUserId.value = null
|
||||
registerForm.value = {
|
||||
userId: '',
|
||||
nickname: '',
|
||||
avatar: '',
|
||||
gender: 'UNKNOWN',
|
||||
status: 'ACTIVE',
|
||||
}
|
||||
}
|
||||
|
||||
function openCreateGroupDialog() {
|
||||
resetCreateGroupForm()
|
||||
showCreateGroup.value = true
|
||||
}
|
||||
|
||||
function openEditGroupDialog(group: ImGroup) {
|
||||
editingGroup.value = group
|
||||
editGroupForm.value = {
|
||||
name: group.name,
|
||||
groupType: (group.groupType || 'WORK') as 'WORK' | 'PUBLIC',
|
||||
announcement: group.announcement ?? '',
|
||||
}
|
||||
showEditGroup.value = true
|
||||
}
|
||||
|
||||
function resetEditGroupForm() {
|
||||
editingGroup.value = null
|
||||
editGroupForm.value = {
|
||||
name: '',
|
||||
groupType: 'WORK',
|
||||
announcement: '',
|
||||
}
|
||||
}
|
||||
|
||||
function resetSendFriendRequestForm() {
|
||||
sendFriendRequestForm.value = { toUserId: '', remark: '' }
|
||||
}
|
||||
@ -643,9 +684,8 @@ function resetWebhookForm() {
|
||||
|
||||
function resetMessageSearch() {
|
||||
historyForm.value = {
|
||||
userA: '',
|
||||
userB: '',
|
||||
keyword: '',
|
||||
chatType: '',
|
||||
msgType: '',
|
||||
timeRange: [],
|
||||
}
|
||||
@ -689,20 +729,17 @@ async function loadGroups() {
|
||||
}
|
||||
|
||||
async function loadMessages(page = historyPage.value) {
|
||||
const userA = historyForm.value.userA.trim()
|
||||
const userB = historyForm.value.userB.trim()
|
||||
if (!userA || !userB) {
|
||||
ElMessage.warning('请填写用户A和用户B')
|
||||
return
|
||||
}
|
||||
loadingMessages.value = true
|
||||
try {
|
||||
const [startTime, endTime] = historyForm.value.timeRange ?? []
|
||||
const res = await imAdminApi.getMessages(appId, userA, userB, page, historyPageSize, {
|
||||
const res = await imAdminApi.searchMessages(appId, {
|
||||
keyword: historyForm.value.keyword.trim() || undefined,
|
||||
chatType: historyForm.value.chatType || undefined,
|
||||
msgType: historyForm.value.msgType || undefined,
|
||||
startTime: startTime ? toLocalDateTime(startTime) : undefined,
|
||||
endTime: endTime ? toLocalDateTime(endTime) : undefined,
|
||||
page,
|
||||
size: historyPageSize,
|
||||
})
|
||||
messages.value = res.data.data.content
|
||||
historyTotal.value = res.data.data.totalElements
|
||||
@ -966,20 +1003,37 @@ async function dismissGroup(group: ImGroup) {
|
||||
}
|
||||
|
||||
async function submitRegisterUser() {
|
||||
if (!registerForm.value.userId.trim()) {
|
||||
if (!registerForm.value.userId.trim() && !editingUserId.value) {
|
||||
ElMessage.warning('请填写用户ID')
|
||||
return
|
||||
}
|
||||
submittingRegister.value = true
|
||||
try {
|
||||
await imAdminApi.registerUser(appId, registerForm.value.userId.trim(), registerForm.value.nickname.trim())
|
||||
ElMessage.success('用户注册成功')
|
||||
const payload = {
|
||||
nickname: registerForm.value.nickname.trim() || undefined,
|
||||
avatar: registerForm.value.avatar.trim() || undefined,
|
||||
gender: registerForm.value.gender,
|
||||
status: registerForm.value.status,
|
||||
}
|
||||
if (editingUserId.value) {
|
||||
await imAdminApi.updateUser(appId, editingUserId.value, payload)
|
||||
ElMessage.success('用户已更新')
|
||||
} else {
|
||||
await imAdminApi.registerUser(
|
||||
appId,
|
||||
registerForm.value.userId.trim(),
|
||||
payload.nickname,
|
||||
payload.avatar,
|
||||
payload.gender,
|
||||
payload.status,
|
||||
)
|
||||
ElMessage.success('用户注册成功')
|
||||
}
|
||||
showRegisterUser.value = false
|
||||
registerForm.value = { userId: '', nickname: '' }
|
||||
loadUsers()
|
||||
loadStats()
|
||||
} catch {
|
||||
ElMessage.error('注册失败,用户ID可能已存在')
|
||||
ElMessage.error(editingUserId.value ? '更新失败' : '注册失败,用户ID可能已存在')
|
||||
} finally {
|
||||
submittingRegister.value = false
|
||||
}
|
||||
@ -993,7 +1047,14 @@ async function submitCreateGroup() {
|
||||
submittingCreateGroup.value = true
|
||||
try {
|
||||
const memberIds = selectedMembers.value.map(item => item.userId)
|
||||
await imAdminApi.createGroup(appId, createGroupForm.value.name.trim(), createGroupForm.value.creatorId.trim(), memberIds)
|
||||
await imAdminApi.createGroup(
|
||||
appId,
|
||||
createGroupForm.value.name.trim(),
|
||||
createGroupForm.value.creatorId.trim(),
|
||||
memberIds,
|
||||
createGroupForm.value.groupType,
|
||||
createGroupForm.value.announcement.trim() || undefined,
|
||||
)
|
||||
ElMessage.success('群组创建成功')
|
||||
showCreateGroup.value = false
|
||||
loadGroups()
|
||||
@ -1005,13 +1066,32 @@ async function submitCreateGroup() {
|
||||
}
|
||||
}
|
||||
|
||||
async function submitEditGroup() {
|
||||
if (!editingGroup.value) return
|
||||
if (!editGroupForm.value.name.trim()) {
|
||||
ElMessage.warning('请填写群名称')
|
||||
return
|
||||
}
|
||||
submittingEditGroup.value = true
|
||||
try {
|
||||
await imAdminApi.updateGroup(appId, editingGroup.value.id, {
|
||||
name: editGroupForm.value.name.trim(),
|
||||
groupType: editGroupForm.value.groupType,
|
||||
announcement: editGroupForm.value.announcement.trim() || undefined,
|
||||
})
|
||||
ElMessage.success('群组已更新')
|
||||
showEditGroup.value = false
|
||||
loadGroups()
|
||||
} catch {
|
||||
ElMessage.error('更新失败')
|
||||
} finally {
|
||||
submittingEditGroup.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function handleTabChange(tab: string) {
|
||||
if (tab === 'groups' && groups.value.length === 0) {
|
||||
loadGroups()
|
||||
} else if (tab === 'friend-requests' && friendRequests.value.length === 0) {
|
||||
loadFriendRequests()
|
||||
} else if (tab === 'group-requests' && groupJoinRequests.value.length === 0 && groupRequestGroupId.value.trim()) {
|
||||
loadGroupJoinRequests()
|
||||
} else if (tab === 'logs' && operationLogs.value.length === 0) {
|
||||
loadOperationLogs()
|
||||
} else if (tab === 'webhooks' && webhooks.value.length === 0) {
|
||||
@ -1035,8 +1115,58 @@ function handleOperationLogPageChange(page: number) {
|
||||
onMounted(() => {
|
||||
loadStats()
|
||||
loadUsers()
|
||||
loadFriendRequests()
|
||||
})
|
||||
|
||||
function logActionLabel(action: string) {
|
||||
return {
|
||||
REGISTER_USER: '注册用户',
|
||||
UPDATE_USER: '更新用户',
|
||||
UPDATE_USER_STATUS: '调整状态',
|
||||
CREATE_GROUP: '创建群组',
|
||||
UPDATE_GROUP: '更新群组',
|
||||
ADMIN_REVOKE_MESSAGE: '撤回消息',
|
||||
CREATE_WEBHOOK: '创建回调',
|
||||
UPDATE_WEBHOOK: '更新回调',
|
||||
DELETE_WEBHOOK: '删除回调',
|
||||
CREATE_KEYWORD_FILTER: '创建过滤',
|
||||
UPDATE_KEYWORD_FILTER: '更新过滤',
|
||||
DELETE_KEYWORD_FILTER: '删除过滤',
|
||||
SET_GLOBAL_MUTE: '全局禁言',
|
||||
VIEW_STATS: '查看统计',
|
||||
VIEW_HISTORY: '查看消息',
|
||||
SEARCH_USERS: '搜索用户',
|
||||
SEARCH_GROUPS: '搜索群组',
|
||||
SEARCH_MESSAGES: '搜索消息',
|
||||
}[action] ?? action
|
||||
}
|
||||
|
||||
function logResourceLabel(resourceType: string) {
|
||||
return {
|
||||
ACCOUNT: '用户',
|
||||
GROUP: '群组',
|
||||
MESSAGE: '消息',
|
||||
WEBHOOK: '回调',
|
||||
KEYWORD_FILTER: '过滤',
|
||||
GLOBAL_MUTE: '全局禁言',
|
||||
STATS: '统计',
|
||||
}[resourceType] ?? resourceType
|
||||
}
|
||||
|
||||
function formatLogDetail(detail?: string | null) {
|
||||
if (!detail) return '-'
|
||||
const trimmed = detail.trim()
|
||||
if (!trimmed) return '-'
|
||||
try {
|
||||
const parsed = JSON.parse(trimmed)
|
||||
return typeof parsed === 'object' ? JSON.stringify(parsed, null, 2) : String(parsed)
|
||||
} catch {
|
||||
return trimmed
|
||||
}
|
||||
}
|
||||
|
||||
function userAvatarFallback(row: ImUser) {
|
||||
return (row.nickname || row.userId || '?').slice(0, 1).toUpperCase()
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@ -1128,4 +1258,10 @@ onMounted(() => {
|
||||
max-width: 100%;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.log-detail {
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
line-height: 1.5;
|
||||
}
|
||||
</style>
|
||||
|
||||
正在加载...
在新工单中引用
屏蔽一个用户