koishi-plugin-chat-patch 3.0.2 → 5.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/client/vue/index.vue +60 -634
- package/client/web/dist/assets/AppIcon-DIku6-h0.png +0 -0
- package/client/web/dist/assets/AppIconDark-BYymN71t.png +0 -0
- package/client/web/dist/assets/CapacitorAppIcon-CSZwNhvi.png +0 -0
- package/client/web/dist/assets/CapacitorAppIconDark-BMvMOzsg.png +0 -0
- package/client/web/dist/assets/Chat-CTdyeaX2.css +1 -0
- package/client/web/dist/assets/Chat-DkZvD7d8.js +28 -0
- package/client/web/dist/assets/MealHungryPan-Beos3lRI.js +1 -0
- package/client/web/dist/assets/MsgBody-aZ4_aija.js +1 -0
- package/client/web/dist/assets/MsgBody.vue_vue_type_script_setup_true_lang-CPZjRLp6.js +67 -0
- package/client/web/dist/assets/NapcatAppIcon-C7VOAkW9.png +0 -0
- package/client/web/dist/assets/Vue-lEhHouxB.png +0 -0
- package/client/web/dist/assets/append_darwin-CqBNU62J.css +1 -0
- package/client/web/dist/assets/append_full_vibrancy-j0D2LM9m.css +1 -0
- package/client/web/dist/assets/append_linux-uhxeBqeC.css +1 -0
- package/client/web/dist/assets/append_linux_vibrancy-COiZq8MS.css +1 -0
- package/client/web/dist/assets/append_napcat-DV8CAyAd.css +1 -0
- package/client/web/dist/assets/append_new-407Lo5_V.css +1 -0
- package/client/web/dist/assets/append_vibrancy-6FHctkOY.css +1 -0
- package/client/web/dist/assets/append_win32-srmD-78y.css +1 -0
- package/client/web/dist/assets/cakes-CxTVKdnK.svg +189 -0
- package/client/web/dist/assets/core-DhEqZVGG.js +1 -0
- package/client/web/dist/assets/event-Cadp6m6J.js +1 -0
- package/client/web/dist/assets/index-CPTXiemn.js +4045 -0
- package/client/web/dist/assets/index-Cg_esKB0.js +1 -0
- package/client/web/dist/assets/index-Cln012sP.css +1 -0
- package/client/web/dist/assets//346/226/271/346/255/243/345/226/265/345/221/234/344/275/223-CJKIKBn3.ttf +0 -0
- package/client/web/dist/assets//346/234/237/345/276/205-C90_OY60.png +0 -0
- package/client/web/dist/bcui/css/color-dark.css +35 -0
- package/client/web/dist/bcui/css/color-light.css +35 -0
- package/client/web/dist/bcui/css/style.css +468 -0
- package/client/web/dist/bcui/js/main.js +193 -0
- package/client/web/dist/css/append-dark.css +13 -0
- package/client/web/dist/css/append-light.css +13 -0
- package/client/web/dist/css/append-napcat-dark.css +14 -0
- package/client/web/dist/css/append-napcat-light.css +14 -0
- package/client/web/dist/favicon.ico +0 -0
- package/client/web/dist/img/icons/icon-for-fuck-apple.png +0 -0
- package/client/web/dist/img/icons/icon-maskable.png +0 -0
- package/client/web/dist/img/icons/icon.png +0 -0
- package/client/web/dist/img/icons/icon.svg +10 -0
- package/client/web/dist/index.html +22 -0
- package/client/web/dist/robots.txt +2 -0
- package/client/web/dist/sw.js +58 -0
- package/client/web/env.d.ts +56 -0
- package/client/web/index.html +21 -0
- package/client/web/package-lock.json +3419 -0
- package/client/web/package.json +62 -0
- package/client/web/public/bcui/css/color-dark.css +35 -0
- package/client/web/public/bcui/css/color-light.css +35 -0
- package/client/web/public/bcui/css/style.css +468 -0
- package/client/web/public/bcui/js/main.js +193 -0
- package/client/web/public/css/append-dark.css +13 -0
- package/client/web/public/css/append-light.css +13 -0
- package/client/web/public/css/append-napcat-dark.css +14 -0
- package/client/web/public/css/append-napcat-light.css +14 -0
- package/client/web/public/favicon.ico +0 -0
- package/client/web/public/img/icons/icon-for-fuck-apple.png +0 -0
- package/client/web/public/img/icons/icon-maskable.png +0 -0
- package/client/web/public/img/icons/icon.png +0 -0
- package/client/web/public/img/icons/icon.svg +10 -0
- package/client/web/public/robots.txt +2 -0
- package/client/web/public/sw.js +58 -0
- package/client/web/scripts/copy-bcui.mjs +17 -0
- package/client/web/src/App.vue +1127 -0
- package/client/web/src/assets/css/append/append_darwin.css +68 -0
- package/client/web/src/assets/css/append/append_full_vibrancy.css +290 -0
- package/client/web/src/assets/css/append/append_linux.css +125 -0
- package/client/web/src/assets/css/append/append_linux_vibrancy.css +22 -0
- package/client/web/src/assets/css/append/append_napcat.css +35 -0
- package/client/web/src/assets/css/append/append_new.css +843 -0
- package/client/web/src/assets/css/append/append_vibrancy.css +113 -0
- package/client/web/src/assets/css/append/append_win32.css +109 -0
- package/client/web/src/assets/css/append/mobile/append_mobile_horizontal.css +349 -0
- package/client/web/src/assets/css/append/mobile/append_mobile_vertical.css +96 -0
- package/client/web/src/assets/css/chat.css +2104 -0
- package/client/web/src/assets/css/msg.css +941 -0
- package/client/web/src/assets/css/options.css +811 -0
- package/client/web/src/assets/css/sys_notice.css +62 -0
- package/client/web/src/assets/css/view.css +2105 -0
- package/client/web/src/assets/font//346/226/271/346/255/243/345/226/265/345/221/234/344/275/223.ttf +0 -0
- package/client/web/src/assets/img/Vue.png +0 -0
- package/client/web/src/assets/img/cakes.svg +189 -0
- package/client/web/src/assets/img/icons/AppIcon.png +0 -0
- package/client/web/src/assets/img/icons/AppIconDark.png +0 -0
- package/client/web/src/assets/img/icons/CapacitorAppIcon.png +0 -0
- package/client/web/src/assets/img/icons/CapacitorAppIconDark.png +0 -0
- package/client/web/src/assets/img/icons/NapcatAppIcon.png +0 -0
- package/client/web/src/assets/img/icons/NapcatAppIconDark.png +0 -0
- package/client/web/src/assets/img/icons/exit.png +0 -0
- package/client/web/src/assets/img/icons/pen.png +0 -0
- package/client/web/src/assets/img/stars.svg +89 -0
- package/client/web/src/assets/img//346/234/237/345/276/205.png +0 -0
- package/client/web/src/assets/l10n/_About I10n.md +35 -0
- package/client/web/src/assets/l10n/_l10nconfig.json +7 -0
- package/client/web/src/assets/l10n/zh-CN.po +1941 -0
- package/client/web/src/assets/logo.png +0 -0
- package/client/web/src/assets/pathMap/LLOneBot.yaml +2 -0
- package/client/web/src/assets/pathMap/Lagrange.OneBot.yaml +180 -0
- package/client/web/src/assets/pathMap/NapCat.Onebot.yaml +224 -0
- package/client/web/src/assets/pathMap/SnowLuma.yaml +80 -0
- package/client/web/src/assets/qed.txt +1 -0
- package/client/web/src/components/AboutPan.vue +197 -0
- package/client/web/src/components/BulletinBody.vue +129 -0
- package/client/web/src/components/DepPan.vue +310 -0
- package/client/web/src/components/EmojiFace.vue +32 -0
- package/client/web/src/components/FacePan.vue +461 -0
- package/client/web/src/components/FileBody.vue +129 -0
- package/client/web/src/components/FileManager.vue +532 -0
- package/client/web/src/components/FriendBody.vue +54 -0
- package/client/web/src/components/GlobalSessionSearchBar.vue +301 -0
- package/client/web/src/components/History.vue +49 -0
- package/client/web/src/components/LazyLottie.vue +82 -0
- package/client/web/src/components/MergePan.vue +127 -0
- package/client/web/src/components/MsgBody.vue +1478 -0
- package/client/web/src/components/MusicPlayer.vue +549 -0
- package/client/web/src/components/NoticeBody.vue +168 -0
- package/client/web/src/components/QzonePermissionPan.vue +150 -0
- package/client/web/src/components/RawMsgRenderPreviewPan.vue +190 -0
- package/client/web/src/components/ThemeColorPickerPan.vue +420 -0
- package/client/web/src/components/TinySessionBody.vue +51 -0
- package/client/web/src/components/UmamiInfoPan.vue +1549 -0
- package/client/web/src/components/UpdatePan.vue +263 -0
- package/client/web/src/components/ViewerCom.vue +1360 -0
- package/client/web/src/components/VoiceMsg.vue +626 -0
- package/client/web/src/components/WelPan.vue +894 -0
- package/client/web/src/components/msg-body/.gitkeep +0 -0
- package/client/web/src/components/msg-component/JsonSegComp.vue +138 -0
- package/client/web/src/components/msg-component/XmlSegComp.vue +132 -0
- package/client/web/src/components/msg-component/jsonComp/AutoReply.vue +97 -0
- package/client/web/src/components/msg-component/jsonComp/Contact.lua.vue +129 -0
- package/client/web/src/components/msg-component/jsonComp/Feed.lua.vue +94 -0
- package/client/web/src/components/msg-component/jsonComp/Forum.vue +84 -0
- package/client/web/src/components/msg-component/jsonComp/Mannounce.vue +111 -0
- package/client/web/src/components/msg-component/jsonComp/Map.vue +52 -0
- package/client/web/src/components/msg-component/jsonComp/Miniapp.lua.vue +53 -0
- package/client/web/src/components/msg-component/jsonComp/Miniapp.vue +69 -0
- package/client/web/src/components/msg-component/jsonComp/Music.lua.vue +135 -0
- package/client/web/src/components/msg-component/jsonComp/Tuwen.lua.vue +56 -0
- package/client/web/src/components/notice-component/.gitkeep +0 -0
- package/client/web/src/components/notice-component/MealHungryPan.vue +119 -0
- package/client/web/src/components/tooltip/CustomFaceTooltip.vue +22 -0
- package/client/web/src/components/tooltip/Tooltip.vue +50 -0
- package/client/web/src/components/tooltip/Tooltips.vue +14 -0
- package/client/web/src/components/tooltip/UserInfoTooltip.vue +69 -0
- package/client/web/src/function/base.ts +238 -0
- package/client/web/src/function/connect.ts +2042 -0
- package/client/web/src/function/debug.ts +10 -0
- package/client/web/src/function/elements/information.ts +141 -0
- package/client/web/src/function/elements/system.ts +101 -0
- package/client/web/src/function/elements/vueComp.ts +62 -0
- package/client/web/src/function/favicon.ts +76 -0
- package/client/web/src/function/input.ts +69 -0
- package/client/web/src/function/model/emoji.ts +127 -0
- package/client/web/src/function/model/img.ts +150 -0
- package/client/web/src/function/model/msg-body.ts +51 -0
- package/client/web/src/function/msg.ts +2674 -0
- package/client/web/src/function/notify.ts +225 -0
- package/client/web/src/function/option.ts +749 -0
- package/client/web/src/function/satori-model.ts +818 -0
- package/client/web/src/function/satori.ts +325 -0
- package/client/web/src/function/sender.ts +178 -0
- package/client/web/src/function/tooltip.ts +49 -0
- package/client/web/src/function/utils/appUtil.ts +2288 -0
- package/client/web/src/function/utils/avatarUtil.ts +31 -0
- package/client/web/src/function/utils/backgroundUtil.ts +110 -0
- package/client/web/src/function/utils/linkViewUtil.ts +101 -0
- package/client/web/src/function/utils/localHistoryUtil.ts +517 -0
- package/client/web/src/function/utils/msgUtil.ts +1242 -0
- package/client/web/src/function/utils/pinyin.ts +141 -0
- package/client/web/src/function/utils/recordUtil.ts +187 -0
- package/client/web/src/function/utils/sessionUtil.ts +114 -0
- package/client/web/src/function/utils/systemUtil.ts +664 -0
- package/client/web/src/main.ts +91 -0
- package/client/web/src/pages/Chat.vue +3453 -0
- package/client/web/src/pages/Friends.vue +636 -0
- package/client/web/src/pages/Info.vue +717 -0
- package/client/web/src/pages/Messages.vue +875 -0
- package/client/web/src/pages/Options.vue +82 -0
- package/client/web/src/pages/Qzone.vue +1178 -0
- package/client/web/src/pages/chat-view/ChatDanmaku.vue +759 -0
- package/client/web/src/pages/chat-view/ChatGlagame.vue +642 -0
- package/client/web/src/pages/chat-view/ChatTerminal.vue +1044 -0
- package/client/web/src/pages/chat-view/SystemNotice.vue +171 -0
- package/client/web/src/pages/options/OptAccount.vue +107 -0
- package/client/web/src/pages/options/OptAddon.vue +185 -0
- package/client/web/src/pages/options/OptDev.vue +704 -0
- package/client/web/src/pages/options/OptFunction.vue +403 -0
- package/client/web/src/pages/options/OptInfo.vue +165 -0
- package/client/web/src/pages/options/OptView.vue +846 -0
- package/client/web/src/runtime/backend.ts +310 -0
- package/client/web/src/state/auth.ts +14 -0
- package/client/web/src/state/chat.ts +36 -0
- package/client/web/src/state/connection.ts +20 -0
- package/client/web/src/state/contact.ts +37 -0
- package/client/web/src/state/qzone.ts +18 -0
- package/client/web/src/state/sessionHistory.ts +25 -0
- package/client/web/src/state/settings.ts +18 -0
- package/client/web/src/state/sticker.ts +10 -0
- package/client/web/src/state/ui.ts +56 -0
- package/client/web/tsconfig.json +27 -0
- package/client/web/tsconfig.tsbuildinfo +1 -0
- package/client/web/vite.config.ts +33 -0
- package/dist/index.js +1 -12
- package/dist/style.css +1 -1
- package/lib/index.js +2070 -2077
- package/package.json +15 -2
- package/readme.md +0 -22
- package/src/bootstrap.ts +71 -0
- package/src/cache.ts +319 -0
- package/src/config.ts +23 -35
- package/src/database.ts +650 -0
- package/src/index.ts +27 -59
- package/src/media.ts +302 -0
- package/src/recorder.ts +175 -0
- package/src/self-message.ts +456 -0
- package/src/server.d.ts +26 -0
- package/src/types.ts +108 -48
- package/src/web.ts +712 -0
- package/client/vue/chat-logic.ts +0 -683
- package/client/vue/composables/useChatActions.ts +0 -61
- package/client/vue/composables/useChatData.ts +0 -185
- package/client/vue/composables/useImageCache.ts +0 -281
- package/client/vue/composables/useVideoCache.ts +0 -125
- package/client/vue/types.ts +0 -72
- package/lib/api-handlers.d.ts +0 -25
- package/lib/config.d.ts +0 -14
- package/lib/file-manager.d.ts +0 -124
- package/lib/index.d.ts +0 -17
- package/lib/logger.d.ts +0 -9
- package/lib/message-handler.d.ts +0 -35
- package/lib/types.d.ts +0 -57
- package/lib/utils.d.ts +0 -18
- package/src/api-handlers.ts +0 -646
- package/src/file-manager.ts +0 -1436
- package/src/message-handler.ts +0 -507
- package/src/utils.ts +0 -189
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
* @FileDescription: 快速打开会话搜索栏
|
|
3
|
+
* @Author: Mr.Lee
|
|
4
|
+
* @Date:
|
|
5
|
+
* 2025/08/19
|
|
6
|
+
* @Version:
|
|
7
|
+
* 1.0 - 初始版本
|
|
8
|
+
* @Description:
|
|
9
|
+
* 该组件用于在全局范围内快速搜索和选择会话。
|
|
10
|
+
* 通过按下 Ctrl + E 快捷键打开搜索栏,用户可以输入关键词来过滤会话列表。
|
|
11
|
+
* 支持上下键选择会话,回车键确认选择。
|
|
12
|
+
* 被VSC的快捷键 Ctrl + E 激励创作的
|
|
13
|
+
-->
|
|
14
|
+
<template>
|
|
15
|
+
<Teleport to="body">
|
|
16
|
+
<Transition name="global-session-search-bar">
|
|
17
|
+
<div v-if="show"
|
|
18
|
+
class="mask-background"
|
|
19
|
+
@click.stop.prevent="close">
|
|
20
|
+
<div ref="content" class="global-session-search-bar ss-card">
|
|
21
|
+
<label for="global-session-search-input" class="sr-only">{{ $t('搜索会话') }}</label>
|
|
22
|
+
<input
|
|
23
|
+
id="global-session-search-input"
|
|
24
|
+
ref="input"
|
|
25
|
+
v-search="searchInfo"
|
|
26
|
+
class="ss-input"
|
|
27
|
+
:placeholder="$t('搜索 ……')"
|
|
28
|
+
@focusout="close">
|
|
29
|
+
<template v-if="showSessions.length > 0">
|
|
30
|
+
<hr>
|
|
31
|
+
<div ref="sessionList">
|
|
32
|
+
<div v-for="(session, key) in showSessions"
|
|
33
|
+
:key="session.user_id ?? session.group_id"
|
|
34
|
+
:class="{
|
|
35
|
+
'session-item': true,
|
|
36
|
+
'selected': selectId === key,
|
|
37
|
+
}"
|
|
38
|
+
@click="choiceSession(session)">
|
|
39
|
+
<TinySessionBody
|
|
40
|
+
ref="sessionItems"
|
|
41
|
+
:session="session"
|
|
42
|
+
from="global-search" />
|
|
43
|
+
<span v-if="
|
|
44
|
+
(session.group_id ?? session.user_id) === chatStore.chatInfo.show.id">
|
|
45
|
+
{{ $t('当前会话') }}
|
|
46
|
+
</span>
|
|
47
|
+
</div>
|
|
48
|
+
</div>
|
|
49
|
+
</template>
|
|
50
|
+
</div>
|
|
51
|
+
</div>
|
|
52
|
+
</Transition>
|
|
53
|
+
</Teleport>
|
|
54
|
+
</template>
|
|
55
|
+
|
|
56
|
+
<script setup lang="ts">
|
|
57
|
+
import {
|
|
58
|
+
ref,
|
|
59
|
+
watchEffect,
|
|
60
|
+
computed,
|
|
61
|
+
shallowReactive,
|
|
62
|
+
useTemplateRef,
|
|
63
|
+
nextTick,
|
|
64
|
+
} from 'vue'
|
|
65
|
+
import TinySessionBody from './TinySessionBody.vue'
|
|
66
|
+
import { vSearch, useEventListener } from '../function/utils/appUtil'
|
|
67
|
+
import { UserFriendElem, UserGroupElem } from '../function/elements/information'
|
|
68
|
+
import { login } from '../function/connect'
|
|
69
|
+
import { backend } from '../runtime/backend'
|
|
70
|
+
import { matchPinyin } from '../function/utils/pinyin'
|
|
71
|
+
import { useUIStore } from '../state/ui'
|
|
72
|
+
import { useContactStore } from '../state/contact'
|
|
73
|
+
import { useChatStore } from '../state/chat'
|
|
74
|
+
|
|
75
|
+
//#region == 移植相关 ====================================================================
|
|
76
|
+
/**
|
|
77
|
+
* 会话对象
|
|
78
|
+
*/
|
|
79
|
+
type Session = UserGroupElem & UserFriendElem
|
|
80
|
+
/**
|
|
81
|
+
* 切换会话
|
|
82
|
+
* @param session
|
|
83
|
+
*/
|
|
84
|
+
function changeSession(session: Session) {
|
|
85
|
+
const id = session.user_id ?? session.group_id
|
|
86
|
+
if (id === chatStore.chatInfo.show.id) return
|
|
87
|
+
|
|
88
|
+
contactStore.baseOnMsgList.set(String(session.user_id ?? session.group_id), session)
|
|
89
|
+
// 切换到这个聊天
|
|
90
|
+
nextTick(() => {
|
|
91
|
+
const item = document.getElementById(
|
|
92
|
+
'user-' + (session.user_id ?? session.group_id),
|
|
93
|
+
)
|
|
94
|
+
if (item) {
|
|
95
|
+
item.click()
|
|
96
|
+
}
|
|
97
|
+
})
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* 会话匹配算法
|
|
101
|
+
* @param session
|
|
102
|
+
* @param query
|
|
103
|
+
*/
|
|
104
|
+
function sessionSearchMatch(session: Session, query: string): boolean {
|
|
105
|
+
const value = query.toLowerCase()
|
|
106
|
+
const name = (
|
|
107
|
+
session.user_id? session.nickname + session.remark: session.group_name
|
|
108
|
+
).toLowerCase()
|
|
109
|
+
if (name.includes(value)) return true
|
|
110
|
+
const id = session.user_id? session.user_id: session.group_id
|
|
111
|
+
if (id.toString() === value) return true
|
|
112
|
+
if (session.py_name && matchPinyin(session.py_name, value)) return true
|
|
113
|
+
return false
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* 判断有没有弹窗
|
|
117
|
+
*/
|
|
118
|
+
function hasPopBox() {
|
|
119
|
+
return uiStore.popBoxList.length > 0
|
|
120
|
+
}
|
|
121
|
+
//#endregion
|
|
122
|
+
|
|
123
|
+
//#region == 常量声明 ====================================================================
|
|
124
|
+
const uiStore = useUIStore()
|
|
125
|
+
const contactStore = useContactStore()
|
|
126
|
+
const chatStore = useChatStore()
|
|
127
|
+
const show = ref<boolean>(false)
|
|
128
|
+
const input = useTemplateRef<HTMLInputElement>('input')
|
|
129
|
+
const sessionList = useTemplateRef<HTMLElement>('sessionList')
|
|
130
|
+
const sessionItems = useTemplateRef<InstanceType<typeof TinySessionBody>[]>('sessionItems')
|
|
131
|
+
const selectId = ref<number>(0)
|
|
132
|
+
|
|
133
|
+
const searchInfo = shallowReactive({
|
|
134
|
+
originList: shallowReactive([] as Session[]),
|
|
135
|
+
query: shallowReactive([] as Session[]),
|
|
136
|
+
isSearch: false,
|
|
137
|
+
match: sessionSearchMatch,
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
const showSessions = computed(() => {
|
|
141
|
+
return searchInfo.isSearch ? searchInfo.query : searchInfo.originList
|
|
142
|
+
})
|
|
143
|
+
// 限制selectId的范围
|
|
144
|
+
watchEffect(()=>{
|
|
145
|
+
if (selectId.value < 0)
|
|
146
|
+
selectId.value = showSessions.value.length - 1
|
|
147
|
+
if (selectId.value >= showSessions.value.length)
|
|
148
|
+
selectId.value = 0
|
|
149
|
+
|
|
150
|
+
// 出界滚动
|
|
151
|
+
scrollToSelected()
|
|
152
|
+
})
|
|
153
|
+
//#endregion
|
|
154
|
+
|
|
155
|
+
//#region == 方法函数 ====================================================================
|
|
156
|
+
/**
|
|
157
|
+
* 打开搜索栏
|
|
158
|
+
*/
|
|
159
|
+
function open() {
|
|
160
|
+
init()
|
|
161
|
+
show.value = true
|
|
162
|
+
selectId.value = 0
|
|
163
|
+
|
|
164
|
+
// 聚焦输入框
|
|
165
|
+
nextTick(() => {
|
|
166
|
+
input.value?.focus()
|
|
167
|
+
})
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* 关闭搜索栏
|
|
172
|
+
*/
|
|
173
|
+
function close() {
|
|
174
|
+
show.value = false
|
|
175
|
+
selectId.value = 0
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* 初始化搜索框
|
|
180
|
+
*/
|
|
181
|
+
function init() {
|
|
182
|
+
// 主要列表
|
|
183
|
+
const mainList: Session[] = []
|
|
184
|
+
for (const session of contactStore.userList) {
|
|
185
|
+
if (contactStore.onMsgList.find(
|
|
186
|
+
s => (
|
|
187
|
+
s.user_id && s.user_id === session.user_id
|
|
188
|
+
) || (
|
|
189
|
+
s.group_id && s.group_id === session.group_id
|
|
190
|
+
)
|
|
191
|
+
)) continue
|
|
192
|
+
mainList.push(session)
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
searchInfo.originList = shallowReactive([...contactStore.onMsgList, ...mainList])
|
|
196
|
+
|
|
197
|
+
searchInfo.query = shallowReactive([])
|
|
198
|
+
searchInfo.isSearch = false
|
|
199
|
+
selectId.value = 0
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* 选择会话
|
|
204
|
+
* @param session 被选择的会话
|
|
205
|
+
*/
|
|
206
|
+
function choiceSession(session: Session) {
|
|
207
|
+
uiStore.openSideBar = false
|
|
208
|
+
|
|
209
|
+
changeSession(session)
|
|
210
|
+
close()
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* 根据id选择会话
|
|
215
|
+
* @param id
|
|
216
|
+
*/
|
|
217
|
+
function choiceSessionById(id: number) {
|
|
218
|
+
if (id < 0 || id >= showSessions.value.length) return
|
|
219
|
+
choiceSession(showSessions.value[id])
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* 滚动到选中的会话项
|
|
224
|
+
*/
|
|
225
|
+
function scrollToSelected() {
|
|
226
|
+
nextTick(() => {
|
|
227
|
+
if (!sessionItems.value) return
|
|
228
|
+
const selectedElement = sessionItems.value[selectId.value]
|
|
229
|
+
const container = sessionList.value!
|
|
230
|
+
|
|
231
|
+
if (selectedElement && container) {
|
|
232
|
+
const containerRect = container.getBoundingClientRect()
|
|
233
|
+
const elementRect = selectedElement.$el.getBoundingClientRect()
|
|
234
|
+
|
|
235
|
+
// 计算相对于容器的位置
|
|
236
|
+
const elementTop = elementRect.top - containerRect.top + container.scrollTop
|
|
237
|
+
const elementBottom = elementTop + elementRect.height
|
|
238
|
+
|
|
239
|
+
// 如果元素在可见区域上方
|
|
240
|
+
if (elementTop < container.scrollTop) {
|
|
241
|
+
container.scrollTop = elementTop
|
|
242
|
+
}
|
|
243
|
+
// 如果元素在可见区域下方
|
|
244
|
+
else if (elementBottom > container.scrollTop + container.clientHeight) {
|
|
245
|
+
container.scrollTop = elementBottom - container.clientHeight
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
})
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* 匹配到事件处理
|
|
253
|
+
* 会组织事件冒泡和默认行为
|
|
254
|
+
* @param e 事件
|
|
255
|
+
*/
|
|
256
|
+
function handleEvent(e: Event) {
|
|
257
|
+
e.stopPropagation()
|
|
258
|
+
e.preventDefault()
|
|
259
|
+
}
|
|
260
|
+
//#endregion
|
|
261
|
+
|
|
262
|
+
//#region == 开启关闭 ====================================================================
|
|
263
|
+
useEventListener(document, 'keydown', (e: KeyboardEvent) => {
|
|
264
|
+
let isOpen = false;
|
|
265
|
+
if(backend.platform === 'darwin') {
|
|
266
|
+
// macOS 使用 Command + E
|
|
267
|
+
isOpen = e.metaKey && e.key === 'e'
|
|
268
|
+
} else {
|
|
269
|
+
isOpen = e.ctrlKey && e.key === 'e'
|
|
270
|
+
}
|
|
271
|
+
if (isOpen) {
|
|
272
|
+
//开启
|
|
273
|
+
if (show.value) return
|
|
274
|
+
if (!login.status) return
|
|
275
|
+
if (hasPopBox()) return
|
|
276
|
+
handleEvent(e)
|
|
277
|
+
open()
|
|
278
|
+
}else if (e.key === 'Escape') {
|
|
279
|
+
// 关闭
|
|
280
|
+
if (!show.value) return
|
|
281
|
+
handleEvent(e)
|
|
282
|
+
close()
|
|
283
|
+
}else if (e.key === 'ArrowDown') {
|
|
284
|
+
// 下
|
|
285
|
+
if (!show.value) return
|
|
286
|
+
handleEvent(e)
|
|
287
|
+
selectId.value ++
|
|
288
|
+
}else if (e.key === 'ArrowUp') {
|
|
289
|
+
// 上
|
|
290
|
+
if (!show.value) return
|
|
291
|
+
handleEvent(e)
|
|
292
|
+
selectId.value --
|
|
293
|
+
}else if (e.key === 'Enter') {
|
|
294
|
+
// 确认
|
|
295
|
+
if (!show.value) return
|
|
296
|
+
handleEvent(e)
|
|
297
|
+
choiceSessionById(selectId.value)
|
|
298
|
+
}
|
|
299
|
+
})
|
|
300
|
+
//#endregion
|
|
301
|
+
</script>
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="history-box">
|
|
3
|
+
<div v-if="history.record.length === 0" class="space">
|
|
4
|
+
<font-awesome-icon :icon="['fas', 'inbox']" />
|
|
5
|
+
<span>{{ $t('空空如也') }}</span>
|
|
6
|
+
</div>
|
|
7
|
+
<div v-else class="main">
|
|
8
|
+
<tiny-session-body
|
|
9
|
+
v-for="session in history.record"
|
|
10
|
+
:key="session.user_id ?? session.group_id"
|
|
11
|
+
:session="session"
|
|
12
|
+
@click="changeSession(session)" />
|
|
13
|
+
</div>
|
|
14
|
+
</div>
|
|
15
|
+
</template>
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
<script setup lang="ts">
|
|
19
|
+
import { Session } from '../function/elements/information';
|
|
20
|
+
import { useChatStore } from '../state/chat';
|
|
21
|
+
import { useSessionHistoryStore } from '../state/sessionHistory';
|
|
22
|
+
import { useContactStore } from '../state/contact';
|
|
23
|
+
import { nextTick } from 'vue';
|
|
24
|
+
import TinySessionBody from './TinySessionBody.vue';
|
|
25
|
+
|
|
26
|
+
const history = useSessionHistoryStore()
|
|
27
|
+
const contactStore = useContactStore()
|
|
28
|
+
const chatStore = useChatStore()
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* 切换会话
|
|
32
|
+
* @param session
|
|
33
|
+
*/
|
|
34
|
+
function changeSession(session: Session) {
|
|
35
|
+
const id = session.user_id ?? session.group_id
|
|
36
|
+
if (id === chatStore.chatInfo.show.id) return
|
|
37
|
+
|
|
38
|
+
contactStore.baseOnMsgList.set(String(session.user_id ?? session.group_id), session)
|
|
39
|
+
// 切换到这个聊天
|
|
40
|
+
nextTick(() => {
|
|
41
|
+
const item = document.getElementById(
|
|
42
|
+
'user-' + (session.user_id ?? session.group_id),
|
|
43
|
+
)
|
|
44
|
+
if (item) {
|
|
45
|
+
item.click()
|
|
46
|
+
}
|
|
47
|
+
})
|
|
48
|
+
}
|
|
49
|
+
</script>
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div ref="lottieContainer" :class="['lazy-lottie', {'loaded': loaded }]">
|
|
3
|
+
<Lottie
|
|
4
|
+
ref="lottieRef"
|
|
5
|
+
:animation-link="animationLink"
|
|
6
|
+
:title="title"
|
|
7
|
+
:auto-play="false"
|
|
8
|
+
@on-animation-loaded="loaded = true" />
|
|
9
|
+
</div>
|
|
10
|
+
</template>
|
|
11
|
+
|
|
12
|
+
<script setup lang="ts">
|
|
13
|
+
import { ref, onMounted, onUnmounted } from 'vue'
|
|
14
|
+
import { Vue3Lottie as Lottie } from 'vue3-lottie'
|
|
15
|
+
|
|
16
|
+
defineProps<{
|
|
17
|
+
animationLink: string
|
|
18
|
+
title?: string
|
|
19
|
+
}>()
|
|
20
|
+
|
|
21
|
+
const lottieContainer = ref<HTMLElement | null>(null)
|
|
22
|
+
const lottieRef = ref<any>(null)
|
|
23
|
+
const loaded = ref(false)
|
|
24
|
+
let observer: IntersectionObserver | null = null
|
|
25
|
+
|
|
26
|
+
const playAnimation = () => {
|
|
27
|
+
if (!lottieRef.value) return
|
|
28
|
+
try {
|
|
29
|
+
lottieRef.value.play?.()
|
|
30
|
+
} catch (e) {
|
|
31
|
+
// do nothing
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const pauseAnimation = () => {
|
|
36
|
+
if (lottieRef.value) {
|
|
37
|
+
lottieRef.value.pause?.()
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
onMounted(() => {
|
|
42
|
+
if (!lottieContainer.value) return
|
|
43
|
+
|
|
44
|
+
observer = new IntersectionObserver(
|
|
45
|
+
(entries) => {
|
|
46
|
+
entries.forEach((entry) => {
|
|
47
|
+
if (entry.isIntersecting && entry.intersectionRatio >= 0.5) {
|
|
48
|
+
playAnimation()
|
|
49
|
+
} else if (entry.intersectionRatio < 0.1) {
|
|
50
|
+
pauseAnimation()
|
|
51
|
+
}
|
|
52
|
+
})
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
rootMargin: '50px',
|
|
56
|
+
threshold: [0.1, 0.5, 0.9]
|
|
57
|
+
}
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
observer.observe(lottieContainer.value)
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
onUnmounted(() => {
|
|
64
|
+
pauseAnimation()
|
|
65
|
+
if (observer && lottieContainer.value) {
|
|
66
|
+
observer.unobserve(lottieContainer.value)
|
|
67
|
+
observer.disconnect()
|
|
68
|
+
observer = null
|
|
69
|
+
}
|
|
70
|
+
})
|
|
71
|
+
</script>
|
|
72
|
+
|
|
73
|
+
<style scoped>
|
|
74
|
+
.lazy-lottie {
|
|
75
|
+
display: inline-block;
|
|
76
|
+
min-height: 100px;
|
|
77
|
+
min-width: 100px;
|
|
78
|
+
}
|
|
79
|
+
.lazy-lottie.loaded {
|
|
80
|
+
background: transparent !important;
|
|
81
|
+
}
|
|
82
|
+
</style>
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div :class="'merge-pan' + (chatStore.mergeMsgStack.length > 0 ? ' show' : '')">
|
|
3
|
+
<div @click="closeMergeMsg" />
|
|
4
|
+
<div class="ss-card">
|
|
5
|
+
<div>
|
|
6
|
+
<font-awesome-icon style="margin-top: 5px" :icon="['fas', 'message']" />
|
|
7
|
+
<span>{{ $t('合并消息') }}</span>
|
|
8
|
+
<font-awesome-icon v-if="stack.length <= 1" :icon="['fas', 'xmark']" @click="exitMergeMsg" />
|
|
9
|
+
<font-awesome-icon v-else :icon="['fas', 'angles-left']" @click="exitMergeMsg" />
|
|
10
|
+
</div>
|
|
11
|
+
<div v-if="nowData === undefined">
|
|
12
|
+
<!-- 无内容 -->
|
|
13
|
+
</div>
|
|
14
|
+
<TransitionGroup v-else :name="settingsStore.sysConfig.opt_fast_animation ? '' : 'msglist'" tag="div">
|
|
15
|
+
<template v-for="(msgIndex, index) in nowData.messageList" :key="'merge-' + nowData.forwardMsg.message[0].id + '-' + index">
|
|
16
|
+
<NoticeBody v-if=" isShowTime( nowData.messageList[index - 1] ?
|
|
17
|
+
nowData.messageList[index - 1].time : undefined, msgIndex.time, index == 0)"
|
|
18
|
+
:id="uuid()"
|
|
19
|
+
:key="'notice-time-' + index"
|
|
20
|
+
:data="{ sub_type: 'time', time: msgIndex.time }" />
|
|
21
|
+
<!-- [已删除]消息 -->
|
|
22
|
+
<NoticeBody
|
|
23
|
+
v-else-if="isDeleteMsg(msgIndex)"
|
|
24
|
+
:key="'delete-' + msgIndex.message_id"
|
|
25
|
+
:data="{ sub_type: 'delete' }" />
|
|
26
|
+
<!-- 合并转发消息忽略是不是自己的判定 -->
|
|
27
|
+
<MsgBody
|
|
28
|
+
:data="msgIndex"
|
|
29
|
+
:type="'merge'"
|
|
30
|
+
:image-list-header="mergeImgHead" />
|
|
31
|
+
</template>
|
|
32
|
+
</TransitionGroup>
|
|
33
|
+
</div>
|
|
34
|
+
</div>
|
|
35
|
+
</template>
|
|
36
|
+
|
|
37
|
+
<script setup lang="ts">
|
|
38
|
+
import { v4 as uuid } from 'uuid'
|
|
39
|
+
import { computed, ref, watch, onMounted } from 'vue'
|
|
40
|
+
|
|
41
|
+
import MsgBody from '../components/MsgBody.vue'
|
|
42
|
+
import NoticeBody from '../components/NoticeBody.vue'
|
|
43
|
+
|
|
44
|
+
import { Img } from '../function/model/img'
|
|
45
|
+
import { i18n } from '../main'
|
|
46
|
+
import { useSettingsStore } from '../state/settings'
|
|
47
|
+
import { type MergeStackData } from '../function/elements/information'
|
|
48
|
+
import { isDeleteMsg, isShowTime } from '../function/utils/msgUtil'
|
|
49
|
+
import { useChatStore } from '../state/chat'
|
|
50
|
+
|
|
51
|
+
defineOptions({ name: 'MergePan' })
|
|
52
|
+
|
|
53
|
+
const $t = i18n.global.t
|
|
54
|
+
|
|
55
|
+
const chatStore = useChatStore()
|
|
56
|
+
const settingsStore = useSettingsStore()
|
|
57
|
+
const stack = chatStore.mergeMsgStack
|
|
58
|
+
const nowData = ref<MergeStackData | undefined>()
|
|
59
|
+
const mergeImgHead = computed(() =>
|
|
60
|
+
Img.fromList((nowData.value?.imageList ?? []).map(item => item.img_url))
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* 退出一层合并转发弹窗
|
|
65
|
+
*/
|
|
66
|
+
function exitMergeMsg() {
|
|
67
|
+
stack.length--
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* 关闭合并转发弹窗
|
|
72
|
+
*/
|
|
73
|
+
function closeMergeMsg() {
|
|
74
|
+
stack.length = 0
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function isMergeOpen() {
|
|
78
|
+
return stack.length > 0
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
defineExpose({ closeMergeMsg, isMergeOpen })
|
|
82
|
+
|
|
83
|
+
onMounted(() => {
|
|
84
|
+
watch(
|
|
85
|
+
() => chatStore.mergeMsgStack.length,
|
|
86
|
+
() => {
|
|
87
|
+
// 最后一个保留下来做展开关闭动画
|
|
88
|
+
if (stack.length === 0) {
|
|
89
|
+
// 清理下垃圾
|
|
90
|
+
chatStore.mergeMessageImgList = undefined
|
|
91
|
+
}
|
|
92
|
+
else nowData.value = stack.at(-1)
|
|
93
|
+
}
|
|
94
|
+
)
|
|
95
|
+
watch(
|
|
96
|
+
() => nowData.value?.imageList,
|
|
97
|
+
() => {
|
|
98
|
+
if (chatStore.mergeMsgStack.length === 0 || nowData.value?.imageList === undefined) return
|
|
99
|
+
chatStore.mergeMessageImgList = nowData.value.imageList
|
|
100
|
+
}
|
|
101
|
+
)
|
|
102
|
+
})
|
|
103
|
+
</script>
|
|
104
|
+
|
|
105
|
+
<style scoped>
|
|
106
|
+
/* 消息动画 */
|
|
107
|
+
.msglist-move {
|
|
108
|
+
transition: all 0.3s;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
.msglist-enter-active {
|
|
112
|
+
transition: all 0.4s;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
.msglist-leave-active {
|
|
116
|
+
transition: all 0.2s;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
.msglist-enter-from {
|
|
120
|
+
transform: translateX(-50px);
|
|
121
|
+
opacity: 0;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
.msglist-leave-to {
|
|
125
|
+
opacity: 0;
|
|
126
|
+
}
|
|
127
|
+
</style>
|