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.
Files changed (237) hide show
  1. package/client/vue/index.vue +60 -634
  2. package/client/web/dist/assets/AppIcon-DIku6-h0.png +0 -0
  3. package/client/web/dist/assets/AppIconDark-BYymN71t.png +0 -0
  4. package/client/web/dist/assets/CapacitorAppIcon-CSZwNhvi.png +0 -0
  5. package/client/web/dist/assets/CapacitorAppIconDark-BMvMOzsg.png +0 -0
  6. package/client/web/dist/assets/Chat-CTdyeaX2.css +1 -0
  7. package/client/web/dist/assets/Chat-DkZvD7d8.js +28 -0
  8. package/client/web/dist/assets/MealHungryPan-Beos3lRI.js +1 -0
  9. package/client/web/dist/assets/MsgBody-aZ4_aija.js +1 -0
  10. package/client/web/dist/assets/MsgBody.vue_vue_type_script_setup_true_lang-CPZjRLp6.js +67 -0
  11. package/client/web/dist/assets/NapcatAppIcon-C7VOAkW9.png +0 -0
  12. package/client/web/dist/assets/Vue-lEhHouxB.png +0 -0
  13. package/client/web/dist/assets/append_darwin-CqBNU62J.css +1 -0
  14. package/client/web/dist/assets/append_full_vibrancy-j0D2LM9m.css +1 -0
  15. package/client/web/dist/assets/append_linux-uhxeBqeC.css +1 -0
  16. package/client/web/dist/assets/append_linux_vibrancy-COiZq8MS.css +1 -0
  17. package/client/web/dist/assets/append_napcat-DV8CAyAd.css +1 -0
  18. package/client/web/dist/assets/append_new-407Lo5_V.css +1 -0
  19. package/client/web/dist/assets/append_vibrancy-6FHctkOY.css +1 -0
  20. package/client/web/dist/assets/append_win32-srmD-78y.css +1 -0
  21. package/client/web/dist/assets/cakes-CxTVKdnK.svg +189 -0
  22. package/client/web/dist/assets/core-DhEqZVGG.js +1 -0
  23. package/client/web/dist/assets/event-Cadp6m6J.js +1 -0
  24. package/client/web/dist/assets/index-CPTXiemn.js +4045 -0
  25. package/client/web/dist/assets/index-Cg_esKB0.js +1 -0
  26. package/client/web/dist/assets/index-Cln012sP.css +1 -0
  27. package/client/web/dist/assets//346/226/271/346/255/243/345/226/265/345/221/234/344/275/223-CJKIKBn3.ttf +0 -0
  28. package/client/web/dist/assets//346/234/237/345/276/205-C90_OY60.png +0 -0
  29. package/client/web/dist/bcui/css/color-dark.css +35 -0
  30. package/client/web/dist/bcui/css/color-light.css +35 -0
  31. package/client/web/dist/bcui/css/style.css +468 -0
  32. package/client/web/dist/bcui/js/main.js +193 -0
  33. package/client/web/dist/css/append-dark.css +13 -0
  34. package/client/web/dist/css/append-light.css +13 -0
  35. package/client/web/dist/css/append-napcat-dark.css +14 -0
  36. package/client/web/dist/css/append-napcat-light.css +14 -0
  37. package/client/web/dist/favicon.ico +0 -0
  38. package/client/web/dist/img/icons/icon-for-fuck-apple.png +0 -0
  39. package/client/web/dist/img/icons/icon-maskable.png +0 -0
  40. package/client/web/dist/img/icons/icon.png +0 -0
  41. package/client/web/dist/img/icons/icon.svg +10 -0
  42. package/client/web/dist/index.html +22 -0
  43. package/client/web/dist/robots.txt +2 -0
  44. package/client/web/dist/sw.js +58 -0
  45. package/client/web/env.d.ts +56 -0
  46. package/client/web/index.html +21 -0
  47. package/client/web/package-lock.json +3419 -0
  48. package/client/web/package.json +62 -0
  49. package/client/web/public/bcui/css/color-dark.css +35 -0
  50. package/client/web/public/bcui/css/color-light.css +35 -0
  51. package/client/web/public/bcui/css/style.css +468 -0
  52. package/client/web/public/bcui/js/main.js +193 -0
  53. package/client/web/public/css/append-dark.css +13 -0
  54. package/client/web/public/css/append-light.css +13 -0
  55. package/client/web/public/css/append-napcat-dark.css +14 -0
  56. package/client/web/public/css/append-napcat-light.css +14 -0
  57. package/client/web/public/favicon.ico +0 -0
  58. package/client/web/public/img/icons/icon-for-fuck-apple.png +0 -0
  59. package/client/web/public/img/icons/icon-maskable.png +0 -0
  60. package/client/web/public/img/icons/icon.png +0 -0
  61. package/client/web/public/img/icons/icon.svg +10 -0
  62. package/client/web/public/robots.txt +2 -0
  63. package/client/web/public/sw.js +58 -0
  64. package/client/web/scripts/copy-bcui.mjs +17 -0
  65. package/client/web/src/App.vue +1127 -0
  66. package/client/web/src/assets/css/append/append_darwin.css +68 -0
  67. package/client/web/src/assets/css/append/append_full_vibrancy.css +290 -0
  68. package/client/web/src/assets/css/append/append_linux.css +125 -0
  69. package/client/web/src/assets/css/append/append_linux_vibrancy.css +22 -0
  70. package/client/web/src/assets/css/append/append_napcat.css +35 -0
  71. package/client/web/src/assets/css/append/append_new.css +843 -0
  72. package/client/web/src/assets/css/append/append_vibrancy.css +113 -0
  73. package/client/web/src/assets/css/append/append_win32.css +109 -0
  74. package/client/web/src/assets/css/append/mobile/append_mobile_horizontal.css +349 -0
  75. package/client/web/src/assets/css/append/mobile/append_mobile_vertical.css +96 -0
  76. package/client/web/src/assets/css/chat.css +2104 -0
  77. package/client/web/src/assets/css/msg.css +941 -0
  78. package/client/web/src/assets/css/options.css +811 -0
  79. package/client/web/src/assets/css/sys_notice.css +62 -0
  80. package/client/web/src/assets/css/view.css +2105 -0
  81. package/client/web/src/assets/font//346/226/271/346/255/243/345/226/265/345/221/234/344/275/223.ttf +0 -0
  82. package/client/web/src/assets/img/Vue.png +0 -0
  83. package/client/web/src/assets/img/cakes.svg +189 -0
  84. package/client/web/src/assets/img/icons/AppIcon.png +0 -0
  85. package/client/web/src/assets/img/icons/AppIconDark.png +0 -0
  86. package/client/web/src/assets/img/icons/CapacitorAppIcon.png +0 -0
  87. package/client/web/src/assets/img/icons/CapacitorAppIconDark.png +0 -0
  88. package/client/web/src/assets/img/icons/NapcatAppIcon.png +0 -0
  89. package/client/web/src/assets/img/icons/NapcatAppIconDark.png +0 -0
  90. package/client/web/src/assets/img/icons/exit.png +0 -0
  91. package/client/web/src/assets/img/icons/pen.png +0 -0
  92. package/client/web/src/assets/img/stars.svg +89 -0
  93. package/client/web/src/assets/img//346/234/237/345/276/205.png +0 -0
  94. package/client/web/src/assets/l10n/_About I10n.md +35 -0
  95. package/client/web/src/assets/l10n/_l10nconfig.json +7 -0
  96. package/client/web/src/assets/l10n/zh-CN.po +1941 -0
  97. package/client/web/src/assets/logo.png +0 -0
  98. package/client/web/src/assets/pathMap/LLOneBot.yaml +2 -0
  99. package/client/web/src/assets/pathMap/Lagrange.OneBot.yaml +180 -0
  100. package/client/web/src/assets/pathMap/NapCat.Onebot.yaml +224 -0
  101. package/client/web/src/assets/pathMap/SnowLuma.yaml +80 -0
  102. package/client/web/src/assets/qed.txt +1 -0
  103. package/client/web/src/components/AboutPan.vue +197 -0
  104. package/client/web/src/components/BulletinBody.vue +129 -0
  105. package/client/web/src/components/DepPan.vue +310 -0
  106. package/client/web/src/components/EmojiFace.vue +32 -0
  107. package/client/web/src/components/FacePan.vue +461 -0
  108. package/client/web/src/components/FileBody.vue +129 -0
  109. package/client/web/src/components/FileManager.vue +532 -0
  110. package/client/web/src/components/FriendBody.vue +54 -0
  111. package/client/web/src/components/GlobalSessionSearchBar.vue +301 -0
  112. package/client/web/src/components/History.vue +49 -0
  113. package/client/web/src/components/LazyLottie.vue +82 -0
  114. package/client/web/src/components/MergePan.vue +127 -0
  115. package/client/web/src/components/MsgBody.vue +1478 -0
  116. package/client/web/src/components/MusicPlayer.vue +549 -0
  117. package/client/web/src/components/NoticeBody.vue +168 -0
  118. package/client/web/src/components/QzonePermissionPan.vue +150 -0
  119. package/client/web/src/components/RawMsgRenderPreviewPan.vue +190 -0
  120. package/client/web/src/components/ThemeColorPickerPan.vue +420 -0
  121. package/client/web/src/components/TinySessionBody.vue +51 -0
  122. package/client/web/src/components/UmamiInfoPan.vue +1549 -0
  123. package/client/web/src/components/UpdatePan.vue +263 -0
  124. package/client/web/src/components/ViewerCom.vue +1360 -0
  125. package/client/web/src/components/VoiceMsg.vue +626 -0
  126. package/client/web/src/components/WelPan.vue +894 -0
  127. package/client/web/src/components/msg-body/.gitkeep +0 -0
  128. package/client/web/src/components/msg-component/JsonSegComp.vue +138 -0
  129. package/client/web/src/components/msg-component/XmlSegComp.vue +132 -0
  130. package/client/web/src/components/msg-component/jsonComp/AutoReply.vue +97 -0
  131. package/client/web/src/components/msg-component/jsonComp/Contact.lua.vue +129 -0
  132. package/client/web/src/components/msg-component/jsonComp/Feed.lua.vue +94 -0
  133. package/client/web/src/components/msg-component/jsonComp/Forum.vue +84 -0
  134. package/client/web/src/components/msg-component/jsonComp/Mannounce.vue +111 -0
  135. package/client/web/src/components/msg-component/jsonComp/Map.vue +52 -0
  136. package/client/web/src/components/msg-component/jsonComp/Miniapp.lua.vue +53 -0
  137. package/client/web/src/components/msg-component/jsonComp/Miniapp.vue +69 -0
  138. package/client/web/src/components/msg-component/jsonComp/Music.lua.vue +135 -0
  139. package/client/web/src/components/msg-component/jsonComp/Tuwen.lua.vue +56 -0
  140. package/client/web/src/components/notice-component/.gitkeep +0 -0
  141. package/client/web/src/components/notice-component/MealHungryPan.vue +119 -0
  142. package/client/web/src/components/tooltip/CustomFaceTooltip.vue +22 -0
  143. package/client/web/src/components/tooltip/Tooltip.vue +50 -0
  144. package/client/web/src/components/tooltip/Tooltips.vue +14 -0
  145. package/client/web/src/components/tooltip/UserInfoTooltip.vue +69 -0
  146. package/client/web/src/function/base.ts +238 -0
  147. package/client/web/src/function/connect.ts +2042 -0
  148. package/client/web/src/function/debug.ts +10 -0
  149. package/client/web/src/function/elements/information.ts +141 -0
  150. package/client/web/src/function/elements/system.ts +101 -0
  151. package/client/web/src/function/elements/vueComp.ts +62 -0
  152. package/client/web/src/function/favicon.ts +76 -0
  153. package/client/web/src/function/input.ts +69 -0
  154. package/client/web/src/function/model/emoji.ts +127 -0
  155. package/client/web/src/function/model/img.ts +150 -0
  156. package/client/web/src/function/model/msg-body.ts +51 -0
  157. package/client/web/src/function/msg.ts +2674 -0
  158. package/client/web/src/function/notify.ts +225 -0
  159. package/client/web/src/function/option.ts +749 -0
  160. package/client/web/src/function/satori-model.ts +818 -0
  161. package/client/web/src/function/satori.ts +325 -0
  162. package/client/web/src/function/sender.ts +178 -0
  163. package/client/web/src/function/tooltip.ts +49 -0
  164. package/client/web/src/function/utils/appUtil.ts +2288 -0
  165. package/client/web/src/function/utils/avatarUtil.ts +31 -0
  166. package/client/web/src/function/utils/backgroundUtil.ts +110 -0
  167. package/client/web/src/function/utils/linkViewUtil.ts +101 -0
  168. package/client/web/src/function/utils/localHistoryUtil.ts +517 -0
  169. package/client/web/src/function/utils/msgUtil.ts +1242 -0
  170. package/client/web/src/function/utils/pinyin.ts +141 -0
  171. package/client/web/src/function/utils/recordUtil.ts +187 -0
  172. package/client/web/src/function/utils/sessionUtil.ts +114 -0
  173. package/client/web/src/function/utils/systemUtil.ts +664 -0
  174. package/client/web/src/main.ts +91 -0
  175. package/client/web/src/pages/Chat.vue +3453 -0
  176. package/client/web/src/pages/Friends.vue +636 -0
  177. package/client/web/src/pages/Info.vue +717 -0
  178. package/client/web/src/pages/Messages.vue +875 -0
  179. package/client/web/src/pages/Options.vue +82 -0
  180. package/client/web/src/pages/Qzone.vue +1178 -0
  181. package/client/web/src/pages/chat-view/ChatDanmaku.vue +759 -0
  182. package/client/web/src/pages/chat-view/ChatGlagame.vue +642 -0
  183. package/client/web/src/pages/chat-view/ChatTerminal.vue +1044 -0
  184. package/client/web/src/pages/chat-view/SystemNotice.vue +171 -0
  185. package/client/web/src/pages/options/OptAccount.vue +107 -0
  186. package/client/web/src/pages/options/OptAddon.vue +185 -0
  187. package/client/web/src/pages/options/OptDev.vue +704 -0
  188. package/client/web/src/pages/options/OptFunction.vue +403 -0
  189. package/client/web/src/pages/options/OptInfo.vue +165 -0
  190. package/client/web/src/pages/options/OptView.vue +846 -0
  191. package/client/web/src/runtime/backend.ts +310 -0
  192. package/client/web/src/state/auth.ts +14 -0
  193. package/client/web/src/state/chat.ts +36 -0
  194. package/client/web/src/state/connection.ts +20 -0
  195. package/client/web/src/state/contact.ts +37 -0
  196. package/client/web/src/state/qzone.ts +18 -0
  197. package/client/web/src/state/sessionHistory.ts +25 -0
  198. package/client/web/src/state/settings.ts +18 -0
  199. package/client/web/src/state/sticker.ts +10 -0
  200. package/client/web/src/state/ui.ts +56 -0
  201. package/client/web/tsconfig.json +27 -0
  202. package/client/web/tsconfig.tsbuildinfo +1 -0
  203. package/client/web/vite.config.ts +33 -0
  204. package/dist/index.js +1 -12
  205. package/dist/style.css +1 -1
  206. package/lib/index.js +2070 -2077
  207. package/package.json +15 -2
  208. package/readme.md +0 -22
  209. package/src/bootstrap.ts +71 -0
  210. package/src/cache.ts +319 -0
  211. package/src/config.ts +23 -35
  212. package/src/database.ts +650 -0
  213. package/src/index.ts +27 -59
  214. package/src/media.ts +302 -0
  215. package/src/recorder.ts +175 -0
  216. package/src/self-message.ts +456 -0
  217. package/src/server.d.ts +26 -0
  218. package/src/types.ts +108 -48
  219. package/src/web.ts +712 -0
  220. package/client/vue/chat-logic.ts +0 -683
  221. package/client/vue/composables/useChatActions.ts +0 -61
  222. package/client/vue/composables/useChatData.ts +0 -185
  223. package/client/vue/composables/useImageCache.ts +0 -281
  224. package/client/vue/composables/useVideoCache.ts +0 -125
  225. package/client/vue/types.ts +0 -72
  226. package/lib/api-handlers.d.ts +0 -25
  227. package/lib/config.d.ts +0 -14
  228. package/lib/file-manager.d.ts +0 -124
  229. package/lib/index.d.ts +0 -17
  230. package/lib/logger.d.ts +0 -9
  231. package/lib/message-handler.d.ts +0 -35
  232. package/lib/types.d.ts +0 -57
  233. package/lib/utils.d.ts +0 -18
  234. package/src/api-handlers.ts +0 -646
  235. package/src/file-manager.ts +0 -1436
  236. package/src/message-handler.ts +0 -507
  237. package/src/utils.ts +0 -189
