Browse Source

私聊消息

master
xsx 1 year ago
parent
commit
a8b8d2a748
  1. 28
      db/im-admin.sql
  2. 63
      im-admin-ui/src/api/im/group/index.ts
  3. 193
      im-admin-ui/src/api/im/group/types.ts
  4. 18
      im-admin-ui/src/api/im/groupMember/index.ts
  5. 173
      im-admin-ui/src/api/im/groupMember/types.ts
  6. 63
      im-admin-ui/src/api/im/groupMessage/index.ts
  7. 191
      im-admin-ui/src/api/im/groupMessage/types.ts
  8. 29
      im-admin-ui/src/api/im/privateMessage/index.ts
  9. 129
      im-admin-ui/src/api/im/privateMessage/types.ts
  10. 63
      im-admin-ui/src/api/im/sensitiveWord/index.ts
  11. 71
      im-admin-ui/src/api/im/sensitiveWord/types.ts
  12. 63
      im-admin-ui/src/api/im/smPushTask/index.ts
  13. 161
      im-admin-ui/src/api/im/smPushTask/types.ts
  14. 63
      im-admin-ui/src/api/im/systemMessage/index.ts
  15. 161
      im-admin-ui/src/api/im/systemMessage/types.ts
  16. 63
      im-admin-ui/src/api/im/user/index.ts
  17. 204
      im-admin-ui/src/api/im/user/types.ts
  18. 52
      im-admin-ui/src/components/im/ImGroupSelect/index.vue
  19. 39
      im-admin-ui/src/components/im/ImMessageContent/index.vue
  20. 51
      im-admin-ui/src/components/im/ImSysMsgSelect/index.vue
  21. 51
      im-admin-ui/src/components/im/ImUserSelect/index.vue
  22. 292
      im-admin-ui/src/views/im/group/index.vue
  23. 99
      im-admin-ui/src/views/im/group/member.vue
  24. 339
      im-admin-ui/src/views/im/groupMember/index.vue
  25. 333
      im-admin-ui/src/views/im/message/group/index.vue
  26. 231
      im-admin-ui/src/views/im/message/private/index.vue
  27. 304
      im-admin-ui/src/views/im/message/system/index.vue
  28. 318
      im-admin-ui/src/views/im/message/task/index.vue
  29. 235
      im-admin-ui/src/views/im/sensitiveWord/index.vue
  30. 284
      im-admin-ui/src/views/im/user/index.vue
  31. 1
      im-admin/ruoyi-admin/src/main/resources/application.yml
  32. 47
      im-admin/ruoyi-admin/src/main/resources/logback-plus.xml
  33. 59
      im-admin/ruoyi-im/src/main/java/org/dromara/im/controller/ImPrivateMessageController.java
  34. 4
      im-admin/ruoyi-im/src/main/java/org/dromara/im/domain/bo/ImGroupBo.java
  35. 17
      im-admin/ruoyi-im/src/main/java/org/dromara/im/domain/bo/ImPrivateMessageBo.java
  36. 27
      im-admin/ruoyi-im/src/main/java/org/dromara/im/domain/vo/ImPrivateMessageVo.java
  37. 24
      im-admin/ruoyi-im/src/main/java/org/dromara/im/service/IImPrivateMessageService.java
  38. 61
      im-admin/ruoyi-im/src/main/java/org/dromara/im/service/impl/ImPrivateMessageServiceImpl.java

28
db/im-admin.sql

