Browse Source

fix:合并冲突

master
prr 7 months ago
parent
commit
e395736202
  1. 4
      frontend/auto-imports.d.ts
  2. 153
      frontend/components.d.ts
  3. 142
      frontend/src/components/chat/Chat.vue
  4. 57
      frontend/src/components/chat/ChatBox.vue
  5. 151
      frontend/src/components/chat/ChatMessage.vue
  6. 124
      frontend/src/components/chat/ChatMsgList.vue
  7. 11
      frontend/src/components/chat/ChatUserList.vue
  8. 6
      frontend/src/components/chat/chatUserInfo.vue
  9. 638
      frontend/src/stores/chat.ts
  10. 42
      frontend/src/stores/db.ts
  11. 170
      frontend/src/stores/notify.ts
  12. 22
      frontend/src/stores/upgrade.ts
  13. 1
      frontend/src/system/index.ts
  14. 5
      frontend/src/util/sharePath.ts
  15. 6
      godo/files/os.go

4
frontend/auto-imports.d.ts

@ -3,9 +3,11 @@
// @ts-nocheck
// noinspection JSUnusedGlobalSymbols
// Generated by unplugin-auto-import
// biome-ignore lint: disable
export {}
declare global {
const EffectScope: typeof import('vue')['EffectScope']
const ElMessage: typeof import('element-plus/es')['ElMessage']
const computed: typeof import('vue')['computed']
const createApp: typeof import('vue')['createApp']
const customRef: typeof import('vue')['customRef']
@ -70,6 +72,6 @@ declare global {
// for type re-export
declare global {
// @ts-ignore
export type { Component, ComponentPublicInstance, ComputedRef, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, VNode, WritableComputedRef } from 'vue'
export type { Component, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue'
import('vue')
}

153
frontend/components.d.ts

@ -0,0 +1,153 @@
/* eslint-disable */
// @ts-nocheck
// Generated by unplugin-vue-components
// Read more: https://github.com/vuejs/core/pull/3399
export {}
/* prettier-ignore */
declare module 'vue' {
export interface GlobalComponents {
Ad: typeof import('./src/components/desktop/Ad.vue')['default']
AddApp: typeof import('./src/components/store/AddApp.vue')['default']
AppIcon: typeof import('./src/components/taskbar/AppIcon.vue')['default']
AppIconGroup: typeof import('./src/components/taskbar/AppIconGroup.vue')['default']
AppItem: typeof import('./src/components/store/AppItem.vue')['default']
Battery: typeof import('./src/components/taskbar/Battery.vue')['default']
BatteryPop: typeof import('./src/components/taskbar/BatteryPop.vue')['default']
Browser: typeof import('./src/components/builtin/Browser.vue')['default']
Calendar: typeof import('./src/components/builtin/Calendar.vue')['default']
Chat: typeof import('./src/components/chat/Chat.vue')['default']
ChatBox: typeof import('./src/components/chat/ChatBox.vue')['default']
ChatContent: typeof import('./src/components/localchat/ChatContent.vue')['default']
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']
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']
ChatNav: typeof import('./src/components/localchat/ChatNav.vue')['default']
ChatUserInfo: typeof import('./src/components/chat/chatUserInfo.vue')['default']
ChatUserList: typeof import('./src/components/chat/ChatUserList.vue')['default']
ChatUserSetting: typeof import('./src/components/chat/ChatUserSetting.vue')['default']
ChatWorkList: typeof import('./src/components/chat/ChatWorkList.vue')['default']
CloseButton: typeof import('./src/components/ui/CloseButton.vue')['default']
CloseDesktop: typeof import('./src/components/desktop/CloseDesktop.vue')['default']
ColorPicker: typeof import('./src/components/setting/ColorPicker.vue')['default']
Computer: typeof import('./src/components/computer/Computer.vue')['default']
ComputerNavBar: typeof import('./src/components/computer/ComputerNavBar.vue')['default']
ContextMenu: typeof import('./src/components/builtin/ContextMenu.vue')['default']
CreateUrl: typeof import('./src/components/builtin/CreateUrl.vue')['default']
CustomIcon: typeof import('./src/components/taskbar/CustomIcon.vue')['default']
DateNote: typeof import('./src/components/builtin/DateNote.vue')['default']
DateTime: typeof import('./src/components/taskbar/DateTime.vue')['default']
DateTimePop: typeof import('./src/components/taskbar/DateTimePop.vue')['default']
DeskItem: typeof import('./src/components/desktop/DeskItem.vue')['default']
Desktop: typeof import('./src/components/desktop/Desktop.vue')['default']
DesktopBackground: typeof import('./src/components/desktop/DesktopBackground.vue')['default']
DialogProcess: typeof import('./src/components/window/DialogProcess.vue')['default']
DialogTemp: typeof import('./src/components/window/DialogTemp.vue')['default']
EditFileName: typeof import('./src/components/builtin/EditFileName.vue')['default']
EditType: typeof import('./src/components/builtin/EditType.vue')['default']
ElAside: typeof import('element-plus/es')['ElAside']
ElAvatar: typeof import('element-plus/es')['ElAvatar']
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']
ElCol: typeof import('element-plus/es')['ElCol']
ElContainer: typeof import('element-plus/es')['ElContainer']
ElDialog: typeof import('element-plus/es')['ElDialog']
ElDrawer: typeof import('element-plus/es')['ElDrawer']
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']
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']
ElPagination: typeof import('element-plus/es')['ElPagination']
ElProgress: typeof import('element-plus/es')['ElProgress']
ElRow: typeof import('element-plus/es')['ElRow']
ElScrollbar: typeof import('element-plus/es')['ElScrollbar']
ElTooltip: typeof import('element-plus/es')['ElTooltip']
ElTransfer: typeof import('element-plus/es')['ElTransfer']
ElTree: typeof import('element-plus/es')['ElTree']
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']
FileIconIs: typeof import('./src/components/builtin/FileIconIs.vue')['default']
FileList: typeof import('./src/components/builtin/FileList.vue')['default']
FileProps: typeof import('./src/components/builtin/FileProps.vue')['default']
FileTree: typeof import('./src/components/builtin/FileTree.vue')['default']
FileViewer: typeof import('./src/components/builtin/FileViewer.vue')['default']
IframeFile: typeof import('./src/components/window/IframeFile.vue')['default']
ImageViewer: typeof import('./src/components/builtin/ImageViewer.vue')['default']
Install: typeof import('./src/components/install/Install.vue')['default']
InstallCompony: typeof import('./src/components/install/InstallCompony.vue')['default']
InstallMember: typeof import('./src/components/install/InstallMember.vue')['default']
InstallPerson: typeof import('./src/components/install/InstallPerson.vue')['default']
LocalChat: typeof import('./src/components/localchat/LocalChat.vue')['default']
LockDesktop: typeof import('./src/components/desktop/LockDesktop.vue')['default']
Magnet: typeof import('./src/components/taskbar/Magnet.vue')['default']
MarkDown: typeof import('./src/components/builtin/MarkDown.vue')['default']
MenuBar: typeof import('./src/components/window/MenuBar.vue')['default']
MenuList: typeof import('./src/components/taskbar/MenuList.vue')['default']
MessageCenterPop: typeof import('./src/components/taskbar/MessageCenterPop.vue')['default']
MessageIcon: typeof import('./src/components/taskbar/MessageIcon.vue')['default']
MusicStore: typeof import('./src/components/builtin/MusicStore.vue')['default']
MusicViewer: typeof import('./src/components/builtin/MusicViewer.vue')['default']
NetWork: typeof import('./src/components/taskbar/NetWork.vue')['default']
NetworkPop: typeof import('./src/components/taskbar/NetworkPop.vue')['default']
NotFound: typeof import('./src/components/window/NotFound.vue')['default']
Notice: typeof import('./src/components/desktop/Notice.vue')['default']
NotifyGroup: typeof import('./src/components/builtin/NotifyGroup.vue')['default']
OpeningDesktop: typeof import('./src/components/desktop/OpeningDesktop.vue')['default']
OpenWiteDialog: typeof import('./src/components/builtin/OpenWiteDialog.vue')['default']
OsImage: typeof import('./src/components/builtin/OsImage.vue')['default']
PdfViewer: typeof import('./src/components/builtin/PdfViewer.vue')['default']
PictureStore: typeof import('./src/components/builtin/PictureStore.vue')['default']
PlanTasks: typeof import('./src/components/oa/PlanTasks.vue')['default']
ProcessManager: typeof import('./src/components/setting/ProcessManager.vue')['default']
QuickLink: typeof import('./src/components/computer/QuickLink.vue')['default']
RectChosen: typeof import('./src/components/builtin/RectChosen.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
Screen: typeof import('./src/components/desktop/Screen.vue')['default']
ScreenContent: typeof import('./src/components/desktop/ScreenContent.vue')['default']
ScreenRecorder: typeof import('./src/components/taskbar/ScreenRecorder.vue')['default']
Screenshort: typeof import('./src/components/taskbar/Screenshort.vue')['default']
SetAccount: typeof import('./src/components/setting/SetAccount.vue')['default']
SetCustom: typeof import('./src/components/setting/SetCustom.vue')['default']
SetFilePwd: typeof import('./src/components/setting/SetFilePwd.vue')['default']
SetLang: typeof import('./src/components/setting/SetLang.vue')['default']
SetSystem: typeof import('./src/components/setting/SetSystem.vue')['default']
Setting: typeof import('./src/components/setting/Setting.vue')['default']
SetUpdate: typeof import('./src/components/setting/SetUpdate.vue')['default']
ShareFiles: typeof import('./src/components/oa/ShareFiles.vue')['default']
ShowNews: typeof import('./src/components/taskbar/ShowNews.vue')['default']
StartMenu: typeof import('./src/components/taskbar/StartMenu.vue')['default']
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']
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']
UrlBrowser: typeof import('./src/components/builtin/UrlBrowser.vue')['default']
Version: typeof import('./src/components/builtin/Version.vue')['default']
VideoViewer: typeof import('./src/components/builtin/VideoViewer.vue')['default']
WinButton: typeof import('./src/components/ui/WinButton.vue')['default']
WinCheckBox: typeof import('./src/components/ui/WinCheckBox.vue')['default']
WindowGroup: typeof import('./src/components/window/WindowGroup.vue')['default']
WindowInner: typeof import('./src/components/window/WindowInner.vue')['default']
WindowNode: typeof import('./src/components/window/WindowNode.vue')['default']
WindowTemplate: typeof import('./src/components/window/WindowTemplate.vue')['default']
WinInput: typeof import('./src/components/ui/WinInput.vue')['default']
WinLoading: typeof import('./src/components/ui/WinLoading.vue')['default']
WinLogo: typeof import('./src/components/ui/WinLogo.vue')['default']
WinProcess: typeof import('./src/components/ui/WinProcess.vue')['default']
WinSelect: typeof import('./src/components/ui/WinSelect.vue')['default']
WinUpButtonGroup: typeof import('./src/components/ui/WinUpButtonGroup.vue')['default']
}
}

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

@ -18,13 +18,27 @@
};
const data = ref(generateData());
const users = ref([]);
// users any[]
const users = ref<any[]>([]);
watchEffect(() => {
if (store.allUserList.length > 0) {
data.value = generateData();
}
});
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);
}
</script>
<template>
<el-container class="container">
@ -89,6 +103,7 @@
v-model="store.groupChatInvitedDialogVisible"
title="发起群聊"
width="80%"
align-center
>
<div class="dialog-body">
<!-- 添加输入部门名的输入框 -->
@ -96,22 +111,57 @@
v-model="store.departmentName"
placeholder="请输入群聊名称"
></el-input>
<el-transfer
v-model="users"
:data="data"
>
<!-- 义穿梭框 -->
<template #default="{ option }">
<div class="user-item">
<el-avatar
:size="20"
:src="option.avatar"
class="avatar"
/>
<span>{{ option.label }}</span>
<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>
</template>
</el-transfer>
<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>
</div>
<template #footer>
<span class="dialog-footer">
@ -200,19 +250,16 @@
.dialog-body {
width: 100%;
}
.transfer-container >>> .el-transfer-panel {
width: 300px;
}
.el-transfer {
display: flex;
align-items: center;
justify-content: center;
}
/* .el-transfer >>> .el-transfer-panel >>> .el-transfer__buttons {
width: 50px;
} */
/* .el-transfer__button {
width: 20px;;
} */
.chat-box {
flex: 3;
/* 占据剩余宽度的三倍 */
@ -240,4 +287,53 @@
font-size: 14px;
justify-content: center;
}
.transfer-box {
display: flex;
gap: 0;
}
.list-box {
width: 50%;
border: 1px solid #ccc;
border-radius: 5px;
padding: 10px;
box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.1);
}
.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;
}
</style>

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