@@ -0,0 +1,717 @@
1
+ <!--
2
+ * @FileDescription: 群 / 好友信息页面
3
+ * @Author: Stapxs
4
+ * @Date: missing
5
+ * @Version: 1.0
6
+ -->
7
+
8
+ <template>
9
+ <div v-if="tags.openChatInfo"
10
+ class="chat-info-pan">
11
+ <div class="ss-card chat-info">
12
+ <header>
13
+ <span v-if="chat.show.type === 'group'">{{ $t('群资料') }}</span>
14
+ <span v-if="chat.show.type === 'user'">{{ $t('好友') }}</span>
15
+ <font-awesome-icon :icon="['fas', 'xmark']" @click="closeChatInfoPan" />
16
+ </header>
17
+ <div class="chat-info-base user">
18
+ <div>
19
+ <img :src="chat.show.avatar || '/img/icons/icon.svg'" @error="avatarError">
20
+ <div>
21
+ <a>{{ chat.show.name }}</a>
22
+ <span>{{ chat.show.id }}</span>
23
+ </div>
24
+ <div style="display: flex;align-items: center;justify-content: center;cursor: pointer;"
25
+ @click="copyText(chat.show.id)">
26
+ <font-awesome-icon :icon="['fas', 'copy']" />
27
+ </div>
28
+ </div>
29
+ <div v-if="chat.show.type === 'group'">
30
+ <header>
31
+ <span>{{ $t('群组 ID') }}</span>
32
+ </header>
33
+ <div class="info-copy">
34
+ <span>{{ chat.show.id }}</span>
35
+ <font-awesome-icon :icon="['fas', 'copy']" @click="copyText(chat.show.id)" />
36
+ </div>
37
+ <header>
38
+ <span>{{ $t('频道 ID') }}</span>
39
+ </header>
40
+ <div class="info-copy">
41
+ <span>{{ groupChannelId() }}</span>
42
+ <font-awesome-icon :icon="['fas', 'copy']" @click="copyText(groupChannelId())" />
43
+ </div>
44
+ <header>
45
+ <span>{{ $t('群头像') }}</span>
46
+ </header>
47
+ <div class="info-copy">
48
+ <span style="word-break: break-all;">{{ isDefaultAvatar(chat.show.avatar) ? $t('无') : chat.show.avatar }}</span>
49
+ <font-awesome-icon v-if="!isDefaultAvatar(chat.show.avatar)"
50
+ :icon="['fas', 'copy']" @click="copyText(chat.show.avatar)" />
51
+ </div>
52
+ </div>
53
+ <div v-if="chat.show.type === 'group'"
54
+ v-show="Object.keys(chat.info.group_info).length > 0">
55
+ <header>
56
+ <span>{{ $t('介绍') }}</span>
57
+ </header>
58
+ <span v-html=" chat.info.group_info.gIntro === undefined || chat.info.group_info.gIntro === '' ?
59
+ $t('群主很懒,还没有群介绍哦~') : chat.info.group_info.gIntro" />
60
+ <div class="tags">
61
+ <div v-for="item in chat.info.group_info.tags" :key="item.md">
62
+ {{ item.tag }}
63
+ </div>
64
+ </div>
65
+ </div>
66
+ <div v-else-if="chat.show.type === 'user'">
67
+ <header>
68
+ <span>{{ $t('用户 ID') }}</span>
69
+ </header>
70
+ <div class="info-copy">
71
+ <span>{{ chat.show.id }}</span>
72
+ <font-awesome-icon :icon="['fas', 'copy']" @click="copyText(chat.show.id)" />
73
+ </div>
74
+ <header>
75
+ <span>{{ $t('用户名称') }}</span>
76
+ </header>
77
+ <div class="info-copy">
78
+ <span>{{ chat.show.name }}</span>
79
+ <font-awesome-icon :icon="['fas', 'copy']" @click="copyText(chat.show.name)" />
80
+ </div>
81
+ <header>
82
+ <span>{{ $t('用户头像链接') }}</span>
83
+ </header>
84
+ <div class="info-copy">
85
+ <span style="word-break: break-all;">{{ isDefaultAvatar(chat.show.avatar) ? $t('无') : chat.show.avatar }}</span>
86
+ <font-awesome-icon v-if="!isDefaultAvatar(chat.show.avatar)"
87
+ :icon="['fas', 'copy']" @click="copyText(chat.show.avatar)" />
88
+ </div>
89
+ <header>
90
+ <span>{{ $t('频道 ID') }}</span>
91
+ </header>
92
+ <div class="info-copy">
93
+ <span>{{ userChannelId() }}</span>
94
+ <font-awesome-icon :icon="['fas', 'copy']" @click="copyText(userChannelId())" />
95
+ </div>
96
+ </div>
97
+ </div>
98
+ <BcTab v-if="chat.show.type === 'group'"
99
+ class="chat-info-tab">
100
+ <div :name="$t('成员')">
101
+ <div class="search-view">
102
+ <label for="info-member-search" class="sr-only">{{ $t('搜索成员') }}</label>
103
+ <input id="info-member-search" :placeholder="$t('搜索 ……')" @input="searchList($event)">
104
+ </div>
105
+ <RecycleScroller
106
+ v-slot="{ item }"
107
+ class="member-scroller"
108
+ :items="memberSearch.trim() ? number_cache : chat.info.group_members"
109
+ :item-size="60"
110
+ key-field="user_id">
111
+ <div class="member-item edit">
112
+ <img alt="nk" loading="lazy"
113
+ :src="memberAvatar(item) || '/img/icons/icon.svg'">
114
+ <div>
115
+ <a @click="startChat(item)">{{
116
+ memberName(item)
117
+ }}</a>
118
+ <font-awesome-icon v-if="memberRole(item) === 'owner'" :icon="['fas', 'crown']" />
119
+ <font-awesome-icon v-if="memberRole(item) === 'admin'" :icon="['fas', 'star']" />
120
+ </div>
121
+ <!-- 在手机端戳 id 就能触发 -->
122
+ <span @click="moreConfig(item)">{{ memberId(item) }}</span>
123
+ <font-awesome-icon v-if="canEditMember(memberRole(item))" :icon="['fas', 'wrench']" @click="moreConfig(item)" />
124
+ <font-awesome-icon v-else :icon="['fas', 'copy']" @click="copyText(memberId(item))" />
125
+ </div>
126
+ </RecycleScroller>
127
+ </div>
128
+ </BcTab>
129
+ <div :class="'ss-card user-config' + (Object.keys(showUserConfig).length > 0 ? ' show' : '')">
130
+ <div>
131
+ <img alt="nk" :src="showUserConfig.avatar || '/img/icons/icon.svg'">
132
+ <div>
133
+ <a>{{ showUserConfig.card != '' ? showUserConfig.card : showUserConfig.nickname }}</a>
134
+ <span>{{ showUserConfig.user_id }}</span>
135
+ </div>
136
+ <font-awesome-icon
137
+ style="margin-right: 20px;"
138
+ :icon="['fas', 'copy']"
139
+ @click="copyText(showUserConfig.user_id)" />
140
+ <font-awesome-icon :icon="['fas', 'angle-down']" @click="showUserConfig = {}" />
141
+ </div>
142
+ <div>
143
+ <header>{{ $t('成员信息') }}</header>
144
+ <div class="opt-item">
145
+ <font-awesome-icon :icon="['fas', 'clipboard-list']" />
146
+ <div>
147
+ <label for="info-member-card">{{ $t('成员昵称') }}</label>
148
+ <span>{{
149
+ $t('啊吧啊吧……')
150
+ }}</span>
151
+ </div>
152
+ <input id="info-member-card" v-model="showUserConfigRaw.card"
153
+ style="width: 50%"
154
+ class="ss-input"
155
+ type="text"
156
+ @change="updateMumberCard($event, showUserConfig)">
157
+ </div>
158
+ <div v-if="chat.info.me_info.role === 'owner'" class="opt-item">
159
+ <font-awesome-icon :icon="['fas', 'clipboard-list']" />
160
+ <div>
161
+ <label for="info-member-title">{{ $t('成员头衔') }}</label>
162
+ <span>{{
163
+ $t('猪咪猪咪')
164
+ }}</span>
165
+ </div>
166
+ <input id="info-member-title" v-model="showUserConfigRaw.title"
167
+ style="width: 50%"
168
+ class="ss-input"
169
+ type="text"
170
+ @change="updateMumberTitle($event, showUserConfig)">
171
+ </div>
172
+ <template v-if="(chat.info.me_info.role === 'owner' && showUserConfig.role != 'owner') || (chat.info.me_info.role === 'admin' && showUserConfig.role === 'member')">
173
+ <header>{{ $t('操作') }}</header>
174
+ <div class="opt-item">
175
+ <font-awesome-icon :icon="['fas', 'clipboard-list']" />
176
+ <div>
177
+ <label for="info-member-ban-min">{{ $t('禁言成员') }}</label>
178
+ <span>{{
179
+ $t('要让小猫咪不许说话几分钟呢?')
180
+ }}</span>
181
+ </div>
182
+ <input id="info-member-ban-min" v-model="mumberInfo.banMin"
183
+ style="width: 50%"
184
+ class="ss-input"
185
+ type="text"
186
+ @input="checkNumber"
187
+ @change="banMumber($event, showUserConfig)">
188
+ </div>
189
+ <button class="ss-button"
190
+ @click="removeUser(showUserConfig.nickname, chat.show.id, showUserConfig.user_id)">
191
+ {{ $t('移出群聊') }}
192
+ </button>
193
+ </template>
194
+ </div>
195
+ </div>
196
+ </div>
197
+ </div>
198
+ </template>
199
+
200
+ <script setup lang="ts">
201
+ import app, { i18n } from '../main'
202
+ import { avatarError } from '../function/utils/avatarUtil'
203
+ import BcTab from 'vue3-bcui/packages/bc-tab'
204
+ import { RecycleScroller } from 'vue-virtual-scroller'
205
+ import 'vue-virtual-scroller/dist/vue-virtual-scroller.css'
206
+
207
+ import { Connector, loadGroupMembersFromCache } from '../function/connect'
208
+ import { PopInfo, PopType } from '../function/base'
209
+ import { toRaw, ref, nextTick, watch } from 'vue'
210
+ import { delay } from '../function/utils/systemUtil'
211
+ import { useAuthStore } from '../state/auth'
212
+ import { useContactStore } from '../state/contact'
213
+ import { useChatStore } from '../state/chat'
214
+ import { useUIStore } from '../state/ui'
215
+ import {
216
+ UserFriendElem,
217
+ UserGroupElem,
218
+ } from '../function/elements/information'
219
+
220
+ defineOptions({ name: 'ViewInfo' })
221
+
222
+ const authStore = useAuthStore()
223
+ const contactStore = useContactStore()
224
+ const chatStore = useChatStore()
225
+ const uiStore = useUIStore()
226
+
227
+ const props = defineProps<{
228
+ tags: any
229
+ chat: any
230
+ }>()
231
+
232
+ const emit = defineEmits<{
233
+ close: []
234
+ }>()
235
+
236
+ const { t: $t } = i18n.global
237
+
238
+ function isDefaultAvatar(value: unknown): boolean {
239
+ const avatar = String(value ?? '')
240
+ return !avatar || avatar === '/img/icons/icon.svg' || avatar.includes('/img/icons/icon.svg')
241
+ }
242
+
243
+ function userChannelId(): string {
244
+ return String(
245
+ props.chat.show.channel_id
246
+ || props.chat.show.channelId
247
+ || props.chat.info.user_info.channel_id
248
+ || props.chat.info.user_info.channelId
249
+ || `private:${props.chat.show.id}`,
250
+ )
251
+ }
252
+
253
+ function groupChannelId(): string {
254
+ return String(
255
+ props.chat.show.channel_id
256
+ || props.chat.show.channelId
257
+ || props.chat.info.group_info.channel_id
258
+ || props.chat.info.group_info.channelId
259
+ || `group:${props.chat.show.id}`,
260
+ )
261
+ }
262
+
263
+ // Reactive state
264
+ const number_cache = ref<any[]>([])
265
+ const memberSearch = ref('')
266
+ const showUserConfig = ref<any>({})
267
+ const showUserConfigRaw = ref<any>({})
268
+
269
+ interface MemberLike {
270
+ user_id?: unknown
271
+ card?: unknown
272
+ nickname?: unknown
273
+ name?: unknown
274
+ avatar?: unknown
275
+ role?: unknown
276
+ }
277
+
278
+ function asMember(value: unknown): MemberLike {
279
+ if (typeof value !== 'object' || value === null) return {}
280
+ return value as MemberLike
281
+ }
282
+
283
+ function memberId(value: unknown): string {
284
+ return String(asMember(value).user_id ?? '')
285
+ }
286
+
287
+ function memberName(value: unknown): string {
288
+ const member = asMember(value)
289
+ return String(member.card || member.nickname || member.name || memberId(value))
290
+ }
291
+
292
+ function memberAvatar(value: unknown): string {
293
+ return String(asMember(value).avatar ?? '')
294
+ }
295
+
296
+ function memberRole(value: unknown): string {
297
+ return String(asMember(value).role ?? '')
298
+ }
299
+
300
+ const mumberInfo = ref({
301
+ banMin: 0,
302
+ })
303
+
304
+ /**
305
+ * 移出群聊
306
+ */
307
+ function removeUser(nickname: string, group_id: number, user_id: number) {
308
+ const popInfo = {
309
+ title: $t('提醒'),
310
+ html: `<span>${$t('真的要将 {user} 移出群聊吗', { user: nickname })}</span>`,
311
+ button: [
312
+ {
313
+ text: $t('确定'),
314
+ fun: () => {
315
+ Connector.send(
316
+ 'set_group_kick',
317
+ {
318
+ group_id: group_id,
319
+ user_id: user_id,
320
+ },
321
+ 'setGroupKick',
322
+ )
323
+ uiStore.popBoxList.shift()
324
+ showUserConfig.value = {}
325
+ const popInfo = {
326
+ title: $t('操作'),
327
+ html: `<span>${$t('正在确认操作……')}</span>`
328
+ }
329
+ uiStore.popBoxList.push(popInfo)
330
+ // 稍微等一下再刷新成员列表
331
+ delay(1000).then(() => {
332
+ void loadGroupMembersFromCache(String(chatStore.chatInfo.show.id))
333
+ return delay(1000)
334
+ }).then(() => {
335
+ void loadGroupMembersFromCache(String(chatStore.chatInfo.show.id))
336
+ uiStore.popBoxList.shift()
337
+ })
338
+ },
339
+ },
340
+ {
341
+ text: $t('取消'),
342
+ master: true,
343
+ fun: () => {
344
+ uiStore.popBoxList.shift()
345
+ },
346
+ },
347
+ ],
348
+ }
349
+ uiStore.popBoxList.push(popInfo)
350
+ }
351
+
352
+ function copyText(text: any) {
353
+ const popInfo = new PopInfo()
354
+ app.config.globalProperties.$copyText(String(text)).then(
355
+ () => {
356
+ popInfo.add(PopType.INFO, $t('复制成功'), true)
357
+ },
358
+ () => {
359
+ popInfo.add(PopType.ERR, $t('复制失败'), true)
360
+ },
361
+ )
362
+ }
363
+
364
+ function banMumber(event: Event, info: any) {
365
+ const value = (event.target as HTMLInputElement).value
366
+ if (value !== '') {
367
+ const num = parseInt(value)
368
+ if (num > 0) {
369
+ const popInfo = {
370
+ title: $t('操作'),
371
+ html: `<span>${$t('确认禁言?')}</span>`,
372
+ button: [
373
+ {
374
+ text: $t('确认'),
375
+ fun: () => {
376
+ const name = authStore.jsonMap.ban_mumber?.name
377
+ if (name)
378
+ Connector.send(name, {
379
+ group_id: chatStore.chatInfo.show.id,
380
+ user_id: info.user_id,
381
+ duration: num * 60,
382
+ }, 'banMumber')
383
+ uiStore.popBoxList.shift()
384
+ closeChatInfoPan()
385
+ },
386
+ },
387
+ {
388
+ text: $t('取消'),
389
+ master: true,
390
+ fun: () => {
391
+ showUserConfigRaw.value = JSON.parse(JSON.stringify(info))
392
+ uiStore.popBoxList.shift()
393
+ },
394
+ },
395
+ ],
396
+ }
397
+ uiStore.popBoxList.push(popInfo)
398
+ }
399
+ }
400
+ }
401
+
402
+ function updateMumberCard(event: Event, info: any) {
403
+ const value = (event.target as HTMLInputElement).value
404
+ if (showUserConfig.value.card !== value) {
405
+ const popInfo = {
406
+ title: $t('操作'),
407
+ html: `<span>${$t('确认修改昵称?')}</span>`,
408
+ button: [
409
+ {
410
+ text: $t('确认'),
411
+ fun: () => {
412
+ const name = authStore.jsonMap.set_group_nickname?.name
413
+ if(name)
414
+ Connector.send(name, {
415
+ group_id: chatStore.chatInfo.show.id,
416
+ user_id: info.user_id,
417
+ card: value,
418
+ }, 'updateGroupMemberInfo')
419
+ uiStore.popBoxList.shift()
420
+ closeChatInfoPan()
421
+ },
422
+ },
423
+ {
424
+ text: $t('取消'),
425
+ master: true,
426
+ fun: () => {
427
+ showUserConfigRaw.value = JSON.parse(JSON.stringify(info))
428
+ uiStore.popBoxList.shift()
429
+ },
430
+ },
431
+ ],
432
+ }
433
+ uiStore.popBoxList.push(popInfo)
434
+ }
435
+ }
436
+
437
+ function updateMumberTitle(event: Event, info: any) {
438
+ const value = (event.target as HTMLInputElement).value
439
+ if (showUserConfig.value.card !== value) {
440
+ const popInfo = {
441
+ title: $t('操作'),
442
+ html: `<span>${$t('确认修改头衔?')}</span>`,
443
+ button: [
444
+ {
445
+ text: $t('确认'),
446
+ fun: () => {
447
+ const name = authStore.jsonMap.set_group_title?.name
448
+ if(name)
449
+ Connector.send(name, {
450
+ group_id: chatStore.chatInfo.show.id,
451
+ user_id: info.user_id,
452
+ special_title: value,
453
+ }, 'updateGroupMemberInfo')
454
+ uiStore.popBoxList.shift()
455
+ closeChatInfoPan()
456
+ },
457
+ },
458
+ {
459
+ text: $t('取消'),
460
+ master: true,
461
+ fun: () => {
462
+ showUserConfigRaw.value = JSON.parse(JSON.stringify(info))
463
+ uiStore.popBoxList.shift()
464
+ },
465
+ },
466
+ ],
467
+ }
468
+ uiStore.popBoxList.push(popInfo)
469
+ }
470
+ }
471
+
472
+ function getBanTimeMin(endTime: number) {
473
+ // endTime 可能是精确到秒的时间戳
474
+ if(endTime < 10000000000) {
475
+ endTime *= 1000
476
+ }
477
+ const now = new Date().getTime()
478
+ const time = endTime - now
479
+ if (time > 0) {
480
+ return Math.floor(time / 1000 / 60)
481
+ } else {
482
+ return 0
483
+ }
484
+ }
485
+
486
+ function checkNumber(event: Event) {
487
+ const value = (event.target as HTMLInputElement).value
488
+ if (value !== '') {
489
+ const num = parseInt(value)
490
+ if (isNaN(num)) {
491
+ (event.target as HTMLInputElement).value = ''
492
+ } else if (num < 0) {
493
+ (event.target as HTMLInputElement).value = '0'
494
+ }
495
+ }
496
+ }
497
+
498
+ /**
499
+ * 关闭面板
500
+ */
501
+ function closeChatInfoPan() {
502
+ showUserConfig.value = {}
503
+ emit('close')
504
+ }
505
+
506
+ /**
507
+ * 发起聊天
508
+ */
509
+ function startChat(info: any) {
510
+ // 如果是自己的话就忽略
511
+ if (info.user_id != authStore.loginInfo.uin) {
512
+
513
+ // 检查这个人是不是好友
514
+ let chat = contactStore.userList.find(
515
+ (item: UserFriendElem & UserGroupElem) => {
516
+ return item.user_id == info.user_id
517
+ },
518
+ )
519
+ if (!chat) {
520
+ // 创建一个临时聊天
521
+ const user = {
522
+ user_id: info.user_id,
523
+ // 因为临时消息没有返回昵称
524
+ nickname:
525
+ $t('临时会话'),
526
+ remark: info.user_id,
527
+ group_id: info.group_id,
528
+ group_name: '',
529
+ } as UserFriendElem & UserGroupElem
530
+ chat = user
531
+ }
532
+ contactStore.baseOnMsgList.set(String(info.user_id), chat)
533
+ // 切换到这个聊天
534
+ nextTick(() => {
535
+ if (chat) {
536
+ const item = document.getElementById(
537
+ 'user-' + chat.user_id,
538
+ )
539
+ if (item) {
540
+ item.click()
541
+ }
542
+ }
543
+ })
544
+ }
545
+ }
546
+
547
+ function moreConfig(info: any) {
548
+ if(canEditMember(info.role)) {
549
+ showUserConfig.value = info
550
+ showUserConfigRaw.value = JSON.parse(JSON.stringify(info))
551
+ // 初始化一些内容
552
+ mumberInfo.value.banMin = getBanTimeMin(info.shut_up_timestamp)
553
+ } else {
554
+ copyText(info.user_id)
555
+ }
556
+ }
557
+
558
+ function searchList(event?: Event) {
559
+ if (event) {
560
+ memberSearch.value = (event.target as HTMLInputElement).value
561
+ }
562
+ const keyword = memberSearch.value.trim()
563
+ if (!keyword) {
564
+ number_cache.value = [] as any[]
565
+ return
566
+ }
567
+ const lower = keyword.toLowerCase()
568
+ const source = Array.isArray(props.chat.info.group_members)
569
+ ? props.chat.info.group_members
570
+ : []
571
+ number_cache.value = toRaw(source).filter((item: any) => {
572
+ const name = memberName(item).toLowerCase()
573
+ const id = memberId(item)
574
+ return name.includes(lower) || id.includes(keyword) || id.toLowerCase().includes(lower)
575
+ })
576
+ }
577
+
578
+ watch(() => props.chat.info.group_members, () => {
579
+ if (memberSearch.value.trim()) searchList()
580
+ }, { deep: true })
581
+
582
+ function canEditMember(role: string) {
583
+ return (
584
+ props.chat.info.me_info.role === 'owner' ||
585
+ (props.chat.info.me_info.role === 'admin'
586
+ && role !== 'owner') // 管理员不能编辑群主
587
+ )
588
+ }
589
+ </script>
590
+
591
+ <style scoped>
592
+ .search-view {
593
+ background: transparent !important;
594
+ padding: 0 20px;
595
+ margin-bottom: 10px;
596
+ }
597
+ .search-view > input {
598
+ background: var(--color-card-1);
599
+ border-radius: 7px;
600
+ padding: 0 10px;
601
+ height: 35px;
602
+ width: 100%;
603
+ border: 0;
604
+ }
605
+
606
+ div[name="成员"] {
607
+ overflow: hidden;
608
+ }
609
+ /* 虚拟滚动容器样式 */
610
+ .member-scroller {
611
+ flex: 1;
612
+ height: calc(100vh - 330px);
613
+ min-height: 210px;
614
+ }
615
+
616
+ /* 成员项样式 */
617
+ .member-item {
618
+ transition: background 0.3s;
619
+ margin: 0 20px -10px 20px;
620
+ align-items: center;
621
+ border-radius: 7px;
622
+ cursor: pointer;
623
+ display: flex;
624
+ padding: 10px;
625
+ }
626
+
627
+ .member-item:hover {
628
+ background: var(--color-card-1);
629
+ }
630
+
631
+ .member-item > img {
632
+ border-radius: 100%;
633
+ margin-right: 10px;
634
+ height: 30px;
635
+ width: 30px;
636
+ }
637
+
638
+ .member-item > div {
639
+ overflow: hidden;
640
+ align-items: center;
641
+ display: flex;
642
+ flex: 1;
643
+ }
644
+
645
+ .member-item > div > a {
646
+ color: var(--color-font);
647
+ white-space: nowrap;
648
+ text-overflow: ellipsis;
649
+ max-width: 80%;
650
+ overflow: hidden;
651
+ cursor: pointer;
652
+ }
653
+
654
+ .member-item > div > svg {
655
+ color: var(--color-main);
656
+ margin-left: 5px;
657
+ height: 0.8rem;
658
+ }
659
+
660
+ .member-item > span {
661
+ color: var(--color-font-2);
662
+ transition: all .2s;
663
+ opacity: 1;
664
+ }
665
+
666
+ .member-item.edit:hover > span {
667
+ transform: translateX(-10px);
668
+ opacity: 0;
669
+ }
670
+
671
+ .member-item > svg {
672
+ color: var(--color-font-2);
673
+ transition: all .2s;
674
+ margin-right: -25px;
675
+ margin-left: 10px;
676
+ width: 15px;
677
+ opacity: 0;
678
+ }
679
+
680
+ .member-item > svg:hover {
681
+ color: var(--color-main);
682
+ }
683
+
684
+ .member-item.edit:hover > svg {
685
+ margin-right: 5px;
686
+ display: block;
687
+ opacity: 1;
688
+ }
689
+
690
+ .info-copy {
691
+ display: flex;
692
+ align-items: center;
693
+ gap: 8px;
694
+ }
695
+ .info-copy > span {
696
+ flex: 1;
697
+ min-width: 0;
698
+ }
699
+ .info-copy > svg {
700
+ color: var(--color-font-2);
701
+ cursor: pointer;
702
+ }
703
+ .info-copy > svg:hover {
704
+ color: var(--color-main);
705
+ }
706
+ </style>
707
+ <style>
708
+ .tab-body {
709
+ overflow: hidden !important;
710
+ flex-direction: column;
711
+ display: flex;
712
+ }
713
+ .tab-body > div {
714
+ overflow: scroll;
715
+ flex: 1;
716
+ }
717
+ </style>