@ -349,12 +349,16 @@ insert into sys_menu values('4002', '用户封禁', '4', '2', '#', '', '', 1, 0
insert into sys_menu values('4003', '用户导出', '4', '3', '#', '', '', 1, 0, 'F', '0', '0', 'im:user:export', '#', 103, 1, sysdate(), null, null, ''); insert into sys_menu values('4003', '用户导出', '4', '3', '#', '', '', 1, 0, 'F', '0', '0', 'im:user:export', '#', 103, 1, sysdate(), null, null, '');
-- IM-群聊管理 -- IM-群聊管理
insert into sys_menu values('5', '群聊管理', '0', '2', 'im/group', 'im/group/index', '', 1, 0, 'C', '0', '0', 'im:group:list', 'user', 103, 1, sysdate(), null, null, 'IM群聊管理'); insert into sys_menu values('5', '群聊管理', '0', '2', 'im/group', 'im/group/index', '', 1, 0, 'C', '0', '0', 'im:group:list', 'peoples', 103, 1, sysdate(), null, null, 'IM群聊管理');
insert into sys_menu values('5001', '群聊查询', '5', '1', '#', '', '', 1, 0, 'F', '0', '0', 'im:group:query', '#', 103, 1, sysdate(), null, null, ''); insert into sys_menu values('5001', '群聊查询', '5', '1', '#', '', '', 1, 0, 'F', '0', '0', 'im:group:query', '#', 103, 1, sysdate(), null, null, '');
insert into sys_menu values('5002', '群聊封禁', '5', '2', '#', '', '', 1, 0, 'F', '0', '0', 'im:group:ban', '#', 103, 1, sysdate(), null, null, ''); insert into sys_menu values('5002', '群聊封禁', '5', '2', '#', '', '', 1, 0, 'F', '0', '0', 'im:group:ban', '#', 103, 1, sysdate(), null, null, '');
insert into sys_menu values('5003', '群聊导出', '5', '3', '#', '', '', 1, 0, 'F', '0', '0', 'im:group:export', '#', 103, 1, sysdate(), null, null, ''); insert into sys_menu values('5003', '群聊导出', '5', '3', '#', '', '', 1, 0, 'F', '0', '0', 'im:group:export', '#', 103, 1, sysdate(), null, null, '');
insert into sys_menu values('5004', '群聊成员', '5', '4', '#', '', '', 1, 0, 'F', '0', '0', 'im:group:member', '#', 103, 1, sysdate(), null, null, ''); insert into sys_menu values('5004', '群聊成员', '5', '4', '#', '', '', 1, 0, 'F', '0', '0', 'im:group:member', '#', 103, 1, sysdate(), null, null, '');
-- IM-私聊管理
insert into sys_menu values('6', '消息管理', '0', '3', 'message', null, '', 1, 0, 'M', '0', '0', '', 'message', 103, 1, sysdate(), null, null, 'IM消息管理');
insert into sys_menu values('60', '私聊消息', '6', '1', 'private', 'im/message/private/index', '', 1, 0, 'C', '0', '0', 'im:privateMessage:list', 'education, 103, 1, sysdate(), null, null, 'IM私聊消息');
insert into sys_menu values('6001', '私聊消息查询', '60', '1', '#', '', '', 1, 0, 'F', '0', '0', 'im:privateMessage:query', '#', 103, 1, sysdate(), null, null, '');
-- ---------------------------- -- ----------------------------
-- 6、用户和角色关联表 用户N-1角色 -- 6、用户和角色关联表 用户N-1角色
@ -463,6 +467,8 @@ insert into sys_dict_type values(11, '000000', '授权类型', 'sys_grant_type',
insert into sys_dict_type values(12, '000000', '设备类型', 'sys_device_type', 103, 1, sysdate(), null, null, '客户端设备类型'); insert into sys_dict_type values(12, '000000', '设备类型', 'sys_device_type', 103, 1, sysdate(), null, null, '客户端设备类型');
insert into sys_dict_type values(13, '000000', '布尔值', 'sys_bool', 103, 1, sysdate(), null, null, '布尔值, true 或 false'); insert into sys_dict_type values(13, '000000', '布尔值', 'sys_bool', 103, 1, sysdate(), null, null, '布尔值, true 或 false');
insert into sys_dict_type values(14, '000000', '用户状态', 'im_user_status', 103, 1, sysdate(), null, null, 'IM用户状态'); insert into sys_dict_type values(14, '000000', '用户状态', 'im_user_status', 103, 1, sysdate(), null, null, 'IM用户状态');
insert into sys_dict_type values(15, '000000', '消息状态', 'im_message_status', 103, 1, sysdate(), null, null, 'IM消息状态');
insert into sys_dict_type values(16, '000000', '消息类型', 'im_message_type', 103, 1, sysdate(), null, null, 'IM消息类型');
-- ---------------------------- -- ----------------------------
@ -522,10 +528,22 @@ insert into sys_dict_data values(35, '000000', 0, 'PC', 'pc', 'sys_d
insert into sys_dict_data values(36, '000000', 0, '安卓', 'android', 'sys_device_type', '', 'default', 'N', 103, 1, sysdate(), null, null, '安卓'); insert into sys_dict_data values(36, '000000', 0, '安卓', 'android', 'sys_device_type', '', 'default', 'N', 103, 1, sysdate(), null, null, '安卓');
insert into sys_dict_data values(37, '000000', 0, 'iOS', 'ios', 'sys_device_type', '', 'default', 'N', 103, 1, sysdate(), null, null, 'iOS'); insert into sys_dict_data values(37, '000000', 0, 'iOS', 'ios', 'sys_device_type', '', 'default', 'N', 103, 1, sysdate(), null, null, 'iOS');
insert into sys_dict_data values(38, '000000', 0, '小程序', 'xcx', 'sys_device_type', '', 'default', 'N', 103, 1, sysdate(), null, null, '小程序'); insert into sys_dict_data values(38, '000000', 0, '小程序', 'xcx', 'sys_device_type', '', 'default', 'N', 103, 1, sysdate(), null, null, '小程序');
insert into sys_dict_data values(39, '000000', 0, '', 'false', 'sys_bool', '', 'danger', 'N', 103, 1, sysdate(), null, null, 'PC'); insert into sys_dict_data values(39, '000000', 0, '', 'false', 'sys_bool', '', 'danger', 'N', 103, 1, sysdate(), null, null, '');
insert into sys_dict_data values(40, '000000', 0, '', 'true', 'sys_bool', '', 'primary', 'N', 103, 1, sysdate(), null, null, '安卓'); insert into sys_dict_data values(40, '000000', 0, '', 'true', 'sys_bool', '', 'primary', 'N', 103, 1, sysdate(), null, null, '');
insert into sys_dict_data values(41, '000000', 0, '正常', '0', 'im_user_status', '', 'primary', 'N', 103, 1, sysdate(), null, null, 'iOS'); insert into sys_dict_data values(41, '000000', 0, '正常', '0', 'im_user_status', '', 'primary', 'N', 103, 1, sysdate(), null, null, '');
insert into sys_dict_data values(42, '000000', 0, '已注销', '1', 'im_user_status', '', 'danger', 'N', 103, 1, sysdate(), null, null, '小程序'); insert into sys_dict_data values(42, '000000', 0, '已注销', '1', 'im_user_status', '', 'danger', 'N', 103, 1, sysdate(), null, null, '');
insert into sys_dict_data values(43, '000000', 1, '未发送', '0', 'im_message_status', '', 'danger', 'N', 103, 1, sysdate(), null, null, '');
insert into sys_dict_data values(44, '000000', 2, '送达', '1', 'im_message_status', '', 'primary', 'N', 103, 1, sysdate(), null, null, '');
insert into sys_dict_data values(45, '000000', 3, '撤回', '2', 'im_message_status', '', 'warning', 'N', 103, 1, sysdate(), null, null, '');
insert into sys_dict_data values(46, '000000', 4, '已读', '3', 'im_message_status', '', 'success', 'N', 103, 1, sysdate(), null, null, '');
insert into sys_dict_data values(47, '000000', 1, '文字', '0', 'im_message_type', '', 'primary', 'N', 103, 1, sysdate(), null, null, '');
insert into sys_dict_data values(48, '000000', 2, '图片', '1', 'im_message_type', '', 'primary', 'N', 103, 1, sysdate(), null, null, '');
insert into sys_dict_data values(49, '000000', 3, '文件', '2', 'im_message_type', '', 'primary', 'N', 103, 1, sysdate(), null, null, '');
insert into sys_dict_data values(50, '000000', 4, '语音', '3', 'im_message_type', '', 'primary', 'N', 103, 1, sysdate(), null, null, '');
insert into sys_dict_data values(51, '000000', 5, '视频', '4', 'im_message_type', '', 'primary', 'N', 103, 1, sysdate(), null, null, '');
insert into sys_dict_data values(52, '000000', 6, '文字提示', '21', 'im_message_type', '', 'primary', 'N', 103, 1, sysdate(), null, null, '');
insert into sys_dict_data values(53, '000000', 7, '语音通话', '40', 'im_message_type', '', 'primary', 'N', 103, 1, sysdate(), null, null, '');
insert into sys_dict_data values(54, '000000', 8, '视频通话', '41', 'im_message_type', '', 'primary', 'N', 103, 1, sysdate(), null, null, '');
-- ---------------------------- -- ----------------------------

63
im-admin-ui/src/api/im/group/index.ts

@ -0,0 +1,63 @@
import request from '@/utils/request';
import { AxiosPromise } from 'axios';
import { GroupVO, GroupBanDTO, GroupUnbanDTO, GroupQuery } from '@/api/im/group/types';
/**
*
* @param query
* @returns {*}
*/
export const listGroup = (query?: GroupQuery): AxiosPromise<GroupVO[]> => {
return request({
url: '/im/group/list',
method: 'get',
params: query
});
};
/**
*
* @param id
*/
export const getGroup = (id: string | number): AxiosPromise<GroupVO> => {
return request({
url: '/im/group/' + id,
method: 'get'
});
};
/**
*
* @param data
*/
export const ban = (data: GroupBanDTO) => {
return request({
url: '/im/group/ban',
method: 'put',
data: data
});
};
/**
*
* @param data
*/
export const unban = (data: GroupUnbanDTO) => {
return request({
url: '/im/group/unban',
method: 'put',
data: data
});
};
export const findGroupByName = (name?: String): AxiosPromise<GroupVO[]> => {
return request({
url: '/im/group/findByName?name='+name,
method: 'get'
});
};

193
im-admin-ui/src/api/im/group/types.ts

@ -0,0 +1,193 @@
export interface GroupVO {
/**
* id
*/
id: string | number;
/**
*
*/
name: string;
/**
* id
*/
ownerId: string | number;
/**
*
*/
headImage: string;
/**
* Url
*/
headImageUrl: string;
/**
*
*/
headImageThumb: string;
/**
*
*/
notice: string;
/**
*
*/
dissolve: number;
/**
*
*/
createdTime: string;
/**
* 0: 1:
*/
isBanned: number;
/**
*
*/
reason: string;
/**
*
*/
memberCount: number;
}
export interface GroupForm extends BaseEntity {
/**
* id
*/
id?: string | number;
/**
*
*/
name?: string;
/**
* id
*/
ownerId?: string | number;
/**
*
*/
ownerUserName: string,
/**
*
*/
headImage?: string;
/**
*
*/
headImageThumb?: string;
/**
*
*/
notice?: string;
/**
*
*/
dissolve?: number;
/**
*
*/
createdTime?: string;
/**
* 0: 1:
*/
isBanned?: number;
/**
*
*/
reason?: string;
}
export interface GroupQuery extends PageQuery {
/**
*
*/
name?: string;
/**
* id
*/
ownerId?: number;
/**
*
*/
headImage?: string;
/**
*
*/
headImageThumb?: string;
/**
*
*/
notice?: string;
/**
*
*/
dissolve?: number;
/**
*
*/
createdTime?: string;
/**
* 0: 1:
*/
isBanned?: number;
/**
*
*/
reason?: string;
/**
*
*/
params?: any;
}
export interface GroupBanDTO {
/**
* id
*/
id?: string | number;
/**
*
*/
reason?: string;
}
export interface GroupUnbanDTO {
/**
* id
*/
id?: string | number;
}

18
im-admin-ui/src/api/im/groupMember/index.ts

@ -0,0 +1,18 @@
import request from '@/utils/request';
import { AxiosPromise } from 'axios';
import { GroupMemberVO, GroupMemberQuery } from '@/api/im/groupMember/types';
/**
*
* @param query
* @returns {*}
*/
export const listGroupMember = (query?: GroupMemberQuery): AxiosPromise<GroupMemberVO[]> => {
return request({
url: '/im/group/member/list',
method: 'get',
params: query
});
};

173
im-admin-ui/src/api/im/groupMember/types.ts

@ -0,0 +1,173 @@
export interface GroupMemberVO {
/**
* id
*/
id: string | number;
/**
* id
*/
groupId: string | number;
/**
* id
*/
userId: string | number;
/**
*
*/
remarkNickName: string;
/**
*
*/
headImage: string;
/**
* Url
*/
headImageUrl: string;
/**
*
*/
remarkGroupName: string;
/**
* 退
*/
quit: number;
/**
*
*/
createdTime: string;
/**
* 退
*/
quitTime: string;
/**
*
*/
userNickName: string;
}
export interface GroupMemberForm extends BaseEntity {
/**
* id
*/
id?: string | number;
/**
* id
*/
groupId?: string | number;
/**
* id
*/
userId?: string | number;
/**
*
*/
remarkNickName?: string;
/**
*
*/
headImage?: string;
/**
*
*/
remarkGroupName?: string;
/**
* 退
*/
quit?: number;
/**
*
*/
createdTime?: string;
/**
* 退
*/
quitTime?: string;
/**
*
*/
userNickName?: string;
}
export interface GroupMemberQuery extends PageQuery {
/**
* id
*/
groupId?: string | number;
/**
* id
*/
userId?: number;
/**
*
*/
userName: String,
/**
*
*/
remarkNickName?: string;
/**
*
*/
headImage?: string;
/**
*
*/
remarkGroupName?: string;
/**
* 退
*/
quit?: number;
/**
*
*/
createdTime?: string;
/**
* 退
*/
quitTime?: string;
/**
*
*/
userNickName?: string;
/**
*
*/
showNickName?: string;
/**
*
*/
params?: any;
}

63
im-admin-ui/src/api/im/groupMessage/index.ts

@ -0,0 +1,63 @@
import request from '@/utils/request';
import { AxiosPromise } from 'axios';
import { GroupMessageVO, GroupMessageForm, GroupMessageQuery } from '@/api/im/groupMessage/types';
/**
*
* @param query
* @returns {*}
*/
export const listGroupMessage = (query?: GroupMessageQuery): AxiosPromise<GroupMessageVO[]> => {
return request({
url: '/im/groupMessage/list',
method: 'get',
params: query
});
};
/**
*
* @param id
*/
export const getGroupMessage = (id: string | number): AxiosPromise<GroupMessageVO> => {
return request({
url: '/im/groupMessage/' + id,
method: 'get'
});
};
/**
*
* @param data
*/
export const addGroupMessage = (data: GroupMessageForm) => {
return request({
url: '/im/groupMessage',
method: 'post',
data: data
});
};
/**
*
* @param data
*/
export const updateGroupMessage = (data: GroupMessageForm) => {
return request({
url: '/im/groupMessage',
method: 'put',
data: data
});
};
/**
*
* @param id
*/
export const delGroupMessage = (id: string | number | Array<string | number>) => {
return request({
url: '/im/groupMessage/' + id,
method: 'delete'
});
};

191
im-admin-ui/src/api/im/groupMessage/types.ts

@ -0,0 +1,191 @@
export interface GroupMessageVO {
/**
* id
*/
id: string | number;
/**
* id
*/
groupId: string | number;
/**
* id
*/
sendId: string | number;
/**
*
*/
sendNickName: string;
/**
* @用户id列表
*/
atUserIds: string | number;
/**
*
*/
content: string;
/**
*
*/
status: number;
/**
* 0:文字 1:图片 2:文件
*/
type: number;
/**
*
*/
sendTime: string;
/**
*
*/
receiptOk: number;
/**
*
*/
receipt: number;
/**
* id,
*/
recvIds: string | number;
}
export interface GroupMessageForm extends BaseEntity {
/**
* id
*/
id?: string | number;
/**
* id
*/
groupId?: string | number;
/**
* id
*/
sendId?: string | number;
/**
*
*/
sendNickName?: string;
/**
* @用户id列表
*/
atUserIds?: string | number;
/**
*
*/
content?: string;
/**
*
*/
status?: number;
/**
* 0:文字 1:图片 2:文件
*/
type?: number;
/**
*
*/
sendTime?: string;
/**
*
*/
receiptOk?: number;
/**
*
*/
receipt?: number;
/**
* id,
*/
recvIds?: string | number;
}
export interface GroupMessageQuery extends PageQuery {
/**
* id
*/
groupId?: string | number;
/**
* id
*/
sendId?: string | number;
/**
*
*/
sendNickName?: string;
/**
* @用户id列表
*/
atUserIds?: string | number;
/**
*
*/
content?: string;
/**
*
*/
status?: number;
/**
* 0:文字 1:图片 2:文件
*/
type?: number;
/**
*
*/
sendTime?: string;
/**
*
*/
receiptOk?: number;
/**
*
*/
receipt?: number;
/**
* id,
*/
recvIds?: string | number;
/**
*
*/
params?: any;
}

29
im-admin-ui/src/api/im/privateMessage/index.ts

@ -0,0 +1,29 @@
import request from '@/utils/request';
import { AxiosPromise } from 'axios';
import { PrivateMessageVO, PrivateMessageQuery } from '@/api/im/privateMessage/types';
/**
*
* @param query
* @returns {*}
*/
export const listPrivateMessage = (query?: PrivateMessageQuery): AxiosPromise<PrivateMessageVO[]> => {
return request({
url: '/im/privateMessage/list',
method: 'get',
params: query
});
};
/**
*
* @param id
*/
export const getPrivateMessage = (id: string | number): AxiosPromise<PrivateMessageVO> => {
return request({
url: '/im/privateMessage/' + id,
method: 'get'
});
};

129
im-admin-ui/src/api/im/privateMessage/types.ts

@ -0,0 +1,129 @@
export interface PrivateMessageVO {
/**
* id
*/
id: string | number;
/**
* id
*/
sendId: string | number;
/**
* id
*/
recvId: string | number;
/**
*
*/
content: string;
/**
* 0:文字 1:图片 2:文件
*/
type: number;
/**
* 0:未读 1:已读
*/
status: number;
/**
*
*/
sendTime: string;
}
export interface PrivateMessageForm extends BaseEntity {
/**
* id
*/
id?: string | number;
/**
* id
*/
sendId?: string | number;
/**
* id
*/
sendUserName: string;
/**
* id
*/
recvId?: string | number;
/**
*
*/
recvUserName: string;
/**
*
*/
content?: string;
/**
* 0:文字 1:图片 2:文件
*/
type?: number;
/**
* 0:未读 1:已读
*/
status?: number;
/**
*
*/
sendTime?: string;
}
export interface PrivateMessageQuery extends PageQuery {
/**
* id
*/
sendId?: number;
/**
* id
*/
recvId?: number;
/**
*
*/
content?: string;
/**
* 0:文字 1:图片 2:文件
*/
type?: number;
/**
* 0:未读 1:已读
*/
status?: number;
/**
*
*/
sendTime?: string;
/**
*
*/
params?: any;
}

63
im-admin-ui/src/api/im/sensitiveWord/index.ts

@ -0,0 +1,63 @@
import request from '@/utils/request';
import { AxiosPromise } from 'axios';
import { SensitiveWordVO, SensitiveWordForm, SensitiveWordQuery } from '@/api/im/sensitiveWord/types';
/**
*
* @param query
* @returns {*}
*/
export const listSensitiveWord = (query?: SensitiveWordQuery): AxiosPromise<SensitiveWordVO[]> => {
return request({
url: '/im/sensitiveWord/list',
method: 'get',
params: query
});
};
/**
*
* @param id
*/
export const getSensitiveWord = (id: string | number): AxiosPromise<SensitiveWordVO> => {
return request({
url: '/im/sensitiveWord/' + id,
method: 'get'
});
};
/**
*
* @param data
*/
export const addSensitiveWord = (data: SensitiveWordForm) => {
return request({
url: '/im/sensitiveWord',
method: 'post',
data: data
});
};
/**
*
* @param data
*/
export const updateSensitiveWord = (data: SensitiveWordForm) => {
return request({
url: '/im/sensitiveWord',
method: 'put',
data: data
});
};
/**
*
* @param id
*/
export const delSensitiveWord = (id: string | number | Array<string | number>) => {
return request({
url: '/im/sensitiveWord/' + id,
method: 'delete'
});
};

71
im-admin-ui/src/api/im/sensitiveWord/types.ts

@ -0,0 +1,71 @@
export interface SensitiveWordVO {
/**
* id
*/
id: string | number;
/**
*
*/
content: string;
/**
* 0:未启用 1:启用
*/
enabled: number;
/**
*
*/
creator: number;
}
export interface SensitiveWordForm extends BaseEntity {
/**
* id
*/
id?: string | number;
/**
*
*/
content?: string;
/**
* 0:未启用 1:启用
*/
enabled?: number;
/**
*
*/
creator?: number;
}
export interface SensitiveWordQuery extends PageQuery {
/**
*
*/
content?: string;
/**
* 0:未启用 1:启用
*/
enabled?: number;
/**
*
*/
creator?: number;
/**
*
*/
params?: any;
}

63
im-admin-ui/src/api/im/smPushTask/index.ts

@ -0,0 +1,63 @@
import request from '@/utils/request';
import { AxiosPromise } from 'axios';
import { SmPushTaskVO, SmPushTaskForm, SmPushTaskQuery } from '@/api/im/smPushTask/types';
/**
*
* @param query
* @returns {*}
*/
export const listSmPushTask = (query?: SmPushTaskQuery): AxiosPromise<SmPushTaskVO[]> => {
return request({
url: '/im/smPushTask/list',
method: 'get',
params: query
});
};
/**
*
* @param id
*/
export const getSmPushTask = (id: string | number): AxiosPromise<SmPushTaskVO> => {
return request({
url: '/im/smPushTask/' + id,
method: 'get'
});
};
/**
*
* @param data
*/
export const addSmPushTask = (data: SmPushTaskForm) => {
return request({
url: '/im/smPushTask',
method: 'post',
data: data
});
};
/**
*
* @param data
*/
export const updateSmPushTask = (data: SmPushTaskForm) => {
return request({
url: '/im/smPushTask',
method: 'put',
data: data
});
};
/**
*
* @param id
*/
export const delSmPushTask = (id: string | number | Array<string | number>) => {
return request({
url: '/im/smPushTask/' + id,
method: 'delete'
});
};

161
im-admin-ui/src/api/im/smPushTask/types.ts

@ -0,0 +1,161 @@
export interface SmPushTaskVO {
/**
* id
*/
id: string | number;
/**
* id
*/
messageId: string | number;
/**
*
*/
seqNo: number;
/**
*
*/
sendTime: string;
/**
* 1:待发送 2:发送中 3:已发送 4:已取消
*/
status: number;
/**
*
*/
sendToAll: number;
/**
* id,,send_to_all为false时有效
*/
recvIds: string | number;
/**
* 0 1
*/
deleted: number;
/**
*
*/
creator: number;
/**
*
*/
updater: number;
}
export interface SmPushTaskForm extends BaseEntity {
/**
* id
*/
id?: string | number;
/**
* id
*/
messageId?: string | number;
/**
*
*/
seqNo?: number;
/**
*
*/
sendTime?: string;
/**
* 1:待发送 2:发送中 3:已发送 4:已取消
*/
status?: number;
/**
*
*/
sendToAll?: number;
/**
* id,,send_to_all为false时有效
*/
recvIds?: string | number;
/**
* 0 1
*/
deleted?: number;
/**
*
*/
creator?: number;
/**
*
*/
updater?: number;
}
export interface SmPushTaskQuery extends PageQuery {
/**
* id
*/
messageId?: string | number;
/**
*
*/
seqNo?: number;
/**
*
*/
sendTime?: string;
/**
* 1:待发送 2:发送中 3:已发送 4:已取消
*/
status?: number;
/**
*
*/
sendToAll?: number;
/**
* id,,send_to_all为false时有效
*/
recvIds?: string | number;
/**
* 0 1
*/
deleted?: number;
/**
*
*/
creator?: number;
/**
*
*/
updater?: number;
/**
*
*/
params?: any;
}

63
im-admin-ui/src/api/im/systemMessage/index.ts

@ -0,0 +1,63 @@
import request from '@/utils/request';
import { AxiosPromise } from 'axios';
import { SystemMessageVO, SystemMessageForm, SystemMessageQuery } from '@/api/im/systemMessage/types';
/**
*
* @param query
* @returns {*}
*/
export const listSystemMessage = (query?: SystemMessageQuery): AxiosPromise<SystemMessageVO[]> => {
return request({
url: '/im/systemMessage/list',
method: 'get',
params: query
});
};
/**
*
* @param id
*/
export const getSystemMessage = (id: string | number): AxiosPromise<SystemMessageVO> => {
return request({
url: '/im/systemMessage/' + id,
method: 'get'
});
};
/**
*
* @param data
*/
export const addSystemMessage = (data: SystemMessageForm) => {
return request({
url: '/im/systemMessage',
method: 'post',
data: data
});
};
/**
*
* @param data
*/
export const updateSystemMessage = (data: SystemMessageForm) => {
return request({
url: '/im/systemMessage',
method: 'put',
data: data
});
};
/**
*
* @param id
*/
export const delSystemMessage = (id: string | number | Array<string | number>) => {
return request({
url: '/im/systemMessage/' + id,
method: 'delete'
});
};

161
im-admin-ui/src/api/im/systemMessage/types.ts

@ -0,0 +1,161 @@
export interface SystemMessageVO {
/**
* id
*/
id: string | number;
/**
*
*/
title: string;
/**
*
*/
coverUrl: string;
/**
*
*/
intro: string;
/**
* 0:富文本 1:外部链接
*/
contentType: number;
/**
* base64编码
*/
richText: string;
/**
*
*/
externLink: string;
/**
* 0 1
*/
deleted: number;
/**
*
*/
creator: number;
/**
*
*/
updater: number;
}
export interface SystemMessageForm extends BaseEntity {
/**
* id
*/
id?: string | number;
/**
*
*/
title?: string;
/**
*
*/
coverUrl?: string;
/**
*
*/
intro?: string;
/**
* 0:富文本 1:外部链接
*/
contentType?: number;
/**
* base64编码
*/
richText?: string;
/**
*
*/
externLink?: string;
/**
* 0 1
*/
deleted?: number;
/**
*
*/
creator?: number;
/**
*
*/
updater?: number;
}
export interface SystemMessageQuery extends PageQuery {
/**
*
*/
title?: string;
/**
*
*/
coverUrl?: string;
/**
*
*/
intro?: string;
/**
* 0:富文本 1:外部链接
*/
contentType?: number;
/**
* base64编码
*/
richText?: string;
/**
*
*/
externLink?: string;
/**
* 0 1
*/
deleted?: number;
/**
*
*/
creator?: number;
/**
*
*/
updater?: number;
/**
*
*/
params?: any;
}

63
im-admin-ui/src/api/im/user/index.ts

@ -0,0 +1,63 @@
import request from '@/utils/request';
import { AxiosPromise } from 'axios';
import { UserVO, UserBanDTO, UserUnbanDTO, UserQuery } from '@/api/im/user/types';
/**
*
* @param query
* @returns {*}
*/
export const listUser = (query?: UserQuery): AxiosPromise<UserVO[]> => {
return request({
url: '/im/user/list',
method: 'get',
params: query
});
};
/**
*
* @param id
*/
export const getUser = (id: string | number): AxiosPromise<UserVO> => {
return request({
url: '/im/user/' + id,
method: 'get'
});
};
/**
*
* @param data
*/
export const ban = (data: UserBanDTO) => {
return request({
url: '/im/user/ban',
method: 'put',
data: data
});
};
/**
*
* @param data
*/
export const unban = (data: UserUnbanDTO) => {
return request({
url: '/im/user/unban',
method: 'put',
data: data
});
};
export const findUserByName = (name?: String): AxiosPromise<UserVO[]> => {
return request({
url: '/im/user/findByName?name='+name,
method: 'get'
});
};

204
im-admin-ui/src/api/im/user/types.ts

@ -0,0 +1,204 @@
export interface UserVO {
/**
* id
*/
id: string | number;
/**
*
*/
userName: string;
/**
*
*/
nickName: string;
/**
*
*/
headImage: string;
/**
* Url
*/
headImageUrl: string;
/**
*
*/
headImageThumb: string;
/**
* ()
*/
password: string;
/**
* 0: 1::
*/
sex: number;
/**
*
*/
signature: string;
/**
*
*/
lastLoginTime: string;
/**
*
*/
createdTime: string;
/**
*
*/
type: number;
/**
* 0: 1:
*/
isBanned: number;
/**
*
*/
reason: string;
/**
* id,uni-push推送
*/
cid: string | number;
/**
* 0 1:已注销
*/
status: number;
}
export interface UserForm extends BaseEntity {
/**
* id
*/
id?: string | number;
/**
*
*/
userName?: string;
/**
*
*/
nickName?: string;
/**
*
*/
headImage?: string;
/**
*
*/
headImageThumb?: string;
/**
* ()
*/
password?: string;
/**
* 0: 1::
*/
sex?: number;
/**
*
*/
signature?: string;
/**
*
*/
lastLoginTime?: string;
/**
*
*/
createdTime?: string;
/**
*
*/
type?: number;
/**
* 0: 1:
*/
isBanned?: number;
/**
*
*/
reason?: string;
/**
* id,uni-push推送
*/
cid?: string | number;
/**
* 0 1:已注销
*/
status?: number;
}
export interface UserQuery extends PageQuery {
/**
*
*/
userName?: string;
/**
*
*/
nickName?: string;
/**
*
*/
createdTime?: string;
/**
*
*/
params?: any;
}
export interface UserBanDTO {
/**
* id
*/
id?: string | number;
/**
*
*/
reason?: string;
}
export interface UserUnbanDTO {
/**
* id
*/
id?: string | number;
}

52
im-admin-ui/src/components/im/ImGroupSelect/index.vue

@ -0,0 +1,52 @@
<template>
<el-select v-model="groupIds" :multiple="multiple" filterable remote clearable
:placeholder="placeholder" :remote-method="handleRemote" :loading="loading"
style="width: 240px">
<el-option v-for="group in options" :key="group.id" :label="group.name" :value="group.id" />
</el-select>
</template>
<script setup lang="ts" name="ImGroupSelect">
import { computed } from 'vue'
import { ref } from 'vue'
import { findGroupByName } from '@/api/im/group'
const props = defineProps({
multiple: {
type: Boolean,
required: false,
default: () => false
},
placeholder: {
type: String,
required: false,
default: () => ''
}
})
const loading = ref(false)
const options = ref()
const model = defineModel<number | Array<Number>>()
const groupIds = computed({
get() {
if(model.value != undefined){
return model.value
}else if(props.multiple){
return []
}
},
set(value) {
model.value = value
}
})
const handleRemote = (name: String)=>{
loading.value = true
findGroupByName().then((res) => {
loading.value = false;
options.value = res.data;
});
}
</script>

39
im-admin-ui/src/components/im/ImMessageContent/index.vue

@ -0,0 +1,39 @@
<template>
<div>
<div v-if="message.type == 1" >
<image-preview :src="JSON.parse(message.content).thumbUrl" :full-src="JSON.parse(message.content).originUrl" :width="40" :height="40" />
</div>
<div v-else-if="message.type == 2">
<el-link type="primary" :href="JSON.parse(message.content).url" :download="JSON.parse(message.content).name" target="_blank">
{{ JSON.parse(message.content).name }}
</el-link>
</div>
<div v-else-if="message.type == 3">
<audio style="height: 40px;" controls :src="JSON.parse(message.content).url"></audio>
</div>
<div v-else-if="message.type == 4">
<video style="max-height: 120px;max-width: 200px;" controls :poster="JSON.parse(message.content).coverUrl" :src="JSON.parse(message.content).videoUrl"></video>
</div>
<div v-else>
{{ message.content }}
</div>
</div>
</template>
<script setup lang="ts" name="ImMessageContent">
import { ref} from 'vue'
const props = defineProps({
message: {
type: Object,
required: true
}
})
</script>
<style scoped>
.link {
color: inherit;
text-decoration: none;
}
</style>

51
im-admin-ui/src/components/im/ImSysMsgSelect/index.vue

@ -0,0 +1,51 @@
<template>
<el-select v-model="messageIds" :multiple="multiple" filterable remote clearable
:placeholder="placeholder" :remote-method="handleRemote" :loading="loading">
<el-option v-for="message in options" :key="message.id" :label="message.title" :value="message.id" />
</el-select>
</template>
<script setup lang="ts" name="ImSmSelect">
import { computed } from 'vue'
import { ref } from 'vue'
import { listSystemMessage } from '@/api/im/systemMessage'
const props = defineProps({
multiple: {
type: Boolean,
required: false,
default: () => false
},
placeholder: {
type: String,
required: false,
default: () => ''
}
})
const loading = ref(false)
const options = ref()
const model = defineModel<number | Array<Number>>()
const messageIds = computed({
get() {
if(model.value != undefined){
return model.value
}else if(props.multiple){
return []
}
},
set(value) {
model.value = value
}
})
const handleRemote = (name: string)=>{
loading.value = true
listSystemMessage().then((res) => {
loading.value = false;
options.value = res.data;
});
}
</script>

51
im-admin-ui/src/components/im/ImUserSelect/index.vue

@ -0,0 +1,51 @@
<template>
<el-select v-model="userIds" :multiple="multiple" filterable remote clearable
:placeholder="placeholder" :remote-method="handleRemote" :loading="loading">
<el-option v-for="user in options" :key="user.id" :label="user.userName" :value="user.id" />
</el-select>
</template>
<script setup lang="ts" name="ImUserSelect">
import { computed } from 'vue'
import { ref } from 'vue'
import { findUserByName } from '@/api/im/user'
const props = defineProps({
multiple: {
type: Boolean,
required: false,
default: () => false
},
placeholder: {
type: String,
required: false,
default: () => ''
}
})
const loading = ref(false)
const options = ref()
const model = defineModel<number | Array<Number>>()
const userIds = computed({
get() {
if(model.value != undefined){
return model.value
}else if(props.multiple){
return []
}
},
set(value) {
model.value = value
}
})
const handleRemote = (name: String)=>{
loading.value = true
findUserByName(name).then((res) => {
loading.value = false;
options.value = res.data;
});
}
</script>

292
im-admin-ui/src/views/im/group/index.vue

@ -0,0 +1,292 @@
<template>
<div class="p-2">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter"
:leave-active-class="proxy?.animate.searchAnimate.leave">
<div v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover">
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
<el-form-item label="群名" prop="name">
<el-input v-model="queryParams.name" placeholder="请输入群名字" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="群主" prop="ownerIds">
<im-user-select v-model="queryParams.ownerId"></im-user-select>
</el-form-item>
<el-form-item label="创建时间" prop="createdTime">
<el-date-picker
v-model="dateRange"
value-format="YYYY-MM-DD HH:mm:ss"
type="daterange"
range-separator="-"
start-placeholder="开始日期"
end-placeholder="结束日期"
:default-time="[new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 1, 1, 23, 59, 59)]"
></el-date-picker>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
</el-card>
</div>
</transition>
<el-card shadow="never">
<template #header>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="warning" plain icon="Download" @click="handleExport"
v-hasPermi="['im:group:export']">导出</el-button>
</el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
</template>
<el-table v-loading="loading" :data="groupList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="群头像" align="center" prop="headImage" width="100">
<template #default="scope">
<image-preview :src="scope.row.headImageThumb" :full-src="scope.row.headImage" :width="40" :height="40" />
</template>
</el-table-column>
<el-table-column label="群名" align="center" prop="name" />
<el-table-column label="群主" align="center" prop="ownerUserName" />
<el-table-column label="成员数量" align="center" prop="memberCount" />
<el-table-column label="创建时间" align="center" prop="createdTime" width="180">
<template #default="scope">
<span>{{ parseTime(scope.row.createdTime, '{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
<el-table-column label="是否解散" align="center" prop="dissolve">
<template #default="scope">
<dict-tag :options="sys_bool" :value="scope.row.dissolve" />
</template>
</el-table-column>
<el-table-column label="是否封禁" align="center" prop="isBanned">
<template #default="scope">
<dict-tag :options="sys_bool" :value="scope.row.isBanned" />
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template #default="scope">
<el-tooltip content="详情" placement="top">
<el-button link type="primary" v-hasPermi="['im:group:query']"
@click="handleDetail(scope.row)">详情</el-button>
</el-tooltip>
<el-tooltip v-if="scope.row.isBanned" placement="top">
<el-button link type="danger" v-hasPermi="['im:group:ban']" @click="handleUnban(scope.row)">解封</el-button>
</el-tooltip>
<el-tooltip v-else placement="top">
<el-button link type="danger" v-hasPermi="['im:group:ban']" @click="handleBan(scope.row)">封禁</el-button>
</el-tooltip>
<el-tooltip placement="top">
<el-button link type="primary" @click="handleShowMember(scope.row.id)">查看成员</el-button>
</el-tooltip>
</template>
</el-table-column>
</el-table>
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize" @pagination="getList" />
</el-card>
<!-- 添加或修改群对话框 -->
<el-dialog :title="dialog.title" v-model="dialog.visible" width="800px" append-to-body>
<el-form ref="groupFormRef" :model="form" label-width="100px" disabled>
<el-form-item label="群头像" prop="headImage">
<image-preview :src="form.headImageThumb" :full-src="form.headImage" :width="50" :height="50" />
</el-form-item>
<el-form-item label="群名字" prop="name">
<el-input v-model="form.name" placeholder="请输入群名字" />
</el-form-item>
<el-form-item label="群主" prop="name">
<el-input v-model="form.ownerUserName" placeholder="请输入群名字" />
</el-form-item>
<el-form-item label="创建时间" prop="createdTime">
<el-date-picker clearable v-model="form.createdTime" type="datetime" value-format="YYYY-MM-DD HH:mm:ss">
</el-date-picker>
</el-form-item>
<el-form-item label="是否已解散" prop="dissolve">
<dict-tag :options="sys_bool" :value="form.dissolve" />
</el-form-item>
<el-form-item label="是否被封禁" prop="isBanned">
<dict-tag :options="sys_bool" :value="form.isBanned" />
</el-form-item>
<el-form-item v-if="form.isBanned" label="被封禁原因" prop="reason">
<el-input v-model="form.reason" placeholder="请输入被封禁原因" />
</el-form-item>
<el-form-item label="群公告" prop="notice">
<el-input type="textarea" v-model="form.notice"></el-input>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button :loading="buttonLoading" type="primary" @click="submitForm"> </el-button>
</div>
</template>
</el-dialog>
<el-drawer v-model="memberVisible" title="成员列表" :size="900" :close-on-press-escape="false" :close-on-click-modal="true">
<member ref="memberRef"></member>
</el-drawer>
</div>
</template>
<script setup name="Group" lang="ts">
import { listGroup, getGroup, ban, unban } from '@/api/im/group';
import { GroupVO, GroupQuery, GroupForm } from '@/api/im/group/types';
import member from './member.vue';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const groupList = ref<GroupVO[]>([]);
const buttonLoading = ref(false);
const loading = ref(true);
const showSearch = ref(true);
const ids = ref<Array<string | number>>([]);
const single = ref(true);
const multiple = ref(true);
const total = ref(0);
const memberVisible = ref(false);
const dateRange = ref<[DateModelType, DateModelType]>(['', '']);
const queryFormRef = ref<ElFormInstance>();
const groupFormRef = ref<ElFormInstance>();
const memberRef = ref();
const dialog = reactive<DialogOption>({
visible: false,
title: ''
});
const initFormData: GroupForm = {
id: undefined,
name: undefined,
ownerId: undefined,
ownerUserName: undefined,
headImage: undefined,
headImageThumb: undefined,
notice: undefined,
dissolve: undefined,
createdTime: undefined,
isBanned: undefined,
reason: undefined
}
const data = reactive<PageData<GroupForm, GroupQuery>>({
form: { ...initFormData },
queryParams: {
pageNum: 1,
pageSize: 10,
name: undefined,
ownerId: undefined,
headImage: undefined,
headImageThumb: undefined,
notice: undefined,
dissolve: undefined,
createdTime: undefined,
isBanned: undefined,
reason: undefined,
params: {
}
},
rules: {
}
});
const { queryParams, form } = toRefs(data);
const { sys_bool } = toRefs<any>(proxy?.useDict('sys_bool'));
/** 查询群列表 */
const getList = async () => {
loading.value = true;
const res = await listGroup(proxy?.addDateRange(queryParams.value, dateRange.value));
groupList.value = res.rows;
total.value = res.total;
loading.value = false;
}
/** 表单重置 */
const reset = () => {
form.value = { ...initFormData };
groupFormRef.value?.resetFields();
}
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.value.pageNum = 1;
getList();
}
/** 重置按钮操作 */
const resetQuery = () => {
dateRange.value = ['', ''];
queryFormRef.value?.resetFields();
handleQuery();
}
/** 多选框选中数据 */
const handleSelectionChange = (selection: GroupVO[]) => {
ids.value = selection.map(item => item.id);
single.value = selection.length != 1;
multiple.value = !selection.length;
}
/** 详情按钮操作 */
const handleDetail = async (row?: GroupVO) => {
reset();
const _id = row?.id || ids.value[0]
const res = await getGroup(_id);
Object.assign(form.value, res.data);
dialog.visible = true;
dialog.title = "用户信息";
}
const handleBan = (group: any) => {
ElMessageBox.prompt('封禁原因:', '确定对该群组进行封禁?', {
inputPattern: /\S/,
inputErrorMessage: '请输入封禁原因',
confirmButtonText: '确定',
cancelButtonText: '取消'
}).then(({ value }) => {
const data = { id: group.id, reason: value }
ban(data).then(() => {
group.isBanned = true;
ElMessage.success(`群组'${group.name}'已被封禁`);
})
})
}
const handleUnban = (group: any) => {
ElMessageBox.confirm('确定解除该群组的封禁状态?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消'
}).then(() => {
const data = { id: group.id }
unban(data).then(() => {
group.isBanned = false;
ElMessage.success(`群组'${group.name}'解封成功`);
})
})
}
const handleShowMember = (id: number) => {
memberVisible.value = true;
nextTick(() => memberRef.value.init(id));
}
/** 提交按钮 */
const submitForm = () => {
dialog.visible = false;
}
/** 导出按钮操作 */
const handleExport = () => {
proxy?.download('im/group/export', {
...queryParams.value
}, `group_${new Date().getTime()}.xlsx`)
}
onMounted(() => {
getList();
});
</script>

99
im-admin-ui/src/views/im/group/member.vue

@ -0,0 +1,99 @@
<template>
<div class="p-2">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter"
:leave-active-class="proxy?.animate.searchAnimate.leave">
<div v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover">
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
<el-form-item label="用户" prop="userId">
<im-user-select v-model="queryParams.userId"></im-user-select>
</el-form-item>
<el-form-item label="昵称" prop="userNickName">
<el-input v-model="queryParams.userNickName" placeholder="请输入用户昵称" clearable />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
</el-card>
</div>
</transition>
<el-card shadow="never">
<el-table v-loading="loading" :data="groupMemberList">
<el-table-column label="头像" align="center" prop="headImage" width="100">
<template #default="scope">
<image-preview :src="scope.row.headImage" :width="50" :height="50" />
</template>
</el-table-column>
<el-table-column label="用户" align="center" prop="userName" />
<el-table-column label="群内昵称" align="center" prop="showNickName" />
<el-table-column label="进群时间" align="center" prop="createdTime" width="180">
<template #default="scope">
<span>{{ parseTime(scope.row.createdTime, '{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
</el-table>
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize" @pagination="getList" />
</el-card>
</div>
</template>
<script setup name="member" lang="ts">
import { listGroupMember } from '@/api/im/groupMember';
import { GroupMemberVO, GroupMemberQuery } from '@/api/im/groupMember/types';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const groupMemberList = ref<GroupMemberVO[]>([]);
const loading = ref(true);
const showSearch = ref(true);
const total = ref(0);
const queryFormRef = ref<ElFormInstance>();
const queryParams = ref<GroupMemberQuery>({
pageNum: 1,
pageSize: 10,
groupId: undefined,
userId: undefined,
userName: undefined,
remarkNickName: undefined,
headImage: undefined,
remarkGroupName: undefined,
quit: undefined,
createdTime: undefined,
quitTime: undefined,
userNickName: undefined,
showNickName: undefined
})
/** 查询群成员列表 */
const getList = async () => {
loading.value = true;
const res = await listGroupMember(queryParams.value);
groupMemberList.value = res.rows;
total.value = res.total;
loading.value = false;
}
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.value.pageNum = 1;
getList();
}
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value?.resetFields();
handleQuery();
}
const init = (id: number) => {
queryParams.value.groupId = id;
getList();
}
defineExpose({
init
})
</script>

339
im-admin-ui/src/views/im/groupMember/index.vue

@ -0,0 +1,339 @@
<template>
<div class="p-2">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover">
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
<el-form-item label="群id" prop="groupId">
<el-input v-model="queryParams.groupId" placeholder="请输入群id" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="用户id" prop="userId">
<el-input v-model="queryParams.userId" placeholder="请输入用户id" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="组内显示名称" prop="remarkNickName">
<el-input v-model="queryParams.remarkNickName" placeholder="请输入组内显示名称" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="群名备注" prop="remarkGroupName">
<el-input v-model="queryParams.remarkGroupName" placeholder="请输入群名备注" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="是否已退出" prop="quit">
<el-input v-model="queryParams.quit" placeholder="请输入是否已退出" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="创建时间" prop="createdTime">
<el-date-picker clearable
v-model="queryParams.createdTime"
type="date"
value-format="YYYY-MM-DD"
placeholder="请选择创建时间"
/>
</el-form-item>
<el-form-item label="退出时间" prop="quitTime">
<el-date-picker clearable
v-model="queryParams.quitTime"
type="date"
value-format="YYYY-MM-DD"
placeholder="请选择退出时间"
/>
</el-form-item>
<el-form-item label="用户昵称" prop="userNickName">
<el-input v-model="queryParams.userNickName" placeholder="请输入用户昵称" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
</el-card>
</div>
</transition>
<el-card shadow="never">
<template #header>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['im:groupMember:add']">新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['im:groupMember:edit']">修改</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['im:groupMember:remove']">删除</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['im:groupMember:export']">导出</el-button>
</el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
</template>
<el-table v-loading="loading" :data="groupMemberList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="id" align="center" prop="id" v-if="true" />
<el-table-column label="群id" align="center" prop="groupId" />
<el-table-column label="用户id" align="center" prop="userId" />
<el-table-column label="组内显示名称" align="center" prop="remarkNickName" />
<el-table-column label="用户头像" align="center" prop="headImageUrl" width="100">
<template #default="scope">
<image-preview :src="scope.row.headImageUrl" :width="50" :height="50"/>
</template>
</el-table-column>
<el-table-column label="群名备注" align="center" prop="remarkGroupName" />
<el-table-column label="是否已退出" align="center" prop="quit" />
<el-table-column label="创建时间" align="center" prop="createdTime" width="180">
<template #default="scope">
<span>{{ parseTime(scope.row.createdTime, '{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
<el-table-column label="退出时间" align="center" prop="quitTime" width="180">
<template #default="scope">
<span>{{ parseTime(scope.row.quitTime, '{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
<el-table-column label="用户昵称" align="center" prop="userNickName" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template #default="scope">
<el-tooltip content="修改" placement="top">
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['im:groupMember:edit']"></el-button>
</el-tooltip>
<el-tooltip content="删除" placement="top">
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['im:groupMember:remove']"></el-button>
</el-tooltip>
</template>
</el-table-column>
</el-table>
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
</el-card>
<!-- 添加或修改群成员对话框 -->
<el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
<el-form ref="groupMemberFormRef" :model="form" :rules="rules" label-width="80px">
<el-form-item label="群id" prop="groupId">
<el-input v-model="form.groupId" placeholder="请输入群id" />
</el-form-item>
<el-form-item label="用户id" prop="userId">
<el-input v-model="form.userId" placeholder="请输入用户id" />
</el-form-item>
<el-form-item label="组内显示名称" prop="remarkNickName">
<el-input v-model="form.remarkNickName" placeholder="请输入组内显示名称" />
</el-form-item>
<el-form-item label="用户头像" prop="headImage">
<image-upload v-model="form.headImage"/>
</el-form-item>
<el-form-item label="群名备注" prop="remarkGroupName">
<el-input v-model="form.remarkGroupName" placeholder="请输入群名备注" />
</el-form-item>
<el-form-item label="是否已退出" prop="quit">
<el-input v-model="form.quit" placeholder="请输入是否已退出" />
</el-form-item>
<el-form-item label="创建时间" prop="createdTime">
<el-date-picker clearable
v-model="form.createdTime"
type="datetime"
value-format="YYYY-MM-DD HH:mm:ss"
placeholder="请选择创建时间">
</el-date-picker>
</el-form-item>
<el-form-item label="退出时间" prop="quitTime">
<el-date-picker clearable
v-model="form.quitTime"
type="datetime"
value-format="YYYY-MM-DD HH:mm:ss"
placeholder="请选择退出时间">
</el-date-picker>
</el-form-item>
<el-form-item label="用户昵称" prop="userNickName">
<el-input v-model="form.userNickName" placeholder="请输入用户昵称" />
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button :loading="buttonLoading" type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup name="GroupMember" lang="ts">
import { listGroupMember, getGroupMember, delGroupMember, addGroupMember, updateGroupMember } from '@/api/im/groupMember';
import { GroupMemberVO, GroupMemberQuery, GroupMemberForm } from '@/api/im/groupMember/types';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const groupMemberList = ref<GroupMemberVO[]>([]);
const buttonLoading = ref(false);
const loading = ref(true);
const showSearch = ref(true);
const ids = ref<Array<string | number>>([]);
const single = ref(true);
const multiple = ref(true);
const total = ref(0);
const queryFormRef = ref<ElFormInstance>();
const groupMemberFormRef = ref<ElFormInstance>();
const dialog = reactive<DialogOption>({
visible: false,
title: ''
});
const initFormData: GroupMemberForm = {
id: undefined,
groupId: undefined,
userId: undefined,
remarkNickName: undefined,
headImage: undefined,
remarkGroupName: undefined,
quit: undefined,
createdTime: undefined,
quitTime: undefined,
userNickName: undefined
}
const data = reactive<PageData<GroupMemberForm, GroupMemberQuery>>({
form: {...initFormData},
queryParams: {
pageNum: 1,
pageSize: 10,
groupId: undefined,
userId: undefined,
remarkNickName: undefined,
headImage: undefined,
remarkGroupName: undefined,
quit: undefined,
createdTime: undefined,
quitTime: undefined,
userNickName: undefined,
params: {
}
},
rules: {
id: [
{ required: true, message: "id不能为空", trigger: "blur" }
],
groupId: [
{ required: true, message: "群id不能为空", trigger: "blur" }
],
userId: [
{ required: true, message: "用户id不能为空", trigger: "blur" }
],
remarkNickName: [
{ required: true, message: "组内显示名称不能为空", trigger: "blur" }
],
headImage: [
{ required: true, message: "用户头像不能为空", trigger: "blur" }
],
remarkGroupName: [
{ required: true, message: "群名备注不能为空", trigger: "blur" }
],
quit: [
{ required: true, message: "是否已退出不能为空", trigger: "blur" }
],
createdTime: [
{ required: true, message: "创建时间不能为空", trigger: "blur" }
],
quitTime: [
{ required: true, message: "退出时间不能为空", trigger: "blur" }
],
userNickName: [
{ required: true, message: "用户昵称不能为空", trigger: "blur" }
]
}
});
const { queryParams, form, rules } = toRefs(data);
/** 查询群成员列表 */
const getList = async () => {
loading.value = true;
const res = await listGroupMember(queryParams.value);
groupMemberList.value = res.rows;
total.value = res.total;
loading.value = false;
}
/** 取消按钮 */
const cancel = () => {
reset();
dialog.visible = false;
}
/** 表单重置 */
const reset = () => {
form.value = {...initFormData};
groupMemberFormRef.value?.resetFields();
}
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.value.pageNum = 1;
getList();
}
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value?.resetFields();
handleQuery();
}
/** 多选框选中数据 */
const handleSelectionChange = (selection: GroupMemberVO[]) => {
ids.value = selection.map(item => item.id);
single.value = selection.length != 1;
multiple.value = !selection.length;
}
/** 新增按钮操作 */
const handleAdd = () => {
reset();
dialog.visible = true;
dialog.title = "添加群成员";
}
/** 修改按钮操作 */
const handleUpdate = async (row?: GroupMemberVO) => {
reset();
const _id = row?.id || ids.value[0]
const res = await getGroupMember(_id);
Object.assign(form.value, res.data);
dialog.visible = true;
dialog.title = "修改群成员";
}
/** 提交按钮 */
const submitForm = () => {
groupMemberFormRef.value?.validate(async (valid: boolean) => {
if (valid) {
buttonLoading.value = true;
if (form.value.id) {
await updateGroupMember(form.value).finally(() => buttonLoading.value = false);
} else {
await addGroupMember(form.value).finally(() => buttonLoading.value = false);
}
proxy?.$modal.msgSuccess("操作成功");
dialog.visible = false;
await getList();
}
});
}
/** 删除按钮操作 */
const handleDelete = async (row?: GroupMemberVO) => {
const _ids = row?.id || ids.value;
await proxy?.$modal.confirm('是否确认删除群成员编号为"' + _ids + '"的数据项?').finally(() => loading.value = false);
await delGroupMember(_ids);
proxy?.$modal.msgSuccess("删除成功");
await getList();
}
/** 导出按钮操作 */
const handleExport = () => {
proxy?.download('im/groupMember/export', {
...queryParams.value
}, `groupMember_${new Date().getTime()}.xlsx`)
}
onMounted(() => {
getList();
});
</script>

333
im-admin-ui/src/views/im/message/group/index.vue

@ -0,0 +1,333 @@
<template>
<div class="p-2">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover">
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
<el-form-item label="群id" prop="groupId">
<el-input v-model="queryParams.groupId" placeholder="请输入群id" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="发送用户id" prop="sendId">
<el-input v-model="queryParams.sendId" placeholder="请输入发送用户id" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="发送用户昵称" prop="sendNickName">
<el-input v-model="queryParams.sendNickName" placeholder="请输入发送用户昵称" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="被@用户id列表,逗号分隔" prop="atUserIds">
<el-input v-model="queryParams.atUserIds" placeholder="请输入被@用户id列表,逗号分隔" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="发送时间" prop="sendTime">
<el-date-picker clearable
v-model="queryParams.sendTime"
type="date"
value-format="YYYY-MM-DD"
placeholder="请选择发送时间"
/>
</el-form-item>
<el-form-item label="回执消息是否完成" prop="receiptOk">
<el-input v-model="queryParams.receiptOk" placeholder="请输入回执消息是否完成" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="是否回执消息" prop="receipt">
<el-input v-model="queryParams.receipt" placeholder="请输入是否回执消息" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="接收用户id,逗号分隔,为空表示发给所有成员" prop="recvIds">
<el-input v-model="queryParams.recvIds" placeholder="请输入接收用户id,逗号分隔,为空表示发给所有成员" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
</el-card>
</div>
</transition>
<el-card shadow="never">
<template #header>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['im:groupMessage:add']">新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['im:groupMessage:edit']">修改</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['im:groupMessage:remove']">删除</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['im:groupMessage:export']">导出</el-button>
</el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
</template>
<el-table v-loading="loading" :data="groupMessageList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="id" align="center" prop="id" v-if="true" />
<el-table-column label="群id" align="center" prop="groupId" />
<el-table-column label="发送用户id" align="center" prop="sendId" />
<el-table-column label="发送用户昵称" align="center" prop="sendNickName" />
<el-table-column label="被@用户id列表,逗号分隔" align="center" prop="atUserIds" />
<el-table-column label="发送内容" align="center" prop="content" />
<el-table-column label="" align="center" prop="status" />
<el-table-column label="消息类型" align="center" prop="type" />
<el-table-column label="发送时间" align="center" prop="sendTime" width="180">
<template #default="scope">
<span>{{ parseTime(scope.row.sendTime, '{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
<el-table-column label="回执消息是否完成" align="center" prop="receiptOk" />
<el-table-column label="是否回执消息" align="center" prop="receipt" />
<el-table-column label="接收用户id,逗号分隔,为空表示发给所有成员" align="center" prop="recvIds" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template #default="scope">
<el-tooltip content="修改" placement="top">
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['im:groupMessage:edit']"></el-button>
</el-tooltip>
<el-tooltip content="删除" placement="top">
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['im:groupMessage:remove']"></el-button>
</el-tooltip>
</template>
</el-table-column>
</el-table>
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
</el-card>
<!-- 添加或修改群消息对话框 -->
<el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
<el-form ref="groupMessageFormRef" :model="form" :rules="rules" label-width="80px">
<el-form-item label="群id" prop="groupId">
<el-input v-model="form.groupId" placeholder="请输入群id" />
</el-form-item>
<el-form-item label="发送用户id" prop="sendId">
<el-input v-model="form.sendId" placeholder="请输入发送用户id" />
</el-form-item>
<el-form-item label="发送用户昵称" prop="sendNickName">
<el-input v-model="form.sendNickName" placeholder="请输入发送用户昵称" />
</el-form-item>
<el-form-item label="被@用户id列表,逗号分隔" prop="atUserIds">
<el-input v-model="form.atUserIds" placeholder="请输入被@用户id列表,逗号分隔" />
</el-form-item>
<el-form-item label="发送内容">
<editor v-model="form.content" :min-height="192"/>
</el-form-item>
<el-form-item label="发送时间" prop="sendTime">
<el-date-picker clearable
v-model="form.sendTime"
type="datetime"
value-format="YYYY-MM-DD HH:mm:ss"
placeholder="请选择发送时间">
</el-date-picker>
</el-form-item>
<el-form-item label="回执消息是否完成" prop="receiptOk">
<el-input v-model="form.receiptOk" placeholder="请输入回执消息是否完成" />
</el-form-item>
<el-form-item label="是否回执消息" prop="receipt">
<el-input v-model="form.receipt" placeholder="请输入是否回执消息" />
</el-form-item>
<el-form-item label="接收用户id,逗号分隔,为空表示发给所有成员" prop="recvIds">
<el-input v-model="form.recvIds" placeholder="请输入接收用户id,逗号分隔,为空表示发给所有成员" />
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button :loading="buttonLoading" type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup name="GroupMessage" lang="ts">
import { listGroupMessage, getGroupMessage, delGroupMessage, addGroupMessage, updateGroupMessage } from '@/api/im/groupMessage';
import { GroupMessageVO, GroupMessageQuery, GroupMessageForm } from '@/api/im/groupMessage/types';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const groupMessageList = ref<GroupMessageVO[]>([]);
const buttonLoading = ref(false);
const loading = ref(true);
const showSearch = ref(true);
const ids = ref<Array<string | number>>([]);
const single = ref(true);
const multiple = ref(true);
const total = ref(0);
const queryFormRef = ref<ElFormInstance>();
const groupMessageFormRef = ref<ElFormInstance>();
const dialog = reactive<DialogOption>({
visible: false,
title: ''
});
const initFormData: GroupMessageForm = {
id: undefined,
groupId: undefined,
sendId: undefined,
sendNickName: undefined,
atUserIds: undefined,
content: undefined,
status: undefined,
type: undefined,
sendTime: undefined,
receiptOk: undefined,
receipt: undefined,
recvIds: undefined
}
const data = reactive<PageData<GroupMessageForm, GroupMessageQuery>>({
form: {...initFormData},
queryParams: {
pageNum: 1,
pageSize: 10,
groupId: undefined,
sendId: undefined,
sendNickName: undefined,
atUserIds: undefined,
content: undefined,
status: undefined,
type: undefined,
sendTime: undefined,
receiptOk: undefined,
receipt: undefined,
recvIds: undefined,
params: {
}
},
rules: {
id: [
{ required: true, message: "id不能为空", trigger: "blur" }
],
groupId: [
{ required: true, message: "群id不能为空", trigger: "blur" }
],
sendId: [
{ required: true, message: "发送用户id不能为空", trigger: "blur" }
],
sendNickName: [
{ required: true, message: "发送用户昵称不能为空", trigger: "blur" }
],
atUserIds: [
{ required: true, message: "被@用户id列表,逗号分隔不能为空", trigger: "blur" }
],
content: [
{ required: true, message: "发送内容不能为空", trigger: "blur" }
],
status: [
{ required: true, message: "不能为空", trigger: "change" }
],
type: [
{ required: true, message: "消息类型 0:文字 1:图片 2:文件不能为空", trigger: "change" }
],
sendTime: [
{ required: true, message: "发送时间不能为空", trigger: "blur" }
],
receiptOk: [
{ required: true, message: "回执消息是否完成不能为空", trigger: "blur" }
],
receipt: [
{ required: true, message: "是否回执消息不能为空", trigger: "blur" }
],
recvIds: [
{ required: true, message: "接收用户id,逗号分隔,为空表示发给所有成员不能为空", trigger: "blur" }
]
}
});
const { queryParams, form, rules } = toRefs(data);
/** 查询群消息列表 */
const getList = async () => {
loading.value = true;
const res = await listGroupMessage(queryParams.value);
groupMessageList.value = res.rows;
total.value = res.total;
loading.value = false;
}
/** 取消按钮 */
const cancel = () => {
reset();
dialog.visible = false;
}
/** 表单重置 */
const reset = () => {
form.value = {...initFormData};
groupMessageFormRef.value?.resetFields();
}
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.value.pageNum = 1;
getList();
}
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value?.resetFields();
handleQuery();
}
/** 多选框选中数据 */
const handleSelectionChange = (selection: GroupMessageVO[]) => {
ids.value = selection.map(item => item.id);
single.value = selection.length != 1;
multiple.value = !selection.length;
}
/** 新增按钮操作 */
const handleAdd = () => {
reset();
dialog.visible = true;
dialog.title = "添加群消息";
}
/** 修改按钮操作 */
const handleUpdate = async (row?: GroupMessageVO) => {
reset();
const _id = row?.id || ids.value[0]
const res = await getGroupMessage(_id);
Object.assign(form.value, res.data);
dialog.visible = true;
dialog.title = "修改群消息";
}
/** 提交按钮 */
const submitForm = () => {
groupMessageFormRef.value?.validate(async (valid: boolean) => {
if (valid) {
buttonLoading.value = true;
if (form.value.id) {
await updateGroupMessage(form.value).finally(() => buttonLoading.value = false);
} else {
await addGroupMessage(form.value).finally(() => buttonLoading.value = false);
}
proxy?.$modal.msgSuccess("操作成功");
dialog.visible = false;
await getList();
}
});
}
/** 删除按钮操作 */
const handleDelete = async (row?: GroupMessageVO) => {
const _ids = row?.id || ids.value;
await proxy?.$modal.confirm('是否确认删除群消息编号为"' + _ids + '"的数据项?').finally(() => loading.value = false);
await delGroupMessage(_ids);
proxy?.$modal.msgSuccess("删除成功");
await getList();
}
/** 导出按钮操作 */
const handleExport = () => {
proxy?.download('im/groupMessage/export', {
...queryParams.value
}, `groupMessage_${new Date().getTime()}.xlsx`)
}
onMounted(() => {
getList();
});
</script>

231
im-admin-ui/src/views/im/message/private/index.vue

@ -0,0 +1,231 @@
<template>
<div class="p-2">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover">
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
<el-form-item label="发送时间" prop="createdTime">
<el-date-picker
v-model="dateRange"
value-format="YYYY-MM-DD HH:mm:ss"
type="daterange"
range-separator="-"
start-placeholder="开始日期"
end-placeholder="结束日期"
:default-time="[new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 1, 1, 23, 59, 59)]"
></el-date-picker>
</el-form-item>
<el-form-item label="发送用户" prop="sendId">
<im-user-select v-model="queryParams.sendId"></im-user-select>
</el-form-item>
<el-form-item label="接收用户" prop="recvId">
<im-user-select v-model="queryParams.recvId"></im-user-select>
</el-form-item>
<el-form-item label="内容" prop="content">
<el-input v-model="queryParams.content"></el-input>
</el-form-item>
<el-form-item label="消息类型" prop="type">
<el-select v-model="queryParams.type" clearable>
<el-option v-for="dict in im_message_type" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-select>
</el-form-item>
<el-form-item label="消息类型" prop="status">
<el-select v-model="queryParams.status" clearable>
<el-option v-for="dict in im_message_status" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
</el-card>
</div>
</transition>
<el-card shadow="never">
<el-table v-loading="loading" :data="privateMessageList" @selection-change="handleSelectionChange">
<el-table-column label="发送用户" align="center" prop="sendUserName" />
<el-table-column label="接收用户" align="center" prop="recvUserName" />
<el-table-column label="发送内容" align="center" prop="content" >
<template #default="scope">
<im-message-content :message="scope.row" ></im-message-content>
</template>
</el-table-column>
<el-table-column label="消息类型" align="center" prop="type" >
<template #default="scope">
<dict-tag :options="im_message_type" :value="scope.row.type" />
</template>
</el-table-column>
<el-table-column label="状态 " align="center" prop="status" >
<template #default="scope">
<dict-tag :options="im_message_status" :value="scope.row.status" />
</template>
</el-table-column>
<el-table-column label="发送时间" align="center" prop="sendTime" width="180">
<template #default="scope">
<span>{{ parseTime(scope.row.sendTime, '{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template #default="scope">
<el-tooltip content="详情" placement="top">
<el-button link type="primary" @click="handleUpdate(scope.row)" v-hasPermi="['im:privateMessage:query']">详情</el-button>
</el-tooltip>
</template>
</el-table-column>
</el-table>
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
</el-card>
<!-- 添加或修改私聊消息对话框 -->
<el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
<el-form ref="privateMessageFormRef" :model="form" :rules="rules" label-width="80px" disabled>
<el-form-item label="发送用户" prop="sendUserName">
<el-input v-model="form.sendUserName" />
</el-form-item>
<el-form-item label="接收用户" prop="sendUserName">
<el-input v-model="form.recvUserName" />
</el-form-item>
<el-form-item label="发送内容">
<el-input v-model="form.content" :min-height="192"/>
</el-form-item>
<el-form-item label="发送时间" prop="sendTime">
<el-date-picker clearable
v-model="form.sendTime"
type="datetime"
value-format="YYYY-MM-DD HH:mm:ss"
placeholder="请选择发送时间">
</el-date-picker>
</el-form-item>
<el-form-item label="消息类型">
<dict-tag :options="im_message_type" :value="form.type" />
</el-form-item>
<el-form-item label="消息状态">
<dict-tag :options="im_message_status" :value="form.status" />
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button :loading="buttonLoading" type="primary" @click="submitForm"> </el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup name="PrivateMessage" lang="ts">
import { listPrivateMessage, getPrivateMessage } from '@/api/im/privateMessage';
import { PrivateMessageVO, PrivateMessageQuery, PrivateMessageForm } from '@/api/im/privateMessage/types';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const privateMessageList = ref<PrivateMessageVO[]>([]);
const buttonLoading = ref(false);
const loading = ref(true);
const showSearch = ref(true);
const ids = ref<Array<string | number>>([]);
const single = ref(true);
const multiple = ref(true);
const total = ref(0);
const queryFormRef = ref<ElFormInstance>();
const privateMessageFormRef = ref<ElFormInstance>();
const dateRange = ref<[DateModelType, DateModelType]>(['', '']);
const dialog = reactive<DialogOption>({
visible: false,
title: ''
});
const initFormData: PrivateMessageForm = {
id: undefined,
sendId: undefined,
sendUserName: undefined,
recvUserName: undefined,
recvId: undefined,
content: undefined,
type: undefined,
status: undefined,
sendTime: undefined
}
const data = reactive<PageData<PrivateMessageForm, PrivateMessageQuery>>({
form: {...initFormData},
queryParams: {
pageNum: 1,
pageSize: 10,
sendId: undefined,
recvId: undefined,
content: undefined,
type: undefined,
status: undefined,
sendTime: undefined,
params: {
}
},
rules: {
}
});
const { queryParams, form, rules } = toRefs(data);
const { im_message_type, im_message_status } = toRefs<any>(proxy?.useDict('im_message_type', 'im_message_status'));
/** 查询私聊消息列表 */
const getList = async () => {
loading.value = true;
const res = await listPrivateMessage(proxy?.addDateRange(queryParams.value, dateRange.value));
privateMessageList.value = res.rows;
total.value = res.total;
loading.value = false;
}
/** 确定按钮 */
const submitForm = () => {
dialog.visible = false;
}
/** 表单重置 */
const reset = () => {
form.value = {...initFormData};
privateMessageFormRef.value?.resetFields();
}
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.value.pageNum = 1;
getList();
}
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value?.resetFields();
handleQuery();
}
/** 多选框选中数据 */
const handleSelectionChange = (selection: PrivateMessageVO[]) => {
ids.value = selection.map(item => item.id);
single.value = selection.length != 1;
multiple.value = !selection.length;
}
/** 修改按钮操作 */
const handleUpdate = async (row?: PrivateMessageVO) => {
reset();
const _id = row?.id || ids.value[0]
const res = await getPrivateMessage(_id);
Object.assign(form.value, res.data);
dialog.visible = true;
dialog.title = "消息详情";
}
/** 导出按钮操作 */
const handleExport = () => {
proxy?.download('im/privateMessage/export', {
...queryParams.value
}, `privateMessage_${new Date().getTime()}.xlsx`)
}
onMounted(() => {
getList();
});
</script>

304
im-admin-ui/src/views/im/message/system/index.vue

@ -0,0 +1,304 @@
<template>
<div class="p-2">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover">
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
<el-form-item label="标题" prop="title">
<el-input v-model="queryParams.title" placeholder="请输入标题" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="封面" prop="coverUrl">
<el-input v-model="queryParams.coverUrl" placeholder="请输入封面" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="简介" prop="intro">
<el-input v-model="queryParams.intro" placeholder="请输入简介" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="富文本内容,base64编码" prop="richText">
<el-input v-model="queryParams.richText" placeholder="请输入富文本内容,base64编码" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="外部链接" prop="externLink">
<el-input v-model="queryParams.externLink" placeholder="请输入外部链接" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="删除标识 0:正常 1:已删除" prop="deleted">
<el-input v-model="queryParams.deleted" placeholder="请输入删除标识 0:正常 1:已删除" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="创建者" prop="creator">
<el-input v-model="queryParams.creator" placeholder="请输入创建者" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="更新者" prop="updater">
<el-input v-model="queryParams.updater" placeholder="请输入更新者" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
</el-card>
</div>
</transition>
<el-card shadow="never">
<template #header>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['im:systemMessage:add']">新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['im:systemMessage:edit']">修改</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['im:systemMessage:remove']">删除</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['im:systemMessage:export']">导出</el-button>
</el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
</template>
<el-table v-loading="loading" :data="systemMessageList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="id" align="center" prop="id" v-if="true" />
<el-table-column label="标题" align="center" prop="title" />
<el-table-column label="封面" align="center" prop="coverUrl" />
<el-table-column label="简介" align="center" prop="intro" />
<el-table-column label="内容类型 0:富文本 1:外部链接" align="center" prop="contentType" />
<el-table-column label="富文本内容,base64编码" align="center" prop="richText" />
<el-table-column label="外部链接" align="center" prop="externLink" />
<el-table-column label="删除标识 0:正常 1:已删除" align="center" prop="deleted" />
<el-table-column label="创建者" align="center" prop="creator" />
<el-table-column label="更新者" align="center" prop="updater" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template #default="scope">
<el-tooltip content="修改" placement="top">
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['im:systemMessage:edit']"></el-button>
</el-tooltip>
<el-tooltip content="删除" placement="top">
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['im:systemMessage:remove']"></el-button>
</el-tooltip>
</template>
</el-table-column>
</el-table>
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
</el-card>
<!-- 添加或修改系统消息对话框 -->
<el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
<el-form ref="systemMessageFormRef" :model="form" :rules="rules" label-width="80px">
<el-form-item label="标题" prop="title">
<el-input v-model="form.title" placeholder="请输入标题" />
</el-form-item>
<el-form-item label="封面" prop="coverUrl">
<el-input v-model="form.coverUrl" placeholder="请输入封面" />
</el-form-item>
<el-form-item label="简介" prop="intro">
<el-input v-model="form.intro" placeholder="请输入简介" />
</el-form-item>
<el-form-item label="富文本内容,base64编码" prop="richText">
<el-input v-model="form.richText" type="textarea" placeholder="请输入内容" />
</el-form-item>
<el-form-item label="外部链接" prop="externLink">
<el-input v-model="form.externLink" placeholder="请输入外部链接" />
</el-form-item>
<el-form-item label="删除标识 0:正常 1:已删除" prop="deleted">
<el-input v-model="form.deleted" placeholder="请输入删除标识 0:正常 1:已删除" />
</el-form-item>
<el-form-item label="创建者" prop="creator">
<el-input v-model="form.creator" placeholder="请输入创建者" />
</el-form-item>
<el-form-item label="更新者" prop="updater">
<el-input v-model="form.updater" placeholder="请输入更新者" />
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button :loading="buttonLoading" type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup name="SystemMessage" lang="ts">
import { listSystemMessage, getSystemMessage, delSystemMessage, addSystemMessage, updateSystemMessage } from '@/api/im/systemMessage';
import { SystemMessageVO, SystemMessageQuery, SystemMessageForm } from '@/api/im/systemMessage/types';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const systemMessageList = ref<SystemMessageVO[]>([]);
const buttonLoading = ref(false);
const loading = ref(true);
const showSearch = ref(true);
const ids = ref<Array<string | number>>([]);
const single = ref(true);
const multiple = ref(true);
const total = ref(0);
const queryFormRef = ref<ElFormInstance>();
const systemMessageFormRef = ref<ElFormInstance>();
const dialog = reactive<DialogOption>({
visible: false,
title: ''
});
const initFormData: SystemMessageForm = {
id: undefined,
title: undefined,
coverUrl: undefined,
intro: undefined,
contentType: undefined,
richText: undefined,
externLink: undefined,
deleted: undefined,
creator: undefined,
updater: undefined,
}
const data = reactive<PageData<SystemMessageForm, SystemMessageQuery>>({
form: {...initFormData},
queryParams: {
pageNum: 1,
pageSize: 10,
title: undefined,
coverUrl: undefined,
intro: undefined,
contentType: undefined,
richText: undefined,
externLink: undefined,
deleted: undefined,
creator: undefined,
updater: undefined,
params: {
}
},
rules: {
id: [
{ required: true, message: "id不能为空", trigger: "blur" }
],
title: [
{ required: true, message: "标题不能为空", trigger: "blur" }
],
coverUrl: [
{ required: true, message: "封面不能为空", trigger: "blur" }
],
intro: [
{ required: true, message: "简介不能为空", trigger: "blur" }
],
contentType: [
{ required: true, message: "内容类型 0:富文本 1:外部链接不能为空", trigger: "change" }
],
richText: [
{ required: true, message: "富文本内容,base64编码不能为空", trigger: "blur" }
],
externLink: [
{ required: true, message: "外部链接不能为空", trigger: "blur" }
],
deleted: [
{ required: true, message: "删除标识 0:正常 1:已删除不能为空", trigger: "blur" }
],
creator: [
{ required: true, message: "创建者不能为空", trigger: "blur" }
],
updater: [
{ required: true, message: "更新者不能为空", trigger: "blur" }
],
}
});
const { queryParams, form, rules } = toRefs(data);
/** 查询系统消息列表 */
const getList = async () => {
loading.value = true;
const res = await listSystemMessage(queryParams.value);
systemMessageList.value = res.rows;
total.value = res.total;
loading.value = false;
}
/** 取消按钮 */
const cancel = () => {
reset();
dialog.visible = false;
}
/** 表单重置 */
const reset = () => {
form.value = {...initFormData};
systemMessageFormRef.value?.resetFields();
}
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.value.pageNum = 1;
getList();
}
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value?.resetFields();
handleQuery();
}
/** 多选框选中数据 */
const handleSelectionChange = (selection: SystemMessageVO[]) => {
ids.value = selection.map(item => item.id);
single.value = selection.length != 1;
multiple.value = !selection.length;
}
/** 新增按钮操作 */
const handleAdd = () => {
reset();
dialog.visible = true;
dialog.title = "添加系统消息";
}
/** 修改按钮操作 */
const handleUpdate = async (row?: SystemMessageVO) => {
reset();
const _id = row?.id || ids.value[0]
const res = await getSystemMessage(_id);
Object.assign(form.value, res.data);
dialog.visible = true;
dialog.title = "修改系统消息";
}
/** 提交按钮 */
const submitForm = () => {
systemMessageFormRef.value?.validate(async (valid: boolean) => {
if (valid) {
buttonLoading.value = true;
if (form.value.id) {
await updateSystemMessage(form.value).finally(() => buttonLoading.value = false);
} else {
await addSystemMessage(form.value).finally(() => buttonLoading.value = false);
}
proxy?.$modal.msgSuccess("操作成功");
dialog.visible = false;
await getList();
}
});
}
/** 删除按钮操作 */
const handleDelete = async (row?: SystemMessageVO) => {
const _ids = row?.id || ids.value;
await proxy?.$modal.confirm('是否确认删除系统消息编号为"' + _ids + '"的数据项?').finally(() => loading.value = false);
await delSystemMessage(_ids);
proxy?.$modal.msgSuccess("删除成功");
await getList();
}
/** 导出按钮操作 */
const handleExport = () => {
proxy?.download('im/systemMessage/export', {
...queryParams.value
}, `systemMessage_${new Date().getTime()}.xlsx`)
}
onMounted(() => {
getList();
});
</script>

318
im-admin-ui/src/views/im/message/task/index.vue

@ -0,0 +1,318 @@
<template>
<div class="p-2">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover">
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
<el-form-item label="系统消息id" prop="messageId">
<el-input v-model="queryParams.messageId" placeholder="请输入系统消息id" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="发送序列号" prop="seqNo">
<el-input v-model="queryParams.seqNo" placeholder="请输入发送序列号" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="推送时间" prop="sendTime">
<el-date-picker clearable
v-model="queryParams.sendTime"
type="date"
value-format="YYYY-MM-DD"
placeholder="请选择推送时间"
/>
</el-form-item>
<el-form-item label="是否发送给全体用户" prop="sendToAll">
<el-input v-model="queryParams.sendToAll" placeholder="请输入是否发送给全体用户" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="接收用户id,逗号分隔,send_to_all为false时有效" prop="recvIds">
<el-input v-model="queryParams.recvIds" placeholder="请输入接收用户id,逗号分隔,send_to_all为false时有效" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="删除标识 0:正常 1:已删除" prop="deleted">
<el-input v-model="queryParams.deleted" placeholder="请输入删除标识 0:正常 1:已删除" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="创建者" prop="creator">
<el-input v-model="queryParams.creator" placeholder="请输入创建者" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="更新者" prop="updater">
<el-input v-model="queryParams.updater" placeholder="请输入更新者" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
</el-card>
</div>
</transition>
<el-card shadow="never">
<template #header>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['im:smPushTask:add']">新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['im:smPushTask:edit']">修改</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['im:smPushTask:remove']">删除</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['im:smPushTask:export']">导出</el-button>
</el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
</template>
<el-table v-loading="loading" :data="smPushTaskList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="id" align="center" prop="id" v-if="true" />
<el-table-column label="系统消息id" align="center" prop="messageId" />
<el-table-column label="发送序列号" align="center" prop="seqNo" />
<el-table-column label="推送时间" align="center" prop="sendTime" width="180">
<template #default="scope">
<span>{{ parseTime(scope.row.sendTime, '{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
<el-table-column label="状态 1:待发送 2:发送中 3:已发送 4:已取消" align="center" prop="status" />
<el-table-column label="是否发送给全体用户" align="center" prop="sendToAll" />
<el-table-column label="接收用户id,逗号分隔,send_to_all为false时有效" align="center" prop="recvIds" />
<el-table-column label="删除标识 0:正常 1:已删除" align="center" prop="deleted" />
<el-table-column label="创建者" align="center" prop="creator" />
<el-table-column label="更新者" align="center" prop="updater" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template #default="scope">
<el-tooltip content="修改" placement="top">
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['im:smPushTask:edit']"></el-button>
</el-tooltip>
<el-tooltip content="删除" placement="top">
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['im:smPushTask:remove']"></el-button>
</el-tooltip>
</template>
</el-table-column>
</el-table>
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
</el-card>
<!-- 添加或修改系统消息推送任务对话框 -->
<el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
<el-form ref="smPushTaskFormRef" :model="form" :rules="rules" label-width="80px">
<el-form-item label="系统消息id" prop="messageId">
<el-input v-model="form.messageId" placeholder="请输入系统消息id" />
</el-form-item>
<el-form-item label="发送序列号" prop="seqNo">
<el-input v-model="form.seqNo" placeholder="请输入发送序列号" />
</el-form-item>
<el-form-item label="推送时间" prop="sendTime">
<el-date-picker clearable
v-model="form.sendTime"
type="datetime"
value-format="YYYY-MM-DD HH:mm:ss"
placeholder="请选择推送时间">
</el-date-picker>
</el-form-item>
<el-form-item label="是否发送给全体用户" prop="sendToAll">
<el-input v-model="form.sendToAll" placeholder="请输入是否发送给全体用户" />
</el-form-item>
<el-form-item label="接收用户id,逗号分隔,send_to_all为false时有效" prop="recvIds">
<el-input v-model="form.recvIds" placeholder="请输入接收用户id,逗号分隔,send_to_all为false时有效" />
</el-form-item>
<el-form-item label="删除标识 0:正常 1:已删除" prop="deleted">
<el-input v-model="form.deleted" placeholder="请输入删除标识 0:正常 1:已删除" />
</el-form-item>
<el-form-item label="创建者" prop="creator">
<el-input v-model="form.creator" placeholder="请输入创建者" />
</el-form-item>
<el-form-item label="更新者" prop="updater">
<el-input v-model="form.updater" placeholder="请输入更新者" />
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button :loading="buttonLoading" type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup name="SmPushTask" lang="ts">
import { listSmPushTask, getSmPushTask, delSmPushTask, addSmPushTask, updateSmPushTask } from '@/api/im/smPushTask';
import { SmPushTaskVO, SmPushTaskQuery, SmPushTaskForm } from '@/api/im/smPushTask/types';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const smPushTaskList = ref<SmPushTaskVO[]>([]);
const buttonLoading = ref(false);
const loading = ref(true);
const showSearch = ref(true);
const ids = ref<Array<string | number>>([]);
const single = ref(true);
const multiple = ref(true);
const total = ref(0);
const queryFormRef = ref<ElFormInstance>();
const smPushTaskFormRef = ref<ElFormInstance>();
const dialog = reactive<DialogOption>({
visible: false,
title: ''
});
const initFormData: SmPushTaskForm = {
id: undefined,
messageId: undefined,
seqNo: undefined,
sendTime: undefined,
status: undefined,
sendToAll: undefined,
recvIds: undefined,
deleted: undefined,
creator: undefined,
updater: undefined,
}
const data = reactive<PageData<SmPushTaskForm, SmPushTaskQuery>>({
form: {...initFormData},
queryParams: {
pageNum: 1,
pageSize: 10,
messageId: undefined,
seqNo: undefined,
sendTime: undefined,
status: undefined,
sendToAll: undefined,
recvIds: undefined,
deleted: undefined,
creator: undefined,
updater: undefined,
params: {
}
},
rules: {
id: [
{ required: true, message: "id不能为空", trigger: "blur" }
],
messageId: [
{ required: true, message: "系统消息id不能为空", trigger: "blur" }
],
seqNo: [
{ required: true, message: "发送序列号不能为空", trigger: "blur" }
],
sendTime: [
{ required: true, message: "推送时间不能为空", trigger: "blur" }
],
status: [
{ required: true, message: "状态 1:待发送 2:发送中 3:已发送 4:已取消不能为空", trigger: "change" }
],
sendToAll: [
{ required: true, message: "是否发送给全体用户不能为空", trigger: "blur" }
],
recvIds: [
{ required: true, message: "接收用户id,逗号分隔,send_to_all为false时有效不能为空", trigger: "blur" }
],
deleted: [
{ required: true, message: "删除标识 0:正常 1:已删除不能为空", trigger: "blur" }
],
creator: [
{ required: true, message: "创建者不能为空", trigger: "blur" }
],
updater: [
{ required: true, message: "更新者不能为空", trigger: "blur" }
],
}
});
const { queryParams, form, rules } = toRefs(data);
/** 查询系统消息推送任务列表 */
const getList = async () => {
loading.value = true;
const res = await listSmPushTask(queryParams.value);
smPushTaskList.value = res.rows;
total.value = res.total;
loading.value = false;
}
/** 取消按钮 */
const cancel = () => {
reset();
dialog.visible = false;
}
/** 表单重置 */
const reset = () => {
form.value = {...initFormData};
smPushTaskFormRef.value?.resetFields();
}
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.value.pageNum = 1;
getList();
}
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value?.resetFields();
handleQuery();
}
/** 多选框选中数据 */
const handleSelectionChange = (selection: SmPushTaskVO[]) => {
ids.value = selection.map(item => item.id);
single.value = selection.length != 1;
multiple.value = !selection.length;
}
/** 新增按钮操作 */
const handleAdd = () => {
reset();
dialog.visible = true;
dialog.title = "添加系统消息推送任务";
}
/** 修改按钮操作 */
const handleUpdate = async (row?: SmPushTaskVO) => {
reset();
const _id = row?.id || ids.value[0]
const res = await getSmPushTask(_id);
Object.assign(form.value, res.data);
dialog.visible = true;
dialog.title = "修改系统消息推送任务";
}
/** 提交按钮 */
const submitForm = () => {
smPushTaskFormRef.value?.validate(async (valid: boolean) => {
if (valid) {
buttonLoading.value = true;
if (form.value.id) {
await updateSmPushTask(form.value).finally(() => buttonLoading.value = false);
} else {
await addSmPushTask(form.value).finally(() => buttonLoading.value = false);
}
proxy?.$modal.msgSuccess("操作成功");
dialog.visible = false;
await getList();
}
});
}
/** 删除按钮操作 */
const handleDelete = async (row?: SmPushTaskVO) => {
const _ids = row?.id || ids.value;
await proxy?.$modal.confirm('是否确认删除系统消息推送任务编号为"' + _ids + '"的数据项?').finally(() => loading.value = false);
await delSmPushTask(_ids);
proxy?.$modal.msgSuccess("删除成功");
await getList();
}
/** 导出按钮操作 */
const handleExport = () => {
proxy?.download('im/smPushTask/export', {
...queryParams.value
}, `smPushTask_${new Date().getTime()}.xlsx`)
}
onMounted(() => {
getList();
});
</script>

235
im-admin-ui/src/views/im/sensitiveWord/index.vue

@ -0,0 +1,235 @@
<template>
<div class="p-2">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover">
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
<el-form-item label="是否启用 0:未启用 1:启用" prop="enabled">
<el-input v-model="queryParams.enabled" placeholder="请输入是否启用 0:未启用 1:启用" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="创建者" prop="creator">
<el-input v-model="queryParams.creator" placeholder="请输入创建者" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
</el-card>
</div>
</transition>
<el-card shadow="never">
<template #header>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['im:sensitiveWord:add']">新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['im:sensitiveWord:edit']">修改</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['im:sensitiveWord:remove']">删除</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['im:sensitiveWord:export']">导出</el-button>
</el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
</template>
<el-table v-loading="loading" :data="sensitiveWordList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="id" align="center" prop="id" v-if="true" />
<el-table-column label="敏感词内容" align="center" prop="content" />
<el-table-column label="是否启用 0:未启用 1:启用" align="center" prop="enabled" />
<el-table-column label="创建者" align="center" prop="creator" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template #default="scope">
<el-tooltip content="修改" placement="top">
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['im:sensitiveWord:edit']"></el-button>
</el-tooltip>
<el-tooltip content="删除" placement="top">
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['im:sensitiveWord:remove']"></el-button>
</el-tooltip>
</template>
</el-table-column>
</el-table>
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
</el-card>
<!-- 添加或修改敏感词对话框 -->
<el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
<el-form ref="sensitiveWordFormRef" :model="form" :rules="rules" label-width="80px">
<el-form-item label="敏感词内容">
<editor v-model="form.content" :min-height="192"/>
</el-form-item>
<el-form-item label="是否启用 0:未启用 1:启用" prop="enabled">
<el-input v-model="form.enabled" placeholder="请输入是否启用 0:未启用 1:启用" />
</el-form-item>
<el-form-item label="创建者" prop="creator">
<el-input v-model="form.creator" placeholder="请输入创建者" />
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button :loading="buttonLoading" type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup name="SensitiveWord" lang="ts">
import { listSensitiveWord, getSensitiveWord, delSensitiveWord, addSensitiveWord, updateSensitiveWord } from '@/api/im/sensitiveWord';
import { SensitiveWordVO, SensitiveWordQuery, SensitiveWordForm } from '@/api/im/sensitiveWord/types';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const sensitiveWordList = ref<SensitiveWordVO[]>([]);
const buttonLoading = ref(false);
const loading = ref(true);
const showSearch = ref(true);
const ids = ref<Array<string | number>>([]);
const single = ref(true);
const multiple = ref(true);
const total = ref(0);
const queryFormRef = ref<ElFormInstance>();
const sensitiveWordFormRef = ref<ElFormInstance>();
const dialog = reactive<DialogOption>({
visible: false,
title: ''
});
const initFormData: SensitiveWordForm = {
id: undefined,
content: undefined,
enabled: undefined,
creator: undefined,
}
const data = reactive<PageData<SensitiveWordForm, SensitiveWordQuery>>({
form: {...initFormData},
queryParams: {
pageNum: 1,
pageSize: 10,
content: undefined,
enabled: undefined,
creator: undefined,
params: {
}
},
rules: {
id: [
{ required: true, message: "id不能为空", trigger: "blur" }
],
content: [
{ required: true, message: "敏感词内容不能为空", trigger: "blur" }
],
enabled: [
{ required: true, message: "是否启用 0:未启用 1:启用不能为空", trigger: "blur" }
],
creator: [
{ required: true, message: "创建者不能为空", trigger: "blur" }
],
}
});
const { queryParams, form, rules } = toRefs(data);
/** 查询敏感词列表 */
const getList = async () => {
loading.value = true;
const res = await listSensitiveWord(queryParams.value);
sensitiveWordList.value = res.rows;
total.value = res.total;
loading.value = false;
}
/** 取消按钮 */
const cancel = () => {
reset();
dialog.visible = false;
}
/** 表单重置 */
const reset = () => {
form.value = {...initFormData};
sensitiveWordFormRef.value?.resetFields();
}
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.value.pageNum = 1;
getList();
}
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value?.resetFields();
handleQuery();
}
/** 多选框选中数据 */
const handleSelectionChange = (selection: SensitiveWordVO[]) => {
ids.value = selection.map(item => item.id);
single.value = selection.length != 1;
multiple.value = !selection.length;
}
/** 新增按钮操作 */
const handleAdd = () => {
reset();
dialog.visible = true;
dialog.title = "添加敏感词";
}
/** 修改按钮操作 */
const handleUpdate = async (row?: SensitiveWordVO) => {
reset();
const _id = row?.id || ids.value[0]
const res = await getSensitiveWord(_id);
Object.assign(form.value, res.data);
dialog.visible = true;
dialog.title = "修改敏感词";
}
/** 提交按钮 */
const submitForm = () => {
sensitiveWordFormRef.value?.validate(async (valid: boolean) => {
if (valid) {
buttonLoading.value = true;
if (form.value.id) {
await updateSensitiveWord(form.value).finally(() => buttonLoading.value = false);
} else {
await addSensitiveWord(form.value).finally(() => buttonLoading.value = false);
}
proxy?.$modal.msgSuccess("操作成功");
dialog.visible = false;
await getList();
}
});
}
/** 删除按钮操作 */
const handleDelete = async (row?: SensitiveWordVO) => {
const _ids = row?.id || ids.value;
await proxy?.$modal.confirm('是否确认删除敏感词编号为"' + _ids + '"的数据项?').finally(() => loading.value = false);
await delSensitiveWord(_ids);
proxy?.$modal.msgSuccess("删除成功");
await getList();
}
/** 导出按钮操作 */
const handleExport = () => {
proxy?.download('im/sensitiveWord/export', {
...queryParams.value
}, `sensitiveWord_${new Date().getTime()}.xlsx`)
}
onMounted(() => {
getList();
});
</script>

284
im-admin-ui/src/views/im/user/index.vue

@ -0,0 +1,284 @@
<template>
<div class="p-2">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter"
:leave-active-class="proxy?.animate.searchAnimate.leave">
<div v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover">
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
<el-form-item label="用户名" prop="userName">
<el-input v-model="queryParams.userName" placeholder="请输入用户名" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="用户昵称" prop="nickName">
<el-input v-model="queryParams.nickName" placeholder="请输入用户昵称" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
</el-card>
</div>
</transition>
<el-card shadow="never">
<template #header>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="warning" plain icon="Download" @click="handleExport"
v-hasPermi="['im:user:export']">导出</el-button>
</el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
</template>
<el-table v-loading="loading" :data="userList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="用户名" align="center" prop="userName" />
<el-table-column label="用户昵称" align="center" prop="nickName" />
<el-table-column label="用户头像" align="center" prop="headImageThumb" width="100">
<template #default="scope">
<image-preview :src="scope.row.headImageThumb" :full-src="scope.row.headImage" :width="50" :height="50" />
</template>
</el-table-column>
<el-table-column label="性别" align="center" prop="sex">
<template #default="scope">
<dict-tag :options="sys_user_sex" :value="scope.row.sex" />
</template>
</el-table-column>
<el-table-column label="是否被封禁" align="center" prop="isBanned">
<template #default="scope">
<dict-tag :options="sys_bool" :value="scope.row.isBanned" />
</template>
</el-table-column>
<el-table-column label="状态" align="center" prop="status">
<template #default="scope">
<dict-tag :options="im_user_status" :value="scope.row.status" />
</template>
</el-table-column>
<el-table-column label="注册时间" align="center" prop="createdTime" width="180">
<template #default="scope">
<span>{{ parseTime(scope.row.createdTime, '{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
<el-table-column label="最后登录时间" align="center" prop="lastLoginTime" width="180">
<template #default="scope">
<span>{{ parseTime(scope.row.lastLoginTime, '{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template #default="scope">
<el-tooltip content="详情" placement="top">
<el-button link type="primary" v-hasPermi="['im:user:query']"
@click="handleDetail(scope.row)">详情</el-button>
</el-tooltip>
<el-tooltip v-if="scope.row.isBanned" content="解封" placement="top">
<el-button link type="danger" v-hasPermi="['im:user:ban']" @click="unbanHandle(scope.row)">解封</el-button>
</el-tooltip>
<el-tooltip v-else content="封禁" placement="top">
<el-button link type="danger" v-hasPermi="['im:user:ban']" @click="banHandle(scope.row)">封禁</el-button>
</el-tooltip>
</template>
</el-table-column>
</el-table>
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize" @pagination="getList" />
</el-card>
<!-- 添加或修改用户对话框 -->
<el-dialog :title="dialog.title" v-model="dialog.visible" width="800px" append-to-body>
<el-form ref="userFormRef" :model="form" :rules="rules" label-width="100px" disabled>
<el-form-item label="用户头像" prop="headImage">
<image-preview :src="form.headImageThumb" :full-src="form.headImage" :width="100" :height="100" />
</el-form-item>
<el-form-item label="用户名" prop="userName">
<el-input v-model="form.userName" placeholder="请输入用户名" />
</el-form-item>
<el-form-item label="性别" prop="sex">
<dict-tag :options="sys_user_sex" :value="form.sex" />
</el-form-item>
<el-form-item label="用户昵称" prop="nickName">
<el-input v-model="form.nickName" placeholder="请输入用户昵称" />
</el-form-item>
<el-form-item label="个性签名" prop="signature">
<el-input v-model="form.signature" placeholder="请输入个性签名" />
</el-form-item>
<el-form-item label="最后登录时间" prop="lastLoginTime">
<el-date-picker clearable v-model="form.lastLoginTime" type="datetime" value-format="YYYY-MM-DD HH:mm:ss"
placeholder="请选择最后登录时间">
</el-date-picker>
</el-form-item>
<el-form-item label="注册时间" prop="createdTime">
<el-date-picker clearable v-model="form.createdTime" type="datetime" value-format="YYYY-MM-DD HH:mm:ss"
placeholder="请选择创建时间">
</el-date-picker>
</el-form-item>
<el-form-item label="是否被封禁" prop="isBanned">
<dict-tag :options="sys_bool" :value="form.isBanned" />
</el-form-item>
<el-form-item v-if="form.isBanned" label="被封禁原因" prop="reason">
<el-input v-model="form.reason" placeholder="请输入被封禁原因" />
</el-form-item>
<el-form-item label="客户端id" prop="cid">
<el-input v-model="form.cid" placeholder="请输入客户端id,用于uni-push推送" />
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="submitForm"> </el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup name="User" lang="ts">
import { listUser, getUser, ban, unban } from '@/api/im/user';
import { UserVO, UserQuery, UserForm } from '@/api/im/user/types';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const userList = ref<UserVO[]>([]);
const loading = ref(true);
const showSearch = ref(true);
const ids = ref<Array<string | number>>([]);
const single = ref(true);
const multiple = ref(true);
const total = ref(0);
const queryFormRef = ref<ElFormInstance>();
const userFormRef = ref<ElFormInstance>();
const dialog = reactive<DialogOption>({
visible: false,
title: ''
});
const initFormData: UserForm = {
id: undefined,
userName: undefined,
nickName: undefined,
headImage: undefined,
headImageThumb: undefined,
password: undefined,
sex: undefined,
signature: undefined,
lastLoginTime: undefined,
createdTime: undefined,
type: undefined,
isBanned: undefined,
reason: undefined,
cid: undefined,
status: undefined
}
const data = reactive<PageData<UserForm, UserQuery>>({
form: { ...initFormData },
queryParams: {
pageNum: 1,
pageSize: 10,
userName: undefined,
nickName: undefined,
params: {
}
},
rules: {}
});
const { queryParams, form, rules } = toRefs(data);
const { sys_bool } = toRefs<any>(proxy?.useDict('sys_bool'));
const { im_user_status } = toRefs<any>(proxy?.useDict('im_user_status'));
const { sys_user_sex } = toRefs<any>(proxy?.useDict('sys_user_sex'));
/** 查询用户列表 */
const getList = async () => {
loading.value = true;
const res = await listUser(queryParams.value);
userList.value = res.rows;
total.value = res.total;
loading.value = false;
console.log("getList")
}
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.value.pageNum = 1;
getList();
console.log("handleQuery")
}
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value?.resetFields();
handleQuery();
console.log("handleQuery")
}
/** 多选框选中数据 */
const handleSelectionChange = (selection: UserVO[]) => {
ids.value = selection.map(item => item.id);
single.value = selection.length != 1;
multiple.value = !selection.length;
console.log("handleSelectionChange")
}
/** 表单重置 */
const reset = () => {
form.value = { ...initFormData };
userFormRef.value?.resetFields();
console.log("reset")
}
/** 修改按钮操作 */
const handleDetail = async (row?: UserVO) => {
reset();
const _id = row?.id || ids.value[0]
const res = await getUser(_id);
Object.assign(form.value, res.data);
dialog.visible = true;
dialog.title = "用户信息";
}
/** 提交按钮 */
const submitForm = () => {
dialog.visible = false;
}
const banHandle = (user: any) => {
ElMessageBox.prompt('封禁原因:', '确定对该用户进行封禁?', {
inputPattern: /\S/,
inputErrorMessage: '请输入封禁原因',
confirmButtonText: '确定',
cancelButtonText: '取消'
}).then(({ value }) => {
const data = { id: user.id, reason: value }
ban(data).then(() => {
user.isBanned = true
ElMessage.success(`用户'${user.userName}'已被封禁`);
})
})
}
const unbanHandle = (user: any) => {
ElMessageBox.confirm('确定解除该用户的封禁状态??', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消'
}).then(() => {
const data = { id: user.id }
unban(data).then(() => {
user.isBanned = false
ElMessage.success(`用户'${user.userName}'解锁成功`);
})
})
}
/** 导出按钮操作 */
const handleExport = () => {
proxy?.download('im/user/export', {
...queryParams.value
}, `user_${new Date().getTime()}.xlsx`)
}
onMounted(() => {
getList();
});
</script>

1
im-admin/ruoyi-admin/src/main/resources/application.yml

@ -152,6 +152,7 @@ mybatis-encryptor:
publicKey: publicKey:
privateKey: privateKey:
# 【bug】ruoyi的这个文档配置似乎没有生效,跟代码也对不上
springdoc: springdoc:
api-docs: api-docs:
# 是否开启接口文档 # 是否开启接口文档

47
im-admin/ruoyi-admin/src/main/resources/logback-plus.xml

@ -8,7 +8,7 @@
<!-- 控制台输出 --> <!-- 控制台输出 -->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender"> <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder> <encoder>
<pattern>${console.log.pattern}</pattern> <pattern>${log.pattern}</pattern>
<charset>utf-8</charset> <charset>utf-8</charset>
</encoder> </encoder>
</appender> </appender>
@ -79,7 +79,7 @@
<!-- info异步输出 --> <!-- info异步输出 -->
<appender name="async_info" class="ch.qos.logback.classic.AsyncAppender"> <appender name="async_info" class="ch.qos.logback.classic.AsyncAppender">
<!-- 不丢失日志.默认的,如果队列的80%已,则会丢弃TRACT、DEBUG、INFO级别的日志 --> <!-- 不丢失日志.默认的,如果队列的80%已,则会丢弃TRACT、DEBUG、INFO级别的日志 -->
<discardingThreshold>0</discardingThreshold> <discardingThreshold>0</discardingThreshold>
<!-- 更改默认的队列的深度,该值会影响性能.默认值为256 --> <!-- 更改默认的队列的深度,该值会影响性能.默认值为256 -->
<queueSize>512</queueSize> <queueSize>512</queueSize>
@ -97,33 +97,20 @@
<appender-ref ref="file_error"/> <appender-ref ref="file_error"/>
</appender> </appender>
<!-- 整合 skywalking 控制台输出 tid --> <!-- 开发环境下的日志配置 -->
<!-- <appender name="console" class="ch.qos.logback.core.ConsoleAppender">--> <springProfile name="dev">
<!-- <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">--> <root level="INFO">
<!-- <layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout">--> <appender-ref ref="console" />
<!-- <pattern>[%tid] ${console.log.pattern}</pattern>--> </root>
<!-- </layout>--> </springProfile>
<!-- <charset>utf-8</charset>--> <!-- 生产环境下的日志配置 -->
<!-- </encoder>--> <springProfile name="prod">
<!-- </appender>--> <root level="INFO">
<appender-ref ref="console" />
<!-- 整合 skywalking 推送采集日志 --> <appender-ref ref="async_info" />
<!-- <appender name="sky_log" class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.log.GRPCLogClientAppender">--> <appender-ref ref="async_error" />
<!-- <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">--> <appender-ref ref="file_console" />
<!-- <layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout">--> </root>
<!-- <pattern>[%tid] ${console.log.pattern}</pattern>--> </springProfile>
<!-- </layout>-->
<!-- <charset>utf-8</charset>-->
<!-- </encoder>-->
<!-- </appender>-->
<!--系统操作日志-->
<root level="info">
<appender-ref ref="console" />
<appender-ref ref="async_info" />
<appender-ref ref="async_error" />
<appender-ref ref="file_console" />
<!-- <appender-ref ref="sky_log"/>-->
</root>
</configuration> </configuration>

59
im-admin/ruoyi-im/src/main/java/org/dromara/im/controller/ImPrivateMessageController.java

@ -1,17 +1,9 @@
package org.dromara.im.controller; package org.dromara.im.controller;
import cn.dev33.satoken.annotation.SaCheckPermission; import cn.dev33.satoken.annotation.SaCheckPermission;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.NotNull;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.dromara.common.core.domain.R; import org.dromara.common.core.domain.R;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.excel.utils.ExcelUtil;
import org.dromara.common.idempotent.annotation.RepeatSubmit;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.mybatis.core.page.PageQuery; import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo; import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.web.core.BaseController; import org.dromara.common.web.core.BaseController;
@ -19,9 +11,10 @@ import org.dromara.im.domain.bo.ImPrivateMessageBo;
import org.dromara.im.domain.vo.ImPrivateMessageVo; import org.dromara.im.domain.vo.ImPrivateMessageVo;
import org.dromara.im.service.IImPrivateMessageService; import org.dromara.im.service.IImPrivateMessageService;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import java.util.List; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/** /**
* 私聊消息 * 私聊消息
@ -46,16 +39,6 @@ public class ImPrivateMessageController extends BaseController {
return imPrivateMessageService.queryPageList(bo, pageQuery); return imPrivateMessageService.queryPageList(bo, pageQuery);
} }
/**
* 导出私聊消息列表
*/
@SaCheckPermission("im:privateMessage:export")
@Log(title = "私聊消息", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(ImPrivateMessageBo bo, HttpServletResponse response) {
List<ImPrivateMessageVo> list = imPrivateMessageService.queryList(bo);
ExcelUtil.exportExcel(list, "私聊消息", ImPrivateMessageVo.class, response);
}
/** /**
* 获取私聊消息详细信息 * 获取私聊消息详细信息
@ -69,38 +52,4 @@ public class ImPrivateMessageController extends BaseController {
return R.ok(imPrivateMessageService.queryById(id)); return R.ok(imPrivateMessageService.queryById(id));
} }
/**
* 新增私聊消息
*/
@SaCheckPermission("im:privateMessage:add")
@Log(title = "私聊消息", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping()
public R<Void> add(@Validated(AddGroup.class) @RequestBody ImPrivateMessageBo bo) {
return toAjax(imPrivateMessageService.insertByBo(bo));
}
/**
* 修改私聊消息
*/
@SaCheckPermission("im:privateMessage:edit")
@Log(title = "私聊消息", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PutMapping()
public R<Void> edit(@Validated(EditGroup.class) @RequestBody ImPrivateMessageBo bo) {
return toAjax(imPrivateMessageService.updateByBo(bo));
}
/**
* 删除私聊消息
*
* @param ids 主键串
*/
@SaCheckPermission("im:privateMessage:remove")
@Log(title = "私聊消息", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public R<Void> remove(@NotEmpty(message = "主键不能为空")
@PathVariable Long[] ids) {
return toAjax(imPrivateMessageService.deleteWithValidByIds(List.of(ids), true));
}
} }

4
im-admin/ruoyi-im/src/main/java/org/dromara/im/domain/bo/ImGroupBo.java

@ -1,7 +1,5 @@
package org.dromara.im.domain.bo; package org.dromara.im.domain.bo;
import com.baomidou.mybatisplus.annotation.TableField;
import com.fasterxml.jackson.annotation.JsonInclude;
import io.github.linpeilie.annotations.AutoMapper; import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data; import lombok.Data;
import org.dromara.im.domain.ImGroup; import org.dromara.im.domain.ImGroup;
@ -73,8 +71,6 @@ public class ImGroupBo {
/** /**
* 请求参数 * 请求参数
*/ */
@JsonInclude(JsonInclude.Include.NON_EMPTY)
@TableField(exist = false)
private Map<String, Object> params = new HashMap<>(); private Map<String, Object> params = new HashMap<>();

17
im-admin/ruoyi-im/src/main/java/org/dromara/im/domain/bo/ImPrivateMessageBo.java

@ -1,16 +1,14 @@
package org.dromara.im.domain.bo; package org.dromara.im.domain.bo;
import io.github.linpeilie.annotations.AutoMapper; import io.github.linpeilie.annotations.AutoMapper;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.mybatis.core.domain.BaseEntity; import org.dromara.common.mybatis.core.domain.BaseEntity;
import org.dromara.im.domain.ImPrivateMessage; import org.dromara.im.domain.ImPrivateMessage;
import java.util.Date; import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/** /**
* 私聊消息业务对象 im_private_message * 私聊消息业务对象 im_private_message
@ -26,44 +24,41 @@ public class ImPrivateMessageBo extends BaseEntity {
/** /**
* id * id
*/ */
@NotNull(message = "id不能为空", groups = { EditGroup.class })
private Long id; private Long id;
/** /**
* 发送用户id * 发送用户id
*/ */
@NotNull(message = "发送用户id不能为空", groups = { AddGroup.class, EditGroup.class })
private Long sendId; private Long sendId;
/** /**
* 接收用户id * 接收用户id
*/ */
@NotNull(message = "接收用户id不能为空", groups = { AddGroup.class, EditGroup.class })
private Long recvId; private Long recvId;
/** /**
* 发送内容 * 发送内容
*/ */
@NotBlank(message = "发送内容不能为空", groups = { AddGroup.class, EditGroup.class })
private String content; private String content;
/** /**
* 消息类型 0:文字 1:图片 2:文件 * 消息类型 0:文字 1:图片 2:文件
*/ */
@NotNull(message = "消息类型 0:文字 1:图片 2:文件不能为空", groups = { AddGroup.class, EditGroup.class })
private Long type; private Long type;
/** /**
* 状态 0:未读 1:已读 * 状态 0:未读 1:已读
*/ */
@NotNull(message = "状态 0:未读 1:已读 不能为空", groups = { AddGroup.class, EditGroup.class })
private Long status; private Long status;
/** /**
* 发送时间 * 发送时间
*/ */
@NotNull(message = "发送时间不能为空", groups = { AddGroup.class, EditGroup.class })
private Date sendTime; private Date sendTime;
/**
* 请求参数
*/
private Map<String, Object> params = new HashMap<>();
} }

27
im-admin/ruoyi-im/src/main/java/org/dromara/im/domain/vo/ImPrivateMessageVo.java

@ -2,12 +2,16 @@ package org.dromara.im.domain.vo;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty; import com.alibaba.excel.annotation.ExcelProperty;
import com.fhs.core.trans.anno.Trans;
import com.fhs.core.trans.constant.TransType;
import com.fhs.core.trans.vo.TransPojo;
import io.github.linpeilie.annotations.AutoMapper; import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data; import lombok.Data;
import org.dromara.im.constant.ImConstant;
import org.dromara.im.domain.ImPrivateMessage; import org.dromara.im.domain.ImPrivateMessage;
import org.dromara.im.domain.ImUser;
import java.io.Serial; import java.io.Serial;
import java.io.Serializable;
import java.util.Date; import java.util.Date;
@ -21,7 +25,7 @@ import java.util.Date;
@Data @Data
@ExcelIgnoreUnannotated @ExcelIgnoreUnannotated
@AutoMapper(target = ImPrivateMessage.class) @AutoMapper(target = ImPrivateMessage.class)
public class ImPrivateMessageVo implements Serializable { public class ImPrivateMessageVo implements TransPojo {
@Serial @Serial
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@ -35,15 +39,21 @@ public class ImPrivateMessageVo implements Serializable {
/** /**
* 发送用户id * 发送用户id
*/ */
@ExcelProperty(value = "发送用户id") @Trans(type = TransType.SIMPLE,dataSource = ImConstant.DS_IM_PLATFORM,target = ImUser.class, fields = "userName", ref = "sendUserName")
private Long sendId; private Long sendId;
@ExcelProperty(value = "发送用户")
private String sendUserName;
/** /**
* 接收用户id * 接收用户id
*/ */
@ExcelProperty(value = "接收用户id") @Trans(type = TransType.SIMPLE,dataSource = ImConstant.DS_IM_PLATFORM,target = ImUser.class, fields = "userName", ref = "recvUserName")
private Long recvId; private Long recvId;
@ExcelProperty(value = "接收用户")
private String recvUserName;
/** /**
* 发送内容 * 发送内容
*/ */
@ -51,15 +61,15 @@ public class ImPrivateMessageVo implements Serializable {
private String content; private String content;
/** /**
* 消息类型 0:文字 1:图片 2:文件 * 消息类型
*/ */
@ExcelProperty(value = "消息类型 0:文字 1:图片 2:文件") @ExcelProperty(value = "消息类型")
private Long type; private Long type;
/** /**
* 状态 0:未读 1:已读 * 状态
*/ */
@ExcelProperty(value = "状态 0:未读 1:已读 ") @ExcelProperty(value = "状态")
private Long status; private Long status;
/** /**
@ -68,5 +78,4 @@ public class ImPrivateMessageVo implements Serializable {
@ExcelProperty(value = "发送时间") @ExcelProperty(value = "发送时间")
private Date sendTime; private Date sendTime;
} }

24
im-admin/ruoyi-im/src/main/java/org/dromara/im/service/IImPrivateMessageService.java

@ -5,7 +5,6 @@ import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.im.domain.bo.ImPrivateMessageBo; import org.dromara.im.domain.bo.ImPrivateMessageBo;
import org.dromara.im.domain.vo.ImPrivateMessageVo; import org.dromara.im.domain.vo.ImPrivateMessageVo;
import java.util.Collection;
import java.util.List; import java.util.List;
/** /**
@ -41,28 +40,5 @@ public interface IImPrivateMessageService {
*/ */
List<ImPrivateMessageVo> queryList(ImPrivateMessageBo bo); List<ImPrivateMessageVo> queryList(ImPrivateMessageBo bo);
/**
* 新增私聊消息
*
* @param bo 私聊消息
* @return 是否新增成功
*/
Boolean insertByBo(ImPrivateMessageBo bo);
/**
* 修改私聊消息
*
* @param bo 私聊消息
* @return 是否修改成功
*/
Boolean updateByBo(ImPrivateMessageBo bo);
/**
* 校验并批量删除私聊消息信息
*
* @param ids 待删除的主键集合
* @param isValid 是否进行有效性校验
* @return 是否删除成功
*/
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
} }

61
im-admin/ruoyi-im/src/main/java/org/dromara/im/service/impl/ImPrivateMessageServiceImpl.java

@ -5,7 +5,6 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StringUtils; import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.PageQuery; import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo; import org.dromara.common.mybatis.core.page.TableDataInfo;
@ -17,7 +16,6 @@ import org.dromara.im.mapper.ImPrivateMessageMapper;
import org.dromara.im.service.IImPrivateMessageService; import org.dromara.im.service.IImPrivateMessageService;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -41,7 +39,7 @@ public class ImPrivateMessageServiceImpl implements IImPrivateMessageService {
* @return 私聊消息 * @return 私聊消息
*/ */
@Override @Override
public ImPrivateMessageVo queryById(Long id){ public ImPrivateMessageVo queryById(Long id) {
return baseMapper.selectVoById(id); return baseMapper.selectVoById(id);
} }
@ -76,62 +74,13 @@ public class ImPrivateMessageServiceImpl implements IImPrivateMessageService {
LambdaQueryWrapper<ImPrivateMessage> lqw = Wrappers.lambdaQuery(); LambdaQueryWrapper<ImPrivateMessage> lqw = Wrappers.lambdaQuery();
lqw.eq(bo.getSendId() != null, ImPrivateMessage::getSendId, bo.getSendId()); lqw.eq(bo.getSendId() != null, ImPrivateMessage::getSendId, bo.getSendId());
lqw.eq(bo.getRecvId() != null, ImPrivateMessage::getRecvId, bo.getRecvId()); lqw.eq(bo.getRecvId() != null, ImPrivateMessage::getRecvId, bo.getRecvId());
lqw.eq(StringUtils.isNotBlank(bo.getContent()), ImPrivateMessage::getContent, bo.getContent()); lqw.like(StringUtils.isNotBlank(bo.getContent()), ImPrivateMessage::getContent, bo.getContent());
lqw.eq(bo.getType() != null, ImPrivateMessage::getType, bo.getType()); lqw.eq(bo.getType() != null, ImPrivateMessage::getType, bo.getType());
lqw.eq(bo.getStatus() != null, ImPrivateMessage::getStatus, bo.getStatus()); lqw.eq(bo.getStatus() != null, ImPrivateMessage::getStatus, bo.getStatus());
lqw.eq(bo.getSendTime() != null, ImPrivateMessage::getSendTime, bo.getSendTime()); lqw.between(params.get("beginTime") != null && params.get("endTime") != null, ImPrivateMessage::getSendTime,
params.get("beginTime"), params.get("endTime"));
lqw.orderByDesc(ImPrivateMessage::getId);
return lqw; return lqw;
} }
/**
* 新增私聊消息
*
* @param bo 私聊消息
* @return 是否新增成功
*/
@Override
public Boolean insertByBo(ImPrivateMessageBo bo) {
ImPrivateMessage add = MapstructUtils.convert(bo, ImPrivateMessage.class);
validEntityBeforeSave(add);
boolean flag = baseMapper.insert(add) > 0;
if (flag) {
bo.setId(add.getId());
}
return flag;
}
/**
* 修改私聊消息
*
* @param bo 私聊消息
* @return 是否修改成功
*/
@Override
public Boolean updateByBo(ImPrivateMessageBo bo) {
ImPrivateMessage update = MapstructUtils.convert(bo, ImPrivateMessage.class);
validEntityBeforeSave(update);
return baseMapper.updateById(update) > 0;
}
/**
* 保存前的数据校验
*/
private void validEntityBeforeSave(ImPrivateMessage entity){
//TODO 做一些数据校验,如唯一约束
}
/**
* 校验并批量删除私聊消息信息
*
* @param ids 待删除的主键集合
* @param isValid 是否进行有效性校验
* @return 是否删除成功
*/
@Override
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
if(isValid){
//TODO 做一些业务上的校验,判断是否需要校验
}
return baseMapper.deleteByIds(ids) > 0;
}
} }

Loading…
Cancel
Save