Browse Source

fix:test

master
qiutianhong 7 months ago
parent
commit
e34e6fe1c9
  1. 16
      frontend/components.d.ts
  2. 221
      frontend/src/components/chat/Chat.vue
  3. 241
      frontend/src/components/chat/ChatBox.vue
  4. 61
      frontend/src/components/chat/ChatMessage.vue
  5. 102
      frontend/src/components/chat/ChatMsgList.vue
  6. 19
      frontend/src/components/chat/ChatUserList.vue
  7. 196
      frontend/src/stores/chat.ts

16
frontend/components.d.ts

@ -50,31 +50,45 @@ 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']
ElBadge: typeof import('element-plus/es')['ElBadge']
ElButton: typeof import('element-plus/es')['ElButton']
ElCard: typeof import('element-plus/es')['ElCard']
ElCarousel: typeof import('element-plus/es')['ElCarousel']
ElCarouselItem: typeof import('element-plus/es')['ElCarouselItem']
ElCheckbox: typeof import('element-plus/es')['ElCheckbox']
ElCol: typeof import('element-plus/es')['ElCol']
ElColorPicker: typeof import('element-plus/es')['ElColorPicker']
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']
ElFormItem: typeof import('element-plus/es')['ElFormItem']
ElHeader: typeof import('element-plus/es')['ElHeader']
ElIcon: typeof import('element-plus/es')['ElIcon']
ElImage: typeof import('element-plus/es')['ElImage']
ElInput: typeof import('element-plus/es')['ElInput']
ElMain: typeof import('element-plus/es')['ElMain']
ElMenu: typeof import('element-plus/es')['ElMenu']
ElMenuItem: typeof import('element-plus/es')['ElMenuItem']
ElOption: typeof import('element-plus/es')['ElOption']
ElPagination: typeof import('element-plus/es')['ElPagination']
ElPopover: typeof import('element-plus/es')['ElPopover']
ElProgress: typeof import('element-plus/es')['ElProgress']
ElRow: typeof import('element-plus/es')['ElRow']
ElScrollbar: typeof import('element-plus/es')['ElScrollbar']
ElSelect: typeof import('element-plus/es')['ElSelect']
ElSwitch: typeof import('element-plus/es')['ElSwitch']
ElTable: typeof import('element-plus/es')['ElTable']
ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
ElText: typeof import('element-plus/es')['ElText']
ElTooltip: typeof import('element-plus/es')['ElTooltip']
ElTransfer: typeof import('element-plus/es')['ElTransfer']
ElTree: typeof import('element-plus/es')['ElTree']
ElUpload: typeof import('element-plus/es')['ElUpload']
Error: typeof import('./src/components/taskbar/Error.vue')['default']
FileIcon: typeof import('./src/components/builtin/FileIcon.vue')['default']
FileIconImg: typeof import('./src/components/builtin/FileIconImg.vue')['default']

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

