Browse Source

add:新增搜索、邀请进群功能。优化整体UI,在线用户展示优化,预览消息UI优化。

master
qiutianhong 7 months ago
parent
commit
c71ae1cf2a
  1. 3
      frontend/components.d.ts
  2. 50
      frontend/src/components/chat/Chat.vue
  3. 144
      frontend/src/components/chat/ChatBox.vue
  4. 3
      frontend/src/components/chat/ChatMessage.vue
  5. 3
      frontend/src/components/chat/ChatMsgList.vue
  6. 93
      frontend/src/stores/chat.ts

3
frontend/components.d.ts

@ -61,9 +61,6 @@ declare module 'vue' {
ElContainer: typeof import('element-plus/es')['ElContainer']
ElDialog: typeof import('element-plus/es')['ElDialog']
ElDrawer: typeof import('element-plus/es')['ElDrawer']
ElDropdown: typeof import('element-plus/es')['ElDropdown']
ElDropdownItem: typeof import('element-plus/es')['ElDropdownItem']
ElDropdownMenu: typeof import('element-plus/es')['ElDropdownMenu']
ElEmpty: typeof import('element-plus/es')['ElEmpty']
ElFooter: typeof import('element-plus/es')['ElFooter']
ElForm: typeof import('element-plus/es')['ElForm']

50
frontend/src/components/chat/Chat.vue

@ -26,7 +26,6 @@
}
});
// searchList
watch(
() => store.searchInput,
@ -103,14 +102,16 @@
<!-- 邀请群聊弹窗 -->
<el-dialog
v-model="store.groupChatInvitedDialogVisible"
title="发起群聊"
width="80%"
style="height: 550px"
align-center
>
<template #title>
<span>{{ store.groupTitle }}</span>
</template>
<div class="dialog-body">
<!-- 添加输入部门名的输入框 -->
<div>
<div v-if="store.groupTitle === '创建群聊'">
<el-form label-position="top">
<el-form-item label="群聊名称:">
<el-input
@ -162,34 +163,41 @@
</template>
<style scoped>
:deep(.el-transfer) {
display: flex;
flex-direction: row; /* 将布局方向设置为横向 */
width: 550px; /* 让穿梭框占满宽度 */
.el-transfer__buttons {
display: flex;
justify-content: space-evenly;
align-items: center;
width: 160px;
padding: 0 0 !important;
.el-button {
width: 50px !important;
}
}
}
.el-transfer {
display: flex;
flex-direction: row; /* 将布局方向设置为横向 */
width: 550px; /* 让穿梭框占满宽度 */
height: 250px;
}
.el-transfer-panel {
width: 300px !important;
height: 530px !important;
::v-deep .el-transfer-panel {
width: 270px !important; /* 设置每个穿梭框面板的宽度 */
height: 250px !important;
}
.el-transfer-panel__body {
height: 450px !important;
::v-deep .el-transfer-panel__body {
height: 200px !important;
}
.el-checkbox__label {
::v-deep .el-checkbox__label {
margin-left: 10px !important;
}
.el-transfer-panel
.el-transfer-panel__header
.el-checkbox
.el-checkbox__label
span {
left: 150px;
right: 0px;
}
.container {
display: flex;
height: 100%;
@ -221,7 +229,6 @@
align-items: center;
justify-content: space-evenly;
width: 100%;
/* 占据整个宽度 */
height: 50px;
padding: 0;
padding-right: 10px;
@ -246,7 +253,6 @@
.search-input {
width: calc(100% - 20px);
/* 减去左右边距 */
margin: 10px;
height: 32px;
-webkit-app-region: no-drag;
@ -257,7 +263,6 @@
.list {
width: 100%;
height: 100%;
/* 占据整个宽度 */
padding: 0;
overflow-y: hidden;
overflow-x: hidden;
@ -265,18 +270,17 @@
}
.dialog-body {
width: 100%;
height: 350px;
}
.chat-box {
flex: 3;
/* 占据剩余宽度的三倍 */
max-height: max-content;
background-color: #f5f5f5;
}
.chat-setting {
width: calc(100% - 65px);
/* 占据整个宽度 */
height: 100%;
overflow: hidden;
}

144
frontend/src/components/chat/ChatBox.vue

@ -2,7 +2,8 @@
<div
class="chatbox-main"
v-if="store.targetChatId"
>
<!--聊天顶部区-->
<el-header class="chat-header">
<div class="header-title">
@ -14,35 +15,73 @@
<span
v-else-if="store.targetGroupInfo.displayName"
class="header-title-name"
>{{ store.targetGroupInfo.displayName }}</span
>
<!-- 更多 -->
<el-dropdown
placement="bottom"
v-if="
store.targetGroupInfo &&
Object.keys(store.targetGroupInfo).length > 0
"
>{{ store.targetGroupInfo.displayName }}{{ 1 }}</span
>
<!-- 设置 -->
<el-icon><More /></el-icon>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item
@click="store.inviteFriendDialogVisible = true"
>邀请好友</el-dropdown-item
>
<el-dropdown-item
@click="
store.quitGroup(
store.targetGroupInfo.chatId
)
"
>退出群聊</el-dropdown-item
>
</el-dropdown-menu>
</template>
</el-dropdown>
<div @click="store.drawerVisible = true">
<el-icon><Tools /></el-icon>
<!-- 抽屉 -->
<el-drawer
v-model="store.drawerVisible"
direction="ltr"
title="群设置"
:with-header="true"
>
<div
style="
display: flex;
flex-direction: column;
justify-content: space-between;
height: 100%;
"
>
<div>
<!-- 群名 -->
<div class="group-name">
{{ store.targetGroupInfo.displayName }}
</div>
<!-- 群成员 -->
<div class="group-member">
<div><span>群成员</span></div>
<div class="group-member-list">
<button
@click="store.addMember"
class="group-member-add"
>
<el-icon><Plus /></el-icon>
</button>
<div
style="
display: flex;
flex-direction: row;
"
v-for="member in store.groupMemberList"
>
<el-avatar :src="member.avatar" />
</div>
</div>
</div>
</div>
<!-- 退出按钮 -->
<div class="group-exit">
<el-button
style="
background-color: #0078d4;
color: #fff;
"
@click="
store.quitGroup(
store.targetGroupInfo.group_id
)
"
>退出群聊</el-button
>
</div>
</div>
</el-drawer>
</div>
<!-- 邀请好友对话框 -->
<el-dialog
@ -163,6 +202,16 @@
const imgExt = ["png", "jpg", "jpeg", "gif", "bmp", "webp", "svg"];
const choosetype = ref("");
// store.drawerVisible
watch(
() => store.drawerVisible,
(newVal, _) => {
if (newVal) {
store.getGroupMember(store.targetGroupInfo.group_id);
}
}
);
const generateData = () => {
return store.allUserList.map((user: any) => ({
key: user.id,
@ -202,6 +251,43 @@
</script>
<style scoped>
.group-name {
font-size: 16px;
border-top: 1px solid #edebeb;
border-bottom: 1px solid #edebeb;
display: flex;
align-items: center;
justify-content: start;
height: 50px;
}
.group-member-list {
display: flex;
gap: 5px;
width: 100%;
align-items: center;
height: 50px;
overflow: hidden;
padding-bottom: 10px;
border-bottom: 1px solid #edebeb;
}
.group-member-add {
background-color: #0078d4;
border-radius: 50px;
width: 40px;
height: 40px;
color: #fff;
border: none;
}
.group-member {
display: flex;
flex-direction: column;
align-items: start;
justify-content: start;
}
.el-transfer {
display: flex;
flex-direction: row; /* 将布局方向设置为横向 */

3
frontend/src/components/chat/ChatMessage.vue

@ -55,10 +55,11 @@
v-else-if="item.content_type == 'image'"
class="chat-item-image"
>
{{ item.message }}
<el-image
fit="cover"
loading="lazy"
:src="item.message"
:src="store.getImageSrc()"
/>
</div>
</div>

3
frontend/src/components/chat/ChatMsgList.vue

@ -1,4 +1,5 @@
<template>
<div
v-if="
(store.searchList.length > 0 ? store.searchList : store.chatList)
@ -149,7 +150,7 @@
justify-content: space-between;
}
.previewName {
font-size: 16px;
font-size: 14px;
color: #000000;
font-family: Arial, sans-serif;
line-height: 1.5;

93
frontend/src/stores/chat.ts

@ -16,6 +16,8 @@ export const useChatStore = defineStore('chatStore', () => {
username: string;
nickname: string;
}
// 文件消息类型
interface ChatMessageType {
type: string;
@ -52,6 +54,7 @@ export const useChatStore = defineStore('chatStore', () => {
const setGroupChatInvitedDialogVisible = (visible: boolean) => {
getAllUser()
groupChatInvitedDialogVisible.value = visible;
groupTitle.value = '创建群聊'
};
// 设置群信息抽屉状态
const setGroupInfoDrawerVisible = (visible: boolean) => {
@ -65,6 +68,8 @@ export const useChatStore = defineStore('chatStore', () => {
// 在线用户列表
const onlineUserList = ref<OnlineUserInfoType[]>([]);
// 添加成员
const addMemberDialogVisible = ref(false)
// 聊天列表
const chatList: any = ref([]);
@ -72,9 +77,8 @@ export const useChatStore = defineStore('chatStore', () => {
const chatHistory: any = ref([]);
// 群组l列表
const groupList: any = ref([
]);
const groupList: any = ref([]);
const drawerVisible = ref(false)
const targetGroupInfo: any = ref({})
const activeNames = ref([]);
const userInfo: any = ref({});
@ -93,7 +97,7 @@ export const useChatStore = defineStore('chatStore', () => {
const searchInput = ref('');
// 群成员列表
const groupMemberList = ref([])
const groupMemberList = ref<any[]>([])
// 所有用户列表
const allUserList = ref([])
@ -101,6 +105,9 @@ export const useChatStore = defineStore('chatStore', () => {
// 部门列表
const departmentList = ref([])
// groupTitle
const groupTitle = ref('')
// 邀请好友
const inviteFriendDialogVisible = ref(false)
const contextMenu = ref({
@ -489,12 +496,14 @@ export const useChatStore = defineStore('chatStore', () => {
// 获取用户表中所有用户
const getAllUser = async () => {
allUserList.value = await db.getAll("workbenchChatUser");
console.log(allUserList.value)
};
// 创建群聊
const createGroupChat = async (userIds: number[]) => {
console.log(userIds)
if (userIds.length === 0) {
notifyError('请选择用户')
return false
@ -675,16 +684,6 @@ export const useChatStore = defineStore('chatStore', () => {
const updateOrAddUsers = async (users: OnlineUserInfoType[]) => {
// 从数据库中获取所有用户信息
const allUsers = await db.getAll("workbenchChatUser");
const onlineUserIds = new Set(users.map(user => user.id));
// 筛选出需要删除的用户(即不在在线列表中的用户)
const usersToDelete = allUsers.filter((user: { id: string; }) => !onlineUserIds.has(user.id));
const deleteIds = usersToDelete.map((user: { id: any; }) => user.id);
// 删除不在在线列表中的用户
if (deleteIds.length > 0) {
await db.table("workbenchChatUser").bulkDelete(deleteIds);
}
// 添加或更新在线用户
for (const user of users) {
@ -873,14 +872,57 @@ export const useChatStore = defineStore('chatStore', () => {
targetGroupInfo.value = {}
targetChatId.value = ''
}
// const group = await db.getByField("workbenchGroupUserList", "group_id", group_id);
// if (group.length > 0) {
// // 删除当前用户
// console.log(group[0])
// const updatedUserIdArray = group[0].userIdArray.filter((user: any) => user.id !== userInfo.value.id);
// await db.update("workbenchGroupUserList", group[0].id, { userIdArray: updatedUserIdArray });
// }
// 获取群成员
const getGroupMember = async (group_id: string) => {
const member = await db.getByField("workbenchGroupUserList", "group_id", group_id)
groupMemberList.value = member[0].userIdArray
}
const inviteUserList = ref([])// 添加群成员
const addMember = async () => {
groupChatInvitedDialogVisible.value = true;
await getAllUser();
// 确保 groupMemberList 是一个包含 ID 的数组
const existingMemberIds = groupMemberList.value.map((member: any) => member.id);
// 使用 filter 方法过滤掉已在群成员列表中的用户
inviteUserList.value = allUserList.value.filter(
(user: any) => !existingMemberIds.includes(user.id)
);
allUserList.value = inviteUserList.value;
groupTitle.value = '邀请群聊';
};
// 获取图片消息预览
const getImageSrc = async (imageMessage: string = "/C/Users/Desktop/bizhi.png") => {
const path = userInfo.value.url + "/chat/image/view?path=" + imageMessage
console.log(path)
const response = await fetchGet(path)
if (!response.ok) {
return ''
}
// 检查Content-Type是否为图片
const contentType = response.headers.get("Content-Type");
if (!contentType || !contentType.startsWith("image/")) {
console.error("Expected an image content type, but received:", contentType);
return '';
}
// 将响应数据转换为 Blob
const imageBlob = await response.blob();
// 将 Blob 转换为一个 URL 以供展示
const imageUrl = URL.createObjectURL(imageBlob);
console.log(imageUrl)
return imageUrl; // 返回 Blob URL
}
return {
@ -914,6 +956,10 @@ export const useChatStore = defineStore('chatStore', () => {
groupMemberList,
groups,
inviteFriendDialogVisible,
drawerVisible,
addMemberDialogVisible,
groupTitle,
inviteUserList,
initChat,
showContextMenu,
setCurrentNavId,
@ -932,5 +978,8 @@ export const useChatStore = defineStore('chatStore', () => {
getAllUser,
quitGroup,
inviteFriend,
getGroupMember,
addMember,
getImageSrc
};
});
Loading…
Cancel
Save