Browse Source

add:优化聊天功能整体UI。新增删除聊天记录功能

master
qiutianhong 7 months ago
parent
commit
98441c800f
  1. 5
      frontend/components.d.ts
  2. 1
      frontend/package.json
  3. 950
      frontend/pnpm-lock.yaml
  4. BIN
      frontend/src/assets/icons/group.png
  5. 0
      frontend/src/assets/icons/group_avatar.svg
  6. 11
      frontend/src/components/chat/Chat.vue
  7. 164
      frontend/src/components/chat/ChatBox.vue
  8. 230
      frontend/src/components/chat/ChatMenu.vue
  9. 102
      frontend/src/components/chat/ChatMessage.vue
  10. 39
      frontend/src/components/chat/ChatMsgList.vue
  11. 25
      frontend/src/components/chat/ChatUserList.vue
  12. 66
      frontend/src/components/chat/chatGroupMember.vue
  13. 255
      frontend/src/components/chat/chatUserInfo.vue
  14. 4
      frontend/src/components/window/WindowTemplate.vue
  15. 21
      frontend/src/main.ts
  16. 73
      frontend/src/stores/chat.ts
  17. 5
      frontend/src/stores/db.ts
  18. 2
      frontend/src/system/member.ts

5
frontend/components.d.ts