@ -6,12 +6,42 @@
<!--聊天顶部区-->
<el-header class="chat-header">
<div class="header-title">
<span v-if="store.targetUserInfo.displayName">{{
store.targetUserInfo.displayName
}}</span>
<span v-else-if="store.targetGroupInfo.displayName">{{
store.targetGroupInfo.displayName
}}</span>
<span
v-if="store.targetUserInfo.displayName"
class="header-title-name"
>{{ store.targetUserInfo.displayName }}</span
>
<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
"
>
<!-- 设置 -->
<el-icon><More /></el-icon>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item @click="store.inviteFriend"
>邀请好友</el-dropdown-item
>
<el-dropdown-item
@click="
store.quitGroup(
store.targetGroupInfo.chatId
)
"
>退出群聊</el-dropdown-item
>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
</el-header>
@ -103,6 +133,7 @@
choose.select("选择图片", imgExt);
}
function selectFile() {
console.log("selectFile")
choosetype.value = "applyfile";
choose.select("选择文件", "*");
}
@ -115,6 +146,7 @@
if (paths.length > 0) {
store.sendInfo = paths;
choose.path = [];
console.log(choosetype.value);
store.sendMessage(choosetype.value);
}
},
@ -137,10 +169,17 @@
-webkit-app-region: drag;
}
header-title {
.header-title {
display: flex;
align-items: center;
justify-content: space-between;
margin: 0px 10px;
}
.header-title-name {
height: 50px;
line-height: 50px;
font-size: 20px;
text-align: left;
margin-left: 15px;
}
.msg-main {

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

@ -2,10 +2,10 @@
<div
v-if="store.chatHistory && store.chatHistory.length > 0"
v-for="item in store.chatHistory"
:key="item.time"
:key="item.chatId"
>
<div
v-if="item.userId == store.userInfo.id"
v-if="item.isMe"
class="chat-item"
>
<el-row>
@ -14,19 +14,54 @@
<el-row>
<el-col :span="24">
<div class="chat-name-me">
{{ item.userInfo.nickname }}
{{ store.userInfo.nickname }}
</div>
</el-col>
</el-row>
<div
class="bubble-me"
@contextmenu.prevent="
store.showContextMenu($event, item.userId)
store.showContextMenu($event, item.chatId)
"
>
<div class="chat-font">
<!-- 文本消息展示 -->
<div
v-if="item.content_type == 'text'"
class="chat-font"
>
{{ item.message }}
</div>
<!-- 文件消息展示 -->
<div
v-else-if="item.content_type == 'file'"
:class="['chat-item-file', 'file-me']"
>
<!-- 文件图标 -->
<div class="chat-item-file-icon">
<el-icon
size="30"
color="#303133"
><Document
/></el-icon>
</div>
<!-- 文件名 -->
<div class="chat-item-file-name">
{{ item.message }}
</div>
</div>
<!-- 图片消息展示 -->
<div
v-else-if="item.content_type == 'image'"
class="chat-item-image"
>
<el-image
fit="cover"
loading="lazy"
:src="item.message"
/>
</div>
</div>
</el-col>
<el-col :span="2">
@ -36,7 +71,7 @@
style="margin: 0; float: left"
:size="32"
class="userAvatar"
:src="item.userInfo.avatar"
:src="item.avatar"
/>
</div>
</el-col>
@ -54,7 +89,7 @@
style="margin: 0; float: right"
:size="32"
class="userAvatar"
:src="item.userInfo.avatar"
:src="item.avatar"
/>
</div>
</el-col>
@ -62,14 +97,49 @@
<el-row>
<el-col :span="24">
<div class="chat-name-other">
{{ item.userInfo.nickname }}
{{ item.displayName }}
</div>
</el-col>
</el-row>
<div class="bubble-other">
<div class="chat-font">
<!-- 文本消息展示 -->
<div
v-if="item.content_type == 'text'"
class="chat-font"
>
{{ item.message }}
</div>
<!-- 文件消息展示 -->
<div
v-else-if="item.content_type == 'file'"
:class="['chat-item-file', 'file-other']"
>
<!-- 文件图标 -->
<div class="chat-item-file-icon">
<el-icon
size="30"
color="#303133"
><Document
/></el-icon>
</div>
<!-- 文件名 -->
<div class="chat-item-file-name">
{{ item.message }}
</div>
</div>
<!-- 图片消息展示 -->
<div
v-else-if="item.content_type == 'image'"
class="chat-item-image"
>
<el-image
fit="cover"
loading="lazy"
:src="item.message"
/>
</div>
</div>
</el-col>
<el-col :span="8" />
@ -107,22 +177,22 @@
</script>
<style scoped>
.bubble-me {
background-color: #95ec69;
float: right;
border-radius: 4px;
margin-right: 5px;
margin-top: 5px;
padding: 5px;
}
.bubble-me,
.bubble-other {
display: flex;
align-items: center;
background-color: #ffffff;
float: left;
border-radius: 4px;
margin-left: 5px;
margin-top: 5px;
padding: 5px;
margin-bottom: 20px;
}
.bubble-me {
background-color: #95ec69;
float: right;
margin-right: 5px;
margin-left: 0;
}
.chat-name-me,
@ -131,12 +201,51 @@
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;
}
.chat-font {
margin: 8px;
font-size: 15px;
font-family: Arial, sans-serif;
line-height: 1.5;
margin: 10px;
}
.chat-item-file {
border-radius: 4px;
display: flex;
align-items: center;
justify-content: space-between;
margin: 10px;
}
.file-me {
flex-direction: row; /* 图标在左边 */
}
.file-other {
flex-direction: row-reverse; /* 图标在右边 */
}
.chat-item-file-icon {
width: 40px;
}
.chat-item-file-name {
font-size: 14px;
font-family: Arial, sans-serif;
line-height: 1.5;
color: #409eff;
}
.chat-item-image {
width: 180px;
height: 180px;
margin: 10px;
}
</style>

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

@ -1,55 +1,75 @@
<template>
<div v-if="store.chatList.length > 0" v-for="item in store.chatList" :key="item.id">
<div
class="list-item"
@click="store.changeChatList(item.chatId,item.type)"
:style="{
backgroundColor: item.userId == store.targetChatId ? '#bae7ff' : '',
}"
>
<el-row>
<el-col :span="6">
<el-avatar
shape="square"
:size="40"
class="avatar"
:src="item.avatar"
/>
</el-col>
<el-col :span="18" class="preview">
<el-row class="preview-content">
<el-col :span="18" class="preview-left">
<div class="previewName">{{ item.displayName }}</div>
<div class="previewChat">
<span>{{ item.previewMessage }}</span>
</div>
</el-col>
<el-col :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">
<el-icon :size="60" class="chat-icon">
<ChatSquare />
</el-icon>
<p class="empty-message">暂无数据</p>
</div>
<div
v-if="store.chatList.length > 0"
v-for="item in store.chatList"
:key="item.id"
>
<div
class="list-item"
@click="store.getSessionInfo(item.chatId, item.type)"
:style="{
backgroundColor:
item.chatId == store.targetChatId ? '#bae7ff' : '',
}"
>
<el-row>
<el-col :span="6">
<el-avatar
shape="square"
:size="40"
class="avatar"
:src="item.avatar"
/>
</el-col>
<el-col
:span="18"
class="preview"
>
<el-row class="preview-content">
<el-col
:span="18"
class="preview-left"
>
<div class="previewName">
{{ item.displayName }}
</div>
<div class="previewChat">
<span>{{ item.previewMessage }}</span>
</div>
</el-col>
<el-col
: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"
>
<el-icon
:size="60"
class="chat-icon"
>
<ChatSquare />
</el-icon>
<p class="empty-message">暂无数据</p>
</div>
</template>
<script setup>
import { useChatStore } from "@/stores/chat";
import { ref } from "vue";
const store = useChatStore();
const id = ref("1");
import { useChatStore } from "@/stores/chat";
import { ref } from "vue";
const store = useChatStore();
const id = ref("1");
</script>
<style scoped>
@ -57,10 +77,10 @@ const id = ref("1");
width: 94%;
height: 60px;
display: flex;
margin: 0 auto;
border-radius: 4px;
transition: all 0.5s;
margin-top: 5px;
margin: 0 auto;
border-radius: 4px;
transition: all 0.5s;
margin-top: 5px;
}
.list-item:hover {
@ -106,7 +126,7 @@ const id = ref("1");
}
.previewChat {
height: 20px;
height: 20px;
margin-left: 10px;
font-size: 10px;
font-family: Arial, sans-serif;

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

@ -49,9 +49,9 @@
<el-collapse-item name="1">
<template #title>
<span
v-if="store.userList.length > 0"
v-if="store.onlineUserList.length > 0"
class="title"
>在线{{ store.userList.length }}</span
>在线{{ store.onlineUserList.length }}</span
>
<span
v-else
@ -59,15 +59,14 @@
>在线</span
>
</template>
<div v-if="store.userList.length > 0">
<div v-if="store.onlineUserList.length > 0">
<div
v-for="item in store.userList"
v-for="item in store.onlineUserList"
:key="item.id"
>
<div
class="list-item"
@click="store.changeChatList(item.id,'user')"
@click="store.getSessionInfo(item.chatId, 'user')"
:style="{
backgroundColor:
item.id === store.targetChatId ? '#bae7ff' : '',

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

@ -16,7 +16,7 @@
class="user-info-container"
>
<div class="user-details">
<p>昵称: {{ store.targetUserInfo.nickname }}</p>
<p>昵称: {{ store.targetUserInfo.displayName }}</p>
<p>邮箱: {{ store.targetUserInfo.email }}</p>
<p>电话: {{ store.targetUserInfo.phone }}</p>
<p>描述: {{ store.targetUserInfo.desc }}</p>
@ -37,9 +37,9 @@
const store = useChatStore();
const sendMessage = (userId:number) => {
const sendMessage = (chatId: string) => {
store.currentNavId = 0;
store.changeChatListAndGetChatHistory(userId)
store.addChatListAndGetChatHistory(chatId);
};
</script>

638
frontend/src/stores/chat.ts

File diff suppressed because it is too large

42
frontend/src/stores/db.ts

@ -1,35 +1,37 @@
import Dexie from 'dexie';
export type ChatTable = 'chatuser' | 'chatmsg' | 'chatmessage' | 'groupmessage' | 'chatRecord' | 'workbenchusers' | 'conversationList' | 'group' | 'groupMembers' | 'groupChatList' | 'groupChatRecord';
export type ChatTable = 'chatuser' | 'chatmsg' | 'workbenchChatRecord' | 'workbenchChatUser' | 'workbenchSessionList' | 'groupSessionList' | 'workbenchGroupChatRecord' | 'workbenchGroupUserList';
export const dbInit: any = new Dexie('GodoOSDatabase');
dbInit.version(1).stores({
// 用户列表
workbenchusers: '++id,ip,userName,avatar,mobile,phone,nickName,isOnline,updatedAt,createdAt',
// 聊天记录
chatRecord: '++id,toUserId,messages,messageType,time,createdAt,userInfo',
workbenchChatUser: '++id,ip,userName,chatId,avatar,mobile,phone,nickName,isOnline,updatedAt,createdAt',
// 会话列表
conversationList: '++id,avatar,chatId,username,nickname,userId,toUserId,previewMessage,messages,time,createdAt',
workbenchSessionList: '++id,avatar,chatId,username,nickname,userId,toUserId,previewMessage,messages,time,createdAt',
// 聊天记录
workbenchChatRecord: '++id,toUserId,messages,messageType,time,createdAt,userInfo',
// 群组会话列表
groupSessionList: '++id,groupId,name,message,previewMessage,avatar,createdAt',
// 群组聊天记录
workbenchGroupChatRecord: '++id,chatId,userId,to_groupid,messageType,userInfo,message,time,type,createdAt',
// 群用户列表
workbenchGroupUserList: '++id, group_id, createdAt, userIdArray',
// 用户列表
chatuser: '++id,ip,hostname,userName,avatar,mobile,nickName,isOnline,updatedAt,createdAt',
chatmsg: '++id,toUserId,targetIp,senderInfo,reciperInfo,previewMessage,content,type,status,isRead,isMe,readAt,createdAt',
chatmessage: '++id,userId,toUserId,senderInfo,isMe,isRead,content,type,readAt,createdAt',
groupChatRecord: '++id,userId,groupId,messageType,userInfo,message,time,type,createdAt',
// 群组表
group: '++id,avatar,name,groupId,creator,createdAt',
// 群成员表
groupMembers: '++id,userId,groupTableId,groupId,createdAt',
// 群会话列表
groupChatList: '++id,groupId,name,message,previewMessage,avatar,createdAt',
}).upgrade((tx: {
conversationList: any;
group: any;
chatRecord: { addIndex: (arg0: string, arg1: (obj: { toUserId: any; }) => any) => void; };
workbenchSessionList: any;
workbenchChatUser: any;
workbenchGroupChatRecord: any;
workbenchGroupUserList: any;
workbenchChatRecord: { addIndex: (arg0: string, arg1: (obj: { toUserId: any; }) => any) => void; };
}) => {
// 手动添加索引
tx.conversationList.addIndex('userId', (obj: { userId: any; }) => obj.userId);
tx.chatRecord.addIndex('toUserId', (obj: { toUserId: any; }) => obj.toUserId);
tx.group.addIndex('groupId', (obj: { groupId: any }) => obj.groupId); // 添加索引: 群主 ID
tx.workbenchSessionList.addIndex('userId', (obj: { userId: any; }) => obj.userId);
tx.workbenchChatRecord.addIndex('toUserId', (obj: { toUserId: any; }) => obj.toUserId);
tx.workbenchChatUser.addIndex('chatId', (obj: { chatId: any; }) => obj.chatId);
tx.workbenchGroupChatRecord.addIndex('chatId', (obj: { chatId: any; }) => obj.chatId);
tx.workbenchGroupUserList.addIndex('group_id', (obj: { group_id: any; }) => obj.group_id);
});
export const db = {

170
frontend/src/stores/notify.ts

@ -1,97 +1,97 @@
import { fetchGet, getSystemConfig } from "@/system/config";
import { defineStore } from "pinia";
import { ref } from "vue";
import { getSystemConfig, fetchGet } from "@/system/config";
import { useUpgradeStore } from "./upgrade";
export const useNotifyStore = defineStore('notifyStore', () => {
const config = getSystemConfig();
const upgradeStore = useUpgradeStore()
const notifyList: any = ref([]);
const readList: any = ref([])
const showNews = ref(false)
const newsDetail = ref({})
const page = ref({
current: 1,
size: 10,
total: 0,
pages: 0,
})
async function getList() {
if (config.userType == 'person') {
return
}
const complition = await fetchGet(config.userInfo.url + '/news/list?page=' + page.value.current + '&limit=' + page.value.size)
if (!complition.ok) {
return;
}
const data = await complition.json();
if (data.success) {
page.value.total = data.data.total;
notifyList.value = data.data.list;
page.value.pages = Math.ceil(page.value.total / page.value.size)
checkNotify()
}
const config = getSystemConfig();
const upgradeStore = useUpgradeStore()
const notifyList: any = ref([]);
const readList: any = ref([])
const showNews = ref(false)
const newsDetail = ref({})
const page = ref({
current: 1,
size: 10,
total: 0,
pages: 0,
})
async function getList() {
if (config.userType == 'person') {
return
}
const checkNotify = ()=> {
const list = notifyList.value.filter((item: any) => {
return readList.value.indexOf(item.id) < 0
})
if(list.length < 1)return;
const centerList = list.filter((item: any) => {
return item.position == 'center'
})
const bottomList = list.filter((item: any) => {
return item.position == 'bottom'
})
if(centerList.length > 0){
upgradeStore.hasAd = true
upgradeStore.adList = [...centerList, ...upgradeStore.adList]
}
if(bottomList.length > 0){
upgradeStore.hasNotice = true
upgradeStore.noticeList = [...bottomList, ...upgradeStore.noticeList]
}
const complition = await fetchGet(config.userInfo.url + '/news/list?page=' + page.value.current + '&limit=' + page.value.size)
if (!complition.ok) {
return;
}
const addRead = (id: number) => {
if (readList.value.indexOf(id) < 0) {
readList.value.push(id)
}
const data = await complition.json();
if (data.success) {
page.value.total = data.data.total;
notifyList.value = data.data.list;
page.value.pages = Math.ceil(page.value.total / page.value.size)
checkNotify()
}
const pageClick = async (pageNum: any) => {
//console.log(pageNum)
page.value.current = pageNum
await getList()
}
const checkNotify = () => {
const list = notifyList.value.filter((item: any) => {
return readList.value.indexOf(item.id) < 0
})
if (list.length < 1) return;
const centerList = list.filter((item: any) => {
return item.position == 'center'
})
const bottomList = list.filter((item: any) => {
return item.position == 'bottom'
})
if (centerList.length > 0) {
upgradeStore.hasAd = true
upgradeStore.adList = [...centerList, ...upgradeStore.adList]
}
const viewContent = (item :any) =>{
const timestamp = item.add_time * 1000; // 将 10 位时间戳转换为 13 位
const date = new Date(timestamp);
item.showtime = date.toLocaleString();
newsDetail.value = item
addRead(item.id)
showNews.value = true
if (bottomList.length > 0) {
upgradeStore.hasNotice = true
upgradeStore.noticeList = [...bottomList, ...upgradeStore.noticeList]
}
return {
notifyList,
showNews,
newsDetail,
getList,
addRead,
page,
pageClick,
readList,
viewContent
}
const addRead = (id: number) => {
if (readList.value.indexOf(id) < 0) {
readList.value.push(id)
}
}
const pageClick = async (pageNum: any) => {
//console.log(pageNum)
page.value.current = pageNum
await getList()
}
const viewContent = (item: any) => {
const timestamp = item.add_time * 1000; // 将 10 位时间戳转换为 13 位
const date = new Date(timestamp);
item.showtime = date.toLocaleString();
newsDetail.value = item
addRead(item.id)
showNews.value = true
}
return {
notifyList,
showNews,
newsDetail,
getList,
addRead,
page,
pageClick,
readList,
viewContent
}
}, {
persist: {
enabled: true,
strategies: [
{
storage: localStorage,
paths: [
"readList",
]
}, // name 字段用localstorage存储
],
}
persist: {
enabled: true,
strategies: [
{
storage: localStorage,
paths: [
"readList",
]
}, // name 字段用localstorage存储
],
}
})

22
frontend/src/stores/upgrade.ts

@ -68,22 +68,9 @@ export const useUpgradeStore = defineStore('upgradeStore', () => {
};
}
// 获取用户聊天消息
function userChatMessage() {
const url = getUrl('/chat/message', false)
const source = new EventSource(url);
source.onmessage = function (event) {
const data = JSON.parse(event.data);
handleMessage(data);
};
source.onerror = function (event) {
console.error('EventSource error:', event);
};
}
async function handleMessage(message: any) {
// console.log(message)
switch (message.type) {
case 'update':
checkUpdate(message.data)
@ -92,7 +79,7 @@ export const useUpgradeStore = defineStore('upgradeStore', () => {
localChatStore.handlerMessage(message.data)
break;
case 'online':
chatChatStore.handleUserData(message.data)
chatChatStore.onlineUserData(message.data)
break;
case 'user':
chatChatStore.userChatMessage(message.data)
@ -105,7 +92,7 @@ export const useUpgradeStore = defineStore('upgradeStore', () => {
}
}
async function checkUpdate(res: any) {
//console.log(res)
console.log(res)
if (!res) return
const config = getSystemConfig();
if (!config.account.ad) return;
@ -142,7 +129,6 @@ export const useUpgradeStore = defineStore('upgradeStore', () => {
list.forEach((item: any) => {
if (item.img && item.img.indexOf('http') == -1) {
item.img = `https://godoos.com${item.img}`
}
});
return list
@ -202,7 +188,7 @@ export const useUpgradeStore = defineStore('upgradeStore', () => {
checkUpdate,
systemMessage,
onlineMessage,
userChatMessage,
// userChatMessage,
update
}
})

1
frontend/src/system/index.ts

@ -118,6 +118,7 @@ export class System {
// const messageStore = useMessageStore();
// messageStore.systemMessage()
upgradeStore.onlineMessage();
// upgradeStore.userChatMessage();
}, 3000);
}
setTimeout(() => {

5
frontend/src/util/sharePath.ts

@ -25,3 +25,8 @@ export function turnFilePath(file: OsFileWithoutContent) : string {
const arr = file.path.split('/')
return `/${arr[1]}/${arr[2]}/${arr[arr.length - 1]}`
}
//返回是我的分享还是接收分享的文件路径
export function getFileRootPath(path: string) : string {
const rootPath = path.indexOf('/F/myshare') === 0 ? '/F/myshare' : '/F/othershare'
return rootPath
}

6
godo/files/os.go

@ -364,7 +364,11 @@ func IsHavePwd(pwd string) bool {
func GetSalt(r *http.Request) (string, error) {
data, ishas := libs.GetConfig("salt")
if ishas {
return data.(string), nil
// 断言成功则返回
if salt, ok := data.(string); ok {
return salt, nil
}
return "", fmt.Errorf("类型断言失败,期望类型为 string")
}
salt := r.Header.Get("salt")
if salt != "" {

Loading…
Cancel
Save