@ -8,7 +8,6 @@
store.initChat();
});
// el-transfer
const generateData = () => {
return store.allUserList.map((user: any) => ({
key: user.id,
@ -27,18 +26,21 @@
}
});
function toggleSelectItem(item: any) {
const index = users.value.indexOf(item.key);
if (index === -1) {
users.value.push(item.key);
} else {
users.value.splice(index, 1);
}
}
function removeItem(userId: string) {
users.value = users.value.filter((user) => user !== userId);
}
// searchList
watch(
() => store.searchInput,
(newSearchInput) => {
if (newSearchInput === "") {
store.searchList = []; //
} else {
store.searchList = store.chatList.filter(
(user: { displayName: string | string[] }) =>
user.displayName.includes(newSearchInput)
);
}
}
);
</script>
<template>
<el-container class="container">
@ -59,7 +61,7 @@
placeholder="搜索"
:prefix-icon="Search"
class="search-input"
v-model="store.search"
v-model="store.searchInput"
/>
<!-- 邀请群聊 -->
<button
@ -103,86 +105,54 @@
v-model="store.groupChatInvitedDialogVisible"
title="发起群聊"
width="80%"
style="height: 550px"
align-center
>
<div class="dialog-body">
<!-- 添加输入部门名的输入框 -->
<el-form
label-position="top"
>
<el-form-item label="群聊名称:">
<el-input
style="width: 240px"
class="department-name"
v-model="store.departmentName"
placeholder="请输入群聊名称"
></el-input>
</el-form-item>
<el-upload>
</el-upload>
</el-form>
<div class="transfer-container">
<!-- 自定义穿梭框组件 -->
<div class="transfer-box">
<div class="list-box">
<h3>可选项</h3>
<ul>
<li
v-for="item in data"
:key="item.key"
@click="toggleSelectItem(item)"
:class="{ selected: users.includes(item.key) }"
>
<el-avatar
:size="10"
:src="item.avatar"
class="avatar"
/>
<input
type="checkbox"
:checked="users.includes(item.key)"
/>
<span>{{ item.label }}</span>
<span
v-if="users.includes(item.key)"
class="remove-icon"
@click.stop="removeItem(item.key)"
></span
>
</li>
</ul>
</div>
<div class="list-box">
<h3>已选项</h3>
<ul>
<li
v-for="user in users"
:key="user"
>
<span>{{
data.find((item: any) => item.key === user)
?.label
}}</span>
<span
class="remove-icon"
@click="removeItem(user)"
></span
>
</li>
</ul>
</div>
</div>
<div>
<el-form label-position="top">
<el-form-item label="群聊名称:">
<el-input
style="width: 240px; height: 30px"
class="department-name"
v-model="store.departmentName"
placeholder="请输入群聊名称"
></el-input>
</el-form-item>
</el-form>
</div>
<!-- 使用 Element el-transfer 组件替换自定义穿梭框 -->
<el-transfer
v-model="users"
:data="data"
:titles="['可选项', '已选项']"
filterable
:props="{ key: 'key', label: 'label', avatar: 'avatar' }"
class="transfer-container"
>
<!-- 自定义穿梭框列表项模板 -->
<template #default="{ option }">
<el-avatar
:src="option.avatar"
size="small"
style="margin-right: 5px"
/>
<span>{{ option.label }}</span>
</template>
</el-transfer>
</div>
<template #footer>
<span class="dialog-footer">
<el-button @click="store.groupChatInvitedDialogVisible = false"
<el-button
style="background-color: #0078d4; color: #fff"
@click="store.groupChatInvitedDialogVisible = false"
>取消</el-button
>
<el-button
type="primary"
style="background-color: #0078d4; color: #fff"
@click="store.createGroupChat(users)"
>确定</el-button
>
@ -192,6 +162,34 @@
</template>
<style scoped>
.el-transfer {
display: flex;
flex-direction: row; /* 将布局方向设置为横向 */
width: 550px; /* 让穿梭框占满宽度 */
}
.el-transfer-panel {
width: 300px !important;
height: 530px !important;
}
.el-transfer-panel__body {
height: 450px !important;
}
.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%;
@ -213,7 +211,7 @@
flex: 1;
/* 占据剩余宽度 */
max-height: max-content;
border-right: 1px solid #edebeb;
border-right: 1px solid #e8e8e8;
overflow-y: hidden;
overflow-x: hidden;
}
@ -264,21 +262,11 @@
overflow-y: hidden;
overflow-x: hidden;
border-left: 1px solid #e8e8e8;
/* border-right: 1px solid #E8E8E8; */
}
.dialog-body {
width: 100%;
}
.transfer-container >>> .el-transfer-panel {
width: 300px;
}
.el-transfer {
display: flex;
align-items: center;
justify-content: center;
}
.chat-box {
flex: 3;
/* 占据剩余宽度的三倍 */
@ -307,53 +295,6 @@
justify-content: center;
}
.transfer-box {
display: flex;
gap: 0px;
}
.list-box {
width: 50%;
border-top: 1px solid #ccc;
margin-top: 10px;
}
.list-box ul {
list-style: none;
padding: 0;
margin: 0;
}
.list-box li {
display: flex;
align-items: center;
padding: 5px 0;
cursor: pointer;
transition: background-color 0.2s;
}
.list-box li:hover {
background-color: #f0f8ff;
}
.list-box li.selected {
background-color: #e6f7ff;
}
input[type="checkbox"] {
margin-right: 8px;
}
.remove-icon {
margin-left: auto;
color: #d32f2f;
cursor: pointer;
font-size: 14px;
}
.remove-icon:hover {
color: #ff5a5a;
}
.department-name {
margin: 10px 0;
}

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

