koishi-plugin-chat-patch 3.0.2 → 5.0.0

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.
Files changed (194) hide show
  1. package/client/vue/index.vue +60 -634
  2. package/client/web/env.d.ts +56 -0
  3. package/client/web/index.html +21 -0
  4. package/client/web/package-lock.json +3419 -0
  5. package/client/web/package.json +62 -0
  6. package/client/web/public/bcui/css/color-dark.css +35 -0
  7. package/client/web/public/bcui/css/color-light.css +35 -0
  8. package/client/web/public/bcui/css/style.css +468 -0
  9. package/client/web/public/bcui/js/main.js +193 -0
  10. package/client/web/public/css/append-dark.css +13 -0
  11. package/client/web/public/css/append-light.css +13 -0
  12. package/client/web/public/css/append-napcat-dark.css +14 -0
  13. package/client/web/public/css/append-napcat-light.css +14 -0
  14. package/client/web/public/favicon.ico +0 -0
  15. package/client/web/public/img/icons/icon-for-fuck-apple.png +0 -0
  16. package/client/web/public/img/icons/icon-maskable.png +0 -0
  17. package/client/web/public/img/icons/icon.png +0 -0
  18. package/client/web/public/img/icons/icon.svg +10 -0
  19. package/client/web/public/robots.txt +2 -0
  20. package/client/web/public/sw.js +58 -0
  21. package/client/web/scripts/copy-bcui.mjs +17 -0
  22. package/client/web/src/App.vue +1127 -0
  23. package/client/web/src/assets/css/append/append_darwin.css +68 -0
  24. package/client/web/src/assets/css/append/append_full_vibrancy.css +290 -0
  25. package/client/web/src/assets/css/append/append_linux.css +125 -0
  26. package/client/web/src/assets/css/append/append_linux_vibrancy.css +22 -0
  27. package/client/web/src/assets/css/append/append_napcat.css +35 -0
  28. package/client/web/src/assets/css/append/append_new.css +843 -0
  29. package/client/web/src/assets/css/append/append_vibrancy.css +113 -0
  30. package/client/web/src/assets/css/append/append_win32.css +109 -0
  31. package/client/web/src/assets/css/append/mobile/append_mobile_horizontal.css +349 -0
  32. package/client/web/src/assets/css/append/mobile/append_mobile_vertical.css +96 -0
  33. package/client/web/src/assets/css/chat.css +2104 -0
  34. package/client/web/src/assets/css/msg.css +941 -0
  35. package/client/web/src/assets/css/options.css +811 -0
  36. package/client/web/src/assets/css/sys_notice.css +62 -0
  37. package/client/web/src/assets/css/view.css +2105 -0
  38. package/client/web/src/assets/font//346/226/271/346/255/243/345/226/265/345/221/234/344/275/223.ttf +0 -0
  39. package/client/web/src/assets/img/Vue.png +0 -0
  40. package/client/web/src/assets/img/cakes.svg +189 -0
  41. package/client/web/src/assets/img/icons/AppIcon.png +0 -0
  42. package/client/web/src/assets/img/icons/AppIconDark.png +0 -0
  43. package/client/web/src/assets/img/icons/CapacitorAppIcon.png +0 -0
  44. package/client/web/src/assets/img/icons/CapacitorAppIconDark.png +0 -0
  45. package/client/web/src/assets/img/icons/NapcatAppIcon.png +0 -0
  46. package/client/web/src/assets/img/icons/NapcatAppIconDark.png +0 -0
  47. package/client/web/src/assets/img/icons/exit.png +0 -0
  48. package/client/web/src/assets/img/icons/pen.png +0 -0
  49. package/client/web/src/assets/img/stars.svg +89 -0
  50. package/client/web/src/assets/img//346/234/237/345/276/205.png +0 -0
  51. package/client/web/src/assets/l10n/_About I10n.md +35 -0
  52. package/client/web/src/assets/l10n/_l10nconfig.json +7 -0
  53. package/client/web/src/assets/l10n/zh-CN.po +1941 -0
  54. package/client/web/src/assets/logo.png +0 -0
  55. package/client/web/src/assets/pathMap/LLOneBot.yaml +2 -0
  56. package/client/web/src/assets/pathMap/Lagrange.OneBot.yaml +180 -0
  57. package/client/web/src/assets/pathMap/NapCat.Onebot.yaml +224 -0
  58. package/client/web/src/assets/pathMap/SnowLuma.yaml +80 -0
  59. package/client/web/src/assets/qed.txt +1 -0
  60. package/client/web/src/components/AboutPan.vue +197 -0
  61. package/client/web/src/components/BulletinBody.vue +129 -0
  62. package/client/web/src/components/DepPan.vue +310 -0
  63. package/client/web/src/components/EmojiFace.vue +32 -0
  64. package/client/web/src/components/FacePan.vue +461 -0
  65. package/client/web/src/components/FileBody.vue +129 -0
  66. package/client/web/src/components/FileManager.vue +532 -0
  67. package/client/web/src/components/FriendBody.vue +54 -0
  68. package/client/web/src/components/GlobalSessionSearchBar.vue +301 -0
  69. package/client/web/src/components/History.vue +49 -0
  70. package/client/web/src/components/LazyLottie.vue +82 -0
  71. package/client/web/src/components/MergePan.vue +127 -0
  72. package/client/web/src/components/MsgBody.vue +1478 -0
  73. package/client/web/src/components/MusicPlayer.vue +549 -0
  74. package/client/web/src/components/NoticeBody.vue +168 -0
  75. package/client/web/src/components/QzonePermissionPan.vue +150 -0
  76. package/client/web/src/components/RawMsgRenderPreviewPan.vue +190 -0
  77. package/client/web/src/components/ThemeColorPickerPan.vue +420 -0
  78. package/client/web/src/components/TinySessionBody.vue +51 -0
  79. package/client/web/src/components/UmamiInfoPan.vue +1549 -0
  80. package/client/web/src/components/UpdatePan.vue +263 -0
  81. package/client/web/src/components/ViewerCom.vue +1360 -0
  82. package/client/web/src/components/VoiceMsg.vue +626 -0
  83. package/client/web/src/components/WelPan.vue +894 -0
  84. package/client/web/src/components/msg-body/.gitkeep +0 -0
  85. package/client/web/src/components/msg-component/JsonSegComp.vue +138 -0
  86. package/client/web/src/components/msg-component/XmlSegComp.vue +132 -0
  87. package/client/web/src/components/msg-component/jsonComp/AutoReply.vue +97 -0
  88. package/client/web/src/components/msg-component/jsonComp/Contact.lua.vue +129 -0
  89. package/client/web/src/components/msg-component/jsonComp/Feed.lua.vue +94 -0
  90. package/client/web/src/components/msg-component/jsonComp/Forum.vue +84 -0
  91. package/client/web/src/components/msg-component/jsonComp/Mannounce.vue +111 -0
  92. package/client/web/src/components/msg-component/jsonComp/Map.vue +52 -0
  93. package/client/web/src/components/msg-component/jsonComp/Miniapp.lua.vue +53 -0
  94. package/client/web/src/components/msg-component/jsonComp/Miniapp.vue +69 -0
  95. package/client/web/src/components/msg-component/jsonComp/Music.lua.vue +135 -0
  96. package/client/web/src/components/msg-component/jsonComp/Tuwen.lua.vue +56 -0
  97. package/client/web/src/components/notice-component/.gitkeep +0 -0
  98. package/client/web/src/components/notice-component/MealHungryPan.vue +119 -0
  99. package/client/web/src/components/tooltip/CustomFaceTooltip.vue +22 -0
  100. package/client/web/src/components/tooltip/Tooltip.vue +50 -0
  101. package/client/web/src/components/tooltip/Tooltips.vue +14 -0
  102. package/client/web/src/components/tooltip/UserInfoTooltip.vue +69 -0
  103. package/client/web/src/function/base.ts +238 -0
  104. package/client/web/src/function/connect.ts +2042 -0
  105. package/client/web/src/function/debug.ts +10 -0
  106. package/client/web/src/function/elements/information.ts +141 -0
  107. package/client/web/src/function/elements/system.ts +101 -0
  108. package/client/web/src/function/elements/vueComp.ts +62 -0
  109. package/client/web/src/function/favicon.ts +76 -0
  110. package/client/web/src/function/input.ts +69 -0
  111. package/client/web/src/function/model/emoji.ts +127 -0
  112. package/client/web/src/function/model/img.ts +150 -0
  113. package/client/web/src/function/model/msg-body.ts +51 -0
  114. package/client/web/src/function/msg.ts +2674 -0
  115. package/client/web/src/function/notify.ts +225 -0
  116. package/client/web/src/function/option.ts +749 -0
  117. package/client/web/src/function/satori-model.ts +818 -0
  118. package/client/web/src/function/satori.ts +325 -0
  119. package/client/web/src/function/sender.ts +178 -0
  120. package/client/web/src/function/tooltip.ts +49 -0
  121. package/client/web/src/function/utils/appUtil.ts +2288 -0
  122. package/client/web/src/function/utils/avatarUtil.ts +31 -0
  123. package/client/web/src/function/utils/backgroundUtil.ts +110 -0
  124. package/client/web/src/function/utils/linkViewUtil.ts +101 -0
  125. package/client/web/src/function/utils/localHistoryUtil.ts +517 -0
  126. package/client/web/src/function/utils/msgUtil.ts +1242 -0
  127. package/client/web/src/function/utils/pinyin.ts +141 -0
  128. package/client/web/src/function/utils/recordUtil.ts +187 -0
  129. package/client/web/src/function/utils/sessionUtil.ts +114 -0
  130. package/client/web/src/function/utils/systemUtil.ts +664 -0
  131. package/client/web/src/main.ts +91 -0
  132. package/client/web/src/pages/Chat.vue +3453 -0
  133. package/client/web/src/pages/Friends.vue +636 -0
  134. package/client/web/src/pages/Info.vue +717 -0
  135. package/client/web/src/pages/Messages.vue +875 -0
  136. package/client/web/src/pages/Options.vue +82 -0
  137. package/client/web/src/pages/Qzone.vue +1178 -0
  138. package/client/web/src/pages/chat-view/ChatDanmaku.vue +759 -0
  139. package/client/web/src/pages/chat-view/ChatGlagame.vue +642 -0
  140. package/client/web/src/pages/chat-view/ChatTerminal.vue +1044 -0
  141. package/client/web/src/pages/chat-view/SystemNotice.vue +171 -0
  142. package/client/web/src/pages/options/OptAccount.vue +107 -0
  143. package/client/web/src/pages/options/OptAddon.vue +185 -0
  144. package/client/web/src/pages/options/OptDev.vue +704 -0
  145. package/client/web/src/pages/options/OptFunction.vue +403 -0
  146. package/client/web/src/pages/options/OptInfo.vue +165 -0
  147. package/client/web/src/pages/options/OptView.vue +846 -0
  148. package/client/web/src/runtime/backend.ts +310 -0
  149. package/client/web/src/state/auth.ts +14 -0
  150. package/client/web/src/state/chat.ts +36 -0
  151. package/client/web/src/state/connection.ts +20 -0
  152. package/client/web/src/state/contact.ts +37 -0
  153. package/client/web/src/state/qzone.ts +18 -0
  154. package/client/web/src/state/sessionHistory.ts +25 -0
  155. package/client/web/src/state/settings.ts +18 -0
  156. package/client/web/src/state/sticker.ts +10 -0
  157. package/client/web/src/state/ui.ts +56 -0
  158. package/client/web/tsconfig.json +27 -0
  159. package/client/web/tsconfig.tsbuildinfo +1 -0
  160. package/client/web/vite.config.ts +33 -0
  161. package/dist/index.js +1 -12
  162. package/dist/style.css +1 -1
  163. package/lib/index.js +2070 -2077
  164. package/package.json +15 -2
  165. package/readme.md +0 -22
  166. package/src/bootstrap.ts +71 -0
  167. package/src/cache.ts +319 -0
  168. package/src/config.ts +23 -35
  169. package/src/database.ts +650 -0
  170. package/src/index.ts +27 -59
  171. package/src/media.ts +302 -0
  172. package/src/recorder.ts +175 -0
  173. package/src/self-message.ts +456 -0
  174. package/src/server.d.ts +26 -0
  175. package/src/types.ts +108 -48
  176. package/src/web.ts +712 -0
  177. package/client/vue/chat-logic.ts +0 -683
  178. package/client/vue/composables/useChatActions.ts +0 -61
  179. package/client/vue/composables/useChatData.ts +0 -185
  180. package/client/vue/composables/useImageCache.ts +0 -281
  181. package/client/vue/composables/useVideoCache.ts +0 -125
  182. package/client/vue/types.ts +0 -72
  183. package/lib/api-handlers.d.ts +0 -25
  184. package/lib/config.d.ts +0 -14
  185. package/lib/file-manager.d.ts +0 -124
  186. package/lib/index.d.ts +0 -17
  187. package/lib/logger.d.ts +0 -9
  188. package/lib/message-handler.d.ts +0 -35
  189. package/lib/types.d.ts +0 -57
  190. package/lib/utils.d.ts +0 -18
  191. package/src/api-handlers.ts +0 -646
  192. package/src/file-manager.ts +0 -1436
  193. package/src/message-handler.ts +0 -507
  194. package/src/utils.ts +0 -189
