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,171 @@
1
+ <!--
2
+ * @FileDescription: 聊天面板页面(系统消息面板)
3
+ * @Author: Stapxs
4
+ * @Date: 2023/01/09
5
+ * @Version: 1.0 - 初始版本
6
+ * @Description: 此面板为点击系统消息后单独显示的面板,用于覆盖聊天面板
7
+ -->
8
+
9
+ <template>
10
+ <div id="chat-pan"
11
+ :class=" 'chat-pan sys-not-pan' +
12
+ (uiStore.openSideBar ? ' open' : '') +
13
+ (['linux', 'win32'].includes(backend.platform ?? '') ? ' withBar' : '')">
14
+ <div>
15
+ <font-awesome-icon :icon="['fas', 'angle-left']" @click="exit" />
16
+ <span>{{ $t('系统消息') }}</span>
17
+ </div>
18
+ <div class="sys-not-list">
19
+ <template v-for="(notice, index) in contactStore.systemNoticesList"
20
+ :key="'sysNot-' + index">
21
+ <div v-if="notice.request_type == 'friend'">
22
+ <div>
23
+ <img :src="notice.avatar || '/img/icons/icon.svg'">
24
+ <div>
25
+ <span>{{ notice.user_id }}
26
+ {{ $t('请求加为好友') }}</span>
27
+ <a>{{
28
+ Intl.DateTimeFormat(trueLang, {
29
+ year: 'numeric',
30
+ month: 'short',
31
+ day: 'numeric',
32
+ hour: 'numeric',
33
+ minute: 'numeric',
34
+ }).format(new Date(notice.time * 1000))
35
+ }}</a>
36
+ <a>{{ $t('留言:') + notice.comment }}</a>
37
+ </div>
38
+ </div>
39
+ <div>
40
+ <button class="ss-button"
41
+ @click="dealFriend(notice, false)">
42
+ {{ $t('拒绝') }}
43
+ </button>
44
+ <button class="ss-button"
45
+ @click="dealFriend(notice, true)">
46
+ {{ $t('同意') }}
47
+ </button>
48
+ </div>
49
+ </div>
50
+ <div v-else-if="notice.request_type == 'group'">
51
+ <div v-if="notice.sub_type == 'add'">
52
+ <img :src="notice.avatar || '/img/icons/icon.svg'">
53
+ <div>
54
+ <span>{{ getName(notice.user_id) }}
55
+ {{ $t('申请加入群聊') }}
56
+ {{ getName(notice.group_id) }}</span>
57
+ <a>{{
58
+ Intl.DateTimeFormat(trueLang, {
59
+ year: 'numeric',
60
+ month: 'short',
61
+ day: 'numeric',
62
+ hour: 'numeric',
63
+ minute: 'numeric',
64
+ }).format(new Date(notice.time * 1000))
65
+ }}</a>
66
+ <a>{{ $t('留言:') + notice.comment }}</a>
67
+ </div>
68
+ </div>
69
+ <div v-else>
70
+ <!-- TODO:这情况会出现在 notice 里?记不太清了,先放着吧 😭 -->
71
+ <img :src="notice.group_avatar || '/img/icons/icon.svg'">
72
+ <div>
73
+ <span>{{ getName(notice.user_id) }}
74
+ {{ $t('邀请你加入群聊') }}
75
+ {{ notice.group_id }}</span>
76
+ <a>{{
77
+ Intl.DateTimeFormat(trueLang, {
78
+ year: 'numeric',
79
+ month: 'short',
80
+ day: 'numeric',
81
+ hour: 'numeric',
82
+ minute: 'numeric',
83
+ }).format(new Date(notice.time * 1000))
84
+ }}</a>
85
+ <a>{{ $t('留言:') + notice.comment }}</a>
86
+ </div>
87
+ </div>
88
+ <div>
89
+ <button class="ss-button" @click="dealGroupAdd(notice, false)">
90
+ {{ $t('拒绝') }}
91
+ </button>
92
+ <button class="ss-button" @click="dealGroupAdd(notice, true)">
93
+ {{ $t('同意') }}
94
+ </button>
95
+ </div>
96
+ </div>
97
+ <div v-else v-show="dev">
98
+ <div>
99
+ <img>
100
+ <div>
101
+ <span>{{ $t('sys_notice_unknow') }}</span>
102
+ <a style="color: var(--color-font-2);word-wrap: anywhere;">
103
+ request: {{ notice.request_type }}; sub: {{ notice.sub_type }}
104
+ </a>
105
+ </div>
106
+ </div>
107
+ </div>
108
+ </template>
109
+ </div>
110
+ </div>
111
+ </template>
112
+
113
+ <script lang="ts" setup>
114
+ import { Connector } from '../../function/connect'
115
+ import { getTrueLang } from '../../function/utils/systemUtil'
116
+ import { backend } from '../../runtime/backend'
117
+ import { i18n } from '../../main'
118
+ import { useUIStore } from '../../state/ui'
119
+ import { useContactStore } from '../../state/contact'
120
+
121
+ defineOptions({ name: 'ChatSystemNotice' })
122
+
123
+ const uiStore = useUIStore()
124
+ const contactStore = useContactStore()
125
+ const emit = defineEmits(['userClick'])
126
+
127
+ const $t = i18n.global.t
128
+ const trueLang = getTrueLang()
129
+ const dev = import.meta.env.DEV
130
+
131
+ function exit() {
132
+ emit('userClick', { id: 0 })
133
+ }
134
+
135
+ function dealFriend(notice: { flag: string }, deal: boolean) {
136
+ Connector.send(
137
+ 'set_friend_add_request',
138
+ {
139
+ flag: notice.flag,
140
+ approve: deal,
141
+ },
142
+ 'setFriendAdd_' + notice.flag,
143
+ )
144
+ }
145
+
146
+ function dealGroupAdd(
147
+ notice: { flag: string; sub_type: string },
148
+ deal: boolean,
149
+ ) {
150
+ Connector.send(
151
+ 'set_group_add_request',
152
+ {
153
+ flag: notice.flag,
154
+ approve: deal,
155
+ sub_type: notice.sub_type,
156
+ },
157
+ 'setGroupAdd_' + notice.flag,
158
+ )
159
+ }
160
+
161
+ function getName(id: number) {
162
+ const knowUser = contactStore.userList.filter(
163
+ (item) => item.user_id == id || item.group_id == id,
164
+ )
165
+ if (knowUser.length > 0) {
166
+ return knowUser[0].nickname || knowUser[0].group_name
167
+ } else {
168
+ return null
169
+ }
170
+ }
171
+ </script>
@@ -0,0 +1,107 @@
1
+ <!--
2
+ - @FileDescription: 设置页面(账号子页面)
3
+ - @Author: Stapxs
4
+ - @Date: 2022/9/29
5
+ 2022/12/9
6
+ - @Version: 1.0 - 初始版本
7
+ 1.5 - 重构为 ts 版本,代码格式优化
8
+ -->
9
+
10
+ <template>
11
+ <div class="opt-page account-page">
12
+ <template v-if="accounts.length > 0">
13
+ <div v-for="account in accounts"
14
+ :key="account.platform + ':' + account.selfId"
15
+ :class="['ss-card', 'account-info', { active: isActive(account) }]">
16
+ <img :src="account.avatar || '/img/icons/icon.svg'">
17
+ <div>
18
+ <div>
19
+ <span>{{ account.name || account.selfId }}</span>
20
+ <span>{{ account.selfId }}</span>
21
+ </div>
22
+ <span class="platform">{{ account.platform }}</span>
23
+ </div>
24
+ </div>
25
+ </template>
26
+ <template v-else>
27
+ <div class="ss-card account-not-login">
28
+ <font-awesome-icon :icon="['fas', 'fish']" />
29
+ <span>{{ $t('还没有连接到 Satori 耶') }}</span>
30
+ <button class="ss-button" @click="goLogin">
31
+ {{ $t('去连接') }}
32
+ </button>
33
+ </div>
34
+ </template>
35
+ </div>
36
+ </template>
37
+
38
+ <script setup lang="ts">
39
+ import { computed } from 'vue'
40
+ import { login as loginInfo } from '../../function/connect'
41
+ import { getActiveBot } from '../../function/satori'
42
+ import { useAuthStore } from '../../state/auth'
43
+ import { i18n } from '../../main'
44
+
45
+ defineOptions({ name: 'ViewOptAccount' })
46
+
47
+ const $t = i18n.global.t
48
+ const authStore = useAuthStore()
49
+ interface SatoriAccount {
50
+ platform: string
51
+ selfId: string
52
+ name?: string
53
+ avatar?: string
54
+ status?: number
55
+ features?: string[]
56
+ }
57
+
58
+ const accounts = computed<SatoriAccount[]>(() => {
59
+ const stored = loginInfo.satoriLogins ?? authStore.loginInfo.satoriLogins
60
+ return Array.isArray(stored) ? stored as SatoriAccount[] : []
61
+ })
62
+
63
+ const activeBot = computed(() => {
64
+ const current = getActiveBot()
65
+ if (current?.platform && current.selfId) return current
66
+ return {
67
+ platform: String(authStore.loginInfo.platform ?? ''),
68
+ selfId: String(authStore.loginInfo.uin ?? authStore.loginInfo.selectedSatoriBot ?? ''),
69
+ }
70
+ })
71
+
72
+ function isActive(account: SatoriAccount) {
73
+ return activeBot.value.platform === account.platform && activeBot.value.selfId === account.selfId
74
+ }
75
+
76
+ function goLogin() {
77
+ document.getElementById('bar-msg')?.click()
78
+ }
79
+ </script>
80
+
81
+ <style scoped>
82
+ .account-page {
83
+ height: 100%;
84
+ overflow-y: auto;
85
+ overflow-x: hidden;
86
+ padding-right: 7px;
87
+ scrollbar-width: thin;
88
+ }
89
+ .account-info {
90
+ margin-bottom: 10px;
91
+ border-left: 3px solid transparent;
92
+ }
93
+ .account-info.active {
94
+ border-left-color: var(--color-main);
95
+ }
96
+ .account-info .platform {
97
+ font-size: 0.75rem;
98
+ opacity: 0.65;
99
+ }
100
+ .account-info > svg {
101
+ cursor: pointer;
102
+ opacity: 0.8;
103
+ }
104
+ .account-info > svg:hover {
105
+ opacity: 1;
106
+ }
107
+ </style>
@@ -0,0 +1,185 @@
1
+ <!--
2
+ * @FileDescription: 设置页面(附加子页面)
3
+ * @Author: Stapxs
4
+ * @Date: 2026/04/07
5
+ * @Version: 1.0
6
+ -->
7
+ <!-- eslint-disable max-len -->
8
+
9
+ <template>
10
+ <div class="opt-page">
11
+ <template v-if="cards.length > 0">
12
+ <div v-for="card in cards" :key="card.id" class="ss-card">
13
+ <header>{{ card.title }}</header>
14
+ <div v-if="card.description" class="tip">
15
+ {{ card.description }}
16
+ </div>
17
+ <div v-for="item in card.items" :key="item.id" class="opt-item">
18
+ <template v-if="item.type === 'switch'">
19
+ <font-awesome-icon
20
+ :icon="item.icon || ['fas', 'robot']" />
21
+ <div>
22
+ <label :for="`opt-addon-switch-${item.id}`">{{ item.label }}</label>
23
+ <span v-if="item.description">{{ item.description }}</span>
24
+ </div>
25
+ <label class="ss-switch">
26
+ <input :id="`opt-addon-switch-${item.id}`" :checked="getItemValue(item) === true"
27
+ type="checkbox" @change="onSwitchChange($event, item)">
28
+ <div><div /></div>
29
+ </label>
30
+ </template>
31
+ <template v-else-if="item.type === 'input'">
32
+ <font-awesome-icon
33
+ :icon="item.icon || ['fas', 'paper-plane']" />
34
+ <div>
35
+ <label :for="`opt-addon-input-${item.id}`">{{ item.label }}</label>
36
+ <span v-if="item.description">{{ item.description }}</span>
37
+ </div>
38
+ <input :id="`opt-addon-input-${item.id}`" :value="getItemValue(item) ?? ''" class="ss-input"
39
+ style="width: 150px"
40
+ type="text" @input="onInputChange($event, item)">
41
+ </template>
42
+ <template v-else-if="item.type === 'password'">
43
+ <font-awesome-icon
44
+ :icon="item.icon || ['fas', 'key']" />
45
+ <div>
46
+ <label :for="`opt-addon-password-${item.id}`">{{ item.label }}</label>
47
+ <span v-if="item.description">{{ item.description }}</span>
48
+ </div>
49
+ <input :id="`opt-addon-password-${item.id}`" :value="getItemValue(item) ?? ''" class="ss-input"
50
+ style="width: 150px"
51
+ type="password" @input="onInputChange($event, item)">
52
+ </template>
53
+ <template v-else-if="item.type === 'select'">
54
+ <font-awesome-icon v-if="item.icon" :icon="item.icon" />
55
+ <div v-if="item.label || item.description">
56
+ <label v-if="item.label" :for="`opt-addon-select-${item.id}`">{{ item.label }}</label>
57
+ <span v-if="item.description">{{ item.description }}</span>
58
+ </div>
59
+ <div class="select-wrapper">
60
+ <select
61
+ :id="`opt-addon-select-${item.id}`"
62
+ :value="getItemValue(item) ?? (item.options?.[0]?.value ?? '')"
63
+ @change="onSelectChange($event, item)">
64
+ <option
65
+ v-for="opt in item.options || []"
66
+ :key="String(opt.value)"
67
+ :value="opt.value">
68
+ {{ opt.label }}
69
+ </option>
70
+ </select>
71
+ </div>
72
+ </template>
73
+ <template v-else-if="item.type === 'button'">
74
+ <font-awesome-icon v-if="item.icon" :icon="item.icon" />
75
+ <div v-if="item.label || item.description">
76
+ <span v-if="item.label">{{ item.label }}</span>
77
+ <span v-if="item.description">{{ item.description }}</span>
78
+ </div>
79
+ <button class="ss-button" style="width: 100px; font-size: 0.8rem" @click="onClickButton(item)">
80
+ {{ item.label }}
81
+ </button>
82
+ </template>
83
+ </div>
84
+ </div>
85
+ </template>
86
+ <template v-else>
87
+ <div class="ss-card empty">
88
+ <font-awesome-icon :icon="['fas', 'box-open']" />
89
+ <span>{{ $t('什么都木有') }}</span>
90
+ <a>{{ $t('附加设置页设置项来自页面主题或其他插件注册,现在看起来什么都没有呢。') }}</a>
91
+ <a>{{ $t('注意:有些功能的设置项可能需要在触发后才会被注册。') }}</a>
92
+ </div>
93
+ </template>
94
+ </div>
95
+ </template>
96
+
97
+ <script setup lang="ts">
98
+ import { i18n } from '../../main'
99
+ import { useSettingsStore } from '../../state/settings'
100
+ import {
101
+ extraOptionCards,
102
+ runAS,
103
+ type ExtraOptionItem,
104
+ } from '../../function/option'
105
+
106
+ defineOptions({ name: 'ViewOptAddon' })
107
+
108
+ const $t = i18n.global.t
109
+ const settingsStore = useSettingsStore()
110
+
111
+ const cards = extraOptionCards
112
+
113
+ function getItemValue(item: ExtraOptionItem): any {
114
+ const key = item.optionKey
115
+ if (key && settingsStore.sysConfig &&
116
+ Object.prototype.hasOwnProperty.call(settingsStore.sysConfig, key)) {
117
+ return (settingsStore.sysConfig as any)[key]
118
+ }
119
+ return item.defaultValue
120
+ }
121
+
122
+ function handleChange(item: ExtraOptionItem, value: any) {
123
+ if (item.optionKey) {
124
+ runAS(item.optionKey, value)
125
+ }
126
+ if (typeof item.callback === 'function') {
127
+ try {
128
+ item.callback(value)
129
+ } catch (e) {
130
+ // 回调异常不影响主流程
131
+ // eslint-disable-next-line no-console
132
+ console.error('extra option callback error:', e)
133
+ }
134
+ }
135
+ }
136
+
137
+ function onSwitchChange(event: Event, item: ExtraOptionItem) {
138
+ const sender = event.target as HTMLInputElement
139
+ handleChange(item, sender.checked)
140
+ }
141
+
142
+ function onInputChange(event: Event, item: ExtraOptionItem) {
143
+ const sender = event.target as HTMLInputElement
144
+ handleChange(item, sender.value)
145
+ }
146
+
147
+ function onSelectChange(event: Event, item: ExtraOptionItem) {
148
+ const sender = event.target as HTMLSelectElement
149
+ handleChange(item, sender.value)
150
+ }
151
+
152
+ function onClickButton(item: ExtraOptionItem) {
153
+ if (typeof item.callback === 'function') {
154
+ try {
155
+ item.callback(getItemValue(item))
156
+ } catch (e) {
157
+ // eslint-disable-next-line no-console
158
+ console.error('extra option button callback error:', e)
159
+ }
160
+ }
161
+ }
162
+ </script>
163
+ <style scoped>
164
+ .empty {
165
+ margin: 0 auto;
166
+ width: 50%;
167
+ display: flex;
168
+ flex-direction: column;
169
+ align-items: center;
170
+ }
171
+ .empty svg {
172
+ width: 50px;
173
+ height: 50px;
174
+ margin-bottom: 10px;
175
+ color: var(--color-main);
176
+ }
177
+ .empty span {
178
+ color: var(--color-main);
179
+ margin-bottom: 10px;
180
+ font-size: 0.9rem;
181
+ }
182
+ .empty a {
183
+ font-size: 0.8rem;
184
+ }
185
+ </style>