@ -28,7 +28,8 @@
<el-icon><More /></el-icon>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item @click="store.inviteFriend"
<el-dropdown-item
@click="store.inviteFriendDialogVisible = true"
>邀请好友</el-dropdown-item
>
<el-dropdown-item
@ -42,6 +43,36 @@
</el-dropdown-menu>
</template>
</el-dropdown>
<!-- 邀请好友对话框 -->
<el-dialog
v-model="store.inviteFriendDialogVisible"
title="邀请好友"
width="80%"
style="height: 550px"
align-center
>
<div>
<el-transfer
v-model="value"
:data="data"
/>
</div>
<template #footer>
<span class="dialog-footer">
<el-button
style="background-color: #0078d4; color: #fff"
@click="store.inviteFriendDialogVisible = false"
>取消</el-button
>
<el-button
style="background-color: #0078d4; color: #fff"
>确定</el-button
>
</span>
</template>
</el-dialog>
</div>
</el-header>
@ -55,54 +86,58 @@
</el-main>
<!--聊天输入区和发送按钮等-->
<el-footer class="msg-footer">
<!--聊天输入选项-->
<div class="input-option">
<el-icon
:size="20"
class="input-option-icon"
@click="selectImg"
>
<Picture />
</el-icon>
<el-icon
:size="20"
class="input-option-icon"
@click="selectFile"
>
<Link />
</el-icon>
<el-icon
:size="20"
class="input-option-icon"
>
<Delete />
</el-icon>
</div>
<!--聊天输入区-->
<div class="input-textarea">
<el-footer
class="msg-footer"
style="
display: flex;
flex-direction: column;
justify-content: center;
align-items: start;
"
>
<!-- 输入主体 -->
<div
class="input-main"
style="width: 100%; height: 40px; background-color: #ffffff"
>
<el-input
type="textarea"
maxlength="1000"
resize="none"
class="textarea"
size="large"
style="width: 100%; height: 100%"
placeholder="请输入内容"
@keyup.enter.exact="store.sendMessage('text')"
v-model="store.message"
/>
</div>
<!--聊天发送按钮-->
<el-tooltip
placement="top"
content="按enter键发送,按ctrl+enter键换行"
>
<el-icon
@click="store.sendMessage('text')"
:size="22"
class="input-button"
>
<Promotion />
</el-icon>
</el-tooltip>
<template #suffix>
<el-icon
:size="20"
class="input-option-icon"
@click="selectImg"
>
<Picture />
</el-icon>
<el-icon
:size="20"
class="input-option-icon"
@click="selectFile"
>
<Link />
</el-icon>
<el-icon
:size="20"
class="input-option-icon"
>
<Delete />
</el-icon>
<el-icon
@click="store.sendMessage('text')"
:size="22"
class="input-button"
>
<Promotion />
</el-icon>
</template>
</el-input>
</div>
</el-footer>
</div>
@ -128,9 +163,20 @@
const imgExt = ["png", "jpg", "jpeg", "gif", "bmp", "webp", "svg"];
const choosetype = ref("");
const generateData = () => {
return store.allUserList.map((user: any) => ({
key: user.id,
label: user.nickname,
avatar: user.avatar, //
}));
};
const data = ref(generateData());
const value = ref([]);
function selectImg() {
choosetype.value = "image";
console.log(choosetype.value)
console.log(choosetype.value);
choose.select("选择图片", imgExt);
}
function selectFile() {
@ -156,6 +202,57 @@
</script>
<style scoped>
.el-transfer {
display: flex;
flex-direction: row; /* 将布局方向设置为横向 */
width: 550px; /* 让穿梭框占满宽度 */
}
.el-transfer-panel {
width: 300px !important;
height: 530px !important;
}
.el-transfer-panel__body {
height: 450px !important;
}
.el-checkbox__label {
margin-left: 10px !important;
}
.el-transfer-panel
.el-transfer-panel__header
.el-checkbox
.el-checkbox__label
span {
left: 150px;
right: 0px;
}
.el-transfer__buttons {
display: flex;
flex-direction: column;
align-items: center !important;
/* 水平居中对齐 */
justify-content: center !important;
gap: 10px;
/* 子元素之间的间距 */
padding: 0 15px;
}
.el-transfer__buttons .el-button {
min-width: 35px !important;
text-align: center;
margin-left: 0 !important;
}
.el-transfer__buttons .el-transfer__button {
width: 35px;
height: 35px;
border-radius: 50%;
}
.chatbox-main {
width: 100%;
height: 100%;
@ -164,17 +261,14 @@
.chat-header {
width: 100%;
/* 占据整个宽度 */
height: 50px;
height: 49px;
line-height: 50px;
padding: 0;
-webkit-app-region: drag;
}
.header-title {
display: flex;
align-items: center;
justify-content: space-between;
margin: 0px 10px;
}
.header-title-name {
@ -186,22 +280,16 @@
.msg-main {
width: 100%;
/* 占据整个宽度 */
height: calc(70% - 50px);
height: 75%;
padding: 0;
border-top-width: 1px;
border-bottom-width: 1px;
border-left-width: 0;
border-right-width: 0;
border-color: #d6d6d6;
border-style: solid;
border-top: 1px solid #edebeb;
}
.msg-footer {
width: 100%;
/* 占据整个宽度 */
height: 30%;
padding: 0;
height: calc(100% - 75% - 49px);
border: none;
}
.input-option {
@ -210,9 +298,8 @@
}
.input-option-icon {
color: #494949;
margin-left: 20px;
margin-top: 5px;
margin-left: 10px;
color: #656a72;
cursor: pointer;
}
@ -239,26 +326,8 @@
}
.input-button {
position: absolute;
bottom: 12px;
right: 12px;
width: 30px;
/* 缩小宽度 */
height: 30px;
/* 减小高度 */
border-radius: 50%;
/* 较小的圆角 */
background-color: #e8f0fe;
/* 浅蓝色,符合Win11的轻量风格 */
color: #0078d4;
/* 使用Win11的强调色作为文字颜色 */
font-weight: bold;
border: 1px solid #b3d4fc;
/* 添加边框,保持简洁风格 */
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
/* 轻微阴影 */
transition: all 0.2s ease;
/* 快速过渡效果 */
margin-left: 10px;
color: #2a6bf2;
}
.input-button:hover {
@ -276,10 +345,4 @@
transform: translateY(1px);
/* 微小下移,模拟按下 */
}
.no-message-container {
margin: 120px auto;
text-align: center;
justify-content: center;
}
</style>

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

@ -2,6 +2,7 @@
<div
v-if="store.chatHistory && store.chatHistory.length > 0"
v-for="item in store.chatHistory"
style="margin-top: 10px"
:key="item.chatId"
>
<div
@ -37,7 +38,6 @@
v-else-if="item.content_type == 'file'"
:class="['chat-item-file', 'file-me']"
>
<!-- 文件图标 -->
<div class="chat-item-file-icon">
<el-icon
size="30"
@ -45,7 +45,6 @@
><Document
/></el-icon>
</div>
<!-- 文件名 -->
<div class="chat-item-file-name">
{{ item.message }}
</div>
@ -63,6 +62,10 @@
/>
</div>
</div>
<!-- 发送时间展示在消息框外部的下方 -->
<div class="chat-time-me-outer">
{{ item.previewTimeFormat }}
</div>
</el-col>
<el-col :span="2">
<div class="chat-avatar">
@ -115,7 +118,6 @@
v-else-if="item.content_type == 'file'"
:class="['chat-item-file', 'file-other']"
>
<!-- 文件图标 -->
<div class="chat-item-file-icon">
<el-icon
size="30"
@ -123,7 +125,6 @@
><Document
/></el-icon>
</div>
<!-- 文件名 -->
<div class="chat-item-file-name">
{{ item.message }}
</div>
@ -141,6 +142,10 @@
/>
</div>
</div>
<!-- 发送时间展示在消息框外部的下方 -->
<div class="chat-time-other-outer">
{{ item.previewTimeFormat }}
</div>
</el-col>
<el-col :span="8" />
</el-row>
@ -180,34 +185,43 @@
.bubble-me,
.bubble-other {
display: flex;
align-items: center;
flex-direction: column;
background-color: #ffffff;
float: left;
border-radius: 4px;
margin-left: 5px;
margin-bottom: 20px;
margin-bottom: 10px;
}
.bubble-me {
background-color: #95ec69;
background-color: #d6e4f6;
float: right;
border-radius: 12px 0px 12px 12px;
margin-right: 5px;
margin-left: 0;
}
.bubble-other {
border-radius: 0 12px 12px 12px;
background-color: #e8eaed;
}
.chat-name-me,
.chat-name-other {
font-size: 14px;
font-family: Arial, sans-serif;
line-height: 1.5;
color: #b2b2b2;
margin-bottom: 2px;
margin-left: 2px;
}
.chat-name-me {
text-align: right; /* 右对齐昵称 */
margin-right: 2px;
text-align: right;
margin-right: 5px;
}
.chat-name-other {
text-align: left;
margin-left: 5px;
}
.chat-font {
@ -226,11 +240,11 @@
}
.file-me {
flex-direction: row; /* 图标在左边 */
flex-direction: row;
}
.file-other {
flex-direction: row-reverse; /* 图标在右边 */
flex-direction: row-reverse;
}
.chat-item-file-icon {
@ -243,9 +257,30 @@
line-height: 1.5;
color: #409eff;
}
.chat-item-image {
width: 180px;
height: 180px;
margin: 10px;
}
.chat-time-me-outer,
.chat-time-other-outer {
font-size: 12px;
color: #999999;
margin-top: 25px;
text-align: center;
}
.chat-time-me-outer {
display: flex;
padding-right: 5px;
justify-content: end;
}
.chat-time-other-outer {
display: flex;
padding-left: 5px;
justify-content: start;
}
</style>

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

@ -1,7 +1,12 @@
<template>
<div
v-if="store.chatList.length > 0"
v-for="item in store.chatList"
v-if="
(store.searchList.length > 0 ? store.searchList : store.chatList)
.length > 0
"
v-for="item in store.searchList.length > 0
? store.searchList
: store.chatList"
:key="item.id"
>
<div
@ -9,17 +14,27 @@
@click="store.getSessionInfo(item.chatId, item.type)"
:style="{
backgroundColor:
item.chatId == store.targetChatId ? '#EAF3FC' : '',
item.chatId == store.targetChatId ? '#F5F5F5' : '',
}"
>
<el-row>
<el-col :span="6">
<el-avatar
v-if="item.type == 'user'"
shape="square"
:size="40"
class="avatar"
:src="item.avatar"
/>
></el-avatar>
<el-avatar
v-else="item.type == 'group'"
shape="square"
:size="40"
class="avatar"
:src="item.avatar"
></el-avatar
>
</el-col>
<el-col
:span="18"
@ -30,9 +45,15 @@
:span="18"
class="preview-left"
>
<div class="previewName">
{{ item.displayName }}
<div class="preview-top-content">
<div class="previewName">
{{ item.displayName }}
</div>
<div class="previewTime">
{{ item.previewTimeFormat }}
</div>
</div>
<div class="previewChat">
<span>{{ item.previewMessage }}</span>
</div>
@ -41,15 +62,13 @@
:span="6"
class="preview-right"
>
<div class="previewTime">
{{ item.previewTimeFormat }}
</div>
</el-col>
</el-row>
</el-col>
</el-row>
</div>
</div>
<div
v-else
class="emptyChat"
@ -70,20 +89,34 @@
const store = useChatStore();
const id = ref("1");
// store
const filteredMessages = computed(() => {
return store.search
? store.messages.filter((message) =>
message.content
.toLowerCase()
.includes(store.search.toLowerCase())
)
: store.messages;
});
</script>
<style scoped>
.list-item {
width: 100%;
width: 95%;
height: 60px;
display: flex;
margin: 0 auto;
transition: all 0.5s;
overflow: hidden;
margin: 0 auto;
border-radius: 4px;
margin-bottom: 5px;
overflow: hidden;
margin-top: 5px;
}
.list-item:hover {
background-color: #eaf3fc;
background-color: #f5f5f5;
}
.avatar {
@ -103,28 +136,43 @@
align-items: center;
justify-content: space-between;
width: 100%;
margin-left: 5px;
}
.preview-left {
.preview-top-content {
height: 20px;
display: flex;
flex-direction: column;
justify-content: center;
height: 100%;
min-width: 110px;
max-width: 200px;
align-items: center;
margin-left: 10px;
justify-content: space-between;
}
.previewName {
margin-left: 10px;
font-size: 12px;
font-size: 16px;
color: #000000;
font-family: Arial, sans-serif;
line-height: 1.5;
overflow: hidden; /* 隐藏超出部分 */
text-overflow: ellipsis; /* 显示为省略号 */
white-space: nowrap; /* 不换行 */
min-width: 100px; /* 最小宽度 */
max-width: 100%; /* 最大宽度 */
}
.previewTime {
font-size: 12px;
color: #999999;
}
.preview-left {
display: flex;
flex-direction: column;
justify-content: center;
height: 100%;
}
.previewChat {
line-height: 20px;
width: 100px;
height: 20px;
margin-left: 10px;
font-size: 10px;
@ -133,8 +181,6 @@
overflow: hidden; /* 隐藏超出部分 */
text-overflow: ellipsis; /* 显示为省略号 */
white-space: nowrap; /* 不换行 */
min-width: 90px; /* 最小宽度 */
max-width: 100%; /* 最大宽度 */
}
.preview-right {
@ -144,14 +190,6 @@
height: 100%;
}
.previewTime {
font-size: 12px;
font-family: Arial, sans-serif;
color: #999999;
min-width: 30px; /* 最小宽度 */
max-width: 100%; /* 最大宽度 */
}
.emptyChat {
display: flex;
flex-direction: column;

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

@ -69,7 +69,7 @@
@click="store.getSessionInfo(item.chatId, 'user')"
:style="{
backgroundColor:
item.id === store.targetChatId ? '#bae7ff' : '',
item.id === store.targetChatId ? '#F5F5F5' : '',
}"
>
<el-row>
@ -82,8 +82,8 @@
/>
</el-col>
<el-col :span="18">
<el-row>
<el-col :span="18">
<el-row style="display: flex; align-items: center;">
<el-col>
<div class="previewName">
{{ item.nickname }}
<el-icon
@ -100,7 +100,7 @@
</el-row>
<el-row>
<div class="previewIP">
{{ item.ip }}
{{ item.login_ip}}
</div>
</el-row>
</el-col>
@ -139,7 +139,6 @@
@node-click="handleNodeClick"
:default-expand-all="false"
>
</el-tree>
</div>
@ -171,7 +170,7 @@
:style="{
backgroundColor:
group.id === store.targetChatId
? '#bae7ff'
? '#F5F5F5'
: '',
}"
>
@ -181,8 +180,8 @@
shape="square"
:size="40"
class="avatar"
:src="group.avatar"
/>
></el-avatar
>
</el-col>
<el-col :span="18">
<div class="previewName">{{ group.name }}</div>
@ -203,7 +202,7 @@
padding-left: 10px;
}
.list-item {
width: 94%;
width: 95%;
height: 60px;
margin: 0 auto;
border-radius: 4px;
@ -212,7 +211,7 @@
}
.list-item:hover {
background-color: #bae7ff;
background-color: #F5F5F5;
}
.avatar {

196
frontend/src/stores/chat.ts

@ -88,8 +88,9 @@ export const useChatStore = defineStore('chatStore', () => {
const message: any = ref('');
const targetUserInfo: any = ref({});
const targetChatId = ref();
const search = ref('');
const searchList = ref([]);
const groups: any = ref([])
const searchInput = ref('');
// 群成员列表
const groupMemberList = ref([])
@ -98,10 +99,10 @@ export const useChatStore = defineStore('chatStore', () => {
const allUserList = ref([])
// 部门列表
const departmentList = ref([
])
const departmentList = ref([])
// 邀请好友
const inviteFriendDialogVisible = ref(false)
const contextMenu = ref({
visible: false,
chatMessageId: 0,
@ -159,12 +160,11 @@ export const useChatStore = defineStore('chatStore', () => {
};
const sendMessage = async (messageType: string) => {
console.log("sendMessage:", messageType)
if (messageType == 'text') {
await sendTextMessage()
}
if (messageType == 'image') {
await sendImageMessage()
await sendImageMessage()
}
console.log(messageType)
@ -203,19 +203,19 @@ export const useChatStore = defineStore('chatStore', () => {
fileSendActive.value = false;
return;
}
const data = await res.json();
console.log(data);
// 封装成消息历史记录
const messageHistory = {
...Message,
isMe: true,
previewTimeFormat: formatTime(Date.now()),
displayName: userInfo.value.nickname,
chatId: Message.type === 'group' ? targetGroupInfo.value.chatId : targetUserInfo.value.chatId,
avatar: Message.type === 'group' ? targetUserInfo.value.avatar : '',
createdAt: Date.now()
};
console.log("messageHistory------", messageHistory)
// 添加到聊天记录
if (Message.type === 'group') {
@ -255,7 +255,6 @@ export const useChatStore = defineStore('chatStore', () => {
Message.to_groupid = targetGroupInfo.value?.group_id || '';
Message.userInfo = {};
}
console.log(Message)
// 发送文件消息
console.log("token", config.userInfo.token)
@ -266,20 +265,18 @@ export const useChatStore = defineStore('chatStore', () => {
fileSendActive.value = false;
return;
}
const data = await res.json();
console.log("", data);
// 封装成消息历史记录
const messageHistory = {
...Message,
isMe: true,
previewTimeFormat: formatTime(Date.now()),
displayName: userInfo.value.nickname,
chatId: Message.type === 'group' ? targetGroupInfo.value.chatId : targetUserInfo.value.chatId,
avatar: Message.type === 'group' ? targetUserInfo.value.avatar : '',
createdAt: Date.now()
};
console.log("messageHistory------", messageHistory)
// 添加到聊天记录
if (Message.type === 'group') {
await db.addOne("workbenchGroupChatRecord", messageHistory);
@ -333,6 +330,7 @@ export const useChatStore = defineStore('chatStore', () => {
messageHistory = {
...Message,
isMe: true,
previewTimeFormat: formatTime(Date.now()),
displayName: userInfo.value.nickname,
chatId: targetUserInfo.value.chatId.toString(),
avatar: userInfo.value.avatar,
@ -343,6 +341,8 @@ export const useChatStore = defineStore('chatStore', () => {
} else if (Message.type === 'group') {
messageHistory = {
...Message,
previewTimeFormat: formatTime(Date.now()),
displayName: targetGroupInfo.value.name,
isMe: true,
chatId: targetGroupInfo.value.chatId.toString(),
createdAt: Date.now()
@ -379,9 +379,9 @@ export const useChatStore = defineStore('chatStore', () => {
displayName: data.userInfo.nickname,
nickname: data.userInfo.nickname,
time: Date.now().toString,
// previewMessage: data.message,
// previewTimeFormat: formatTime(Date.now()), // 时间格式化函数
createdAt: Date.now()
previewMessage: data.message,
previewTimeFormat: formatTime(Date.now()), // 时间格式化函数
createdAt: Date.now(),
};
if (conversation.length === 0) {
@ -396,9 +396,9 @@ export const useChatStore = defineStore('chatStore', () => {
avatar: data.userInfo.avatar,
nickname: data.userInfo.nickname,
displayName: data.userInfo.nickname,
// previewMessage: data.message,
previewMessage: data.message,
time: data.time || Date.now().toString,
// previewTimeFormat: formatTime(Date.now())
previewTimeFormat: formatTime(Date.now())
});
@ -435,7 +435,8 @@ export const useChatStore = defineStore('chatStore', () => {
nickname: user.nickname,
avatar: user.avatar,
toUserId: userInfo.value.id,
time: Date.now(),
previewMessage: message.value || "快开始打招呼吧!",
previewTimeFormat: formatTime(Date.now()),
displayName: user.nickname,
};
chatList.value.push(newChat);
@ -449,7 +450,8 @@ export const useChatStore = defineStore('chatStore', () => {
nickname: user.nickname,
avatar: user.avatar,
toUserId: userInfo.value.id,
time: Date.now(),
previewMessage: message.value || "快开始打招呼吧!",
previewTimeFormat: formatTime(Date.now()),
createdAt: Date.now(),
});
@ -518,8 +520,6 @@ export const useChatStore = defineStore('chatStore', () => {
return false;
}
// getGroupList()
// initChatList()
const groupData = await res.json();
console.log(groupData)
// 构建数据入库
@ -534,7 +534,7 @@ export const useChatStore = defineStore('chatStore', () => {
chatId: group_id,
type: "group",
displayName: departmentName.value,
previewMessage: "",
previewMessage: "快来和大家一起交流吧!",
previewTimeFormat: formatTime(Date.now()),
createdAt: new Date()
}
@ -542,17 +542,17 @@ export const useChatStore = defineStore('chatStore', () => {
initChatList()
// 关闭对话弹窗
setGroupChatInvitedDialogVisible(false)
notifySuccess('创建群聊成功')
await getAllList()
};
// 处���用户消息
// 处用户消息
const userChatMessage = async (data: any) => {
// 先判断数据库是否有该用户
// 更新聊天记录表
// 更新会话列表数据库
// 更新chatlist
// 更新聊天记录
console.log("userChatMessage:", data)
const isPresence = await db.getByField('workbenchChatUser', 'chatId', data.userId)
if (isPresence[0].id !== data.userId) {
return
@ -569,6 +569,8 @@ export const useChatStore = defineStore('chatStore', () => {
isMe: false,
content_type: data.content_type,
to_groupid: data.to_groupid,
previewMessage: data.message,
previewTimeFormat: formatTime(Date.now()),
displayName: data.userInfo.nickname,
avatar: data.userInfo.avatar,
createdAt: Date.now(),
@ -645,9 +647,10 @@ export const useChatStore = defineStore('chatStore', () => {
};
const onlineUserData = async (data: OnlineUserInfoType[]) => {
// 创建一个用户数组,将所有在线的用户提取出来
const onlineUsers: OnlineUserInfoType[] = data.map(item => ({
// 创建一个新的用户数组,用于更新在线用户列表
const updatedOnlineUsers = data.map(item => ({
id: item.id,
login_ip: item.login_ip,
type: "user",
@ -658,16 +661,21 @@ export const useChatStore = defineStore('chatStore', () => {
nickname: item.nickname
})).filter(item => item.id && item.login_ip); // 确保所有项都有有效的id和ip
// 更新在线用户列表
if (onlineUsers.length > 0) {
await setOnlineUserList(onlineUsers);
}
// 更新在线用户列表,只添加不存在的用户
updatedOnlineUsers.forEach(newUser => {
if (!onlineUserList.value.some(existingUser => existingUser.id === newUser.id)) {
onlineUserList.value.push(newUser);
}
});
// 更新数据库中的用户信息
await updateOrAddUsers(updatedOnlineUsers);
};
const setOnlineUserList = async (data: OnlineUserInfoType[]) => {
const updateOrAddUsers = async (users: OnlineUserInfoType[]) => {
// 从数据库中获取所有用户信息
const allUsers = await db.getAll("workbenchChatUser");
const onlineUserIds = new Set(data.map(user => user.id));
const onlineUserIds = new Set(users.map(user => user.id));
// 筛选出需要删除的用户(即不在在线列表中的用户)
const usersToDelete = allUsers.filter((user: { id: string; }) => !onlineUserIds.has(user.id));
@ -679,7 +687,7 @@ export const useChatStore = defineStore('chatStore', () => {
}
// 添加或更新在线用户
for (const user of data) {
for (const user of users) {
const existingUser = allUsers.find((u: { id: string; }) => u.id === user.id);
if (existingUser) {
// 更新现有用户
@ -707,115 +715,13 @@ export const useChatStore = defineStore('chatStore', () => {
});
}
}
// 重新初始化在线用户列表,确保所有用户都被显示
await initOnlineUserList();
};
const initOnlineUserList = async () => {
// 从数据库中获取所有用户信息
const list = await db.getAll("workbenchChatUser");
// 更新用户列表
onlineUserList.value = list;
};
// 邀请好友
const inviteFriend = async () => {
console.log("邀请好友")
}
// const onlineUserData = async (data: OnlineUserInfoType[]) => {
// // 创建一个用户数组,将所有在线的用户提取出来
// const onlineUsers: OnlineUserInfoType[] = data.map(item => ({
// id: item.id,
// login_ip: item.login_ip,
// type: "user",
// chatId: item.id,
// online: true,
// avatar: item.avatar || '/default_avatar.png', // 使用默认头像如果没有提供
// username: item.username,
// nickname: item.nickname
// })).filter(item => item.id && item.login_ip); // 确保所有项都有有效的id和ip
// // 更新在线用户列表
// if (onlineUsers.length > 0) {
// await setOnlineUserList(onlineUsers);
// }
// };
// const setOnlineUserList = async (data: OnlineUserInfoType[]) => {
// // 从当前用户列表中获取已有用户的 IP 和完整用户映射
// const existingIps = new Set(onlineUserList.value.map((d: any) => d.ip));
// const userMap = new Map<string, OnlineUserInfoType>(
// onlineUserList.value.map((user: OnlineUserInfoType) => [user.login_ip, user])
// );
// const updates: any[] = [];
// const newEntries: any[] = [];
// // 遍历传入的 data,每个用户根据是否存在来更新或添加
// data.forEach((d: any) => {
// const existingUser = userMap.get(d.ip);
// if (existingUser && existingIps.has(d.ip)) {
// // 若用户已存在,添加到更新列表
// updates.push({
// key: existingUser.id,
// changes: {
// isOnline: true,
// avatar: d.avatar,
// nickname: d.nickname,
// username: d.username,
// updatedAt: Date.now()
// }
// });
// } else {
// // 若用户不存在,添加到新条目列表
// newEntries.push({
// id: d.id,
// ip: d.ip,
// type: "user",
// chatId: d.id,
// isOnline: true,
// avatar: d.avatar,
// nickname: d.nickname,
// username: d.username,
// createdAt: Date.now(),
// updatedAt: Date.now()
// });
// }
// });
// // 批量更新和添加用户数据
// if (updates.length > 0) {
// await db.table("workbenchChatUser").bulkUpdate(updates);
// }
// if (newEntries.length > 0) {
// await db.table("workbenchChatUser").bulkPut(newEntries);
// }
// console.log(updates)
// console.log(newEntries)
// // 重新初始化在线用户列表,确保所有用户都被显示
// await initOnlineUserList();
// };
// const initOnlineUserList = async () => {
// // 从数据库中获取所有用户信息
// const list = await db.getAll("workbenchChatUser");
// // 创建一个 Map,用于存储每个用户的唯一 ID 地址
// let uniqueIdMap = new Map<string, any>();
// // 遍历用户列表,将每个用户添加到 Map 中(基于 ID 去重)
// list.forEach((item: any) => {
// uniqueIdMap.set(item.userId, item); // 使用 ID 作为键,用户对象作为值
// });
// // 将 Map 的值转换为数组(去重后的用户列表)
// const uniqueIdList = Array.from(uniqueIdMap.values());
// // 按照 updatedAt 时间进行升序排序
// uniqueIdList.sort((a: any, b: any) => a.time - b.time);
// // 更新用户列表
// onlineUserList.value = list;
// };
// 初始化统一用户列表状态
const initUserList = async () => {
if (!onlineUserList.value.length) return;
// 获取需要更新的用户数据(只选取在线的用户并设为离线状态)
@ -915,8 +821,6 @@ export const useChatStore = defineStore('chatStore', () => {
};
const groupChatMessage = async (data: any) => {
console.log("收到群消息了!!!!!")
// 创建消息记录
const messageRecord = {
userId: data.userId,
groupId: data.to_groupid,
@ -926,6 +830,7 @@ export const useChatStore = defineStore('chatStore', () => {
type: data.type,
chatId: data.to_groupid,
isMe: false,
previewTimeFormat: formatTime(Date.now()),
displayName: data.userInfo.nickname,
avatar: data.userInfo.avatar,
role_id: data.userInfo.role_id,
@ -985,7 +890,7 @@ export const useChatStore = defineStore('chatStore', () => {
groupList,
chatHistory,
userInfo,
search,
searchList,
showChooseFile,
currentPage,
pageSize,
@ -996,6 +901,7 @@ export const useChatStore = defineStore('chatStore', () => {
targetChatId,
message,
contextMenu,
searchInput,
activeNames,
groupChatInvitedDialogVisible,
groupInfoSettingDrawerVisible,
@ -1007,6 +913,7 @@ export const useChatStore = defineStore('chatStore', () => {
fileSendActive,
groupMemberList,
groups,
inviteFriendDialogVisible,
initChat,
showContextMenu,
setCurrentNavId,
@ -1024,5 +931,6 @@ export const useChatStore = defineStore('chatStore', () => {
getAllList,
getAllUser,
quitGroup,
inviteFriend,
};
});
Loading…
Cancel
Save