@ -22,6 +22,7 @@ declare module 'vue' {
ChatDomain: typeof import('./src/components/localchat/ChatDomain.vue')['default']
ChatEditor: typeof import('./src/components/localchat/ChatEditor.vue')['default']
ChatFoot: typeof import('./src/components/localchat/ChatFoot.vue')['default']
ChatGroupMember: typeof import('./src/components/chat/chatGroupMember.vue')['default']
ChatMenu: typeof import('./src/components/chat/ChatMenu.vue')['default']
ChatMessage: typeof import('./src/components/chat/ChatMessage.vue')['default']
ChatMsgList: typeof import('./src/components/chat/ChatMsgList.vue')['default']
@ -50,6 +51,7 @@ declare module 'vue' {
EditType: typeof import('./src/components/builtin/EditType.vue')['default']
ElAside: typeof import('element-plus/es')['ElAside']
ElAvatar: typeof import('element-plus/es')['ElAvatar']
ElAvimg: typeof import('element-plus/es')['ElAvimg']
ElBadge: typeof import('element-plus/es')['ElBadge']
ElButton: typeof import('element-plus/es')['ElButton']
ElCard: typeof import('element-plus/es')['ElCard']
@ -71,6 +73,7 @@ declare module 'vue' {
ElHeader: typeof import('element-plus/es')['ElHeader']
ElIcon: typeof import('element-plus/es')['ElIcon']
ElImage: typeof import('element-plus/es')['ElImage']
ElImg: typeof import('element-plus/es')['ElImg']
ElInput: typeof import('element-plus/es')['ElInput']
ElMain: typeof import('element-plus/es')['ElMain']
ElMenu: typeof import('element-plus/es')['ElMenu']
@ -80,6 +83,7 @@ declare module 'vue' {
ElPopover: typeof import('element-plus/es')['ElPopover']
ElProgress: typeof import('element-plus/es')['ElProgress']
ElRow: typeof import('element-plus/es')['ElRow']
ElS: typeof import('element-plus/es')['ElS']
ElScrollbar: typeof import('element-plus/es')['ElScrollbar']
ElSelect: typeof import('element-plus/es')['ElSelect']
ElSwitch: typeof import('element-plus/es')['ElSwitch']
@ -147,6 +151,7 @@ declare module 'vue' {
StartOption: typeof import('./src/components/taskbar/StartOption.vue')['default']
StateIcon: typeof import('./src/components/taskbar/StateIcon.vue')['default']
Store: typeof import('./src/components/store/Store.vue')['default']
Svg: typeof import('./src/components/svg/index.vue')['default']
Taskbar: typeof import('./src/components/taskbar/Taskbar.vue')['default']
Upgrade: typeof import('./src/components/desktop/Upgrade.vue')['default']
UpPopover: typeof import('./src/components/computer/UpPopover.vue')['default']

1
frontend/package.json

@ -21,6 +21,7 @@
"pinia": "^2.1.7",
"pinia-plugin-persist": "^1.0.0",
"vditor": "^3.10.4",
"vite-plugin-svg-icons": "^2.0.1",
"vue": "^3.4.31",
"vue-i18n": "^9.13.1",
"vue-router": "^4.4.0",

950
frontend/pnpm-lock.yaml

File diff suppressed because it is too large

BIN
frontend/src/assets/icons/group.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

0
frontend/src/assets/icon/group_avatar.svg → frontend/src/assets/icons/group_avatar.svg

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

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

@ -2,7 +2,7 @@
import { useChatStore } from "@/stores/chat";
import { getWorkflowUrl } from "@/system/config";
import { Search } from "@element-plus/icons-vue";
const store = useChatStore();
const store = useChatStore()
const workUrl = getWorkflowUrl();
onMounted(() => {
store.initChat();
@ -223,8 +223,9 @@
.side {
flex: 1;
/* 占据剩余宽度 */
max-width: 200px;
min-width: 200px;
max-width: 200px;
min-width: 200px;
min-height: 650px;
max-height: max-content;
border-right: 1px solid #e8e8e8;
overflow-y: hidden;
@ -282,8 +283,10 @@
.chat-box {
flex: 3;
width: 100%;
height: 100%;
max-height: max-content;
background-color: #f5f5f5;
background-color: #ffffff;
}
.chat-setting {

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

@ -99,79 +99,11 @@
"
>退出群聊</el-dropdown-item
>
<el-dropdown-item>清空记录</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
<el-icon
style="
cursor: pointer;
color: black;
font-size: 15px;
"
@click="openDrawer()"
><Tools
/></el-icon>
</div>
<!-- 抽屉 -->
<el-drawer
v-model="store.drawerVisible"
direction="ltr"
title="群成员"
:with-header="true"
>
<div
style="
display: flex;
height: 100%;
padding-top: 40px;
flex-direction: column;
justify-content: space-between;
"
>
<div
class="group-member-container"
style="
display: flex;
flex-wrap: wrap;
max-width: 248px; /* 每行4个用户,每个60px + 2px间距 */
"
>
<div
class="group-member"
v-for="member in store.groupMembers"
:key="member.id"
style="
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 50px;
height: 70px;
"
>
<el-avatar
size="40"
src=""
/>
<div
style="
max-width: 46px;
height: 20px;
font-size: 10px;
text-align: center;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
display: inline-block;
line-height: 20px;
"
>
{{ member.nickname }}
</div>
</div>
</div>
</div>
</el-drawer>
</div>
<div
@ -190,11 +122,13 @@
color: black;
font-size: 15px;
"
><Tools
><More
/></el-icon>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item>删除好友</el-dropdown-item>
<el-dropdown-item @click="store.clearChatRecord()"
>清空记录</el-dropdown-item
>
</el-dropdown-menu>
</template>
</el-dropdown>
@ -204,28 +138,29 @@
<!--聊天主体区-->
<el-main class="msg-main">
<el-scrollbar ref="scrollbarRef">
<div ref="innerRef">
<ChatMessage />
</div>
</el-scrollbar>
<!-- 聊天消息滚动区域 -->
<div class="msg-container">
<el-scrollbar ref="scrollbarRef">
<div ref="innerRef">
<ChatMessage />
<!-- <ChatGroupMember /> -->
</div>
</el-scrollbar>
</div>
<!-- 群成员滚动区域 -->
<div class="member-container">
<el-scrollbar ref="memberScrollbarRef">
<div ref="memberInnerRef">
<ChatGroupMember />
</div>
</el-scrollbar>
</div>
</el-main>
<!--聊天输入区和发送按钮等-->
<el-footer
class="msg-footer"
style="
display: flex;
flex-direction: column;
justify-content: center;
align-items: start;
"
>
<el-footer class="msg-footer">
<!-- 输入主体 -->
<div
class="input-main"
style="width: 100%; height: 40px; background-color: #ffffff"
>
<div class="input-main">
<el-input
size="large"
style="width: 100%; height: 100%"
@ -248,12 +183,6 @@
>
<Link />
</el-icon>
<el-icon
:size="20"
class="input-option-icon"
>
<Delete />
</el-icon>
<el-icon
@click="store.sendMessage('text')"
:size="22"
@ -300,6 +229,7 @@
<script setup lang="ts">
import { useChatStore } from "@/stores/chat";
import { useChooseStore } from "@/stores/choose";
const store: any = useChatStore();
const choose = useChooseStore();
const imgExt = ["png", "jpg", "jpeg", "gif", "bmp", "webp", "svg"];
@ -364,7 +294,7 @@
function openDrawer() {
store.drawerVisible = true;
store.getGroupMemberList(store.targetGroupInfo.group_id);
// store.getGroupMemberList(store.targetGroupInfo.group_id);
}
// store.drawerVisible
@ -405,6 +335,35 @@
</script>
<style scoped>
.msg-main {
width: 100%;
/* 占据整个宽度 */
height: 75%;
padding: 0;
border-top: 1px solid #edebeb;
display: flex;
justify-content: space-between;
}
/* 聊天消息滚动区域 */
.msg-container {
width: calc(100% - 150px);
height: 100%;
}
/* 群成员滚动区域 */
.member-container {
border-left: 1px solid #edebeb;
width: 140px;
height: 100%;
}
.input-main {
width: 100%;
height: 40px;
background-color: #ffffff;
}
.invite-group-button {
background-color: #0078d4;
color: #fff;
@ -549,20 +508,15 @@
font-size: 20px;
}
.msg-main {
background-color: re;
/* width: 100%; */
/* 占据整个宽度 */
height: 75%;
padding: 0;
border-top: 1px solid #edebeb;
}
.msg-footer {
width: 100%;
/* 占据整个宽度 */
height: calc(100% - 75% - 49px);
border: none;
display: flex;
flex-direction: column;
justify-content: center;
align-items: start;
}
.input-option {

230
frontend/src/components/chat/ChatMenu.vue

@ -1,115 +1,137 @@
<script setup>
import { useChatStore } from "@/stores/chat";
import { Setting as SettingIcon } from "@element-plus/icons-vue";
import { useChatStore } from "@/stores/chat";
import { Setting as SettingIcon } from "@element-plus/icons-vue";
const store = useChatStore();
const store = useChatStore();
</script>
<template>
<el-row>
<el-avatar
shape="square"
:size="40"
class="userAvatar"
:src="store.userInfo.avatar"
/>
</el-row>
<el-row @click="store.setCurrentNavId(0)">
<div class="menu-icon-box">
<el-icon
v-if="store.currentNavId === 0"
class="menu-icon-on"
>
<ChatLineRound />
</el-icon>
<el-icon
v-else
class="menu-icon"
>
<ChatRound />
</el-icon>
</div>
</el-row>
<el-row @click="store.setCurrentNavId(1)">
<div class="menu-icon-box">
<el-icon
v-if="store.currentNavId === 1"
class="menu-icon-on"
>
<UserFilled />
</el-icon>
<el-icon
v-else
class="menu-icon"
>
<User />
</el-icon>
</div>
</el-row>
<el-row @click="store.setCurrentNavId(2)">
<div class="menu-icon-box">
<el-icon
v-if="store.currentNavId === 2"
class="menu-icon-on"
>
<Platform />
</el-icon>
<el-icon
v-else
class="menu-icon"
>
<Monitor />
</el-icon>
</div>
</el-row>
<el-row @click="store.setCurrentNavId(5)">
<div class="menu-icon-box">
<el-icon
v-if="store.currentNavId === 5"
class="menu-icon-on"
>
<SettingIcon />
</el-icon>
<el-icon
v-else
class="menu-icon"
>
<SettingIcon />
</el-icon>
</div>
</el-row>
<!-- 头像 -->
<el-row>
<el-avatar
shape="square"
:size="40"
class="userAvatar"
:src="store.userInfo.avatar"
/>
</el-row>
<!-- 菜单按钮 -->
<el-row @click="store.setCurrentNavId(0)">
<div class="menu-icon-box">
<el-icon
v-if="store.currentNavId === 0"
class="menu-icon-on"
>
<Comment />
</el-icon>
<el-icon
v-else
class="menu-icon"
>
<Comment />
</el-icon>
</div>
</el-row>
<el-row @click="store.setCurrentNavId(1)">
<div class="menu-icon-box">
<el-icon
v-if="store.currentNavId === 1"
class="menu-icon-on"
>
<UserFilled />
</el-icon>
<el-icon
v-else
class="menu-icon"
>
<User />
</el-icon>
</div>
</el-row>
<el-row @click="store.setCurrentNavId(2)">
<div class="menu-icon-box">
<el-icon
v-if="store.currentNavId === 2"
class="menu-icon-on"
>
<Platform />
</el-icon>
<el-icon
v-else
class="menu-icon"
>
<Monitor />
</el-icon>
</div>
</el-row>
<!-- 设置按钮固定在底部 -->
<el-row
class="settings-icon-row"
@click="store.setCurrentNavId(5)"
>
<div class="menu-icon-box">
<el-icon
v-if="store.currentNavId === 5"
class="menu-icon-on"
>
<Tools />
</el-icon>
<el-icon
v-else
class="menu-icon"
>
<Tools />
</el-icon>
</div>
</el-row>
</template>
<style scoped>
.userAvatar {
margin: 6px auto;
-webkit-app-region: no-drag;
}
.chat-user-info {
position: relative;
height: 100%;
}
.userAvatar {
margin: 6px auto;
-webkit-app-region: no-drag;
}
.menu-icon-box {
display: flex;
align-items: center;
justify-content: center;
width: 40px;
height: 40px;
margin: 10px auto;
transition: all 0.5s;
}
.menu-icon-box {
display: flex;
align-items: center;
justify-content: center;
width: 40px;
height: 40px;
margin: 10px auto;
/* border-radius: 15%; */
transition: all 0.5s;
}
/* .menu-icon-box:hover {
background-color: #d9d9d9;
} */
.menu-icon {
margin: 0 auto;
font-size: 25px;
cursor: pointer;
color: #86909C;
}
.menu-icon {
margin: 0px auto;
font-size: 25px;
cursor: pointer;
}
.menu-icon-on {
margin: 0 auto;
font-size: 25px;
color: #0d42d2;
cursor: pointer;
}
.menu-icon-on {
margin: 0px auto;
font-size: 25px;
color: #2A6BF2;
cursor: pointer;
}
</style>
/* 设置按钮独立在底部 */
.settings-icon-row {
position: absolute;
bottom: 20px;
left: 0;
right: 0;
width: 55px;
height: 55px;
}
</style>

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

@ -82,6 +82,7 @@
<el-col :span="2">
<div class="chat-avatar">
<el-avatar
@click="showUserInfo(item.userId)"
shape="square"
style="margin: 0; float: left"
:size="32"
@ -100,6 +101,7 @@
<el-col :span="2">
<div class="chat-avatar">
<el-avatar
@click="showUserInfo(item.userId)"
shape="square"
style="margin: 0; float: right"
:size="32"
@ -188,16 +190,114 @@
</div>
</div>
</div>
<!-- 用户信息弹窗 -->
<el-dialog
style="border-radius: 10px"
v-model="showUserInfoDialog"
title="用户信息"
width="300"
>
<!-- 用户信息 -->
<div class="user-content">
<div class="user-details">
<h3>{{ targetUserInfo.displayName }}</h3>
<p>工号{{ targetUserInfo.jobNumber }}</p>
<p>岗位{{ targetUserInfo.desc }}</p>
<p>邮箱{{ targetUserInfo.email }}</p>
<p>电话{{ targetUserInfo.phone }}</p>
<p>入职日期{{ targetUserInfo.hiredDate }}</p>
</div>
</div>
<!-- 发送消息按钮 -->
<el-button
@click="sendMessage"
class="send-button-container"
>发送消息</el-button
>
</el-dialog>
</template>
<script setup lang="ts">
import { useChatStore } from "@/stores/chat";
import { System } from "@/system";
import { System } from "@/system";
import { notifyInfo } from "@/util/msg";
const store = useChatStore();
const sys: any = inject<System>("system");
const showUserInfoDialog = ref(false);
var targetUserInfo: any = {};
const currUserId = ref();
const showUserInfo = (chatId: string) => {
console.log(chatId);
currUserId.value = chatId;
//
const userInfo = store.groupMembers.find(
(member: any) => member.id === chatId
);
//
targetUserInfo = {
displayName: userInfo.nickname,
jobNumber: 12345678,
desc: "测试岗位",
email: "12345678@qq.com",
phone: "12345678910",
hiredDate: "2024-01-01",
};
showUserInfoDialog.value = true;
};
const sendMessage = () => {
if (currUserId.value == store.userInfo.id) {
notifyInfo("不能给自己发送消息");
return;
}
store.getSessionInfo(currUserId.value, "user");
showUserInfoDialog.value = false;
};
</script>
<style scoped>
.send-button-container {
width: 230px;
display: flex;
justify-content: center;
margin-top: 20px;
background-color: #0d42d2;
color: #fff;
}
.send-button-container:hover {
background-color: #4080ff;
color: #fff;
}
.user-content {
display: flex;
align-items: center;
justify-content: center;
}
.user-details {
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
}
.user-details h3 {
margin: 0;
font-size: 1.2rem;
}
.user-details p {
margin: 5px 0;
color: #666;
}
.system-message {
background-color: #e8e8e8; /* 设置背景颜色 */
color: #333; /* 设置文字颜色 */

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

@ -14,7 +14,7 @@
@click="store.getSessionInfo(item.chatId, item.type)"
:style="{
backgroundColor:
item.chatId == store.targetChatId ? '#F5F5F5' : '',
item.chatId == store.targetChatId ? '#E8F3FF' : '',
}"
>
<el-row>
@ -27,18 +27,27 @@
:src="item.avatar"
></el-avatar>
<!-- <el-avatar
v-else="item.type == 'group'"
<el-avatar
v-else-if="item.type == 'group'"
shape="square"
:size="40"
class="avatar"
:src="item.avatar"
></el-avatar
> -->
<!-- 引入icon -->
<el-icon :size="40">
</el-icon>
style="
background-color: #165DFF;
display: flex;
align-items: center;
justify-content: center;
"
>
<div
style="
width: 25px;
height: 25px;
background-image: url('./src/assets/icons/group.png');
background-size: cover;
"
></div>
</el-avatar>
</el-col>
<!-- 在线状态 -->
@ -143,10 +152,11 @@
margin-bottom: 5px;
overflow: hidden;
margin-top: 5px;
background-color: #FFF;
}
.list-item:hover {
background-color: #f5f5f5;
background-color: #E8F3FF;
}
.avatar {
@ -179,6 +189,7 @@
justify-content: space-between;
}
.previewName {
font-weight: 400;
font-size: 14px;
color: #000000;
font-family: Arial, sans-serif;
@ -190,7 +201,7 @@
.previewTime {
font-size: 12px;
color: #999999;
color: #86909C;
}
.preview-left {
@ -205,9 +216,9 @@
width: 100px;
height: 20px;
margin-left: 10px;
font-size: 10px;
font-size: 12px;
font-family: Arial, sans-serif;
color: #999999;
color: #86909C;
overflow: hidden; /* 隐藏超出部分 */
text-overflow: ellipsis; /* 显示为省略号 */
white-space: nowrap; /* 不换行 */

25
frontend/src/components/chat/ChatUserList.vue

@ -208,10 +208,11 @@
border-radius: 4px;
transition: all 0.5s;
margin-bottom: 5px;
background-color: #fff;
}
.list-item:hover {
background-color: #f5f5f5;
background-color: #e8f3ff;
}
.avatar {
@ -219,10 +220,12 @@
}
.previewName {
font-weight: 400;
margin-left: 10px;
font-size: 14px;
font-family: Arial, sans-serif;
line-height: 1.5;
color: #000000;
overflow: hidden; /* 隐藏超出部分 */
text-overflow: ellipsis; /* 显示为省略号 */
white-space: nowrap; /* 不换行 */
@ -252,26 +255,6 @@
margin-left: 5px; /* 与用户名之间的间距 */
}
.previewChat {
margin-left: 10px;
font-size: 12px; /* 调整字体大小 */
font-family: Arial, sans-serif;
color: #999999;
overflow: hidden; /* 隐藏超出部分 */
text-overflow: ellipsis; /* 显示为省略号 */
white-space: nowrap; /* 不换行 */
max-width: 100%; /* 最大宽度 */
}
.previewTime {
float: right;
margin-top: 10px;
margin-right: 10px;
font-size: 12px;
font-family: Arial, sans-serif;
color: #999999;
}
.no-data {
text-align: center;
color: #999999;

66
frontend/src/components/chat/chatGroupMember.vue

@ -0,0 +1,66 @@
<template>
<div class="group-member-container">
<!-- 群成员列表 -->
<div
class="member-list"
v-for="item in groupMembers"
:key="item.userId"
>
<!-- 头像和昵称 -->
<div class="member-item">
<div class="avatar-container">
<el-avatar
style="width: 100%; height: 100%;"
:src="item.avatar"
/>
</div>
<span>{{ item.nickname }}</span>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { useChatStore } from "@/stores/chat";
const store = useChatStore();
// 使
// 使40
const groupMembers = ref(
Array.from({ length: 40 }, (v, i) => ({
userId: `${i + 1}`,
avatar: `path/to/avatar${i + 1}.jpg`,
nickname: `用户1111111111111111${i + 1}`,
}))
);
</script>
<style scoped>
.group-member-container {
margin: 10px 0;
width: 100%;
height: 100%;
}
.member-item {
display: flex;
align-items: center;
margin-bottom: 5px;
}
.avatar-container {
width: 25px;
height: 25px;
margin-right: 5px;
}
.member-item span {
width: 90px;
margin-left: 10px;
font-size: 12px;
color: #86909C;
overflow: hidden; /* 防止内容溢出 */
white-space: nowrap; /* 保持文本在一行 */
text-overflow: ellipsis; /* 文本溢出显示省略号 */
max-width: 150px; /* 最大宽度,根据需要调整 */
}
</style>

255
frontend/src/components/chat/chatUserInfo.vue

@ -1,173 +1,172 @@
<template>
<div
v-if="store.targetChatId == null"
style="height: 280px; width: 100%"
class="no-message-container"
v-if="store.targetChatId"
class="chat-user-info"
>
<el-icon
:size="180"
color="#0078d7"
<!-- 用户信息区域 -->
<div
v-if="
store.targetUserInfo &&
Object.keys(store.targetUserInfo).length > 0
"
class="user-content"
>
<ChatDotSquare />
</el-icon>
<p class="welcome-text">欢迎使用GodoOS</p>
</div>
<div
v-else-if="
store.targetUserInfo && Object.keys(store.targetUserInfo).length > 0
"
class="user-info-container"
>
<el-avatar
class="user-avatar"
:size="80"
:src="store.targetUserInfo.avatarUrl || ''"
/>
<h2 class="user-name">{{ store.targetUserInfo.displayName }}</h2>
<div class="user-details">
<p><strong>邮箱:</strong> {{ store.targetUserInfo.email }}</p>
<p><strong>电话:</strong> {{ store.targetUserInfo.phone }}</p>
<p><strong>描述:</strong> {{ store.targetUserInfo.desc }}</p>
<p><strong>工号:</strong> {{ store.targetUserInfo.jobNumber }}</p>
<p>
<strong>工作地点:</strong> {{ store.targetUserInfo.workPlace }}
</p>
<p>
<strong>入职日期:</strong> {{ store.targetUserInfo.hiredDate }}
</p>
<div class="user-details">
<h2>{{ store.targetUserInfo.displayName }}</h2>
<p>工号{{ targetUserInfo.jobNumber }}</p>
<p>岗位{{ targetUserInfo.desc }}</p>
<p>邮箱{{ targetUserInfo.email }}</p>
<p>电话{{ targetUserInfo.phone }}</p>
<p>入职日期{{ targetUserInfo.hiredDate }}</p>
</div>
<div class="avatar">
<el-avatar
style="width: 80px; height: 80px"
:src="targetUserInfo.avatar"
alt="avatar"
/>
</div>
</div>
<el-button
class="send-message-btn"
@click="sendMessage(store.targetChatId, 'user')"
>
发送消息
</el-button>
</div>
<!-- 群聊信息 -->
<div
v-else
class="group-info-container"
>
<el-avatar
class="group-avatar"
:size="80"
><span style="font-size: 40px;"></span></el-avatar>
<h2 class="group-name">{{ store.targetGroupInfo.displayName }}</h2>
<el-button
class="send-message-btn"
@click="sendMessage(store.targetChatId, 'group')"
<!-- 群信息区域 -->
<div
v-else-if="
store.targetGroupInfo &&
Object.keys(store.targetGroupInfo).length > 0
"
class="group-content"
>
发送消息
</el-button>
<div class="group-details">
<h2>{{ store.targetGroupInfo.displayName }}</h2>
<p>群ID{{ store.targetGroupInfo.chatId }}</p>
<p>群人数{{ targetGroupInfo.memberCount }}</p>
<p>创建时间{{ targetGroupInfo.createdAt }}</p>
</div>
<div class="group-avatar">
<div class="avatar-container">
<el-avatar
style="width: 80px; height: 80px"
:src="targetGroupInfo.avatar"
alt="group-avatar"
/>
</div>
</div>
</div>
<!-- 分割线 -->
<div class="divider"></div>
<!-- 发送按钮 -->
<div class="send-button-container">
<el-button
type="primary"
@click="
sendMessage(
store.targetGroupInfo?.chatId ||
store.targetUserInfo.chatId,
store.targetGroupInfo
? 'group'
: store.targetUserInfo.type
)
"
>
发送消息
</el-button>
</div>
</div>
<div v-else>1</div>
</template>
<script lang="ts" setup>
import { useChatStore } from "@/stores/chat";
const store = useChatStore();
//
const targetUserInfo = {
avatar: "./logo.png",
jobNumber: 12345678,
desc: "测试岗位",
email: "12345678@qq.com",
phone: "12345678910",
hiredDate: "2024-01-01",
};
//
const targetGroupInfo = {
avatar: "./logo.png",
chatId: "1234567890",
memberCount: 100,
createdAt: "2024-01-01",
};
const sendMessage = (chatId: string, type: string) => {
store.currentNavId = 0;
console.log(chatId, type);
store.getGroupMemberList(chatId);
store.addChatListAndGetChatHistory(chatId, type);
};
</script>
<style scoped>
/* 无消息提示样式 */
.no-message-container {
.chat-user-info {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
color: #4a4a4a;
width: 100%;
padding: 20px;
}
.welcome-text {
font-size: 18px;
font-weight: 600;
margin-top: 10px;
color: #333;
.user-content,
.group-content {
display: flex;
align-items: center;
justify-content: space-between;
}
/* 用户信息容器 */
.user-info-container {
.user-details,
.group-details {
flex: 1;
display: flex;
width: 100%;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100%;
padding: 20px;
text-align: center;
}
/* 用户头像 */
.user-avatar {
margin-bottom: 15px;
border-radius: 50%;
.user-details h2,
.group-details h2 {
margin: 0;
font-size: 1.5rem;
}
/* 用户名标题 */
.user-name {
font-size: 22px;
font-weight: 700;
color: #333;
margin-bottom: 15px;
.user-details p,
.group-details p {
margin: 5px 0;
color: #666;
}
/* 用户详细信息 */
.user-details {
text-align: left;
background-color: #f8f9fa;
padding: 20px;
border-radius: 10px;
box-shadow: 0px 1px 1px 1px rgba(0, 0, 0, 0.1);
max-width: 200px;
width: 100%;
margin-bottom: 30px;
.avatar,
.group-avatar {
width: 80px;
height: 100%;
object-fit: cover;
margin-left: 20px;
}
.user-details p {
margin: 8px 0;
color: #333;
.divider {
width: 100%;
height: 1px;
background-color: #e0e0e0;
margin: 20px 0;
}
.user-details p strong {
color: #333;
.send-button-container {
display: flex;
justify-content: center;
margin-top: 20px;
}
/* 发送消息按钮 */
.send-message-btn {
width: 100%;
max-width: 240px;
font-size: 16px;
background-color: #0078d4;
.el-button {
background-color: #0d42d2;
color: #fff;
border-radius: 8px;
box-shadow: 0 4px 8px rgba(0, 120, 212, 0.3);
font-weight: 600;
transition: background-color 0.3s;
}
.send-message-btn:hover {
background-color: #005a9e;
}
/* 群聊信息容器 */
.group-info-container {
display: flex;
width: 100%;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100%;
padding: 20px;
text-align: center;
.el-button:hover {
background-color: #4080ff;
color: #fff;
}
</style>

4
frontend/src/components/window/WindowTemplate.vue

@ -191,10 +191,10 @@
margin: 0;
// left: 0;
// top: 0;
min-width: 950px;
// min-width: 900px;
width: max-content;
height: max-content;
min-height: 650px;
// min-height: 650px;
border-radius: 10px;
overflow: hidden;
background-color: #fff;

21
frontend/src/main.ts

@ -1,21 +1,24 @@
import * as ElementPlusIconsVue from '@element-plus/icons-vue';
//svg插件需要配置代码
import ElementPlus from 'element-plus';
import 'element-plus/dist/index.css';
import { createApp } from "vue";
import screenShort from "vue-web-screen-shot";
import App from "./App.vue";
import pinia from './stores/index.ts'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import './assets/windows10.scss'
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
import router from './system/router'
import screenShort from "vue-web-screen-shot"
import {i18n} from './i18n/index.ts'
import './assets/windows10.scss';
import { i18n } from './i18n/index.ts';
import pinia from './stores/index.ts';
import router from './system/router';
const app = createApp(App)
app.use(router)
app.use(ElementPlus)
app.use(pinia)
app.use(i18n)
app.use(screenShort, { enableWebRtc: true })
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
app.component(key, component)
app.component(key, component)
}
app.mount("#app");

73
frontend/src/stores/chat.ts

@ -80,7 +80,6 @@ export const useChatStore = defineStore('chatStore', () => {
// 群组l列表
const groupList: any = ref([]);
const drawerVisible = ref(false)
const targetGroupInfo: any = ref({})
const activeNames = ref([]);
const userInfo: any = ref({});
@ -411,7 +410,7 @@ export const useChatStore = defineStore('chatStore', () => {
previewMessage: message.value,
previewTimeFormat: formatTime(Date.now()),
displayName: userInfo.value.nickname,
chatId: targetUserInfo.value.chatId.toString(),
chatId: targetUserInfo.value.chatId,
avatar: userInfo.value.avatar,
createdAt: Date.now()
}
@ -426,7 +425,7 @@ export const useChatStore = defineStore('chatStore', () => {
previewTimeFormat: formatTime(Date.now()),
displayName: targetGroupInfo.value.name,
isMe: true,
chatId: targetGroupInfo.value.chatId.toString(),
chatId: targetGroupInfo.value.chatId,
createdAt: Date.now()
}
@ -483,10 +482,6 @@ export const useChatStore = defineStore('chatStore', () => {
// 从 conversationList 数据库中查找是否存在对应的会话
const conversation = await db.getByField("workbenchSessionList", 'chatId', data.userId);
console.log("更新的文字", message)
// 准备会话更新数据
const updatedConversation = {
userId: data.userId,
@ -903,8 +898,6 @@ export const useChatStore = defineStore('chatStore', () => {
const updateOrAddUsers = async (users: OnlineUserInfoType[]) => {
// 从数据库中获取所有用户信息
const allUsers = await db.getAll("workbenchChatUser");
console.log(users)
console.log(allUsers)
// 添加或更新在线用户
for (const user of users) {
const existingUser = allUsers.find((u: { id: string; }) => u.id === user.id);
@ -986,12 +979,10 @@ export const useChatStore = defineStore('chatStore', () => {
chatHistory.value = []
targetChatId.value = chatId
console.log("------")
if (type === 'user') {
console.log("user")
// 获取当前用户和目标用户的聊天记录
const history = await getHistory(userInfo.value.id, chatId, type)
console.log(history)
chatHistory.value = [...history];
// 设置目标用户的信息
await setTargetUserInfo(chatId);
@ -1030,12 +1021,18 @@ export const useChatStore = defineStore('chatStore', () => {
// 设置目标用户的信息
const setTargetUserInfo = async (id: string) => {
const userInfoArray = await db.getByField("workbenchChatUser", "chatId", id);
var userInfoArray = await db.getByField("workbenchChatUser", "chatId", id);
// 封装用户信息
const userInfo = {
type: "user",
avatar: userInfoArray[0].avatar || "",
displayName: userInfoArray[0].nickname || "",
jobNumber: userInfoArray[0].jobNumber || "",
desc: userInfoArray[0].desc || "",
email: userInfoArray[0].email || "",
phone: userInfoArray[0].phone || "",
hiredDate: userInfoArray[0].hiredDate || "",
toUserId: config.userInfo.id,
chatId: userInfoArray[0].chatId
}
@ -1045,8 +1042,16 @@ export const useChatStore = defineStore('chatStore', () => {
// 设置目标群信息
const setTargetGrouprInfo = async (id: string) => {
for (const group of groupList.value) {
for (var group of groupList.value) {
if (group.group_id === id) {
// 模拟群信息
// group = {
// chatId: id,
// displayName: "湖南果度科技有限公司",
// avatar: "./src/assets/icons/group.png",
// memberCount: 150, // 假设成员数
// createdAt: "2023-01-01" // 假设创建日期
// }
targetGroupInfo.value = group;
targetUserInfo.value = {};
break;
@ -1149,7 +1154,6 @@ export const useChatStore = defineStore('chatStore', () => {
initChatList()
targetGroupInfo.value = {}
targetChatId.value = ''
drawerVisible.value = false
notifySuccess("退出群聊成功")
}
@ -1256,6 +1260,43 @@ export const useChatStore = defineStore('chatStore', () => {
};
// 清空聊天记录
// 清空聊天记录
const clearChatRecord = async () => {
// 删除我发送给对方的记录
const whereObjSent = {
toUserId: targetChatId.value,
userId: userInfo.value.id
};
const resSent = await db.deleteByWhere("workbenchChatRecord", whereObjSent);
// 删除对方发送给我的记录
const whereObjReceived = {
userId: targetChatId.value,
toUserId: userInfo.value.id
};
const resReceived = await db.deleteByWhere("workbenchChatRecord", whereObjReceived);
// 检查两个删除操作是否都成功
if (resSent == 1 && resReceived == 1) {
// 更新chatHistory,移除相关记录
chatHistory.value = chatHistory.value.filter((item: any) =>
!(item.toUserId === targetChatId.value && item.userId === userInfo.value.id) &&
!(item.userId === targetChatId.value && item.toUserId === userInfo.value.id)
);
// 更新chatList中的预览消息
chatList.value.forEach((item: any) => {
if (item.chatId === targetChatId.value) {
item.previewMessage = "快开始打招呼吧!";
}
});
notifySuccess("清空成功");
return;
}
notifyError("清空失败");
}
return {
emojiList,
groupSystemMessage,
@ -1288,7 +1329,6 @@ export const useChatStore = defineStore('chatStore', () => {
groupMemberList,
groups,
inviteFriendDialogVisible,
drawerVisible,
addMemberDialogVisible,
inviteUserList,
messageSendStatus,
@ -1317,6 +1357,7 @@ export const useChatStore = defineStore('chatStore', () => {
groupInviteMessage,
setScrollToBottom,
getGroupMemberList,
getInviteUserList
getInviteUserList,
clearChatRecord,
};
});

5
frontend/src/stores/db.ts

@ -142,6 +142,11 @@ export const db = {
async deleteByField(tableName: ChatTable, fieldName: string, val: any) {
return dbInit[tableName].where(fieldName).equals(val).delete()
},
// 根据whereObj条件删除
async deleteByWhere(tableName: ChatTable, whereObj: any) {
return dbInit[tableName].where(whereObj).delete()
},
// 获取创建时间最近的记录
async getLatest(tableName: ChatTable, fieldName: string, val: any) {
return dbInit[tableName].where(fieldName).equals(val).reverse().first()

2
frontend/src/system/member.ts

@ -4,7 +4,7 @@ export const memberList = [
appIcon: "chat",
content:"Chat",
frame: true,
width: 1050,
width: 1000,
height: 650,
center: true,
resizable: true,

Loading…
Cancel
Save