feat: complete IM group governance actions

This commit is contained in:
richjohnrambo
2026-08-21 16:34:55 +08:00
parent 21a5b97ac8
commit 77b19a8157
12 changed files with 981 additions and 17 deletions
+54
View File
@@ -72,6 +72,42 @@ export function Unmute(params) {
});
}
// 通过IM入群申请
export function JoinRequestApprove(params) {
return http.request({
url: '/imGroup/joinRequestApprove',
method: 'POST',
params,
});
}
// 拒绝IM入群申请
export function JoinRequestReject(params) {
return http.request({
url: '/imGroup/joinRequestReject',
method: 'POST',
params,
});
}
// 更新IM群成员角色
export function RoleUpdate(params) {
return http.request({
url: '/imGroup/roleUpdate',
method: 'POST',
params,
});
}
// 转让IM群主
export function OwnerTransfer(params) {
return http.request({
url: '/imGroup/ownerTransfer',
method: 'POST',
params,
});
}
// 获取IM入群申请列表
export function JoinRequestList(params) {
return http.request({
@@ -81,6 +117,24 @@ export function JoinRequestList(params) {
});
}
// 获取IM群文件列表
export function FileList(params) {
return http.request({
url: '/imGroup/fileList',
method: 'GET',
params,
});
}
// 删除IM群文件
export function FileDelete(params) {
return http.request({
url: '/imGroup/fileDelete',
method: 'POST',
params,
});
}
// 获取IM群邀请列表
export function InvitationList(params) {
return http.request({
+129 -1
View File
@@ -102,10 +102,22 @@
:columns="joinRequestColumns"
:request="joinRequestData"
:row-key="(row) => row.requestId"
:actionColumn="joinRequestActionColumn"
:scroll-x="joinRequestScrollX"
:resizeHeightOffset="-10000"
/>
</n-tab-pane>
<n-tab-pane name="files" tab="群文件">
<BasicTable
ref="fileRef"
:columns="groupFileColumns"
:request="fileData"
:row-key="(row) => row.groupFileId"
:actionColumn="fileActionColumn"
:scroll-x="fileScrollX"
:resizeHeightOffset="-10000"
/>
</n-tab-pane>
<n-tab-pane name="invitations" tab="群邀请">
<BasicTable
ref="invitationRef"
@@ -183,10 +195,14 @@
AdminLogList,
AnnouncementList,
Dissolve,
FileDelete,
FileList,
Governance,
GovernanceUpdate,
InvitationList,
JoinRequestApprove,
JoinRequestList,
JoinRequestReject,
MuteList,
Unmute,
} from '@/api/imGroup';
@@ -194,6 +210,7 @@
adminLogColumns,
announcementColumns,
GovernanceState,
groupFileColumns,
invitationColumns,
joinRequestColumns,
muteColumns,
@@ -222,10 +239,58 @@
);
const joinRequestRef = ref();
const fileRef = ref();
const invitationRef = ref();
const announcementRef = ref();
const muteRef = ref();
const adminLogRef = ref();
const joinRequestActionColumn = reactive({
width: 150,
title: '操作',
key: 'action',
fixed: 'right',
render(record) {
return h(TableAction as any, {
style: 'button',
actions: [
{
label: '通过',
type: 'primary',
ifShow: () => record.status === 'pending',
auth: ['/imGroup/joinRequestApprove'],
onClick: () => handleJoinRequestApprove(record),
},
{
label: '拒绝',
type: 'error',
ifShow: () => record.status === 'pending',
auth: ['/imGroup/joinRequestReject'],
onClick: () => handleJoinRequestReject(record),
},
],
});
},
});
const fileActionColumn = reactive({
width: 100,
title: '操作',
key: 'action',
fixed: 'right',
render(record) {
return h(TableAction as any, {
style: 'button',
actions: [
{
label: '删除',
type: 'error',
ifShow: () => record.status === 'active',
auth: ['/imGroup/fileDelete'],
onClick: () => handleFileDelete(record),
},
],
});
},
});
const muteActionColumn = reactive({
width: 110,
title: '操作',
@@ -247,7 +312,10 @@
},
});
const joinRequestScrollX = computed(() => adaTableScrollX(joinRequestColumns, 0));
const joinRequestScrollX = computed(() =>
adaTableScrollX(joinRequestColumns, joinRequestActionColumn.width)
);
const fileScrollX = computed(() => adaTableScrollX(groupFileColumns, fileActionColumn.width));
const invitationScrollX = computed(() => adaTableScrollX(invitationColumns, 0));
const announcementScrollX = computed(() => adaTableScrollX(announcementColumns, 0));
const muteScrollX = computed(() => adaTableScrollX(muteColumns, muteActionColumn.width));
@@ -265,6 +333,10 @@
return await JoinRequestList(baseParams(res));
}
async function fileData(res) {
return await FileList(baseParams(res));
}
async function invitationData(res) {
return await InvitationList(baseParams(res));
}
@@ -361,9 +433,65 @@
});
}
function handleJoinRequestApprove(record) {
dialog.success({
title: '通过入群申请',
content: '确定通过该入群申请?',
positiveText: '确认',
negativeText: '取消',
onPositiveClick: async () => {
await JoinRequestApprove({
tenantId: current.value.tenantId,
topic: current.value.topic,
requestId: record.requestId,
});
message.success('操作成功');
joinRequestRef.value?.reload();
},
});
}
function handleJoinRequestReject(record) {
dialog.error({
title: '拒绝入群申请',
content: '确定拒绝该入群申请?',
positiveText: '确认',
negativeText: '取消',
onPositiveClick: async () => {
await JoinRequestReject({
tenantId: current.value.tenantId,
topic: current.value.topic,
requestId: record.requestId,
rejectReason: '后台管理员拒绝',
});
message.success('操作成功');
joinRequestRef.value?.reload();
},
});
}
function handleFileDelete(record) {
dialog.error({
title: '删除群文件',
content: '删除后该群文件会被标记为已删除,并写入群事件等待 IM 服务端同步,是否确认?',
positiveText: '确认',
negativeText: '取消',
onPositiveClick: async () => {
await FileDelete({
tenantId: current.value.tenantId,
topic: current.value.topic,
groupFileId: record.groupFileId,
});
message.success('操作成功');
fileRef.value?.reload();
},
});
}
function reloadActiveTable() {
const refs = {
joinRequests: joinRequestRef,
files: fileRef,
invitations: invitationRef,
announcements: announcementRef,
mutes: muteRef,
+60 -2
View File
@@ -32,7 +32,7 @@
import { useDialog, useMessage } from 'naive-ui';
import { BasicTable, TableAction } from '@/components/Table';
import { usePermission } from '@/hooks/web/usePermission';
import { MemberList, Mute } from '@/api/imGroup';
import { MemberList, Mute, OwnerTransfer, RoleUpdate } from '@/api/imGroup';
import { memberColumns, newState, State } from './model';
import { adaModalWidth, adaTableScrollX } from '@/utils/hotgo';
@@ -44,7 +44,7 @@
const actionRef = ref();
const dialogWidth = computed(() => adaModalWidth(980));
const actionColumn = reactive({
width: 100,
width: 210,
title: '操作',
key: 'action',
fixed: 'right',
@@ -59,6 +59,27 @@
disabled: !hasPermission(['/imGroup/mute']),
onClick: () => handleMute(record),
},
{
label: '设管理员',
type: 'primary',
ifShow: () => record.memberStatus === 'active' && record.role === 'member',
auth: ['/imGroup/roleUpdate'],
onClick: () => handleRoleUpdate(record, 'admin'),
},
{
label: '取消管理员',
type: 'default',
ifShow: () => record.memberStatus === 'active' && record.role === 'admin',
auth: ['/imGroup/roleUpdate'],
onClick: () => handleRoleUpdate(record, 'member'),
},
{
label: '转群主',
type: 'error',
ifShow: () => record.memberStatus === 'active' && record.role !== 'owner',
auth: ['/imGroup/ownerTransfer'],
onClick: () => handleOwnerTransfer(record),
},
],
});
},
@@ -102,6 +123,43 @@
});
}
function handleRoleUpdate(record, role: string) {
dialog.warning({
title: role === 'admin' ? '设置管理员' : '取消管理员',
content: role === 'admin' ? '确定将该成员设置为管理员?' : '确定取消该成员管理员身份?',
positiveText: '确认',
negativeText: '取消',
onPositiveClick: async () => {
await RoleUpdate({
tenantId: current.value.tenantId,
topic: current.value.topic,
userId: record.userId,
role,
});
message.success('操作成功');
actionRef.value?.reload();
},
});
}
function handleOwnerTransfer(record) {
dialog.error({
title: '转让群主',
content: '转让后原群主会变为管理员,是否确认?',
positiveText: '确认',
negativeText: '取消',
onPositiveClick: async () => {
await OwnerTransfer({
tenantId: current.value.tenantId,
topic: current.value.topic,
userId: record.userId,
});
message.success('操作成功');
actionRef.value?.reload();
},
});
}
// 打开弹窗
function openModal(state: State) {
current.value = newState(state);
+34
View File
@@ -368,6 +368,38 @@ export const joinRequestColumns = [
{ title: '创建时间', key: 'createdAt', align: 'left', width: 180 },
];
export const groupFileColumns = [
{ title: '群文件ID', key: 'groupFileId', align: 'left', width: 180 },
{ title: '文件ID', key: 'fileId', align: 'left', width: 160 },
{ title: '文件名', key: 'fileName', align: 'left', width: 220 },
{
title: '类型',
key: 'fileType',
align: 'left',
width: 100,
render(row) {
return dict.getLabel('ImGroupFileTypeOptions', row.fileType) || row.fileType;
},
},
{ title: 'MIME', key: 'mimeType', align: 'left', width: 160 },
{ title: '大小', key: 'fileSize', align: 'left', width: 110 },
{ title: '上传人', key: 'uploaderId', align: 'left', width: 120 },
{ title: '来源Topic', key: 'sourceTopic', align: 'left', width: 160 },
{ title: '来源序号', key: 'sourceSeq', align: 'left', width: 100 },
{
title: '状态',
key: 'status',
align: 'left',
width: 110,
render(row) {
return dict.getLabel('ImGroupFileStatusOptions', row.status);
},
},
{ title: '删除人', key: 'deletedBy', align: 'left', width: 120 },
{ title: '删除时间', key: 'deletedAt', align: 'left', width: 180 },
{ title: '创建时间', key: 'createdAt', align: 'left', width: 180 },
];
export const invitationColumns = [
{ title: '邀请ID', key: 'invitationId', align: 'left', width: 180 },
{ title: '邀请人ID', key: 'inviterUserId', align: 'left', width: 120 },
@@ -450,6 +482,8 @@ export function loadOptions() {
'ImGroupMuteStatusOptions',
'ImGroupAnnouncementStatusOptions',
'ImGroupOutboxStatusOptions',
'ImGroupFileStatusOptions',
'ImGroupFileTypeOptions',
]);
}