@@ -0,0 +1,461 @@
1
+ <!--
2
+ * @FileDescription: 表情面板模板
3
+ * @Author: Stapxs
4
+ * @Date: missing
5
+ * @Version: 1.0
6
+ -->
7
+
8
+ <template>
9
+ <div class="ss-card face-pan">
10
+ <BcTab>
11
+ <div icon="fa-solid fa-face-laugh-squint">
12
+ <div class="system-face-bar">
13
+ <template v-if="recentEmojisList.length > 0">
14
+ <div class="title">
15
+ <span>{{ $t('最近使用') }}</span>
16
+ </div>
17
+ <div class="face">
18
+ <template
19
+ v-for="num in recentEmojisList"
20
+ :key="num">
21
+ <div>
22
+ <EmojiFace
23
+ :emoji="Emoji.get(num)"
24
+ @click="addBaseFace(num)" />
25
+ </div>
26
+ </template>
27
+ </div>
28
+ </template>
29
+ <div class="title">
30
+ <span>{{ $t('小黄脸表情') }}</span>
31
+ </div>
32
+ <div class="face">
33
+ <template v-for="num in Emoji.normalList" :key="num">
34
+ <div>
35
+ <EmojiFace
36
+ :emoji="Emoji.get(num)"
37
+ @click="addBaseFace(num)" />
38
+ </div>
39
+ </template>
40
+ </div>
41
+ <div class="title">
42
+ <span>{{ $t('emoji 表情') }}</span>
43
+ </div>
44
+ <div class="face">
45
+ <div
46
+ v-for="num in Emoji.emojiList"
47
+ :key="num">
48
+ <EmojiFace
49
+ :emoji="Emoji.get(num)"
50
+ @click="addBaseFace(num)" />
51
+ </div>
52
+ </div>
53
+ </div>
54
+ </div>
55
+ <div icon="fa-solid fa-heart">
56
+ <div class="system-face-bar custom-face-bar" @scroll="stickersScroll">
57
+ <template v-if="recentCustomFacesList.length > 0">
58
+ <div class="title">
59
+ <span>{{ $t('最近使用') }}</span>
60
+ </div>
61
+ <div class="face">
62
+ <span
63
+ v-for="(url, num) in recentCustomFacesList"
64
+ :key="num"
65
+ v-tooltip="customFaceTooltip(url)">
66
+ <img
67
+ loading="lazy"
68
+ :src="url"
69
+ :alt="'[' + $t('动画表情') + ']'"
70
+ @click="addImgFace(url)">
71
+ </span>
72
+ </div>
73
+ </template>
74
+ <div class="title">
75
+ <span>{{ $t('收藏的表情') }}</span>
76
+ <font-awesome-icon
77
+ :icon="['fas', 'fa-rotate-right']"
78
+ @click="reloadRoamingStamp" />
79
+ </div>
80
+ <div class="face">
81
+ <div v-if="stickerStore.stickerCache && stickerStore.stickerCache.length <= 0"
82
+ class="ss-card">
83
+ <font-awesome-icon :icon="['fas', 'face-dizzy']" />
84
+ <span>{{ $t('一无所有') }}</span>
85
+ </div>
86
+ <template v-else-if="stickerStore.stickerCache && stickerStore.stickerCache.length > 0">
87
+ <span v-for="(url, index) in stickerStore.stickerCache" :key="'stickers-' + index">
88
+ <img
89
+ v-show="url != 'end'"
90
+ v-tooltip="customFaceTooltip(url)"
91
+ loading="lazy"
92
+ :src="url"
93
+ :alt="'[' + $t('动画表情') + ']'"
94
+ @click="addImgFace(url)">
95
+ </span>
96
+ </template>
97
+ </div>
98
+ </div>
99
+ </div>
100
+ <div v-if="backend.isDesktop()" icon="fa-solid fa-folder-open">
101
+ <div class="system-face-bar custom-face-bar">
102
+ <template v-if="recentLocalFacesList.length > 0">
103
+ <div class="title">
104
+ <span>{{ $t('最近使用') }}</span>
105
+ </div>
106
+ <div class="face">
107
+ <span v-for="(emoji, index) in recentLocalFacesList" :key="index">
108
+ <img
109
+ v-tooltip="customFaceTooltip(emoji.url)"
110
+ loading="lazy"
111
+ :src="emoji.url"
112
+ :alt="'[' + $t('动画表情') + ']'"
113
+ @click="addLocalEmoji(emoji)">
114
+ </span>
115
+ </div>
116
+ </template>
117
+ <div class="title">
118
+ <span>{{ $t('本地表情') }}</span>
119
+ <font-awesome-icon
120
+ :icon="['fas', 'fa-folder-plus']"
121
+ :title="$t('选择文件夹')"
122
+ @click="selectLocalEmojiFolder" />
123
+ <font-awesome-icon
124
+ v-if="settingsStore.sysConfig.local_emoji_folder"
125
+ :icon="['fas', 'fa-rotate-right']"
126
+ :title="$t('重新加载')"
127
+ @click="reloadLocalEmojis" />
128
+ </div>
129
+ <div class="face">
130
+ <div v-if="!settingsStore.sysConfig.local_emoji_folder"
131
+ class="ss-card">
132
+ <font-awesome-icon :icon="['fas', 'folder-open']" />
133
+ <span>{{ $t('选择文件夹') }}</span>
134
+ </div>
135
+ <div v-else-if="settingsStore.sysConfig.local_emoji_folder && localEmojis.length <= 0"
136
+ class="ss-card">
137
+ <font-awesome-icon :icon="['fas', 'face-dizzy']" />
138
+ <span>{{ $t('暂无图片') }}</span>
139
+ </div>
140
+ <template v-else-if="settingsStore.sysConfig.local_emoji_folder && localEmojis.length > 0">
141
+ <span v-for="(emoji, index) in localEmojis" :key="index">
142
+ <img
143
+ v-tooltip="customFaceTooltip(emoji.url)"
144
+ loading="lazy"
145
+ :src="emoji.url"
146
+ :alt="'[' + $t('动画表情') + ']'"
147
+ @click="addLocalEmoji(emoji)">
148
+ </span>
149
+ </template>
150
+ </div>
151
+ </div>
152
+ </div>
153
+ </BcTab>
154
+ </div>
155
+ </template>
156
+
157
+ <script setup lang="ts">
158
+ import {
159
+ MsgItemElem,
160
+ SQCodeElem,
161
+ } from '../function/elements/information'
162
+ import { computed, ComputedRef, Ref, ShallowRef, shallowRef } from 'vue'
163
+ import { Connector } from '../function/connect'
164
+ import { backend } from '../runtime/backend'
165
+ import Option from '../function/option'
166
+ import { PopInfo, PopType } from '../function/base'
167
+ import BcTab from 'vue3-bcui/packages/bc-tab'
168
+ import Emoji from '../function/model/emoji'
169
+ import EmojiFace from './EmojiFace.vue'
170
+ import { VueCompData } from '../function/elements/vueComp'
171
+ import CustomFaceTooltip from './tooltip/CustomFaceTooltip.vue'
172
+ import { useLocalStorage, vTooltip } from '../function/utils/appUtil'
173
+ import app from '../main'
174
+ import { useStickerStore } from '../state/sticker'
175
+ import { useSettingsStore } from '../state/settings'
176
+ import { useAuthStore } from '../state/auth'
177
+
178
+ const { recordList: recentEmojisId, showList: recentEmojisList } =
179
+ getRecentEmojiRecord<number>('recent-emojis-id')
180
+ const { recordList: recentCustomFacesId, showList: recentCustomFacesList } =
181
+ getRecentEmojiRecord<string>('recent-custom-faces-id')
182
+ const { recordList: recentLocalFacesId, showList: _recentLocalFacesList } =
183
+ getRecentEmojiRecord<string>('recent-local-faces-id')
184
+
185
+ const recentLocalFacesList = computed(()=>{
186
+ const list: LocalEmoji[] = []
187
+ for (const path of _recentLocalFacesList.value) {
188
+ const emoji = localEmojis.value.find(e => e.path === path)
189
+ if (!emoji) continue
190
+ list.push(emoji)
191
+ }
192
+ return list
193
+ })
194
+
195
+ interface LocalEmoji {
196
+ name: string
197
+ path: string
198
+ url: string
199
+ }
200
+
201
+ const popInfo = new PopInfo()
202
+ const stickerStore = useStickerStore()
203
+ const settingsStore = useSettingsStore()
204
+ const authStore = useAuthStore()
205
+
206
+ const stickerPage = shallowRef(1)
207
+ const localEmojis = shallowRef<LocalEmoji[]>([])
208
+
209
+ const emit = defineEmits<{
210
+ addSpecialMsg: [data: SQCodeElem],
211
+ sendMsg: [echo?: string]
212
+ }>()
213
+
214
+ // 加载漫游表情
215
+ if (
216
+ stickerStore.stickerCache && stickerStore.stickerCache.length == 0 &&
217
+ authStore.jsonMap.roaming_stamp
218
+ ) {
219
+ reloadRoamingStamp()
220
+ }
221
+ // 加载本地表情
222
+ if (backend.isDesktop() && settingsStore.sysConfig.local_emoji_folder) {
223
+ reloadLocalEmojis()
224
+ }
225
+
226
+ function addSpecialMsg(json: MsgItemElem, addText: boolean) {
227
+ emit('addSpecialMsg', {
228
+ addText: addText,
229
+ msgObj: json,
230
+ })
231
+ }
232
+ function addBaseFace(id: number) {
233
+ recordRecentEmoji(recentEmojisId, id)
234
+ if (id < 5000)
235
+ addSpecialMsg({ type: 'face', id: id }, true)
236
+ else
237
+ addSpecialMsg({ type: 'text', text: Emoji.get(id)!.value }, true)
238
+ }
239
+
240
+ //#region == 漫游表情相关函数 ===========
241
+ function reloadRoamingStamp() {
242
+ stickerStore.stickerCache = undefined
243
+ if (authStore.jsonMap.roaming_stamp.pagerType == 'full') {
244
+ // 全量分页,返回所有内容
245
+ Connector.send(
246
+ authStore.jsonMap.roaming_stamp.name,
247
+ { count: 48 },
248
+ 'getRoamingStamp_48',
249
+ )
250
+ } else {
251
+ // 默认不分页,返回所有内容
252
+ Connector.send(
253
+ authStore.jsonMap.roaming_stamp.name,
254
+ {},
255
+ 'getRoamingStamp',
256
+ )
257
+ }
258
+ }
259
+ function stickersScroll(e: Event) {
260
+ const target = e.target as HTMLDivElement
261
+ // 如果滚到了底部
262
+ if (
263
+ target.scrollHeight - target.scrollTop <
264
+ target.clientHeight + 0.5
265
+ ) {
266
+ if (stickerStore.stickerCache) {
267
+ if (authStore.jsonMap.roaming_stamp.pagerType == 'full' &&
268
+ stickerStore.stickerCache[stickerStore.stickerCache.length - 1] != 'end') {
269
+ const count = 48 + 48 * stickerPage.value
270
+ // 全量分页,返回所有内容(napcat 行为)
271
+ Connector.send(
272
+ authStore.jsonMap.roaming_stamp.name,
273
+ { count: count },
274
+ 'getRoamingStamp_' + count,
275
+ )
276
+ stickerPage.value++
277
+ }
278
+ }
279
+ }
280
+ }
281
+ function addImgFace(url: string) {
282
+ recordRecentEmoji(recentCustomFacesId, url)
283
+ addSpecialMsg(
284
+ { type: 'image', file: url, subType: 1 },
285
+ true,
286
+ )
287
+ // 直接发送表情
288
+ if(settingsStore.sysConfig.send_face == true) {
289
+ emit('sendMsg')
290
+ }
291
+ }
292
+ //#endregion
293
+
294
+ //#region == 本地表情相关函数 ===========
295
+ /**
296
+ * 选择文件夹
297
+ */
298
+ async function selectLocalEmojiFolder() {
299
+ const { $t } = app.config.globalProperties
300
+ try {
301
+ // 调用后端选择文件夹
302
+ const folderPath = await backend.call(
303
+ undefined,
304
+ 'sys:selectFolder',
305
+ true
306
+ )
307
+
308
+ if (folderPath) {
309
+ // 保存文件夹路径
310
+ Option.save('local_emoji_folder', folderPath)
311
+ popInfo.add(PopType.INFO, $t('已设置本地表情文件夹'))
312
+ // 重新加载本地表情
313
+ reloadLocalEmojis()
314
+ }
315
+ } catch (error) {
316
+ popInfo.add(PopType.ERR, $t('选择文件夹失败'))
317
+ }
318
+ }
319
+
320
+ /**
321
+ * 重新加载本地表情
322
+ */
323
+ async function reloadLocalEmojis() {
324
+ try {
325
+ const folderPath = settingsStore.sysConfig.local_emoji_folder
326
+ if (!folderPath) {
327
+ localEmojis.value = []
328
+ return
329
+ }
330
+
331
+ // 调用后端读取文件夹中的图片
332
+ const images = await backend.call(
333
+ undefined,
334
+ 'sys:getLocalEmojis',
335
+ true,
336
+ folderPath
337
+ )
338
+
339
+ if (images && Array.isArray(images)) {
340
+ // 对于 Tauri,需要使用 convertFileSrc 转换文件路径
341
+ if (backend.type === 'tauri') {
342
+ const { convertFileSrc } = await import('@tauri-apps/api/core')
343
+ localEmojis.value = images.map((img: any) => ({
344
+ name: img.name,
345
+ path: img.path,
346
+ url: convertFileSrc(img.path),
347
+ }))
348
+ } else {
349
+ localEmojis.value = images.map((img: any) => ({
350
+ name: img.name,
351
+ path: img.path,
352
+ url: img.url,
353
+ }))
354
+ }
355
+ } else {
356
+ localEmojis.value = []
357
+ }
358
+ } catch (error) {
359
+ localEmojis.value = []
360
+ }
361
+ }
362
+
363
+ /**
364
+ * 添加本地表情到消息
365
+ */
366
+ async function addLocalEmoji(emoji: LocalEmoji) {
367
+ try {
368
+ // 读取文件为 base64
369
+ const base64String = await backend.call(
370
+ undefined,
371
+ 'sys:readFileAsBase64',
372
+ true,
373
+ emoji.path
374
+ )
375
+
376
+ recordRecentEmoji(recentLocalFacesId, emoji.path)
377
+
378
+ // 发送 base64 格式的图片
379
+ addSpecialMsg(
380
+ { type: 'image', file: 'base64://' + base64String, subType: 1 },
381
+ true,
382
+ )
383
+ // 如果设置了直接发送表情
384
+ if(settingsStore.sysConfig.send_face == true) {
385
+ emit('sendMsg')
386
+ }
387
+ } catch (error) {
388
+ const { $t } = app.config.globalProperties
389
+ popInfo.add(PopType.ERR, $t('添加本地表情失败'))
390
+ }
391
+ }
392
+ //#endregion
393
+
394
+ //#region == 最近表情相关函数 ===========
395
+ function getRecentEmojiRecord<T>(storeId: string): {
396
+ recordList: Ref<T[]>
397
+ showList: ComputedRef<T[]>
398
+ } {
399
+ const recordList = useLocalStorage<T[]>(storeId, [])
400
+ const showList = computed(() => {
401
+ if (settingsStore.sysConfig.record_recent_emoji === 'none') {
402
+ return []
403
+ } else if (settingsStore.sysConfig.record_recent_emoji === 'order') {
404
+ return recordList.value.slice(0, 30)
405
+ } else {
406
+ const timesMap = new Map<T, number>()
407
+ for (const id of recordList.value) {
408
+ timesMap.set(id, (timesMap.get(id) || 0) + 1)
409
+ }
410
+ return Array.from(timesMap.entries())
411
+ .sort((a, b) => b[1] - a[1])
412
+ .map((entry) => entry[0])
413
+ .slice(0, 30)
414
+ }
415
+ })
416
+
417
+ return {
418
+ recordList,
419
+ showList,
420
+ }
421
+ }
422
+
423
+ function recordRecentEmoji<T>(recordList: ShallowRef<T[]>, id: T) {
424
+ if (settingsStore.sysConfig.record_recent_emoji === 'none') return
425
+ let limit: number
426
+ switch (settingsStore.sysConfig.record_recent_emoji) {
427
+ case 'order':
428
+ limit = 30
429
+ break
430
+ case '100times':
431
+ limit = 100
432
+ break
433
+ case '500times':
434
+ limit = 500
435
+ break
436
+ default:
437
+ throw new Error('Invalid recent emoji record setting')
438
+ }
439
+ const list = recordList.value
440
+ if (settingsStore.sysConfig.record_recent_emoji === 'order') {
441
+ const index = list.indexOf(id)
442
+ if (index !== -1) {
443
+ list.splice(index, 1)
444
+ }
445
+ }
446
+ list.unshift(id)
447
+ if (list.length > limit) {
448
+ list.pop()
449
+ }
450
+ recordList.value = list
451
+ }
452
+ //#endregion
453
+
454
+ function customFaceTooltip(url: string): VueCompData<typeof CustomFaceTooltip> {
455
+ return {
456
+ comp: CustomFaceTooltip,
457
+ model: undefined,
458
+ props: { url }
459
+ }
460
+ }
461
+ </script>
@@ -0,0 +1,129 @@
1
+ <!--
2
+ * @FileDescription: 群文件列表项模板
3
+ * @Author: Stapxs
4
+ * @Date: missing
5
+ * @Version: 1.0
6
+ -->
7
+
8
+ <template>
9
+ <div :class="(item.folder_id ? ' folder' : '') + (item.items && item.items.length > 0 ? ' open' : '')"
10
+ @click="loadFileDir(item)">
11
+ <font-awesome-icon v-if="item.folder_id" :icon="['fas', 'folder']" />
12
+ <font-awesome-icon v-else :icon="['fas', 'file']" />
13
+ <div class="main">
14
+ <span>{{ toHtml(item.folder_name ?? item.file_name) }}</span>
15
+ <div>
16
+ <span>{{ toHtml(item.creater_name ?? item.uploader_name) }}</span>
17
+ <span>{{
18
+ (item.create_time || item.upload_time) ? Intl.DateTimeFormat(trueLang, {
19
+ year: 'numeric',
20
+ month: 'short',
21
+ day: 'numeric',
22
+ }).format(new Date((item.create_time ?? item.upload_time) * 1000)) : '-'
23
+ }}</span>
24
+ <span v-if="!item.dead_time && item.dead_time">{{
25
+ item.dead_time - item.create_time / 86400 - 1 + $t('天后')
26
+ }}</span>
27
+ <span v-if="item.folder_id">{{
28
+ $t('共 {num} 个文件', { num: item.count })
29
+ }}</span>
30
+ <span v-else>{{ getSize(item.size) }}</span>
31
+ </div>
32
+ </div>
33
+ <template v-if="item.file_id">
34
+ <div class="download"
35
+ @click="getFile(item)">
36
+ <font-awesome-icon :icon="['fas', 'angle-down']" />
37
+ </div>
38
+ </template>
39
+ <div v-show="item.show_items !== false && item.items !== undefined"
40
+ :class="(item.items !== undefined ? 'sub_file ' : '') + 'group-files'">
41
+ <div v-for="sub_item in item.items"
42
+ :key="'sub_file-' + sub_item.file_id">
43
+ <FileBody :chat="chat"
44
+ :item="(sub_item as GroupFileElem & GroupFileFolderElem)"
45
+ :parent="item.folder_id" />
46
+ </div>
47
+ </div>
48
+ </div>
49
+ </template>
50
+
51
+ <script setup lang="ts">
52
+ import { i18n } from '../main'
53
+
54
+ import {
55
+ getTrueLang,
56
+ escape2Html,
57
+ getSizeFromBytes,
58
+ } from '../function/utils/systemUtil'
59
+ import {
60
+ GroupFileElem,
61
+ GroupFileFolderElem
62
+ } from '../function/elements/information'
63
+ import { Connector } from '../function/connect'
64
+ import { useAuthStore } from '../state/auth'
65
+ import { useChatStore } from '../state/chat'
66
+
67
+ defineOptions({ name: 'FileBody' })
68
+
69
+ const authStore = useAuthStore()
70
+ const chatStore = useChatStore()
71
+ const $t = i18n.global.t
72
+
73
+ defineProps({
74
+ item: {
75
+ type: Object as () => GroupFileElem & GroupFileFolderElem,
76
+ required: true,
77
+ },
78
+ chat: {
79
+ type: Object,
80
+ required: true,
81
+ },
82
+ parent: {
83
+ type: String,
84
+ required: false,
85
+ default: undefined,
86
+ },
87
+ })
88
+
89
+ const trueLang = getTrueLang()
90
+ const getSize = getSizeFromBytes
91
+ const toHtml = escape2Html
92
+
93
+ /**
94
+ * 下载文件(获取文件下载地址并下载)
95
+ */
96
+ function getFile(item: GroupFileElem) {
97
+ const name = authStore.jsonMap.file_download?.name
98
+ if(name) {
99
+ Connector.send(
100
+ name,
101
+ {
102
+ group_id: chatStore.chatInfo.show.id,
103
+ file_id: item.file_id,
104
+ },
105
+ 'downloadGroupFile_' + item.file_id + '_' + btoa(encodeURIComponent(item.file_name)),
106
+ )
107
+ }
108
+ }
109
+
110
+ /**
111
+ * 加载子文件夹
112
+ */
113
+ function loadFileDir(item: GroupFileElem & GroupFileFolderElem) {
114
+ const id = item.folder_id
115
+
116
+ const name = authStore.jsonMap.group_folder_files?.name
117
+ if(item.items !== undefined) {
118
+ item.show_items = !item.show_items
119
+ return
120
+ }
121
+
122
+ if (id && name) {
123
+ Connector.send(name, {
124
+ folder_id: id,
125
+ group_id: chatStore.chatInfo.show.id,
126
+ }, 'getGroupDirFiles_' + id)
127
+ }
128
+ }
129
+ </script>