sa2kit 3.2.0 → 3.2.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 (217) hide show
  1. package/dist/CollisionBalls-DgKtscU2.d.mts +41 -0
  2. package/dist/CollisionBalls-DgKtscU2.d.ts +41 -0
  3. package/dist/ConfigService-Oga_zFRS.d.mts +262 -0
  4. package/dist/ConfigService-Oga_zFRS.d.ts +262 -0
  5. package/dist/UniversalFileService-CC4d3wkc.d.ts +139 -0
  6. package/dist/UniversalFileService-CzAE_G4V.d.mts +139 -0
  7. package/dist/boothVaultService-lKcnyA-u.d.mts +83 -0
  8. package/dist/boothVaultService-lKcnyA-u.d.ts +83 -0
  9. package/dist/business/audioDetection/index.d.mts +2 -0
  10. package/dist/business/audioDetection/index.d.ts +2 -0
  11. package/dist/business/audioDetection/index.js +1244 -0
  12. package/dist/business/audioDetection/index.js.map +1 -0
  13. package/dist/business/audioDetection/index.mjs +1227 -0
  14. package/dist/business/audioDetection/index.mjs.map +1 -0
  15. package/dist/business/calendar/index.d.mts +6 -0
  16. package/dist/business/calendar/index.d.ts +6 -0
  17. package/dist/business/calendar/index.js +7433 -0
  18. package/dist/business/calendar/index.js.map +1 -0
  19. package/dist/business/calendar/index.mjs +7257 -0
  20. package/dist/business/calendar/index.mjs.map +1 -0
  21. package/dist/business/calendar/routes/index.d.mts +191 -0
  22. package/dist/business/calendar/routes/index.d.ts +191 -0
  23. package/dist/business/calendar/routes/index.js +844 -0
  24. package/dist/business/calendar/routes/index.js.map +1 -0
  25. package/dist/business/calendar/routes/index.mjs +826 -0
  26. package/dist/business/calendar/routes/index.mjs.map +1 -0
  27. package/dist/business/festivalCard/index.d.mts +4 -0
  28. package/dist/business/festivalCard/index.d.ts +4 -0
  29. package/dist/business/festivalCard/index.js +1492 -0
  30. package/dist/business/festivalCard/index.js.map +1 -0
  31. package/dist/business/festivalCard/index.mjs +1475 -0
  32. package/dist/business/festivalCard/index.mjs.map +1 -0
  33. package/dist/business/festivalCard/routes/index.d.mts +42 -0
  34. package/dist/business/festivalCard/routes/index.d.ts +42 -0
  35. package/dist/business/festivalCard/routes/index.js +361 -0
  36. package/dist/business/festivalCard/routes/index.js.map +1 -0
  37. package/dist/business/festivalCard/routes/index.mjs +356 -0
  38. package/dist/business/festivalCard/routes/index.mjs.map +1 -0
  39. package/dist/business/festivalCard/server/index.d.mts +120 -0
  40. package/dist/business/festivalCard/server/index.d.ts +120 -0
  41. package/dist/business/festivalCard/server/index.js +272 -0
  42. package/dist/business/festivalCard/server/index.js.map +1 -0
  43. package/dist/business/festivalCard/server/index.mjs +265 -0
  44. package/dist/business/festivalCard/server/index.mjs.map +1 -0
  45. package/dist/business/index.d.mts +34 -0
  46. package/dist/business/index.d.ts +34 -0
  47. package/dist/business/index.js +29282 -0
  48. package/dist/business/index.js.map +1 -0
  49. package/dist/business/index.mjs +29237 -0
  50. package/dist/business/index.mjs.map +1 -0
  51. package/dist/business/mikuContest/ui/web/index.d.mts +2 -0
  52. package/dist/business/mikuContest/ui/web/index.d.ts +2 -0
  53. package/dist/business/mikuContest/ui/web/index.js +353 -0
  54. package/dist/business/mikuContest/ui/web/index.js.map +1 -0
  55. package/dist/business/mikuContest/ui/web/index.mjs +343 -0
  56. package/dist/business/mikuContest/ui/web/index.mjs.map +1 -0
  57. package/dist/business/mikuFireworks3D/index.d.mts +2 -0
  58. package/dist/business/mikuFireworks3D/index.d.ts +2 -0
  59. package/dist/business/mikuFireworks3D/index.js +1267 -0
  60. package/dist/business/mikuFireworks3D/index.js.map +1 -0
  61. package/dist/business/mikuFireworks3D/index.mjs +1228 -0
  62. package/dist/business/mikuFireworks3D/index.mjs.map +1 -0
  63. package/dist/business/mikuFusionGame/index.d.mts +2 -0
  64. package/dist/business/mikuFusionGame/index.d.ts +2 -0
  65. package/dist/business/mikuFusionGame/index.js +1208 -0
  66. package/dist/business/mikuFusionGame/index.js.map +1 -0
  67. package/dist/business/mikuFusionGame/index.mjs +1195 -0
  68. package/dist/business/mikuFusionGame/index.mjs.map +1 -0
  69. package/dist/business/mmd/admin/index.d.mts +487 -0
  70. package/dist/business/mmd/admin/index.d.ts +487 -0
  71. package/dist/business/mmd/admin/index.js +1058 -0
  72. package/dist/business/mmd/admin/index.js.map +1 -0
  73. package/dist/business/mmd/admin/index.mjs +1027 -0
  74. package/dist/business/mmd/admin/index.mjs.map +1 -0
  75. package/dist/business/mmd/index.d.mts +5 -0
  76. package/dist/business/mmd/index.d.ts +5 -0
  77. package/dist/business/mmd/index.js +10119 -0
  78. package/dist/business/mmd/index.js.map +1 -0
  79. package/dist/business/mmd/index.mjs +10028 -0
  80. package/dist/business/mmd/index.mjs.map +1 -0
  81. package/dist/business/mmd/server/index.d.mts +139 -0
  82. package/dist/business/mmd/server/index.d.ts +139 -0
  83. package/dist/business/mmd/server/index.js +424 -0
  84. package/dist/business/mmd/server/index.js.map +1 -0
  85. package/dist/business/mmd/server/index.mjs +404 -0
  86. package/dist/business/mmd/server/index.mjs.map +1 -0
  87. package/dist/business/music/index.d.mts +3 -0
  88. package/dist/business/music/index.d.ts +3 -0
  89. package/dist/business/music/index.js +830 -0
  90. package/dist/business/music/index.js.map +1 -0
  91. package/dist/business/music/index.mjs +809 -0
  92. package/dist/business/music/index.mjs.map +1 -0
  93. package/dist/business/music/server/index.d.mts +1 -0
  94. package/dist/business/music/server/index.d.ts +1 -0
  95. package/dist/business/music/server/index.js +194 -0
  96. package/dist/business/music/server/index.js.map +1 -0
  97. package/dist/business/music/server/index.mjs +182 -0
  98. package/dist/business/music/server/index.mjs.map +1 -0
  99. package/dist/business/navigation/index.d.mts +2 -0
  100. package/dist/business/navigation/index.d.ts +2 -0
  101. package/dist/business/navigation/index.js +453 -0
  102. package/dist/business/navigation/index.js.map +1 -0
  103. package/dist/business/navigation/index.mjs +443 -0
  104. package/dist/business/navigation/index.mjs.map +1 -0
  105. package/dist/business/portfolio/index.d.mts +3 -0
  106. package/dist/business/portfolio/index.d.ts +3 -0
  107. package/dist/business/portfolio/index.js +736 -0
  108. package/dist/business/portfolio/index.js.map +1 -0
  109. package/dist/business/portfolio/index.mjs +724 -0
  110. package/dist/business/portfolio/index.mjs.map +1 -0
  111. package/dist/business/qqbot/server/index.d.mts +167 -0
  112. package/dist/business/qqbot/server/index.d.ts +167 -0
  113. package/dist/business/qqbot/server/index.js +394 -0
  114. package/dist/business/qqbot/server/index.js.map +1 -0
  115. package/dist/business/qqbot/server/index.mjs +385 -0
  116. package/dist/business/qqbot/server/index.mjs.map +1 -0
  117. package/dist/business/qqbot/ui/web/index.d.mts +10 -0
  118. package/dist/business/qqbot/ui/web/index.d.ts +10 -0
  119. package/dist/business/qqbot/ui/web/index.js +105 -0
  120. package/dist/business/qqbot/ui/web/index.js.map +1 -0
  121. package/dist/business/qqbot/ui/web/index.mjs +99 -0
  122. package/dist/business/qqbot/ui/web/index.mjs.map +1 -0
  123. package/dist/business/screenReceiver/index.d.mts +2 -0
  124. package/dist/business/screenReceiver/index.d.ts +2 -0
  125. package/dist/business/screenReceiver/index.js +281 -0
  126. package/dist/business/screenReceiver/index.js.map +1 -0
  127. package/dist/business/screenReceiver/index.mjs +273 -0
  128. package/dist/business/screenReceiver/index.mjs.map +1 -0
  129. package/dist/business/testYourself/admin/index.d.mts +58 -0
  130. package/dist/business/testYourself/admin/index.d.ts +58 -0
  131. package/dist/business/testYourself/admin/index.js +1009 -0
  132. package/dist/business/testYourself/admin/index.js.map +1 -0
  133. package/dist/business/testYourself/admin/index.mjs +1002 -0
  134. package/dist/business/testYourself/admin/index.mjs.map +1 -0
  135. package/dist/business/testYourself/index.d.mts +6 -0
  136. package/dist/business/testYourself/index.d.ts +6 -0
  137. package/dist/business/testYourself/index.js +2551 -0
  138. package/dist/business/testYourself/index.js.map +1 -0
  139. package/dist/business/testYourself/index.mjs +2531 -0
  140. package/dist/business/testYourself/index.mjs.map +1 -0
  141. package/dist/business/testYourself/server/index.d.mts +1029 -0
  142. package/dist/business/testYourself/server/index.d.ts +1029 -0
  143. package/dist/business/testYourself/server/index.js +825 -0
  144. package/dist/business/testYourself/server/index.js.map +1 -0
  145. package/dist/business/testYourself/server/index.mjs +816 -0
  146. package/dist/business/testYourself/server/index.mjs.map +1 -0
  147. package/dist/business/vocaloidBooth/index.d.mts +2 -0
  148. package/dist/business/vocaloidBooth/index.d.ts +2 -0
  149. package/dist/business/vocaloidBooth/index.js +172 -0
  150. package/dist/business/vocaloidBooth/index.js.map +1 -0
  151. package/dist/business/vocaloidBooth/index.mjs +166 -0
  152. package/dist/business/vocaloidBooth/index.mjs.map +1 -0
  153. package/dist/business/vocaloidBooth/server/index.d.mts +111 -0
  154. package/dist/business/vocaloidBooth/server/index.d.ts +111 -0
  155. package/dist/business/vocaloidBooth/server/index.js +247 -0
  156. package/dist/business/vocaloidBooth/server/index.js.map +1 -0
  157. package/dist/business/vocaloidBooth/server/index.mjs +237 -0
  158. package/dist/business/vocaloidBooth/server/index.mjs.map +1 -0
  159. package/dist/business/vocaloidBooth/web/index.d.mts +45 -0
  160. package/dist/business/vocaloidBooth/web/index.d.ts +45 -0
  161. package/dist/business/vocaloidBooth/web/index.js +376 -0
  162. package/dist/business/vocaloidBooth/web/index.js.map +1 -0
  163. package/dist/business/vocaloidBooth/web/index.mjs +362 -0
  164. package/dist/business/vocaloidBooth/web/index.mjs.map +1 -0
  165. package/dist/config-BQp3qLAL.d.mts +22 -0
  166. package/dist/config-BQp3qLAL.d.ts +22 -0
  167. package/dist/drizzle-schema-BNhqj2AZ.d.mts +1114 -0
  168. package/dist/drizzle-schema-BNhqj2AZ.d.ts +1114 -0
  169. package/dist/festivalCardService-D60G-sgr.d.mts +13 -0
  170. package/dist/festivalCardService-DnLyJpRh.d.ts +13 -0
  171. package/dist/index-BMgdH5dL.d.mts +1716 -0
  172. package/dist/index-BO9_Do5y.d.mts +93 -0
  173. package/dist/index-BO9_Do5y.d.ts +93 -0
  174. package/dist/index-BSmd4ikf.d.ts +76 -0
  175. package/dist/index-BSwvWYp2.d.mts +2632 -0
  176. package/dist/index-Bo_fW3Tl.d.mts +105 -0
  177. package/dist/index-Bo_fW3Tl.d.ts +105 -0
  178. package/dist/index-BrKazb8M.d.mts +148 -0
  179. package/dist/index-BrKazb8M.d.ts +148 -0
  180. package/dist/index-Bzh6QE4P.d.ts +25 -0
  181. package/dist/index-C5Ic6eSR.d.mts +25 -0
  182. package/dist/index-C8i9SIxk.d.ts +2632 -0
  183. package/dist/index-C_GhVhOT.d.mts +109 -0
  184. package/dist/index-C_GhVhOT.d.ts +109 -0
  185. package/dist/index-Cb3UEpG4.d.mts +101 -0
  186. package/dist/index-CjlkUj01.d.mts +103 -0
  187. package/dist/index-CucXCBNR.d.mts +302 -0
  188. package/dist/index-CucXCBNR.d.ts +302 -0
  189. package/dist/index-DLLPTprx.d.mts +1522 -0
  190. package/dist/index-DRiZy0dv.d.mts +525 -0
  191. package/dist/index-DRiZy0dv.d.ts +525 -0
  192. package/dist/index-Dc_I2t0P.d.mts +103 -0
  193. package/dist/index-DowAHRIP.d.mts +250 -0
  194. package/dist/index-DowAHRIP.d.ts +250 -0
  195. package/dist/index-Dpq_5H2n.d.ts +103 -0
  196. package/dist/index-Ds2M_9zb.d.ts +101 -0
  197. package/dist/index-IXMAeTtN.d.ts +1716 -0
  198. package/dist/index-VFDbZxVM.d.ts +1522 -0
  199. package/dist/index-jadkp96n.d.ts +103 -0
  200. package/dist/index-r2-zE3iC.d.mts +76 -0
  201. package/dist/index.d.mts +10682 -0
  202. package/dist/index.d.ts +10682 -0
  203. package/dist/index.js +38233 -0
  204. package/dist/index.js.map +1 -0
  205. package/dist/index.mjs +37959 -0
  206. package/dist/index.mjs.map +1 -0
  207. package/dist/types-B6B210gX.d.mts +270 -0
  208. package/dist/types-B6B210gX.d.ts +270 -0
  209. package/dist/types-B7voqjjA.d.mts +51 -0
  210. package/dist/types-B7voqjjA.d.ts +51 -0
  211. package/dist/types-Bdnte5EN.d.mts +292 -0
  212. package/dist/types-C2z_QQPI.d.mts +70 -0
  213. package/dist/types-C2z_QQPI.d.ts +70 -0
  214. package/dist/types-HorDyIRv.d.mts +303 -0
  215. package/dist/types-HorDyIRv.d.ts +303 -0
  216. package/dist/types-_rFX1atk.d.ts +292 -0
  217. package/package.json +3 -2
@@ -0,0 +1,2531 @@
1
+ import React2, { useState, useEffect, useRef, useCallback } from 'react';
2
+ import { clsx } from 'clsx';
3
+ import { AlertCircle, X, CheckCircle2, Upload, Plus, RefreshCw, Star, ExternalLink, Edit, Copy, Download, Trash2, Save, Image } from 'lucide-react';
4
+ import { eq, sql, and, or, desc } from 'drizzle-orm';
5
+ import { pgTable, timestamp, varchar, json, uuid, integer, boolean, text, index } from 'drizzle-orm/pg-core';
6
+
7
+ var __defProp = Object.defineProperty;
8
+ var __getOwnPropNames = Object.getOwnPropertyNames;
9
+ var __esm = (fn, res) => function __init() {
10
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
11
+ };
12
+ var __export = (target, all) => {
13
+ for (var name in all)
14
+ __defProp(target, name, { get: all[name], enumerable: true });
15
+ };
16
+
17
+ // src/business/testYourself/server/ConfigService.ts
18
+ var ConfigService_exports = {};
19
+ __export(ConfigService_exports, {
20
+ ConfigService: () => ConfigService,
21
+ createConfigService: () => createConfigService,
22
+ getDefaultConfigService: () => getDefaultConfigService
23
+ });
24
+ function createConfigService(options) {
25
+ return new ConfigService(options);
26
+ }
27
+ function getDefaultConfigService() {
28
+ if (!defaultServiceInstance) {
29
+ defaultServiceInstance = createConfigService();
30
+ }
31
+ return defaultServiceInstance;
32
+ }
33
+ var LocalStorageAdapter, MemoryStorageAdapter, ConfigService, defaultServiceInstance;
34
+ var init_ConfigService = __esm({
35
+ "src/business/testYourself/server/ConfigService.ts"() {
36
+ LocalStorageAdapter = class {
37
+ constructor() {
38
+ this.STORAGE_KEY = "test-yourself-configs";
39
+ this.DEFAULT_KEY = "test-yourself-default-config";
40
+ }
41
+ async getAllConfigsData() {
42
+ if (typeof window === "undefined") return [];
43
+ try {
44
+ const data = localStorage.getItem(this.STORAGE_KEY);
45
+ return data ? JSON.parse(data) : [];
46
+ } catch (error) {
47
+ console.error("\u8BFB\u53D6\u914D\u7F6E\u5931\u8D25:", error);
48
+ return [];
49
+ }
50
+ }
51
+ async saveAllConfigsData(configs) {
52
+ if (typeof window === "undefined") return;
53
+ try {
54
+ localStorage.setItem(this.STORAGE_KEY, JSON.stringify(configs));
55
+ } catch (error) {
56
+ console.error("\u4FDD\u5B58\u914D\u7F6E\u5931\u8D25:", error);
57
+ throw error;
58
+ }
59
+ }
60
+ async saveConfig(config) {
61
+ const configs = await this.getAllConfigsData();
62
+ const existingIndex = configs.findIndex((c) => c.id === config.id);
63
+ if (existingIndex >= 0) {
64
+ configs[existingIndex] = config;
65
+ } else {
66
+ configs.push(config);
67
+ }
68
+ await this.saveAllConfigsData(configs);
69
+ }
70
+ async getConfig(id) {
71
+ const configs = await this.getAllConfigsData();
72
+ return configs.find((c) => c.id === id) || null;
73
+ }
74
+ async getAllConfigs() {
75
+ return this.getAllConfigsData();
76
+ }
77
+ async deleteConfig(id) {
78
+ const configs = await this.getAllConfigsData();
79
+ const filtered = configs.filter((c) => c.id !== id);
80
+ await this.saveAllConfigsData(filtered);
81
+ }
82
+ async updateConfig(id, config) {
83
+ const configs = await this.getAllConfigsData();
84
+ const index2 = configs.findIndex((c) => c.id === id);
85
+ if (index2 >= 0) {
86
+ configs[index2] = { ...config, updatedAt: Date.now() };
87
+ await this.saveAllConfigsData(configs);
88
+ } else {
89
+ throw new Error("\u914D\u7F6E\u4E0D\u5B58\u5728: " + id);
90
+ }
91
+ }
92
+ async setDefaultConfig(id) {
93
+ if (typeof window === "undefined") return;
94
+ const config = await this.getConfig(id);
95
+ if (!config) {
96
+ throw new Error("\u914D\u7F6E\u4E0D\u5B58\u5728: " + id);
97
+ }
98
+ const configs = await this.getAllConfigsData();
99
+ configs.forEach((c) => c.isDefault = c.id === id);
100
+ await this.saveAllConfigsData(configs);
101
+ localStorage.setItem(this.DEFAULT_KEY, id);
102
+ }
103
+ async getDefaultConfig() {
104
+ if (typeof window === "undefined") return null;
105
+ try {
106
+ const defaultId = localStorage.getItem(this.DEFAULT_KEY);
107
+ if (defaultId) {
108
+ return await this.getConfig(defaultId);
109
+ }
110
+ const configs = await this.getAllConfigsData();
111
+ return configs.find((c) => c.isDefault) || null;
112
+ } catch (error) {
113
+ console.error("\u83B7\u53D6\u9ED8\u8BA4\u914D\u7F6E\u5931\u8D25:", error);
114
+ return null;
115
+ }
116
+ }
117
+ };
118
+ MemoryStorageAdapter = class {
119
+ constructor() {
120
+ this.configs = /* @__PURE__ */ new Map();
121
+ this.defaultConfigId = null;
122
+ }
123
+ async saveConfig(config) {
124
+ this.configs.set(config.id, config);
125
+ }
126
+ async getConfig(id) {
127
+ return this.configs.get(id) || null;
128
+ }
129
+ async getAllConfigs() {
130
+ return Array.from(this.configs.values());
131
+ }
132
+ async deleteConfig(id) {
133
+ this.configs.delete(id);
134
+ if (this.defaultConfigId === id) {
135
+ this.defaultConfigId = null;
136
+ }
137
+ }
138
+ async updateConfig(id, config) {
139
+ if (!this.configs.has(id)) {
140
+ throw new Error("\u914D\u7F6E\u4E0D\u5B58\u5728: " + id);
141
+ }
142
+ this.configs.set(id, { ...config, updatedAt: Date.now() });
143
+ }
144
+ async setDefaultConfig(id) {
145
+ if (!this.configs.has(id)) {
146
+ throw new Error("\u914D\u7F6E\u4E0D\u5B58\u5728: " + id);
147
+ }
148
+ this.configs.forEach((c) => c.isDefault = c.id === id);
149
+ this.defaultConfigId = id;
150
+ }
151
+ async getDefaultConfig() {
152
+ if (this.defaultConfigId) {
153
+ return this.configs.get(this.defaultConfigId) || null;
154
+ }
155
+ for (const config of this.configs.values()) {
156
+ if (config.isDefault) {
157
+ return config;
158
+ }
159
+ }
160
+ return null;
161
+ }
162
+ };
163
+ ConfigService = class {
164
+ constructor(options = {}) {
165
+ this.cache = /* @__PURE__ */ new Map();
166
+ const {
167
+ storageType = "localStorage",
168
+ customStorage,
169
+ enableCache = true
170
+ } = options;
171
+ this.enableCache = enableCache;
172
+ if (customStorage) {
173
+ this.storage = customStorage;
174
+ } else if (storageType === "memory") {
175
+ this.storage = new MemoryStorageAdapter();
176
+ } else {
177
+ this.storage = new LocalStorageAdapter();
178
+ }
179
+ }
180
+ /**
181
+ * 初始化服务(如果需要)
182
+ */
183
+ async init() {
184
+ }
185
+ /**
186
+ * 生成唯一ID
187
+ */
188
+ generateId() {
189
+ return "config_" + Date.now() + "_" + Math.random().toString(36).substring(2, 9);
190
+ }
191
+ /**
192
+ * 保存配置
193
+ */
194
+ async saveConfig(config) {
195
+ await this.storage.saveConfig(config);
196
+ if (config.isDefault) {
197
+ await this.storage.setDefaultConfig(config.id);
198
+ }
199
+ if (this.enableCache) {
200
+ this.cache.set(config.id, config);
201
+ }
202
+ }
203
+ /**
204
+ * 创建新配置
205
+ */
206
+ async createConfig(name, config, description, isDefault = false) {
207
+ const savedConfig = {
208
+ id: this.generateId(),
209
+ name,
210
+ description,
211
+ config,
212
+ createdAt: Date.now(),
213
+ updatedAt: Date.now(),
214
+ isDefault
215
+ };
216
+ await this.storage.saveConfig(savedConfig);
217
+ if (isDefault) {
218
+ await this.storage.setDefaultConfig(savedConfig.id);
219
+ }
220
+ if (this.enableCache) {
221
+ this.cache.set(savedConfig.id, savedConfig);
222
+ }
223
+ return savedConfig;
224
+ }
225
+ /**
226
+ * 获取配置
227
+ */
228
+ async getConfig(id) {
229
+ if (this.enableCache && this.cache.has(id)) {
230
+ return this.cache.get(id);
231
+ }
232
+ const config = await this.storage.getConfig(id);
233
+ if (config && this.enableCache) {
234
+ this.cache.set(id, config);
235
+ }
236
+ return config;
237
+ }
238
+ /**
239
+ * 获取所有配置列表
240
+ */
241
+ async getAllConfigs() {
242
+ return await this.storage.getAllConfigs();
243
+ }
244
+ /**
245
+ * 获取配置列表(精简版)
246
+ */
247
+ async getConfigList() {
248
+ const configs = await this.getAllConfigs();
249
+ return configs.map((c) => ({
250
+ id: c.id,
251
+ name: c.name,
252
+ description: c.description,
253
+ resultCount: c.config.results.length,
254
+ createdAt: c.createdAt,
255
+ updatedAt: c.updatedAt,
256
+ isDefault: c.isDefault
257
+ }));
258
+ }
259
+ /**
260
+ * 更新配置
261
+ */
262
+ async updateConfig(id, updates) {
263
+ const existing = await this.getConfig(id);
264
+ if (!existing) {
265
+ throw new Error("\u914D\u7F6E\u4E0D\u5B58\u5728: " + id);
266
+ }
267
+ const updated = {
268
+ ...existing,
269
+ ...updates,
270
+ updatedAt: Date.now()
271
+ };
272
+ await this.storage.updateConfig(id, updated);
273
+ if (updates.isDefault) {
274
+ await this.storage.setDefaultConfig(id);
275
+ }
276
+ if (this.enableCache) {
277
+ this.cache.set(id, updated);
278
+ }
279
+ return updated;
280
+ }
281
+ /**
282
+ * 删除配置
283
+ */
284
+ async deleteConfig(id) {
285
+ await this.storage.deleteConfig(id);
286
+ if (this.enableCache) {
287
+ this.cache.delete(id);
288
+ }
289
+ }
290
+ /**
291
+ * 设置默认配置
292
+ */
293
+ async setDefaultConfig(id) {
294
+ await this.storage.setDefaultConfig(id);
295
+ if (this.enableCache) {
296
+ this.cache.clear();
297
+ }
298
+ }
299
+ /**
300
+ * 获取默认配置
301
+ */
302
+ async getDefaultConfig() {
303
+ return await this.storage.getDefaultConfig();
304
+ }
305
+ /**
306
+ * 清空缓存
307
+ */
308
+ clearCache() {
309
+ this.cache.clear();
310
+ }
311
+ /**
312
+ * 导出配置(JSON格式)
313
+ */
314
+ async exportConfig(id) {
315
+ const config = await this.getConfig(id);
316
+ if (!config) {
317
+ throw new Error("\u914D\u7F6E\u4E0D\u5B58\u5728: " + id);
318
+ }
319
+ return JSON.stringify(config, null, 2);
320
+ }
321
+ /**
322
+ * 导入配置
323
+ */
324
+ async importConfig(jsonString) {
325
+ try {
326
+ const data = JSON.parse(jsonString);
327
+ const newConfig = {
328
+ ...data,
329
+ id: this.generateId(),
330
+ createdAt: Date.now(),
331
+ updatedAt: Date.now(),
332
+ isDefault: false
333
+ // 导入的配置不设为默认
334
+ };
335
+ await this.storage.saveConfig(newConfig);
336
+ if (this.enableCache) {
337
+ this.cache.set(newConfig.id, newConfig);
338
+ }
339
+ return newConfig;
340
+ } catch (error) {
341
+ throw new Error("\u5BFC\u5165\u914D\u7F6E\u5931\u8D25: " + error);
342
+ }
343
+ }
344
+ /**
345
+ * 复制配置
346
+ */
347
+ async duplicateConfig(id, newName) {
348
+ const original = await this.getConfig(id);
349
+ if (!original) {
350
+ throw new Error("\u914D\u7F6E\u4E0D\u5B58\u5728: " + id);
351
+ }
352
+ const duplicated = {
353
+ ...original,
354
+ id: this.generateId(),
355
+ name: newName || original.name + " (\u526F\u672C)",
356
+ createdAt: Date.now(),
357
+ updatedAt: Date.now(),
358
+ isDefault: false
359
+ };
360
+ await this.storage.saveConfig(duplicated);
361
+ if (this.enableCache) {
362
+ this.cache.set(duplicated.id, duplicated);
363
+ }
364
+ return duplicated;
365
+ }
366
+ /**
367
+ * 批量删除配置
368
+ */
369
+ async deleteConfigs(ids) {
370
+ for (const id of ids) {
371
+ await this.deleteConfig(id);
372
+ }
373
+ }
374
+ };
375
+ defaultServiceInstance = null;
376
+ }
377
+ });
378
+
379
+ // src/business/testYourself/utils/fingerprint.ts
380
+ function getCanvasFingerprint() {
381
+ try {
382
+ const canvas = document.createElement("canvas");
383
+ const ctx = canvas.getContext("2d");
384
+ if (!ctx) return "no-canvas";
385
+ canvas.width = 200;
386
+ canvas.height = 50;
387
+ ctx.textBaseline = "top";
388
+ ctx.font = "14px Arial";
389
+ ctx.fillStyle = "#f60";
390
+ ctx.fillRect(0, 0, 200, 50);
391
+ ctx.fillStyle = "#069";
392
+ ctx.fillText("Canvas Fingerprint \u{1F3A8}", 2, 15);
393
+ return canvas.toDataURL();
394
+ } catch (error) {
395
+ return "canvas-error";
396
+ }
397
+ }
398
+ function getWebGLFingerprint() {
399
+ try {
400
+ const canvas = document.createElement("canvas");
401
+ const gl = canvas.getContext("webgl") || canvas.getContext("experimental-webgl");
402
+ if (!gl) return "no-webgl";
403
+ const glContext = gl;
404
+ const debugInfo = glContext.getExtension("WEBGL_debug_renderer_info");
405
+ if (debugInfo) {
406
+ const vendor = glContext.getParameter(debugInfo.UNMASKED_VENDOR_WEBGL);
407
+ const renderer = glContext.getParameter(debugInfo.UNMASKED_RENDERER_WEBGL);
408
+ return vendor + "~" + renderer;
409
+ }
410
+ return "webgl-no-debug";
411
+ } catch (error) {
412
+ return "webgl-error";
413
+ }
414
+ }
415
+ function getAvailableFonts() {
416
+ const testFonts = [
417
+ "Arial",
418
+ "Verdana",
419
+ "Courier New",
420
+ "Georgia",
421
+ "Times New Roman",
422
+ "Comic Sans MS",
423
+ "Trebuchet MS",
424
+ "Arial Black",
425
+ "Impact",
426
+ "Courier",
427
+ "Helvetica",
428
+ "Monaco",
429
+ "Consolas",
430
+ "Menlo"
431
+ ];
432
+ const availableFonts = [];
433
+ const canvas = document.createElement("canvas");
434
+ const ctx = canvas.getContext("2d");
435
+ if (!ctx) return "no-fonts";
436
+ const baseFonts = ["monospace", "sans-serif", "serif"];
437
+ const testString = "mmmmmmmmmmlli";
438
+ const baseWidths = {};
439
+ baseFonts.forEach((font) => {
440
+ ctx.font = "72px " + font;
441
+ baseWidths[font] = ctx.measureText(testString).width;
442
+ });
443
+ testFonts.forEach((font) => {
444
+ let detected = false;
445
+ baseFonts.forEach((baseFont) => {
446
+ ctx.font = "72px " + font + ", " + baseFont;
447
+ const width = ctx.measureText(testString).width;
448
+ if (width !== baseWidths[baseFont]) {
449
+ detected = true;
450
+ }
451
+ });
452
+ if (detected) {
453
+ availableFonts.push(font);
454
+ }
455
+ });
456
+ return availableFonts.join(",") || "no-custom-fonts";
457
+ }
458
+ function getDeviceFingerprint() {
459
+ const fingerprint = {
460
+ // 基础信息
461
+ userAgent: navigator.userAgent,
462
+ screenResolution: window.screen.width + "x" + window.screen.height,
463
+ timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
464
+ language: navigator.language,
465
+ platform: navigator.platform,
466
+ // 显示信息
467
+ colorDepth: window.screen.colorDepth,
468
+ devicePixelRatio: window.devicePixelRatio,
469
+ // 硬件信息
470
+ hardwareConcurrency: navigator.hardwareConcurrency || 0,
471
+ maxTouchPoints: navigator.maxTouchPoints || 0,
472
+ // Canvas和WebGL指纹
473
+ canvasFingerprint: getCanvasFingerprint(),
474
+ webglFingerprint: getWebGLFingerprint(),
475
+ // 字体检测
476
+ fonts: getAvailableFonts(),
477
+ // 浏览器能力
478
+ cookieEnabled: navigator.cookieEnabled,
479
+ localStorageEnabled: (() => {
480
+ try {
481
+ return typeof localStorage !== "undefined";
482
+ } catch {
483
+ return false;
484
+ }
485
+ })(),
486
+ sessionStorageEnabled: (() => {
487
+ try {
488
+ return typeof sessionStorage !== "undefined";
489
+ } catch {
490
+ return false;
491
+ }
492
+ })(),
493
+ indexedDBEnabled: (() => {
494
+ try {
495
+ return typeof indexedDB !== "undefined";
496
+ } catch {
497
+ return false;
498
+ }
499
+ })()
500
+ };
501
+ return fingerprint;
502
+ }
503
+ async function tryGetIPAddress() {
504
+ try {
505
+ const response = await fetch("https://api.ipify.org?format=json", {
506
+ method: "GET",
507
+ mode: "cors"
508
+ });
509
+ if (response.ok) {
510
+ const data = await response.json();
511
+ return data.ip || null;
512
+ }
513
+ } catch (error) {
514
+ console.warn("\u65E0\u6CD5\u83B7\u53D6IP\u5730\u5740:", error);
515
+ }
516
+ return null;
517
+ }
518
+ function simpleHash(str) {
519
+ let hash = 5381;
520
+ for (let i = 0; i < str.length; i++) {
521
+ hash = (hash << 5) + hash + str.charCodeAt(i);
522
+ }
523
+ return Math.abs(hash);
524
+ }
525
+ function generateDeviceHash(fingerprint, salt = "test-yourself-salt-2024") {
526
+ const components = [
527
+ // 基础信息
528
+ fingerprint.userAgent,
529
+ fingerprint.ip || "no-ip",
530
+ fingerprint.screenResolution,
531
+ fingerprint.timezone,
532
+ fingerprint.language,
533
+ fingerprint.platform,
534
+ // 显示信息
535
+ fingerprint.colorDepth?.toString() || "0",
536
+ fingerprint.devicePixelRatio?.toString() || "0",
537
+ // 硬件信息
538
+ fingerprint.hardwareConcurrency?.toString() || "0",
539
+ fingerprint.maxTouchPoints?.toString() || "0",
540
+ // Canvas和WebGL指纹(这些是最独特的)
541
+ fingerprint.canvasFingerprint || "no-canvas",
542
+ fingerprint.webglFingerprint || "no-webgl",
543
+ // 字体(不同设备安装的字体不同)
544
+ fingerprint.fonts || "no-fonts",
545
+ // 浏览器能力
546
+ fingerprint.cookieEnabled ? "1" : "0",
547
+ fingerprint.localStorageEnabled ? "1" : "0",
548
+ fingerprint.sessionStorageEnabled ? "1" : "0",
549
+ fingerprint.indexedDBEnabled ? "1" : "0",
550
+ // 盐值
551
+ salt
552
+ ];
553
+ const combined = components.join("|");
554
+ const hash = simpleHash(combined);
555
+ return hash.toString(36);
556
+ }
557
+ function selectResultIndex(hash, totalResults) {
558
+ const numHash = parseInt(hash, 36);
559
+ return numHash % totalResults;
560
+ }
561
+
562
+ // src/business/testYourself/data/defaultResults.ts
563
+ var DEFAULT_RESULTS = [
564
+ // 动物系列 (1-15)
565
+ {
566
+ id: "animal-cat",
567
+ title: "\u6175\u61D2\u7684\u732B\u54AA \u{1F431}",
568
+ description: "\u4F60\u5C31\u50CF\u4E00\u53EA\u4F18\u96C5\u7684\u732B\uFF0C\u72EC\u7ACB\u81EA\u4E3B\uFF0C\u559C\u6B22\u6309\u7167\u81EA\u5DF1\u7684\u8282\u594F\u751F\u6D3B\u3002\u6709\u65F6\u9AD8\u51B7\uFF0C\u6709\u65F6\u7C98\u4EBA\uFF0C\u5145\u6EE1\u795E\u79D8\u9B45\u529B\u3002",
569
+ image: "\u{1F431}",
570
+ imageType: "emoji"
571
+ },
572
+ {
573
+ id: "animal-dog",
574
+ title: "\u5FE0\u8BDA\u7684\u72D7\u72D7 \u{1F415}",
575
+ description: "\u4F60\u50CF\u4E00\u53EA\u70ED\u60C5\u7684\u72D7\u72D7\uFF0C\u5BF9\u670B\u53CB\u5FE0\u8BDA\uFF0C\u5145\u6EE1\u6D3B\u529B\u3002\u603B\u662F\u80FD\u7ED9\u5468\u56F4\u7684\u4EBA\u5E26\u6765\u6B22\u4E50\u548C\u6E29\u6696\u3002",
576
+ image: "\u{1F415}",
577
+ imageType: "emoji"
578
+ },
579
+ {
580
+ id: "animal-panda",
581
+ title: "\u53EF\u7231\u7684\u718A\u732B \u{1F43C}",
582
+ description: "\u4F60\u5C31\u50CF\u56FD\u5B9D\u718A\u732B\u4E00\u6837\uFF0C\u5446\u840C\u53EF\u7231\uFF0C\u4EBA\u89C1\u4EBA\u7231\u3002\u6162\u8282\u594F\u7684\u751F\u6D3B\u65B9\u5F0F\u8BA9\u4F60\u5145\u6EE1\u6CBB\u6108\u529B\u3002",
583
+ image: "\u{1F43C}",
584
+ imageType: "emoji"
585
+ },
586
+ {
587
+ id: "animal-fox",
588
+ title: "\u673A\u667A\u7684\u72D0\u72F8 \u{1F98A}",
589
+ description: "\u806A\u660E\u4F36\u4FD0\uFF0C\u53CD\u5E94\u654F\u6377\u3002\u4F60\u603B\u80FD\u7528\u667A\u6167\u89E3\u51B3\u95EE\u9898\uFF0C\u662F\u56E2\u961F\u4E2D\u7684\u667A\u56CA\u62C5\u5F53\u3002",
590
+ image: "\u{1F98A}",
591
+ imageType: "emoji"
592
+ },
593
+ {
594
+ id: "animal-owl",
595
+ title: "\u777F\u667A\u7684\u732B\u5934\u9E70 \u{1F989}",
596
+ description: "\u4F60\u50CF\u732B\u5934\u9E70\u4E00\u6837\u5145\u6EE1\u667A\u6167\uFF0C\u559C\u6B22\u5728\u591C\u6DF1\u4EBA\u9759\u65F6\u601D\u8003\u3002\u7406\u6027\u3001\u6C89\u7A33\u662F\u4F60\u7684\u6807\u7B7E\u3002",
597
+ image: "\u{1F989}",
598
+ imageType: "emoji"
599
+ },
600
+ {
601
+ id: "animal-dolphin",
602
+ title: "\u5FEB\u4E50\u7684\u6D77\u8C5A \u{1F42C}",
603
+ description: "\u6D3B\u6CFC\u5F00\u6717\uFF0C\u793E\u4EA4\u8FBE\u4EBA\u3002\u4F60\u7684\u7B11\u5BB9\u80FD\u591F\u611F\u67D3\u8EAB\u8FB9\u7684\u6BCF\u4E00\u4E2A\u4EBA\u3002",
604
+ image: "\u{1F42C}",
605
+ imageType: "emoji"
606
+ },
607
+ {
608
+ id: "animal-butterfly",
609
+ title: "\u81EA\u7531\u7684\u8774\u8776 \u{1F98B}",
610
+ description: "\u8FFD\u6C42\u81EA\u7531\uFF0C\u70ED\u7231\u7F8E\u597D\u4E8B\u7269\u3002\u4F60\u7684\u4EBA\u751F\u5145\u6EE1\u8272\u5F69\uFF0C\u4ECE\u4E0D\u88AB\u675F\u7F1A\u3002",
611
+ image: "\u{1F98B}",
612
+ imageType: "emoji"
613
+ },
614
+ {
615
+ id: "animal-lion",
616
+ title: "\u52C7\u6562\u7684\u72EE\u5B50 \u{1F981}",
617
+ description: "\u5929\u751F\u7684\u9886\u5BFC\u8005\uFF0C\u52C7\u6562\u679C\u65AD\u3002\u4F60\u7684\u6C14\u573A\u5F3A\u5927\uFF0C\u603B\u80FD\u6FC0\u52B1\u4ED6\u4EBA\u3002",
618
+ image: "\u{1F981}",
619
+ imageType: "emoji"
620
+ },
621
+ {
622
+ id: "animal-rabbit",
623
+ title: "\u6E29\u67D4\u7684\u5154\u5B50 \u{1F430}",
624
+ description: "\u5FC3\u5730\u5584\u826F\uFF0C\u6E29\u67D4\u4F53\u8D34\u3002\u4F60\u7684\u5B58\u5728\u5C31\u50CF\u6625\u5929\u7684\u5FAE\u98CE\uFF0C\u8BA9\u4EBA\u611F\u5230\u6E29\u6696\u3002",
625
+ image: "\u{1F430}",
626
+ imageType: "emoji"
627
+ },
628
+ {
629
+ id: "animal-penguin",
630
+ title: "\u5446\u840C\u7684\u4F01\u9E45 \u{1F427}",
631
+ description: "\u61A8\u6001\u53EF\u63AC\uFF0C\u8BA4\u771F\u6267\u7740\u3002\u867D\u7136\u770B\u8D77\u6765\u7B28\u62D9\uFF0C\u4F46\u505A\u4E8B\u4E00\u4E1D\u4E0D\u82DF\u3002",
632
+ image: "\u{1F427}",
633
+ imageType: "emoji"
634
+ },
635
+ {
636
+ id: "animal-eagle",
637
+ title: "\u7FF1\u7FD4\u7684\u96C4\u9E70 \u{1F985}",
638
+ description: "\u76EE\u5149\u8FDC\u5927\uFF0C\u5FD7\u5411\u9AD8\u8FDC\u3002\u4F60\u603B\u662F\u80FD\u770B\u5230\u522B\u4EBA\u770B\u4E0D\u5230\u7684\u673A\u4F1A\u3002",
639
+ image: "\u{1F985}",
640
+ imageType: "emoji"
641
+ },
642
+ {
643
+ id: "animal-koala",
644
+ title: "\u6175\u61D2\u7684\u8003\u62C9 \u{1F428}",
645
+ description: "\u4F5B\u7CFB\u751F\u6D3B\uFF0C\u77E5\u8DB3\u5E38\u4E50\u3002\u4F60\u61C2\u5F97\u4EAB\u53D7\u5F53\u4E0B\uFF0C\u6D3B\u5728\u81EA\u5DF1\u7684\u8282\u594F\u91CC\u3002",
646
+ image: "\u{1F428}",
647
+ imageType: "emoji"
648
+ },
649
+ {
650
+ id: "animal-sloth",
651
+ title: "\u60A0\u95F2\u7684\u6811\u61D2 \u{1F9A5}",
652
+ description: "\u6162\u6162\u6765\uFF0C\u6BD4\u8F83\u5FEB\u3002\u4F60\u76F8\u4FE1\u6025\u4E0D\u6765\u7684\u4E8B\u5C31\u4E0D\u8981\u6025\uFF0C\u4FDD\u6301\u81EA\u5DF1\u7684pace\u3002",
653
+ image: "\u{1F9A5}",
654
+ imageType: "emoji"
655
+ },
656
+ {
657
+ id: "animal-unicorn",
658
+ title: "\u68A6\u5E7B\u7684\u72EC\u89D2\u517D \u{1F984}",
659
+ description: "\u5145\u6EE1\u5E7B\u60F3\uFF0C\u8FFD\u6C42\u5B8C\u7F8E\u3002\u4F60\u7684\u4E16\u754C\u4E94\u5F69\u6591\u6593\uFF0C\u603B\u6709\u72EC\u7279\u7684\u60F3\u6CD5\u3002",
660
+ image: "\u{1F984}",
661
+ imageType: "emoji"
662
+ },
663
+ {
664
+ id: "animal-dragon",
665
+ title: "\u795E\u79D8\u7684\u9F99 \u{1F409}",
666
+ description: "\u5F3A\u5927\u800C\u795E\u79D8\uFF0C\u5145\u6EE1\u9B45\u529B\u3002\u4F60\u662F\u4F20\u8BF4\u4E2D\u7684\u5B58\u5728\uFF0C\u4EE4\u4EBA\u5411\u5F80\u3002",
667
+ image: "\u{1F409}",
668
+ imageType: "emoji"
669
+ },
670
+ // 星球系列 (16-25)
671
+ {
672
+ id: "planet-sun",
673
+ title: "\u6E29\u6696\u7684\u592A\u9633 \u2600\uFE0F",
674
+ description: "\u4F60\u5C31\u50CF\u592A\u9633\u4E00\u6837\uFF0C\u662F\u56E2\u961F\u7684\u80FD\u91CF\u6765\u6E90\uFF0C\u6E29\u6696\u7740\u5468\u56F4\u7684\u6BCF\u4E2A\u4EBA\u3002",
675
+ image: "\u2600\uFE0F",
676
+ imageType: "emoji"
677
+ },
678
+ {
679
+ id: "planet-moon",
680
+ title: "\u6E29\u67D4\u7684\u6708\u4EAE \u{1F319}",
681
+ description: "\u5B89\u9759\u800C\u6E29\u67D4\uFF0C\u5728\u9ED1\u6697\u4E2D\u7ED9\u4EBA\u5149\u660E\u3002\u4F60\u662F\u591C\u665A\u6700\u7F8E\u7684\u966A\u4F34\u3002",
682
+ image: "\u{1F319}",
683
+ imageType: "emoji"
684
+ },
685
+ {
686
+ id: "planet-star",
687
+ title: "\u95EA\u8000\u7684\u661F\u661F \u2B50",
688
+ description: "\u867D\u7136\u6E3A\u5C0F\uFF0C\u4F46\u59CB\u7EC8\u95EA\u8000\u3002\u4F60\u7528\u81EA\u5DF1\u7684\u65B9\u5F0F\u53D1\u5149\u53D1\u70ED\u3002",
689
+ image: "\u2B50",
690
+ imageType: "emoji"
691
+ },
692
+ {
693
+ id: "planet-earth",
694
+ title: "\u5305\u5BB9\u7684\u5730\u7403 \u{1F30D}",
695
+ description: "\u5305\u5BB9\u4E07\u7269\uFF0C\u751F\u673A\u52C3\u52C3\u3002\u4F60\u6709\u7740\u5BBD\u5E7F\u7684\u80F8\u6000\u548C\u65E0\u9650\u7684\u53EF\u80FD\u3002",
696
+ image: "\u{1F30D}",
697
+ imageType: "emoji"
698
+ },
699
+ {
700
+ id: "planet-saturn",
701
+ title: "\u72EC\u7279\u7684\u571F\u661F \u{1FA90}",
702
+ description: "\u4E0E\u4F17\u4E0D\u540C\uFF0C\u81EA\u6210\u4E00\u683C\u3002\u4F60\u7684\u4E2A\u6027\u5C31\u50CF\u571F\u661F\u73AF\u4E00\u6837\u4EE4\u4EBA\u7740\u8FF7\u3002",
703
+ image: "\u{1FA90}",
704
+ imageType: "emoji"
705
+ },
706
+ {
707
+ id: "weather-rainbow",
708
+ title: "\u7EDA\u4E3D\u7684\u5F69\u8679 \u{1F308}",
709
+ description: "\u98CE\u96E8\u8FC7\u540E\u89C1\u5F69\u8679\uFF0C\u4F60\u603B\u80FD\u5728\u56F0\u96BE\u540E\u770B\u5230\u5E0C\u671B\u548C\u7F8E\u597D\u3002",
710
+ image: "\u{1F308}",
711
+ imageType: "emoji"
712
+ },
713
+ {
714
+ id: "weather-cloud",
715
+ title: "\u98D8\u9038\u7684\u4E91\u6735 \u2601\uFE0F",
716
+ description: "\u81EA\u7531\u81EA\u5728\uFF0C\u968F\u98CE\u98D8\u8361\u3002\u4F60\u4E0D\u88AB\u5B9A\u4E49\uFF0C\u6C38\u8FDC\u5145\u6EE1\u53EF\u80FD\u3002",
717
+ image: "\u2601\uFE0F",
718
+ imageType: "emoji"
719
+ },
720
+ {
721
+ id: "weather-lightning",
722
+ title: "\u95EA\u7535 \u26A1",
723
+ description: "\u7206\u53D1\u529B\u5F3A\uFF0C\u884C\u52A8\u8FC5\u901F\u3002\u4F60\u7684\u80FD\u91CF\u5C31\u50CF\u95EA\u7535\u4E00\u6837\u4EE4\u4EBA\u9707\u64BC\u3002",
724
+ image: "\u26A1",
725
+ imageType: "emoji"
726
+ },
727
+ {
728
+ id: "weather-snowflake",
729
+ title: "\u72EC\u7279\u7684\u96EA\u82B1 \u2744\uFE0F",
730
+ description: "\u4E16\u754C\u4E0A\u6CA1\u6709\u4E24\u7247\u76F8\u540C\u7684\u96EA\u82B1\uFF0C\u4F60\u4E5F\u662F\u72EC\u4E00\u65E0\u4E8C\u7684\u5B58\u5728\u3002",
731
+ image: "\u2744\uFE0F",
732
+ imageType: "emoji"
733
+ },
734
+ {
735
+ id: "weather-fire",
736
+ title: "\u70ED\u60C5\u7684\u706B\u7130 \u{1F525}",
737
+ description: "\u70ED\u60C5\u4F3C\u706B\uFF0C\u5145\u6EE1\u6FC0\u60C5\u3002\u4F60\u7684\u5B58\u5728\u80FD\u70B9\u71C3\u5468\u56F4\u7684\u6C14\u6C1B\u3002",
738
+ image: "\u{1F525}",
739
+ imageType: "emoji"
740
+ },
741
+ // 植物系列 (26-35)
742
+ {
743
+ id: "plant-tree",
744
+ title: "\u575A\u97E7\u7684\u5927\u6811 \u{1F333}",
745
+ description: "\u7A33\u91CD\u53EF\u9760\uFF0C\u6839\u57FA\u6DF1\u539A\u3002\u4F60\u662F\u5927\u5BB6\u53EF\u4EE5\u4F9D\u9760\u7684\u5B58\u5728\u3002",
746
+ image: "\u{1F333}",
747
+ imageType: "emoji"
748
+ },
749
+ {
750
+ id: "plant-flower",
751
+ title: "\u7F8E\u4E3D\u7684\u82B1\u6735 \u{1F338}",
752
+ description: "\u7EFD\u653E\u81EA\u6211\uFF0C\u7F8E\u4E3D\u52A8\u4EBA\u3002\u4F60\u7684\u5B58\u5728\u5C31\u662F\u4E00\u9053\u9753\u4E3D\u7684\u98CE\u666F\u3002",
753
+ image: "\u{1F338}",
754
+ imageType: "emoji"
755
+ },
756
+ {
757
+ id: "plant-sunflower",
758
+ title: "\u5411\u9633\u7684\u5411\u65E5\u8475 \u{1F33B}",
759
+ description: "\u6C38\u8FDC\u5411\u7740\u9633\u5149\uFF0C\u79EF\u6781\u5411\u4E0A\u3002\u4F60\u7684\u4E50\u89C2\u611F\u67D3\u7740\u6BCF\u4E2A\u4EBA\u3002",
760
+ image: "\u{1F33B}",
761
+ imageType: "emoji"
762
+ },
763
+ {
764
+ id: "plant-rose",
765
+ title: "\u4F18\u96C5\u7684\u73AB\u7470 \u{1F339}",
766
+ description: "\u9AD8\u8D35\u4F18\u96C5\uFF0C\u5145\u6EE1\u9B45\u529B\u3002\u867D\u6709\u523A\u4F46\u66F4\u6709\u7F8E\u4E3D\u3002",
767
+ image: "\u{1F339}",
768
+ imageType: "emoji"
769
+ },
770
+ {
771
+ id: "plant-cactus",
772
+ title: "\u575A\u5F3A\u7684\u4ED9\u4EBA\u638C \u{1F335}",
773
+ description: "\u5728\u8270\u96BE\u73AF\u5883\u4E2D\u4F9D\u7136\u575A\u5F3A\u3002\u4F60\u7684\u97E7\u6027\u4EE4\u4EBA\u656C\u4F69\u3002",
774
+ image: "\u{1F335}",
775
+ imageType: "emoji"
776
+ },
777
+ {
778
+ id: "plant-clover",
779
+ title: "\u5E78\u8FD0\u7684\u56DB\u53F6\u8349 \u{1F340}",
780
+ description: "\u5E78\u8FD0\u7684\u8C61\u5F81\uFF0C\u603B\u80FD\u7ED9\u4EBA\u5E26\u6765\u597D\u8FD0\u548C\u5E0C\u671B\u3002",
781
+ image: "\u{1F340}",
782
+ imageType: "emoji"
783
+ },
784
+ {
785
+ id: "plant-maple",
786
+ title: "\u6D6A\u6F2B\u7684\u67AB\u53F6 \u{1F341}",
787
+ description: "\u6D6A\u6F2B\u800C\u8BD7\u610F\uFF0C\u4F60\u7684\u4E16\u754C\u5145\u6EE1\u827A\u672F\u6C14\u606F\u3002",
788
+ image: "\u{1F341}",
789
+ imageType: "emoji"
790
+ },
791
+ {
792
+ id: "plant-mushroom",
793
+ title: "\u795E\u79D8\u7684\u8611\u83C7 \u{1F344}",
794
+ description: "\u4F4E\u8C03\u795E\u79D8\uFF0C\u603B\u6709\u610F\u60F3\u4E0D\u5230\u7684\u60CA\u559C\u3002",
795
+ image: "\u{1F344}",
796
+ imageType: "emoji"
797
+ },
798
+ {
799
+ id: "plant-cherry",
800
+ title: "\u6D6A\u6F2B\u7684\u6A31\u82B1 \u{1F338}",
801
+ description: "\u77ED\u6682\u800C\u7F8E\u597D\uFF0C\u4F60\u73CD\u60DC\u6BCF\u4E00\u4E2A\u77AC\u95F4\u7684\u7F8E\u4E3D\u3002",
802
+ image: "\u{1F338}",
803
+ imageType: "emoji"
804
+ },
805
+ {
806
+ id: "plant-bamboo",
807
+ title: "\u575A\u97E7\u7684\u7AF9\u5B50 \u{1F38B}",
808
+ description: "\u865A\u5FC3\u6709\u8282\uFF0C\u6108\u632B\u6108\u52C7\u3002\u4F60\u7684\u7CBE\u795E\u503C\u5F97\u5B66\u4E60\u3002",
809
+ image: "\u{1F38B}",
810
+ imageType: "emoji"
811
+ },
812
+ // 食物系列 (36-45)
813
+ {
814
+ id: "food-coffee",
815
+ title: "\u63D0\u795E\u7684\u5496\u5561 \u2615",
816
+ description: "\u4F60\u5C31\u50CF\u5496\u5561\uFF0C\u662F\u5927\u5BB6\u7684\u80FD\u91CF\u6765\u6E90\uFF0C\u5E2E\u52A9\u4ED6\u4EBA\u4FDD\u6301\u6D3B\u529B\u3002",
817
+ image: "\u2615",
818
+ imageType: "emoji"
819
+ },
820
+ {
821
+ id: "food-pizza",
822
+ title: "\u5FEB\u4E50\u7684\u62AB\u8428 \u{1F355}",
823
+ description: "\u4EBA\u89C1\u4EBA\u7231\uFF0C\u603B\u80FD\u5E26\u6765\u5FEB\u4E50\u3002\u4F60\u662F\u805A\u4F1A\u7684\u7075\u9B42\u3002",
824
+ image: "\u{1F355}",
825
+ imageType: "emoji"
826
+ },
827
+ {
828
+ id: "food-cookie",
829
+ title: "\u751C\u871C\u7684\u997C\u5E72 \u{1F36A}",
830
+ description: "\u6E29\u6696\u751C\u871C\uFF0C\u7ED9\u4EBAcomfort\u3002\u4F60\u662F\u6700\u597D\u7684\u6CBB\u6108\u7CFB\u5B58\u5728\u3002",
831
+ image: "\u{1F36A}",
832
+ imageType: "emoji"
833
+ },
834
+ {
835
+ id: "food-icecream",
836
+ title: "\u6E05\u51C9\u7684\u51B0\u6DC7\u6DCB \u{1F366}",
837
+ description: "\u751C\u7F8E\u53EF\u7231\uFF0C\u5728\u708E\u70ED\u65F6\u7ED9\u4EBA\u6E05\u51C9\u3002\u4F60\u603B\u80FD\u5728\u5173\u952E\u65F6\u523B\u51FA\u73B0\u3002",
838
+ image: "\u{1F366}",
839
+ imageType: "emoji"
840
+ },
841
+ {
842
+ id: "food-honey",
843
+ title: "\u751C\u871C\u7684\u8702\u871C \u{1F36F}",
844
+ description: "\u5929\u7136\u751C\u7F8E\uFF0C\u7EAF\u7CB9\u53EF\u7231\u3002\u4F60\u7684\u5B58\u5728\u5C31\u662F\u5E78\u798F\u7684\u5473\u9053\u3002",
845
+ image: "\u{1F36F}",
846
+ imageType: "emoji"
847
+ },
848
+ {
849
+ id: "food-sushi",
850
+ title: "\u7CBE\u81F4\u7684\u5BFF\u53F8 \u{1F363}",
851
+ description: "\u7CBE\u81F4\u4F18\u96C5\uFF0C\u6CE8\u91CD\u7EC6\u8282\u3002\u4F60\u5BF9\u751F\u6D3B\u6709\u7740\u72EC\u7279\u7684\u54C1\u5473\u3002",
852
+ image: "\u{1F363}",
853
+ imageType: "emoji"
854
+ },
855
+ {
856
+ id: "food-cake",
857
+ title: "\u751C\u7F8E\u7684\u86CB\u7CD5 \u{1F382}",
858
+ description: "\u751C\u871C\u7F8E\u597D\uFF0C\u662F\u5E86\u795D\u7684\u8C61\u5F81\u3002\u4F60\u603B\u80FD\u7ED9\u4EBA\u5E26\u6765\u60CA\u559C\u3002",
859
+ image: "\u{1F382}",
860
+ imageType: "emoji"
861
+ },
862
+ {
863
+ id: "food-donut",
864
+ title: "\u53EF\u7231\u7684\u751C\u751C\u5708 \u{1F369}",
865
+ description: "\u5706\u5706\u6EE1\u6EE1\uFF0C\u751C\u871C\u53EF\u7231\u3002\u4F60\u7684\u5B58\u5728\u8BA9\u751F\u6D3B\u66F4\u7F8E\u597D\u3002",
866
+ image: "\u{1F369}",
867
+ imageType: "emoji"
868
+ },
869
+ {
870
+ id: "food-lollipop",
871
+ title: "\u7AE5\u771F\u7684\u68D2\u68D2\u7CD6 \u{1F36D}",
872
+ description: "\u4FDD\u6301\u7AE5\u5FC3\uFF0C\u6C38\u8FDC\u5E74\u8F7B\u3002\u4F60\u7684\u7EAF\u771F\u4EE4\u4EBA\u7FA1\u6155\u3002",
873
+ image: "\u{1F36D}",
874
+ imageType: "emoji"
875
+ },
876
+ {
877
+ id: "food-watermelon",
878
+ title: "\u6E05\u723D\u7684\u897F\u74DC \u{1F349}",
879
+ description: "\u6E05\u723D\u89E3\u6E34\uFF0C\u590F\u65E5\u5FC5\u5907\u3002\u4F60\u603B\u80FD\u5E26\u6765\u6E05\u65B0\u7684\u611F\u89C9\u3002",
880
+ image: "\u{1F349}",
881
+ imageType: "emoji"
882
+ }
883
+ ];
884
+ if (DEFAULT_RESULTS.length !== 45) {
885
+ console.warn("\u7ED3\u679C\u6570\u636E\u96C6\u5E94\u5305\u542B45\u9879\uFF0C\u5F53\u524D: " + DEFAULT_RESULTS.length);
886
+ }
887
+
888
+ // src/business/testYourself/components/TestYourself.tsx
889
+ var STORAGE_KEY = "test-yourself-result";
890
+ var TestYourself = ({
891
+ config: propConfig,
892
+ configId,
893
+ onResult,
894
+ className = ""
895
+ }) => {
896
+ const [loadedConfig, setLoadedConfig] = useState(null);
897
+ const [configLoading, setConfigLoading] = useState(false);
898
+ useEffect(() => {
899
+ const loadConfig = async () => {
900
+ if (configId) {
901
+ setConfigLoading(true);
902
+ try {
903
+ const { getDefaultConfigService: getDefaultConfigService2 } = await Promise.resolve().then(() => (init_ConfigService(), ConfigService_exports));
904
+ const service = getDefaultConfigService2();
905
+ const savedConfig = await service.getConfig(configId);
906
+ if (savedConfig) {
907
+ setLoadedConfig(savedConfig.config);
908
+ } else {
909
+ console.warn("\u914D\u7F6E " + configId + " \u4E0D\u5B58\u5728\uFF0C\u4F7F\u7528\u9ED8\u8BA4\u914D\u7F6E");
910
+ }
911
+ } catch (error) {
912
+ console.error("\u52A0\u8F7D\u914D\u7F6E\u5931\u8D25:", error);
913
+ } finally {
914
+ setConfigLoading(false);
915
+ }
916
+ }
917
+ };
918
+ loadConfig();
919
+ }, [configId]);
920
+ const config = loadedConfig || propConfig || {
921
+ gameTitle: "\u6D4B\u6D4B\u4F60\u662F\u4EC0\u4E48",
922
+ gameDescription: "\u957F\u6309\u6309\u94AE\uFF0C\u53D1\u73B0\u4F60\u7684\u4E13\u5C5E\u5C5E\u6027",
923
+ results: DEFAULT_RESULTS
924
+ };
925
+ const {
926
+ gameTitle,
927
+ gameDescription,
928
+ buttonText = "\u957F\u6309\u5F00\u59CB\u6D4B\u8BD5",
929
+ longPressDuration = 2e3,
930
+ results = DEFAULT_RESULTS,
931
+ enableIPFetch = false,
932
+ customSalt,
933
+ resultStyle = "card"
934
+ } = config;
935
+ const [status, setStatus] = useState("idle");
936
+ const [result, setResult] = useState(null);
937
+ const [pressProgress, setPressProgress] = useState(0);
938
+ const [ipWarning, setIpWarning] = useState(null);
939
+ const [isLoading, setIsLoading] = useState(true);
940
+ const pressTimerRef = useRef(null);
941
+ const progressIntervalRef = useRef(null);
942
+ const startTimeRef = useRef(0);
943
+ useEffect(() => {
944
+ const initializeTest = async () => {
945
+ if (configLoading) {
946
+ return;
947
+ }
948
+ const storageKey = configId ? STORAGE_KEY + "_" + configId : STORAGE_KEY;
949
+ const savedResult = localStorage.getItem(storageKey);
950
+ if (savedResult) {
951
+ try {
952
+ const parsed = JSON.parse(savedResult);
953
+ setResult(parsed);
954
+ setStatus("completed");
955
+ setIsLoading(false);
956
+ return;
957
+ } catch (error) {
958
+ console.error("\u89E3\u6790\u4FDD\u5B58\u7684\u7ED3\u679C\u5931\u8D25:", error);
959
+ }
960
+ }
961
+ if (enableIPFetch) {
962
+ const ip = await tryGetIPAddress();
963
+ if (!ip) {
964
+ setIpWarning("\u26A0\uFE0F \u65E0\u6CD5\u83B7\u53D6IP\u5730\u5740\uFF0C\u5C06\u4EC5\u4F7F\u7528\u6D4F\u89C8\u5668\u6307\u7EB9\u751F\u6210\u7ED3\u679C");
965
+ }
966
+ }
967
+ setIsLoading(false);
968
+ };
969
+ initializeTest();
970
+ }, [enableIPFetch, configLoading, configId]);
971
+ const calculateResult = async () => {
972
+ try {
973
+ const fingerprint = getDeviceFingerprint();
974
+ if (enableIPFetch) {
975
+ const ip = await tryGetIPAddress();
976
+ if (ip) {
977
+ fingerprint.ip = ip;
978
+ }
979
+ }
980
+ const actualResults = results.length > 0 ? results : DEFAULT_RESULTS;
981
+ const hash = generateDeviceHash(fingerprint, customSalt);
982
+ const index2 = selectResultIndex(hash, actualResults.length);
983
+ const selectedResult = actualResults[index2];
984
+ if (!selectedResult) {
985
+ console.error("\u65E0\u6CD5\u83B7\u53D6\u6D4B\u8BD5\u7ED3\u679C\uFF0Cindex:", index2, "total:", actualResults.length);
986
+ throw new Error("\u65E0\u6CD5\u83B7\u53D6\u6D4B\u8BD5\u7ED3\u679C");
987
+ }
988
+ console.log("\u8BA1\u7B97\u7ED3\u679C\u6210\u529F:", selectedResult);
989
+ const storageKey = configId ? STORAGE_KEY + "_" + configId : STORAGE_KEY;
990
+ localStorage.setItem(storageKey, JSON.stringify(selectedResult));
991
+ return selectedResult;
992
+ } catch (error) {
993
+ console.error("\u8BA1\u7B97\u7ED3\u679C\u5931\u8D25:", error);
994
+ throw error;
995
+ }
996
+ };
997
+ const handlePressStart = (e) => {
998
+ if (status !== "idle") return;
999
+ e.preventDefault();
1000
+ setStatus("pressing");
1001
+ startTimeRef.current = Date.now();
1002
+ progressIntervalRef.current = setInterval(() => {
1003
+ const elapsed = Date.now() - startTimeRef.current;
1004
+ const progress = Math.min(elapsed / longPressDuration * 100, 100);
1005
+ setPressProgress(progress);
1006
+ }, 16);
1007
+ pressTimerRef.current = setTimeout(async () => {
1008
+ try {
1009
+ setPressProgress(100);
1010
+ if (progressIntervalRef.current) {
1011
+ clearInterval(progressIntervalRef.current);
1012
+ progressIntervalRef.current = null;
1013
+ }
1014
+ console.log("\u5F00\u59CB\u8BA1\u7B97\u7ED3\u679C...");
1015
+ const testResult = await calculateResult();
1016
+ console.log("\u7ED3\u679C\u8BA1\u7B97\u5B8C\u6210\uFF0C\u66F4\u65B0\u72B6\u6001:", testResult);
1017
+ setResult(testResult);
1018
+ setTimeout(() => {
1019
+ setStatus("completed");
1020
+ console.log("\u72B6\u6001\u5DF2\u66F4\u65B0\u4E3A completed");
1021
+ }, 0);
1022
+ if (onResult) {
1023
+ onResult(testResult);
1024
+ }
1025
+ } catch (error) {
1026
+ console.error("\u6D4B\u8BD5\u5931\u8D25:", error);
1027
+ setStatus("idle");
1028
+ setPressProgress(0);
1029
+ alert("\u6D4B\u8BD5\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5");
1030
+ }
1031
+ }, longPressDuration);
1032
+ if ("button" in e && e.button === 0) {
1033
+ const handleGlobalMouseUp = () => {
1034
+ handlePressEnd();
1035
+ document.removeEventListener("mouseup", handleGlobalMouseUp);
1036
+ };
1037
+ document.addEventListener("mouseup", handleGlobalMouseUp);
1038
+ }
1039
+ };
1040
+ const handlePressEnd = () => {
1041
+ if (status !== "pressing") return;
1042
+ if (pressTimerRef.current) {
1043
+ clearTimeout(pressTimerRef.current);
1044
+ pressTimerRef.current = null;
1045
+ }
1046
+ if (progressIntervalRef.current) {
1047
+ clearInterval(progressIntervalRef.current);
1048
+ progressIntervalRef.current = null;
1049
+ }
1050
+ setStatus("idle");
1051
+ setPressProgress(0);
1052
+ };
1053
+ const handleMouseLeave = (e) => {
1054
+ };
1055
+ const handleTouchMove = (e) => {
1056
+ const touch = e.touches[0];
1057
+ if (!touch) return;
1058
+ const target = e.currentTarget;
1059
+ const rect = target.getBoundingClientRect();
1060
+ const isInside = touch.clientX >= rect.left && touch.clientX <= rect.right && touch.clientY >= rect.top && touch.clientY <= rect.bottom;
1061
+ if (!isInside && status === "pressing") {
1062
+ handlePressEnd();
1063
+ }
1064
+ };
1065
+ useEffect(() => {
1066
+ return () => {
1067
+ if (pressTimerRef.current) {
1068
+ clearTimeout(pressTimerRef.current);
1069
+ }
1070
+ if (progressIntervalRef.current) {
1071
+ clearInterval(progressIntervalRef.current);
1072
+ }
1073
+ };
1074
+ }, []);
1075
+ const handleReset = () => {
1076
+ const storageKey = configId ? STORAGE_KEY + "_" + configId : STORAGE_KEY;
1077
+ localStorage.removeItem(storageKey);
1078
+ setResult(null);
1079
+ setStatus("idle");
1080
+ setPressProgress(0);
1081
+ };
1082
+ const backgroundStyle = {
1083
+ position: "relative",
1084
+ minHeight: "100vh",
1085
+ overflow: "hidden",
1086
+ background: "linear-gradient(135deg, #f3e8ff 0%, #fce7f3 50%, #dbeafe 100%)"
1087
+ };
1088
+ const DecorativeBackground = () => /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement("div", { style: {
1089
+ position: "absolute",
1090
+ top: 0,
1091
+ left: 0,
1092
+ width: "384px",
1093
+ height: "384px",
1094
+ background: "radial-gradient(circle, rgba(192, 132, 252, 0.3) 0%, transparent 70%)",
1095
+ borderRadius: "50%",
1096
+ filter: "blur(60px)",
1097
+ transform: "translate(-50%, -50%)",
1098
+ pointerEvents: "none"
1099
+ } }), /* @__PURE__ */ React2.createElement("div", { style: {
1100
+ position: "absolute",
1101
+ top: "50%",
1102
+ right: 0,
1103
+ width: "384px",
1104
+ height: "384px",
1105
+ background: "radial-gradient(circle, rgba(244, 114, 182, 0.3) 0%, transparent 70%)",
1106
+ borderRadius: "50%",
1107
+ filter: "blur(60px)",
1108
+ transform: "translateX(50%)",
1109
+ pointerEvents: "none"
1110
+ } }), /* @__PURE__ */ React2.createElement("div", { style: {
1111
+ position: "absolute",
1112
+ bottom: 0,
1113
+ left: "50%",
1114
+ width: "384px",
1115
+ height: "384px",
1116
+ background: "radial-gradient(circle, rgba(147, 197, 253, 0.3) 0%, transparent 70%)",
1117
+ borderRadius: "50%",
1118
+ filter: "blur(60px)",
1119
+ transform: "translate(-50%, 50%)",
1120
+ pointerEvents: "none"
1121
+ } }));
1122
+ if (isLoading) {
1123
+ return /* @__PURE__ */ React2.createElement("div", { className, style: backgroundStyle }, /* @__PURE__ */ React2.createElement(DecorativeBackground, null), /* @__PURE__ */ React2.createElement("div", { style: {
1124
+ position: "relative",
1125
+ zIndex: 10,
1126
+ display: "flex",
1127
+ alignItems: "center",
1128
+ justifyContent: "center",
1129
+ minHeight: "100vh"
1130
+ } }, /* @__PURE__ */ React2.createElement("div", { style: { textAlign: "center" } }, /* @__PURE__ */ React2.createElement("div", { style: {
1131
+ position: "relative",
1132
+ width: "64px",
1133
+ height: "64px",
1134
+ margin: "0 auto 16px"
1135
+ } }, /* @__PURE__ */ React2.createElement("div", { style: {
1136
+ position: "absolute",
1137
+ inset: 0,
1138
+ border: "4px solid #e9d5ff",
1139
+ borderRadius: "50%"
1140
+ } }), /* @__PURE__ */ React2.createElement("div", { style: {
1141
+ position: "absolute",
1142
+ inset: 0,
1143
+ border: "4px solid transparent",
1144
+ borderTopColor: "#a855f7",
1145
+ borderRadius: "50%",
1146
+ animation: "spin 1s linear infinite"
1147
+ } })), /* @__PURE__ */ React2.createElement("p", { style: { fontSize: "14px", color: "#6b7280" } }, "\u2728 \u52A0\u8F7D\u4E2D"))));
1148
+ }
1149
+ if (status === "completed" && result) {
1150
+ return /* @__PURE__ */ React2.createElement("div", { className, style: backgroundStyle }, /* @__PURE__ */ React2.createElement(DecorativeBackground, null), /* @__PURE__ */ React2.createElement("div", { style: {
1151
+ position: "relative",
1152
+ zIndex: 10,
1153
+ minHeight: "100vh",
1154
+ display: "flex",
1155
+ alignItems: "center",
1156
+ justifyContent: "center",
1157
+ padding: "24px"
1158
+ } }, /* @__PURE__ */ React2.createElement("div", { style: { maxWidth: "420px", width: "100%" } }, /* @__PURE__ */ React2.createElement("div", { style: {
1159
+ position: "relative",
1160
+ background: "linear-gradient(135deg, #fdf2f8 0%, #faf5ff 50%, #eff6ff 100%)",
1161
+ borderRadius: "32px",
1162
+ boxShadow: "0 25px 50px -12px rgba(168, 85, 247, 0.25), 0 0 0 1px rgba(168, 85, 247, 0.1)",
1163
+ overflow: "hidden",
1164
+ padding: "40px 32px",
1165
+ textAlign: "center"
1166
+ } }, /* @__PURE__ */ React2.createElement("div", { style: { position: "absolute", top: "20px", left: "20px", fontSize: "24px", opacity: 0.6 } }, "\u2728"), /* @__PURE__ */ React2.createElement("div", { style: { position: "absolute", top: "40px", right: "30px", fontSize: "20px", opacity: 0.5 } }, "\u2B50"), /* @__PURE__ */ React2.createElement("div", { style: { position: "absolute", bottom: "30px", left: "40px", fontSize: "18px", opacity: 0.4 } }, "\u{1F4AB}"), /* @__PURE__ */ React2.createElement("div", { style: { position: "absolute", bottom: "50px", right: "25px", fontSize: "22px", opacity: 0.5 } }, "\u{1F31F}"), /* @__PURE__ */ React2.createElement("div", { style: { marginBottom: "24px" } }, /* @__PURE__ */ React2.createElement("div", { style: {
1167
+ display: "inline-block",
1168
+ fontSize: "80px",
1169
+ animation: "bounce-slow 2s ease-in-out infinite",
1170
+ filter: "drop-shadow(0 10px 20px rgba(0,0,0,0.1))"
1171
+ } }, result.imageType === "emoji" ? result.image : "\u{1F389}")), /* @__PURE__ */ React2.createElement("h2", { style: {
1172
+ fontSize: "32px",
1173
+ fontWeight: 800,
1174
+ background: "linear-gradient(135deg, #9333ea 0%, #ec4899 100%)",
1175
+ WebkitBackgroundClip: "text",
1176
+ WebkitTextFillColor: "transparent",
1177
+ backgroundClip: "text",
1178
+ marginBottom: "16px",
1179
+ lineHeight: 1.3
1180
+ } }, result.title), /* @__PURE__ */ React2.createElement("div", { style: {
1181
+ background: "rgba(255, 255, 255, 0.8)",
1182
+ borderRadius: "20px",
1183
+ padding: "20px 24px",
1184
+ marginBottom: "28px",
1185
+ boxShadow: "0 4px 15px rgba(168, 85, 247, 0.1)",
1186
+ border: "2px dashed rgba(168, 85, 247, 0.2)"
1187
+ } }, /* @__PURE__ */ React2.createElement("p", { style: {
1188
+ fontSize: "16px",
1189
+ color: "#6b7280",
1190
+ lineHeight: 1.7,
1191
+ margin: 0
1192
+ } }, result.description)), /* @__PURE__ */ React2.createElement("div", { style: {
1193
+ display: "flex",
1194
+ alignItems: "center",
1195
+ justifyContent: "center",
1196
+ gap: "8px",
1197
+ marginBottom: "24px"
1198
+ } }, /* @__PURE__ */ React2.createElement("span", { style: { width: "40px", height: "3px", background: "linear-gradient(to right, #a855f7, transparent)", borderRadius: "999px" } }), /* @__PURE__ */ React2.createElement("span", { style: { fontSize: "16px" } }, "\u{1F495}"), /* @__PURE__ */ React2.createElement("span", { style: { width: "40px", height: "3px", background: "linear-gradient(to left, #ec4899, transparent)", borderRadius: "999px" } })), /* @__PURE__ */ React2.createElement(
1199
+ "button",
1200
+ {
1201
+ onClick: handleReset,
1202
+ style: {
1203
+ display: "inline-flex",
1204
+ alignItems: "center",
1205
+ justifyContent: "center",
1206
+ gap: "8px",
1207
+ padding: "14px 32px",
1208
+ fontSize: "16px",
1209
+ fontWeight: 600,
1210
+ color: "white",
1211
+ background: "linear-gradient(135deg, #a855f7 0%, #ec4899 100%)",
1212
+ border: "none",
1213
+ borderRadius: "9999px",
1214
+ cursor: "pointer",
1215
+ boxShadow: "0 10px 25px -5px rgba(168, 85, 247, 0.4)",
1216
+ transition: "all 0.3s ease"
1217
+ },
1218
+ onMouseEnter: (e) => {
1219
+ e.currentTarget.style.transform = "scale(1.05) translateY(-2px)";
1220
+ e.currentTarget.style.boxShadow = "0 15px 35px -5px rgba(168, 85, 247, 0.5)";
1221
+ },
1222
+ onMouseLeave: (e) => {
1223
+ e.currentTarget.style.transform = "scale(1) translateY(0)";
1224
+ e.currentTarget.style.boxShadow = "0 10px 25px -5px rgba(168, 85, 247, 0.4)";
1225
+ }
1226
+ },
1227
+ /* @__PURE__ */ React2.createElement("span", null, "\u{1F504}"),
1228
+ /* @__PURE__ */ React2.createElement("span", null, "\u91CD\u65B0\u6D4B\u8BD5")
1229
+ )))));
1230
+ }
1231
+ return /* @__PURE__ */ React2.createElement("div", { className, style: backgroundStyle }, /* @__PURE__ */ React2.createElement(DecorativeBackground, null), /* @__PURE__ */ React2.createElement("div", { style: {
1232
+ position: "relative",
1233
+ zIndex: 10,
1234
+ minHeight: "100vh",
1235
+ display: "flex",
1236
+ alignItems: "center",
1237
+ justifyContent: "center",
1238
+ padding: "16px"
1239
+ } }, /* @__PURE__ */ React2.createElement("div", { style: { maxWidth: "512px", width: "100%", textAlign: "center", userSelect: "none" } }, /* @__PURE__ */ React2.createElement("div", { style: { marginBottom: "48px" } }, /* @__PURE__ */ React2.createElement("div", { style: {
1240
+ display: "inline-block",
1241
+ marginBottom: "16px",
1242
+ animation: "bounce-slow 2s ease-in-out infinite"
1243
+ } }, /* @__PURE__ */ React2.createElement("span", { style: { fontSize: "56px" } }, "\u{1F3B2}")), /* @__PURE__ */ React2.createElement("h1", { style: {
1244
+ fontSize: "48px",
1245
+ fontWeight: 900,
1246
+ marginBottom: "12px",
1247
+ background: "linear-gradient(135deg, #9333ea 0%, #ec4899 50%, #3b82f6 100%)",
1248
+ WebkitBackgroundClip: "text",
1249
+ WebkitTextFillColor: "transparent",
1250
+ backgroundClip: "text",
1251
+ lineHeight: 1.2
1252
+ } }, gameTitle), gameDescription && /* @__PURE__ */ React2.createElement("p", { style: {
1253
+ fontSize: "18px",
1254
+ color: "#6b7280",
1255
+ fontWeight: 500
1256
+ } }, gameDescription)), /* @__PURE__ */ React2.createElement("div", { style: { marginBottom: "24px" } }, /* @__PURE__ */ React2.createElement(
1257
+ "button",
1258
+ {
1259
+ onMouseDown: handlePressStart,
1260
+ onMouseLeave: handleMouseLeave,
1261
+ onTouchStart: handlePressStart,
1262
+ onTouchEnd: handlePressEnd,
1263
+ onTouchMove: handleTouchMove,
1264
+ onTouchCancel: handlePressEnd,
1265
+ onContextMenu: (e) => e.preventDefault(),
1266
+ onDragStart: (e) => e.preventDefault(),
1267
+ style: {
1268
+ display: "block",
1269
+ margin: "0 auto",
1270
+ width: "200px",
1271
+ height: "200px",
1272
+ borderRadius: "50%",
1273
+ border: "none",
1274
+ fontSize: "20px",
1275
+ fontWeight: "bold",
1276
+ cursor: "pointer",
1277
+ position: "relative",
1278
+ overflow: "hidden",
1279
+ userSelect: "none",
1280
+ WebkitTouchCallout: "none",
1281
+ WebkitUserSelect: "none",
1282
+ touchAction: "none",
1283
+ transition: "transform 0.3s ease",
1284
+ transform: status === "pressing" ? "scale(0.95)" : "scale(1)",
1285
+ background: status === "pressing" ? "linear-gradient(to top, rgb(168, 85, 247) " + pressProgress + "%, rgb(236, 72, 153) " + pressProgress + "%)" : "linear-gradient(135deg, #4f46e5 0%, #7c3aed 25%, #db2777 50%, #f97316 75%, #059669 100%)",
1286
+ boxShadow: status === "pressing" ? "inset 0 4px 12px rgba(0,0,0,0.3), 0 0 0 4px rgba(168, 85, 247, 0.5)" : "0 15px 35px -10px rgba(79, 70, 229, 0.6), 0 0 0 4px rgba(255,255,255,0.8)"
1287
+ }
1288
+ },
1289
+ /* @__PURE__ */ React2.createElement("div", { style: {
1290
+ position: "absolute",
1291
+ top: 0,
1292
+ left: 0,
1293
+ right: 0,
1294
+ bottom: 0,
1295
+ display: "flex",
1296
+ flexDirection: "column",
1297
+ alignItems: "center",
1298
+ justifyContent: "center",
1299
+ color: "white",
1300
+ pointerEvents: "none"
1301
+ } }, status === "pressing" ? /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement("span", { style: { fontSize: "36px", fontWeight: 900, marginBottom: "4px" } }, Math.round(pressProgress), "%"), /* @__PURE__ */ React2.createElement("span", { style: { fontSize: "14px", opacity: 0.8 } }, "\u7EE7\u7EED\u6309\u4F4F")) : /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement("span", { style: { fontSize: "32px", marginBottom: "8px" } }, "\u{1F446}"), /* @__PURE__ */ React2.createElement("span", { style: { fontSize: "16px", fontWeight: "bold", padding: "0 16px" } }, buttonText))),
1302
+ status === "idle" && /* @__PURE__ */ React2.createElement("div", { style: {
1303
+ position: "absolute",
1304
+ top: "16px",
1305
+ left: "16px",
1306
+ right: "16px",
1307
+ bottom: "16px",
1308
+ border: "2px solid rgba(255,255,255,0.3)",
1309
+ borderRadius: "50%"
1310
+ } })
1311
+ ), status === "pressing" && /* @__PURE__ */ React2.createElement("div", { style: {
1312
+ marginTop: "16px",
1313
+ marginLeft: "auto",
1314
+ marginRight: "auto",
1315
+ width: "192px",
1316
+ height: "8px",
1317
+ backgroundColor: "#e5e7eb",
1318
+ borderRadius: "9999px",
1319
+ overflow: "hidden"
1320
+ } }, /* @__PURE__ */ React2.createElement("div", { style: {
1321
+ height: "100%",
1322
+ width: pressProgress + "%",
1323
+ background: "linear-gradient(to right, #a855f7, #ec4899)",
1324
+ transition: "width 0.1s ease"
1325
+ } }))), /* @__PURE__ */ React2.createElement("div", { style: { marginTop: "24px" } }, status === "pressing" ? /* @__PURE__ */ React2.createElement("p", { style: {
1326
+ fontSize: "18px",
1327
+ fontWeight: 500,
1328
+ color: "#9333ea",
1329
+ animation: "pulse 2s ease-in-out infinite"
1330
+ } }, "\u2728 \u6B63\u5728\u5206\u6790\u4E2D...") : /* @__PURE__ */ React2.createElement("div", { style: {
1331
+ display: "flex",
1332
+ alignItems: "center",
1333
+ justifyContent: "center",
1334
+ gap: "8px"
1335
+ } }, /* @__PURE__ */ React2.createElement("span", { style: {
1336
+ display: "inline-block",
1337
+ width: "6px",
1338
+ height: "6px",
1339
+ backgroundColor: "#a855f7",
1340
+ borderRadius: "50%",
1341
+ animation: "bounce 1s infinite"
1342
+ } }), /* @__PURE__ */ React2.createElement("span", { style: {
1343
+ display: "inline-block",
1344
+ width: "6px",
1345
+ height: "6px",
1346
+ backgroundColor: "#ec4899",
1347
+ borderRadius: "50%",
1348
+ animation: "bounce 1s infinite 0.1s"
1349
+ } }), /* @__PURE__ */ React2.createElement("span", { style: {
1350
+ display: "inline-block",
1351
+ width: "6px",
1352
+ height: "6px",
1353
+ backgroundColor: "#3b82f6",
1354
+ borderRadius: "50%",
1355
+ animation: "bounce 1s infinite 0.2s"
1356
+ } }))))));
1357
+ };
1358
+ var touchOptimizationStyles = `
1359
+ @keyframes bounce-slow {
1360
+ 0%, 100% {
1361
+ transform: translateY(0);
1362
+ }
1363
+ 50% {
1364
+ transform: translateY(-10px);
1365
+ }
1366
+ }
1367
+
1368
+ @keyframes bounce {
1369
+ 0%, 100% {
1370
+ transform: translateY(0);
1371
+ }
1372
+ 50% {
1373
+ transform: translateY(-4px);
1374
+ }
1375
+ }
1376
+
1377
+ @keyframes pulse {
1378
+ 0%, 100% {
1379
+ opacity: 1;
1380
+ }
1381
+ 50% {
1382
+ opacity: 0.5;
1383
+ }
1384
+ }
1385
+
1386
+ @keyframes spin {
1387
+ from {
1388
+ transform: rotate(0deg);
1389
+ }
1390
+ to {
1391
+ transform: rotate(360deg);
1392
+ }
1393
+ }
1394
+ `;
1395
+ if (typeof document !== "undefined" && !document.getElementById("test-yourself-styles")) {
1396
+ const style = document.createElement("style");
1397
+ style.id = "test-yourself-styles";
1398
+ style.textContent = touchOptimizationStyles;
1399
+ document.head.appendChild(style);
1400
+ }
1401
+ var ConfigManager = ({
1402
+ configService,
1403
+ onConfigChange,
1404
+ className = "",
1405
+ onImageUpload
1406
+ }) => {
1407
+ const [configs, setConfigs] = useState([]);
1408
+ const [selectedConfig, setSelectedConfig] = useState(null);
1409
+ const [isEditing, setIsEditing] = useState(false);
1410
+ const [isCreating, setIsCreating] = useState(false);
1411
+ const [loading, setLoading] = useState(true);
1412
+ const [error, setError] = useState(null);
1413
+ const [success, setSuccess] = useState(null);
1414
+ const [editForm, setEditForm] = useState({
1415
+ name: "",
1416
+ description: "",
1417
+ gameTitle: "",
1418
+ gameDescription: "",
1419
+ buttonText: "\u957F\u6309\u5F00\u59CB\u6D4B\u8BD5",
1420
+ longPressDuration: 2e3,
1421
+ results: []
1422
+ });
1423
+ const loadConfigs = useCallback(async () => {
1424
+ try {
1425
+ setLoading(true);
1426
+ const allConfigs = await configService.getAllConfigs();
1427
+ setConfigs(allConfigs);
1428
+ onConfigChange?.(allConfigs);
1429
+ } catch (err) {
1430
+ setError("\u52A0\u8F7D\u914D\u7F6E\u5931\u8D25: " + err.message);
1431
+ } finally {
1432
+ setLoading(false);
1433
+ }
1434
+ }, [configService, onConfigChange]);
1435
+ useEffect(() => {
1436
+ loadConfigs();
1437
+ }, [loadConfigs]);
1438
+ const handleStartCreate = () => {
1439
+ setEditForm({
1440
+ name: "\u65B0\u914D\u7F6E",
1441
+ description: "",
1442
+ gameTitle: "\u6D4B\u6D4B\u4F60\u662F\u4EC0\u4E48",
1443
+ gameDescription: "\u957F\u6309\u6309\u94AE\uFF0C\u53D1\u73B0\u4F60\u7684\u4E13\u5C5E\u5C5E\u6027",
1444
+ buttonText: "\u957F\u6309\u5F00\u59CB\u6D4B\u8BD5",
1445
+ longPressDuration: 2e3,
1446
+ results: []
1447
+ });
1448
+ setIsCreating(true);
1449
+ setIsEditing(true);
1450
+ setSelectedConfig(null);
1451
+ };
1452
+ const handleStartEdit = (config) => {
1453
+ setEditForm({
1454
+ name: config.name,
1455
+ description: config.description || "",
1456
+ gameTitle: config.config.gameTitle,
1457
+ gameDescription: config.config.gameDescription || "",
1458
+ buttonText: config.config.buttonText || "\u957F\u6309\u5F00\u59CB\u6D4B\u8BD5",
1459
+ longPressDuration: config.config.longPressDuration || 2e3,
1460
+ results: config.config.results
1461
+ });
1462
+ setIsCreating(false);
1463
+ setIsEditing(true);
1464
+ setSelectedConfig(config);
1465
+ };
1466
+ const handleCancelEdit = () => {
1467
+ setIsEditing(false);
1468
+ setIsCreating(false);
1469
+ setSelectedConfig(null);
1470
+ setError(null);
1471
+ };
1472
+ const handleSave = async () => {
1473
+ try {
1474
+ setError(null);
1475
+ if (!editForm.name.trim()) {
1476
+ setError("\u8BF7\u8F93\u5165\u914D\u7F6E\u540D\u79F0");
1477
+ return;
1478
+ }
1479
+ if (!editForm.gameTitle.trim()) {
1480
+ setError("\u8BF7\u8F93\u5165\u6E38\u620F\u6807\u9898");
1481
+ return;
1482
+ }
1483
+ if (editForm.results.length === 0) {
1484
+ setError("\u8BF7\u81F3\u5C11\u6DFB\u52A0\u4E00\u4E2A\u7ED3\u679C\u9879");
1485
+ return;
1486
+ }
1487
+ const testConfig = {
1488
+ gameTitle: editForm.gameTitle,
1489
+ gameDescription: editForm.gameDescription,
1490
+ buttonText: editForm.buttonText,
1491
+ longPressDuration: editForm.longPressDuration,
1492
+ results: editForm.results
1493
+ };
1494
+ if (isCreating) {
1495
+ await configService.createConfig(
1496
+ editForm.name,
1497
+ testConfig,
1498
+ editForm.description
1499
+ );
1500
+ setSuccess("\u521B\u5EFA\u914D\u7F6E\u6210\u529F\uFF01");
1501
+ } else if (selectedConfig) {
1502
+ await configService.updateConfig(selectedConfig.id, {
1503
+ name: editForm.name,
1504
+ description: editForm.description,
1505
+ config: testConfig
1506
+ });
1507
+ setSuccess("\u66F4\u65B0\u914D\u7F6E\u6210\u529F\uFF01");
1508
+ }
1509
+ await loadConfigs();
1510
+ handleCancelEdit();
1511
+ setTimeout(() => setSuccess(null), 3e3);
1512
+ } catch (err) {
1513
+ setError("\u4FDD\u5B58\u5931\u8D25: " + err.message);
1514
+ }
1515
+ };
1516
+ const handleDelete = async (id) => {
1517
+ if (!confirm("\u786E\u5B9A\u8981\u5220\u9664\u8FD9\u4E2A\u914D\u7F6E\u5417\uFF1F")) return;
1518
+ try {
1519
+ await configService.deleteConfig(id);
1520
+ await loadConfigs();
1521
+ setSuccess("\u5220\u9664\u6210\u529F\uFF01");
1522
+ setTimeout(() => setSuccess(null), 3e3);
1523
+ } catch (err) {
1524
+ setError("\u5220\u9664\u5931\u8D25: " + err.message);
1525
+ }
1526
+ };
1527
+ const handleDuplicate = async (id) => {
1528
+ try {
1529
+ await configService.duplicateConfig(id);
1530
+ await loadConfigs();
1531
+ setSuccess("\u590D\u5236\u6210\u529F\uFF01");
1532
+ setTimeout(() => setSuccess(null), 3e3);
1533
+ } catch (err) {
1534
+ setError("\u590D\u5236\u5931\u8D25: " + err.message);
1535
+ }
1536
+ };
1537
+ const handleSetDefault = async (id) => {
1538
+ try {
1539
+ await configService.setDefaultConfig(id);
1540
+ await loadConfigs();
1541
+ setSuccess("\u8BBE\u7F6E\u9ED8\u8BA4\u914D\u7F6E\u6210\u529F\uFF01");
1542
+ setTimeout(() => setSuccess(null), 3e3);
1543
+ } catch (err) {
1544
+ setError("\u8BBE\u7F6E\u5931\u8D25: " + err.message);
1545
+ }
1546
+ };
1547
+ const handleExport = async (id) => {
1548
+ try {
1549
+ const jsonString = await configService.exportConfig(id);
1550
+ const blob = new Blob([jsonString], { type: "application/json" });
1551
+ const url = URL.createObjectURL(blob);
1552
+ const a = document.createElement("a");
1553
+ a.href = url;
1554
+ a.download = "config_" + id + ".json";
1555
+ document.body.appendChild(a);
1556
+ a.click();
1557
+ document.body.removeChild(a);
1558
+ URL.revokeObjectURL(url);
1559
+ setSuccess("\u5BFC\u51FA\u6210\u529F\uFF01");
1560
+ setTimeout(() => setSuccess(null), 3e3);
1561
+ } catch (err) {
1562
+ setError("\u5BFC\u51FA\u5931\u8D25: " + err.message);
1563
+ }
1564
+ };
1565
+ const handleImport = async () => {
1566
+ const input = document.createElement("input");
1567
+ input.type = "file";
1568
+ input.accept = ".json";
1569
+ input.onchange = async (e) => {
1570
+ const file = e.target.files?.[0];
1571
+ if (!file) return;
1572
+ try {
1573
+ const text2 = await file.text();
1574
+ await configService.importConfig(text2);
1575
+ await loadConfigs();
1576
+ setSuccess("\u5BFC\u5165\u6210\u529F\uFF01");
1577
+ setTimeout(() => setSuccess(null), 3e3);
1578
+ } catch (err) {
1579
+ setError("\u5BFC\u5165\u5931\u8D25: " + err.message);
1580
+ }
1581
+ };
1582
+ input.click();
1583
+ };
1584
+ const handleAddResult = () => {
1585
+ const newResult = {
1586
+ id: "temp_" + Date.now(),
1587
+ _tempId: "temp_" + Date.now(),
1588
+ title: "\u65B0\u7ED3\u679C",
1589
+ description: "\u8FD9\u662F\u4E00\u4E2A\u65B0\u7684\u7ED3\u679C\u63CF\u8FF0",
1590
+ image: "\u{1F389}",
1591
+ imageType: "emoji"
1592
+ };
1593
+ setEditForm({
1594
+ ...editForm,
1595
+ results: [...editForm.results, newResult]
1596
+ });
1597
+ };
1598
+ const handleDeleteResult = (index2) => {
1599
+ const newResults = [...editForm.results];
1600
+ newResults.splice(index2, 1);
1601
+ setEditForm({ ...editForm, results: newResults });
1602
+ };
1603
+ const handleUpdateResult = (index2, updates) => {
1604
+ const newResults = [...editForm.results];
1605
+ newResults[index2] = { ...newResults[index2], ...updates };
1606
+ setEditForm({ ...editForm, results: newResults });
1607
+ };
1608
+ const handleResultImageUpload = async (index2, file) => {
1609
+ try {
1610
+ if (!onImageUpload) {
1611
+ const reader = new FileReader();
1612
+ reader.onload = (e) => {
1613
+ const base64 = e.target?.result;
1614
+ handleUpdateResult(index2, {
1615
+ image: base64,
1616
+ imageType: "url"
1617
+ });
1618
+ };
1619
+ reader.readAsDataURL(file);
1620
+ } else {
1621
+ const url = await onImageUpload(file);
1622
+ handleUpdateResult(index2, {
1623
+ image: url,
1624
+ imageType: "url"
1625
+ });
1626
+ }
1627
+ } catch (err) {
1628
+ setError("\u4E0A\u4F20\u56FE\u7247\u5931\u8D25: " + err.message);
1629
+ }
1630
+ };
1631
+ const handleUseDefaultResults = () => {
1632
+ if (confirm("\u786E\u5B9A\u8981\u4F7F\u7528\u9ED8\u8BA4\u768445\u4E2A\u7ED3\u679C\u5417\uFF1F\u8FD9\u5C06\u66FF\u6362\u5F53\u524D\u7684\u6240\u6709\u7ED3\u679C\u3002")) {
1633
+ setEditForm({ ...editForm, results: DEFAULT_RESULTS });
1634
+ }
1635
+ };
1636
+ const renderConfigList = () => /* @__PURE__ */ React2.createElement("div", { className: "space-y-4" }, configs.map((config) => /* @__PURE__ */ React2.createElement(
1637
+ "div",
1638
+ {
1639
+ key: config.id,
1640
+ className: "bg-white dark:bg-gray-800 rounded-lg shadow-sm p-4 border border-gray-200 dark:border-gray-700 hover:shadow-md transition-shadow"
1641
+ },
1642
+ /* @__PURE__ */ React2.createElement("div", { className: "flex items-start justify-between" }, /* @__PURE__ */ React2.createElement("div", { className: "flex-1" }, /* @__PURE__ */ React2.createElement("div", { className: "flex items-center gap-2 mb-1" }, /* @__PURE__ */ React2.createElement("h3", { className: "text-lg font-semibold text-gray-900 dark:text-white" }, config.name), config.isDefault && /* @__PURE__ */ React2.createElement("span", { className: "inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-xs font-medium bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-200" }, /* @__PURE__ */ React2.createElement(Star, { className: "w-3 h-3" }), "\u9ED8\u8BA4")), config.description && /* @__PURE__ */ React2.createElement("p", { className: "text-sm text-gray-600 dark:text-gray-400 mb-2" }, config.description), /* @__PURE__ */ React2.createElement("div", { className: "flex items-center gap-4 text-xs text-gray-500 dark:text-gray-400" }, /* @__PURE__ */ React2.createElement("span", null, "\u7ED3\u679C\u6570: ", config.config.results.length), /* @__PURE__ */ React2.createElement("span", null, "\u521B\u5EFA: ", new Date(config.createdAt).toLocaleDateString()))), /* @__PURE__ */ React2.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ React2.createElement(
1643
+ "button",
1644
+ {
1645
+ onClick: () => handleSetDefault(config.id),
1646
+ disabled: config.isDefault,
1647
+ className: "p-2 text-gray-600 hover:text-yellow-600 disabled:opacity-50 disabled:cursor-not-allowed",
1648
+ title: "\u8BBE\u4E3A\u9ED8\u8BA4"
1649
+ },
1650
+ /* @__PURE__ */ React2.createElement(Star, { className: "w-5 h-5" })
1651
+ ), /* @__PURE__ */ React2.createElement(
1652
+ "button",
1653
+ {
1654
+ onClick: () => handleStartEdit(config),
1655
+ className: "p-2 text-gray-600 hover:text-blue-600",
1656
+ title: "\u7F16\u8F91"
1657
+ },
1658
+ /* @__PURE__ */ React2.createElement(Edit, { className: "w-5 h-5" })
1659
+ ), /* @__PURE__ */ React2.createElement(
1660
+ "button",
1661
+ {
1662
+ onClick: () => handleDuplicate(config.id),
1663
+ className: "p-2 text-gray-600 hover:text-green-600",
1664
+ title: "\u590D\u5236"
1665
+ },
1666
+ /* @__PURE__ */ React2.createElement(Copy, { className: "w-5 h-5" })
1667
+ ), /* @__PURE__ */ React2.createElement(
1668
+ "button",
1669
+ {
1670
+ onClick: () => handleExport(config.id),
1671
+ className: "p-2 text-gray-600 hover:text-purple-600",
1672
+ title: "\u5BFC\u51FA"
1673
+ },
1674
+ /* @__PURE__ */ React2.createElement(Download, { className: "w-5 h-5" })
1675
+ ), /* @__PURE__ */ React2.createElement(
1676
+ "button",
1677
+ {
1678
+ onClick: () => handleDelete(config.id),
1679
+ className: "p-2 text-gray-600 hover:text-red-600",
1680
+ title: "\u5220\u9664"
1681
+ },
1682
+ /* @__PURE__ */ React2.createElement(Trash2, { className: "w-5 h-5" })
1683
+ )))
1684
+ )));
1685
+ const renderResultEditor = (result, index2) => /* @__PURE__ */ React2.createElement(
1686
+ "div",
1687
+ {
1688
+ key: result._tempId || result.id,
1689
+ className: "bg-gray-50 dark:bg-gray-700 rounded-lg p-4 space-y-3"
1690
+ },
1691
+ /* @__PURE__ */ React2.createElement("div", { className: "flex items-start justify-between" }, /* @__PURE__ */ React2.createElement("h4", { className: "text-sm font-medium text-gray-700 dark:text-gray-300" }, "\u7ED3\u679C #", index2 + 1), /* @__PURE__ */ React2.createElement(
1692
+ "button",
1693
+ {
1694
+ onClick: () => handleDeleteResult(index2),
1695
+ className: "text-red-600 hover:text-red-700"
1696
+ },
1697
+ /* @__PURE__ */ React2.createElement(Trash2, { className: "w-4 h-4" })
1698
+ )),
1699
+ /* @__PURE__ */ React2.createElement("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-3" }, /* @__PURE__ */ React2.createElement("div", null, /* @__PURE__ */ React2.createElement("label", { className: "block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1" }, "\u6807\u9898"), /* @__PURE__ */ React2.createElement(
1700
+ "input",
1701
+ {
1702
+ type: "text",
1703
+ value: result.title,
1704
+ onChange: (e) => handleUpdateResult(index2, { title: e.target.value }),
1705
+ className: "w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 dark:bg-gray-800 dark:text-white",
1706
+ placeholder: "\u4F8B\u5982: \u53EF\u7231\u7684\u732B\u54AA"
1707
+ }
1708
+ )), /* @__PURE__ */ React2.createElement("div", null, /* @__PURE__ */ React2.createElement("label", { className: "block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1" }, "\u56FE\u7247/Emoji"), /* @__PURE__ */ React2.createElement("div", { className: "flex gap-2" }, /* @__PURE__ */ React2.createElement(
1709
+ "input",
1710
+ {
1711
+ type: "text",
1712
+ value: result.image,
1713
+ onChange: (e) => handleUpdateResult(index2, { image: e.target.value }),
1714
+ className: "flex-1 px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 dark:bg-gray-800 dark:text-white",
1715
+ placeholder: "\u{1F389} \u6216 URL"
1716
+ }
1717
+ ), /* @__PURE__ */ React2.createElement("label", { className: "cursor-pointer inline-flex items-center justify-center px-3 py-2 bg-blue-50 dark:bg-blue-900 text-blue-600 dark:text-blue-200 rounded-lg hover:bg-blue-100 dark:hover:bg-blue-800" }, /* @__PURE__ */ React2.createElement(Image, { className: "w-5 h-5" }), /* @__PURE__ */ React2.createElement(
1718
+ "input",
1719
+ {
1720
+ type: "file",
1721
+ accept: "image/*",
1722
+ className: "hidden",
1723
+ onChange: (e) => {
1724
+ const file = e.target.files?.[0];
1725
+ if (file) handleResultImageUpload(index2, file);
1726
+ }
1727
+ }
1728
+ ))))),
1729
+ /* @__PURE__ */ React2.createElement("div", null, /* @__PURE__ */ React2.createElement("label", { className: "block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1" }, "\u63CF\u8FF0"), /* @__PURE__ */ React2.createElement(
1730
+ "textarea",
1731
+ {
1732
+ value: result.description,
1733
+ onChange: (e) => handleUpdateResult(index2, { description: e.target.value }),
1734
+ rows: 2,
1735
+ className: "w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 dark:bg-gray-800 dark:text-white",
1736
+ placeholder: "\u8FD9\u4E2A\u7ED3\u679C\u7684\u8BE6\u7EC6\u63CF\u8FF0..."
1737
+ }
1738
+ )),
1739
+ result.image && /* @__PURE__ */ React2.createElement("div", { className: "flex items-center gap-2 p-2 bg-white dark:bg-gray-600 rounded border border-gray-200 dark:border-gray-500" }, /* @__PURE__ */ React2.createElement("span", { className: "text-sm text-gray-500 dark:text-gray-400" }, "\u9884\u89C8:"), result.imageType === "emoji" ? /* @__PURE__ */ React2.createElement("span", { className: "text-2xl" }, result.image) : /* @__PURE__ */ React2.createElement("img", { src: result.image, alt: result.title, className: "w-8 h-8 object-cover rounded" }), /* @__PURE__ */ React2.createElement("span", { className: "text-sm font-medium text-gray-700 dark:text-gray-300" }, result.title))
1740
+ );
1741
+ const renderEditForm = () => /* @__PURE__ */ React2.createElement("div", { className: "space-y-6" }, /* @__PURE__ */ React2.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React2.createElement("h2", { className: "text-2xl font-bold text-gray-900 dark:text-white" }, isCreating ? "\u521B\u5EFA\u65B0\u914D\u7F6E" : "\u7F16\u8F91\u914D\u7F6E"), /* @__PURE__ */ React2.createElement(
1742
+ "button",
1743
+ {
1744
+ onClick: handleCancelEdit,
1745
+ className: "p-2 text-gray-600 hover:text-gray-800 dark:text-gray-400 dark:hover:text-gray-200"
1746
+ },
1747
+ /* @__PURE__ */ React2.createElement(X, { className: "w-6 h-6" })
1748
+ )), /* @__PURE__ */ React2.createElement("div", { className: "space-y-4 bg-white dark:bg-gray-800 rounded-lg p-6 shadow-sm border border-gray-200 dark:border-gray-700" }, /* @__PURE__ */ React2.createElement("h3", { className: "text-lg font-semibold text-gray-900 dark:text-white mb-4" }, "\u57FA\u672C\u4FE1\u606F"), /* @__PURE__ */ React2.createElement("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4" }, /* @__PURE__ */ React2.createElement("div", null, /* @__PURE__ */ React2.createElement("label", { className: "block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2" }, "\u914D\u7F6E\u540D\u79F0 *"), /* @__PURE__ */ React2.createElement(
1749
+ "input",
1750
+ {
1751
+ type: "text",
1752
+ value: editForm.name,
1753
+ onChange: (e) => setEditForm({ ...editForm, name: e.target.value }),
1754
+ className: "w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 dark:bg-gray-700 dark:text-white",
1755
+ placeholder: "\u4F8B\u5982: \u52A8\u7269\u4E3B\u9898\u6D4B\u8BD5"
1756
+ }
1757
+ )), /* @__PURE__ */ React2.createElement("div", null, /* @__PURE__ */ React2.createElement("label", { className: "block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2" }, "\u6E38\u620F\u6807\u9898 *"), /* @__PURE__ */ React2.createElement(
1758
+ "input",
1759
+ {
1760
+ type: "text",
1761
+ value: editForm.gameTitle,
1762
+ onChange: (e) => setEditForm({ ...editForm, gameTitle: e.target.value }),
1763
+ className: "w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 dark:bg-gray-700 dark:text-white",
1764
+ placeholder: "\u4F8B\u5982: \u6D4B\u6D4B\u4F60\u662F\u4EC0\u4E48\u52A8\u7269"
1765
+ }
1766
+ ))), /* @__PURE__ */ React2.createElement("div", null, /* @__PURE__ */ React2.createElement("label", { className: "block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2" }, "\u914D\u7F6E\u63CF\u8FF0"), /* @__PURE__ */ React2.createElement(
1767
+ "textarea",
1768
+ {
1769
+ value: editForm.description,
1770
+ onChange: (e) => setEditForm({ ...editForm, description: e.target.value }),
1771
+ rows: 2,
1772
+ className: "w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 dark:bg-gray-700 dark:text-white",
1773
+ placeholder: "\u7B80\u8981\u63CF\u8FF0\u8FD9\u4E2A\u914D\u7F6E\u7684\u7528\u9014..."
1774
+ }
1775
+ )), /* @__PURE__ */ React2.createElement("div", null, /* @__PURE__ */ React2.createElement("label", { className: "block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2" }, "\u6E38\u620F\u63CF\u8FF0"), /* @__PURE__ */ React2.createElement(
1776
+ "input",
1777
+ {
1778
+ type: "text",
1779
+ value: editForm.gameDescription,
1780
+ onChange: (e) => setEditForm({ ...editForm, gameDescription: e.target.value }),
1781
+ className: "w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 dark:bg-gray-700 dark:text-white",
1782
+ placeholder: "\u4F8B\u5982: \u957F\u6309\u6309\u94AE\uFF0C\u53D1\u73B0\u4F60\u7684\u4E13\u5C5E\u5C5E\u6027"
1783
+ }
1784
+ )), /* @__PURE__ */ React2.createElement("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4" }, /* @__PURE__ */ React2.createElement("div", null, /* @__PURE__ */ React2.createElement("label", { className: "block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2" }, "\u6309\u94AE\u6587\u672C"), /* @__PURE__ */ React2.createElement(
1785
+ "input",
1786
+ {
1787
+ type: "text",
1788
+ value: editForm.buttonText,
1789
+ onChange: (e) => setEditForm({ ...editForm, buttonText: e.target.value }),
1790
+ className: "w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 dark:bg-gray-700 dark:text-white"
1791
+ }
1792
+ )), /* @__PURE__ */ React2.createElement("div", null, /* @__PURE__ */ React2.createElement("label", { className: "block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2" }, "\u957F\u6309\u65F6\u95F4 (\u6BEB\u79D2)"), /* @__PURE__ */ React2.createElement(
1793
+ "input",
1794
+ {
1795
+ type: "number",
1796
+ value: editForm.longPressDuration,
1797
+ onChange: (e) => setEditForm({ ...editForm, longPressDuration: parseInt(e.target.value) || 2e3 }),
1798
+ className: "w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 dark:bg-gray-700 dark:text-white",
1799
+ min: "500",
1800
+ step: "100"
1801
+ }
1802
+ )))), /* @__PURE__ */ React2.createElement("div", { className: "bg-white dark:bg-gray-800 rounded-lg p-6 shadow-sm border border-gray-200 dark:border-gray-700" }, /* @__PURE__ */ React2.createElement("div", { className: "flex items-center justify-between mb-4" }, /* @__PURE__ */ React2.createElement("h3", { className: "text-lg font-semibold text-gray-900 dark:text-white" }, "\u7ED3\u679C\u5217\u8868 (", editForm.results.length, ")"), /* @__PURE__ */ React2.createElement("div", { className: "flex gap-2" }, /* @__PURE__ */ React2.createElement(
1803
+ "button",
1804
+ {
1805
+ onClick: handleUseDefaultResults,
1806
+ className: "inline-flex items-center gap-2 px-3 py-1.5 text-sm bg-purple-50 dark:bg-purple-900 text-purple-600 dark:text-purple-200 rounded-lg hover:bg-purple-100 dark:hover:bg-purple-800"
1807
+ },
1808
+ "\u4F7F\u7528\u9ED8\u8BA4\u7ED3\u679C"
1809
+ ), /* @__PURE__ */ React2.createElement(
1810
+ "button",
1811
+ {
1812
+ onClick: handleAddResult,
1813
+ className: "inline-flex items-center gap-2 px-3 py-1.5 text-sm bg-blue-50 dark:bg-blue-900 text-blue-600 dark:text-blue-200 rounded-lg hover:bg-blue-100 dark:hover:bg-blue-800"
1814
+ },
1815
+ /* @__PURE__ */ React2.createElement(Plus, { className: "w-4 h-4" }),
1816
+ "\u6DFB\u52A0\u7ED3\u679C"
1817
+ ))), /* @__PURE__ */ React2.createElement("div", { className: "space-y-3 max-h-[500px] overflow-y-auto" }, editForm.results.length === 0 ? /* @__PURE__ */ React2.createElement("div", { className: "text-center py-8 text-gray-500 dark:text-gray-400" }, /* @__PURE__ */ React2.createElement("p", null, "\u8FD8\u6CA1\u6709\u6DFB\u52A0\u4EFB\u4F55\u7ED3\u679C"), /* @__PURE__ */ React2.createElement("p", { className: "text-sm mt-2" }, '\u70B9\u51FB"\u6DFB\u52A0\u7ED3\u679C"\u6216"\u4F7F\u7528\u9ED8\u8BA4\u7ED3\u679C"\u5F00\u59CB')) : editForm.results.map((result, index2) => renderResultEditor(result, index2)))), /* @__PURE__ */ React2.createElement("div", { className: "flex items-center justify-end gap-3" }, /* @__PURE__ */ React2.createElement(
1818
+ "button",
1819
+ {
1820
+ onClick: handleCancelEdit,
1821
+ className: "px-6 py-2 text-gray-700 dark:text-gray-300 border border-gray-300 dark:border-gray-600 rounded-lg hover:bg-gray-50 dark:hover:bg-gray-700"
1822
+ },
1823
+ "\u53D6\u6D88"
1824
+ ), /* @__PURE__ */ React2.createElement(
1825
+ "button",
1826
+ {
1827
+ onClick: handleSave,
1828
+ className: "inline-flex items-center gap-2 px-6 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700"
1829
+ },
1830
+ /* @__PURE__ */ React2.createElement(Save, { className: "w-5 h-5" }),
1831
+ "\u4FDD\u5B58\u914D\u7F6E"
1832
+ )));
1833
+ if (loading) {
1834
+ return /* @__PURE__ */ React2.createElement("div", { className: clsx("p-8", className) }, /* @__PURE__ */ React2.createElement("div", { className: "text-center" }, /* @__PURE__ */ React2.createElement("div", { className: "inline-block w-8 h-8 border-4 border-blue-600 border-t-transparent rounded-full animate-spin" }), /* @__PURE__ */ React2.createElement("p", { className: "mt-2 text-gray-600 dark:text-gray-400" }, "\u52A0\u8F7D\u4E2D...")));
1835
+ }
1836
+ return /* @__PURE__ */ React2.createElement("div", { className: clsx("p-6", className) }, error && /* @__PURE__ */ React2.createElement("div", { className: "mb-4 p-4 bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 rounded-lg flex items-center gap-2 text-red-800 dark:text-red-200" }, /* @__PURE__ */ React2.createElement(AlertCircle, { className: "w-5 h-5 flex-shrink-0" }), /* @__PURE__ */ React2.createElement("span", null, error), /* @__PURE__ */ React2.createElement("button", { onClick: () => setError(null), className: "ml-auto" }, /* @__PURE__ */ React2.createElement(X, { className: "w-5 h-5" }))), success && /* @__PURE__ */ React2.createElement("div", { className: "mb-4 p-4 bg-green-50 dark:bg-green-900/20 border border-green-200 dark:border-green-800 rounded-lg flex items-center gap-2 text-green-800 dark:text-green-200" }, /* @__PURE__ */ React2.createElement(CheckCircle2, { className: "w-5 h-5 flex-shrink-0" }), /* @__PURE__ */ React2.createElement("span", null, success), /* @__PURE__ */ React2.createElement("button", { onClick: () => setSuccess(null), className: "ml-auto" }, /* @__PURE__ */ React2.createElement(X, { className: "w-5 h-5" }))), isEditing ? renderEditForm() : /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement("div", { className: "flex items-center justify-between mb-6" }, /* @__PURE__ */ React2.createElement("h1", { className: "text-3xl font-bold text-gray-900 dark:text-white" }, "\u914D\u7F6E\u7BA1\u7406"), /* @__PURE__ */ React2.createElement("div", { className: "flex gap-2" }, /* @__PURE__ */ React2.createElement(
1837
+ "button",
1838
+ {
1839
+ onClick: handleImport,
1840
+ className: "inline-flex items-center gap-2 px-4 py-2 text-gray-700 dark:text-gray-300 border border-gray-300 dark:border-gray-600 rounded-lg hover:bg-gray-50 dark:hover:bg-gray-700"
1841
+ },
1842
+ /* @__PURE__ */ React2.createElement(Upload, { className: "w-5 h-5" }),
1843
+ "\u5BFC\u5165"
1844
+ ), /* @__PURE__ */ React2.createElement(
1845
+ "button",
1846
+ {
1847
+ onClick: handleStartCreate,
1848
+ className: "inline-flex items-center gap-2 px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700"
1849
+ },
1850
+ /* @__PURE__ */ React2.createElement(Plus, { className: "w-5 h-5" }),
1851
+ "\u521B\u5EFA\u914D\u7F6E"
1852
+ ))), configs.length === 0 ? /* @__PURE__ */ React2.createElement("div", { className: "text-center py-12 bg-white dark:bg-gray-800 rounded-lg border-2 border-dashed border-gray-300 dark:border-gray-700" }, /* @__PURE__ */ React2.createElement("p", { className: "text-gray-600 dark:text-gray-400 mb-4" }, "\u8FD8\u6CA1\u6709\u4EFB\u4F55\u914D\u7F6E"), /* @__PURE__ */ React2.createElement(
1853
+ "button",
1854
+ {
1855
+ onClick: handleStartCreate,
1856
+ className: "inline-flex items-center gap-2 px-6 py-3 bg-blue-600 text-white rounded-lg hover:bg-blue-700"
1857
+ },
1858
+ /* @__PURE__ */ React2.createElement(Plus, { className: "w-5 h-5" }),
1859
+ "\u521B\u5EFA\u7B2C\u4E00\u4E2A\u914D\u7F6E"
1860
+ )) : renderConfigList()));
1861
+ };
1862
+ var ConfigList = ({
1863
+ configService,
1864
+ onSelect,
1865
+ onEdit,
1866
+ onDelete,
1867
+ showActions = true,
1868
+ showPreviewLink = false,
1869
+ previewBaseUrl = "/test-yourself",
1870
+ className = "",
1871
+ pageSize = 10
1872
+ }) => {
1873
+ const [configs, setConfigs] = useState([]);
1874
+ const [loading, setLoading] = useState(true);
1875
+ const [currentPage, setCurrentPage] = useState(1);
1876
+ const [searchTerm, setSearchTerm] = useState("");
1877
+ const loadConfigs = useCallback(async () => {
1878
+ try {
1879
+ setLoading(true);
1880
+ const list = await configService.getConfigList();
1881
+ setConfigs(list);
1882
+ } catch (error) {
1883
+ console.error("\u52A0\u8F7D\u914D\u7F6E\u5217\u8868\u5931\u8D25:", error);
1884
+ } finally {
1885
+ setLoading(false);
1886
+ }
1887
+ }, [configService]);
1888
+ useEffect(() => {
1889
+ loadConfigs();
1890
+ }, [loadConfigs]);
1891
+ const handleRefresh = () => {
1892
+ loadConfigs();
1893
+ };
1894
+ const handleSelect = (id) => {
1895
+ onSelect?.(id);
1896
+ };
1897
+ const handleEdit = (id) => {
1898
+ onEdit?.(id);
1899
+ };
1900
+ const handleDelete = async (id) => {
1901
+ if (!confirm("\u786E\u5B9A\u8981\u5220\u9664\u8FD9\u4E2A\u914D\u7F6E\u5417\uFF1F")) return;
1902
+ try {
1903
+ await configService.deleteConfig(id);
1904
+ await loadConfigs();
1905
+ onDelete?.(id);
1906
+ } catch (error) {
1907
+ console.error("\u5220\u9664\u914D\u7F6E\u5931\u8D25:", error);
1908
+ alert("\u5220\u9664\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5");
1909
+ }
1910
+ };
1911
+ const handleDuplicate = async (id) => {
1912
+ try {
1913
+ await configService.duplicateConfig(id);
1914
+ await loadConfigs();
1915
+ } catch (error) {
1916
+ console.error("\u590D\u5236\u914D\u7F6E\u5931\u8D25:", error);
1917
+ alert("\u590D\u5236\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5");
1918
+ }
1919
+ };
1920
+ const handleSetDefault = async (id) => {
1921
+ try {
1922
+ await configService.setDefaultConfig(id);
1923
+ await loadConfigs();
1924
+ } catch (error) {
1925
+ console.error("\u8BBE\u7F6E\u9ED8\u8BA4\u914D\u7F6E\u5931\u8D25:", error);
1926
+ alert("\u8BBE\u7F6E\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5");
1927
+ }
1928
+ };
1929
+ const handleExport = async (id) => {
1930
+ try {
1931
+ const jsonString = await configService.exportConfig(id);
1932
+ const blob = new Blob([jsonString], { type: "application/json" });
1933
+ const url = URL.createObjectURL(blob);
1934
+ const a = document.createElement("a");
1935
+ a.href = url;
1936
+ a.download = "config_" + id + ".json";
1937
+ document.body.appendChild(a);
1938
+ a.click();
1939
+ document.body.removeChild(a);
1940
+ URL.revokeObjectURL(url);
1941
+ } catch (error) {
1942
+ console.error("\u5BFC\u51FA\u914D\u7F6E\u5931\u8D25:", error);
1943
+ alert("\u5BFC\u51FA\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5");
1944
+ }
1945
+ };
1946
+ const getPreviewUrl = (id) => {
1947
+ return previewBaseUrl + "?configId=" + id;
1948
+ };
1949
+ const filteredConfigs = configs.filter((config) => {
1950
+ if (!searchTerm) return true;
1951
+ const term = searchTerm.toLowerCase();
1952
+ return config.name.toLowerCase().includes(term) || config.description?.toLowerCase().includes(term);
1953
+ });
1954
+ const totalPages = Math.ceil(filteredConfigs.length / pageSize);
1955
+ const startIndex = (currentPage - 1) * pageSize;
1956
+ const endIndex = startIndex + pageSize;
1957
+ const currentConfigs = filteredConfigs.slice(startIndex, endIndex);
1958
+ if (loading) {
1959
+ return /* @__PURE__ */ React2.createElement("div", { className: clsx("p-8", className) }, /* @__PURE__ */ React2.createElement("div", { className: "text-center" }, /* @__PURE__ */ React2.createElement("div", { className: "inline-block w-8 h-8 border-4 border-blue-600 border-t-transparent rounded-full animate-spin" }), /* @__PURE__ */ React2.createElement("p", { className: "mt-2 text-gray-600 dark:text-gray-400" }, "\u52A0\u8F7D\u4E2D...")));
1960
+ }
1961
+ return /* @__PURE__ */ React2.createElement("div", { className }, /* @__PURE__ */ React2.createElement("div", { className: "mb-4 flex items-center justify-between gap-4" }, /* @__PURE__ */ React2.createElement("div", { className: "flex-1" }, /* @__PURE__ */ React2.createElement(
1962
+ "input",
1963
+ {
1964
+ type: "text",
1965
+ placeholder: "\u641C\u7D22\u914D\u7F6E...",
1966
+ value: searchTerm,
1967
+ onChange: (e) => {
1968
+ setSearchTerm(e.target.value);
1969
+ setCurrentPage(1);
1970
+ },
1971
+ className: "w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 dark:bg-gray-800 dark:text-white"
1972
+ }
1973
+ )), /* @__PURE__ */ React2.createElement(
1974
+ "button",
1975
+ {
1976
+ onClick: handleRefresh,
1977
+ className: "p-2 text-gray-600 hover:text-gray-800 dark:text-gray-400 dark:hover:text-gray-200",
1978
+ title: "\u5237\u65B0"
1979
+ },
1980
+ /* @__PURE__ */ React2.createElement(RefreshCw, { className: "w-5 h-5" })
1981
+ )), currentConfigs.length === 0 ? /* @__PURE__ */ React2.createElement("div", { className: "text-center py-12 bg-gray-50 dark:bg-gray-800 rounded-lg border border-gray-200 dark:border-gray-700" }, /* @__PURE__ */ React2.createElement("p", { className: "text-gray-600 dark:text-gray-400" }, searchTerm ? "\u6CA1\u6709\u627E\u5230\u5339\u914D\u7684\u914D\u7F6E" : "\u8FD8\u6CA1\u6709\u4EFB\u4F55\u914D\u7F6E")) : /* @__PURE__ */ React2.createElement("div", { className: "space-y-3" }, currentConfigs.map((config) => /* @__PURE__ */ React2.createElement(
1982
+ "div",
1983
+ {
1984
+ key: config.id,
1985
+ className: "bg-white dark:bg-gray-800 rounded-lg shadow-sm p-4 border border-gray-200 dark:border-gray-700 hover:shadow-md transition-shadow"
1986
+ },
1987
+ /* @__PURE__ */ React2.createElement("div", { className: "flex items-start justify-between gap-4" }, /* @__PURE__ */ React2.createElement(
1988
+ "div",
1989
+ {
1990
+ className: "flex-1 cursor-pointer",
1991
+ onClick: () => handleSelect(config.id)
1992
+ },
1993
+ /* @__PURE__ */ React2.createElement("div", { className: "flex items-center gap-2 mb-1" }, /* @__PURE__ */ React2.createElement("h3", { className: "text-lg font-semibold text-gray-900 dark:text-white" }, config.name), config.isDefault && /* @__PURE__ */ React2.createElement("span", { className: "inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-xs font-medium bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-200" }, /* @__PURE__ */ React2.createElement(Star, { className: "w-3 h-3" }), "\u9ED8\u8BA4")),
1994
+ config.description && /* @__PURE__ */ React2.createElement("p", { className: "text-sm text-gray-600 dark:text-gray-400 mb-2" }, config.description),
1995
+ /* @__PURE__ */ React2.createElement("div", { className: "flex items-center gap-4 text-xs text-gray-500 dark:text-gray-400" }, /* @__PURE__ */ React2.createElement("span", null, "\u7ED3\u679C\u6570: ", config.resultCount), /* @__PURE__ */ React2.createElement("span", null, "\u521B\u5EFA: ", new Date(config.createdAt).toLocaleDateString()), config.updatedAt !== config.createdAt && /* @__PURE__ */ React2.createElement("span", null, "\u66F4\u65B0: ", new Date(config.updatedAt).toLocaleDateString()))
1996
+ ), showActions && /* @__PURE__ */ React2.createElement("div", { className: "flex items-center gap-1" }, showPreviewLink && /* @__PURE__ */ React2.createElement(
1997
+ "a",
1998
+ {
1999
+ href: getPreviewUrl(config.id),
2000
+ target: "_blank",
2001
+ rel: "noopener noreferrer",
2002
+ className: "p-2 text-gray-600 hover:text-blue-600 dark:text-gray-400 dark:hover:text-blue-400",
2003
+ title: "\u9884\u89C8"
2004
+ },
2005
+ /* @__PURE__ */ React2.createElement(ExternalLink, { className: "w-4 h-4" })
2006
+ ), /* @__PURE__ */ React2.createElement(
2007
+ "button",
2008
+ {
2009
+ onClick: () => handleSetDefault(config.id),
2010
+ disabled: config.isDefault,
2011
+ className: "p-2 text-gray-600 hover:text-yellow-600 disabled:opacity-50 disabled:cursor-not-allowed",
2012
+ title: "\u8BBE\u4E3A\u9ED8\u8BA4"
2013
+ },
2014
+ /* @__PURE__ */ React2.createElement(Star, { className: "w-4 h-4" })
2015
+ ), /* @__PURE__ */ React2.createElement(
2016
+ "button",
2017
+ {
2018
+ onClick: () => handleEdit(config.id),
2019
+ className: "p-2 text-gray-600 hover:text-blue-600",
2020
+ title: "\u7F16\u8F91"
2021
+ },
2022
+ /* @__PURE__ */ React2.createElement(Edit, { className: "w-4 h-4" })
2023
+ ), /* @__PURE__ */ React2.createElement(
2024
+ "button",
2025
+ {
2026
+ onClick: () => handleDuplicate(config.id),
2027
+ className: "p-2 text-gray-600 hover:text-green-600",
2028
+ title: "\u590D\u5236"
2029
+ },
2030
+ /* @__PURE__ */ React2.createElement(Copy, { className: "w-4 h-4" })
2031
+ ), /* @__PURE__ */ React2.createElement(
2032
+ "button",
2033
+ {
2034
+ onClick: () => handleExport(config.id),
2035
+ className: "p-2 text-gray-600 hover:text-purple-600",
2036
+ title: "\u5BFC\u51FA"
2037
+ },
2038
+ /* @__PURE__ */ React2.createElement(Download, { className: "w-4 h-4" })
2039
+ ), /* @__PURE__ */ React2.createElement(
2040
+ "button",
2041
+ {
2042
+ onClick: () => handleDelete(config.id),
2043
+ className: "p-2 text-gray-600 hover:text-red-600",
2044
+ title: "\u5220\u9664"
2045
+ },
2046
+ /* @__PURE__ */ React2.createElement(Trash2, { className: "w-4 h-4" })
2047
+ )))
2048
+ ))), totalPages > 1 && /* @__PURE__ */ React2.createElement("div", { className: "mt-4 flex items-center justify-between" }, /* @__PURE__ */ React2.createElement("p", { className: "text-sm text-gray-600 dark:text-gray-400" }, "\u663E\u793A ", startIndex + 1, " - ", Math.min(endIndex, filteredConfigs.length), " \u5171", " ", filteredConfigs.length, " \u4E2A\u914D\u7F6E"), /* @__PURE__ */ React2.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ React2.createElement(
2049
+ "button",
2050
+ {
2051
+ onClick: () => setCurrentPage((p) => Math.max(1, p - 1)),
2052
+ disabled: currentPage === 1,
2053
+ className: "px-3 py-1 border border-gray-300 dark:border-gray-600 rounded-lg disabled:opacity-50 disabled:cursor-not-allowed hover:bg-gray-50 dark:hover:bg-gray-700"
2054
+ },
2055
+ "\u4E0A\u4E00\u9875"
2056
+ ), /* @__PURE__ */ React2.createElement("span", { className: "text-sm text-gray-600 dark:text-gray-400" }, currentPage, " / ", totalPages), /* @__PURE__ */ React2.createElement(
2057
+ "button",
2058
+ {
2059
+ onClick: () => setCurrentPage((p) => Math.min(totalPages, p + 1)),
2060
+ disabled: currentPage === totalPages,
2061
+ className: "px-3 py-1 border border-gray-300 dark:border-gray-600 rounded-lg disabled:opacity-50 disabled:cursor-not-allowed hover:bg-gray-50 dark:hover:bg-gray-700"
2062
+ },
2063
+ "\u4E0B\u4E00\u9875"
2064
+ ))));
2065
+ };
2066
+
2067
+ // src/business/testYourself/server/index.ts
2068
+ init_ConfigService();
2069
+ var testYourselfConfigs = pgTable(
2070
+ "test_yourself_configs",
2071
+ {
2072
+ // ========== 主键 ==========
2073
+ /** 配置唯一ID */
2074
+ id: uuid("id").primaryKey().defaultRandom(),
2075
+ // ========== 基本信息 ==========
2076
+ /** 配置名称 */
2077
+ name: varchar("name", { length: 255 }).notNull(),
2078
+ /** 配置描述 */
2079
+ description: text("description"),
2080
+ /** 配置标签(用于分类和搜索) */
2081
+ tags: json("tags").$type().default([]),
2082
+ // ========== 配置数据 ==========
2083
+ /** 测试配置内容(JSON格式) */
2084
+ config: json("config").$type().notNull(),
2085
+ /** 结果数量(冗余字段,便于查询) */
2086
+ resultCount: integer("result_count").notNull().default(0),
2087
+ // ========== 状态字段 ==========
2088
+ /** 是否为默认配置 */
2089
+ isDefault: boolean("is_default").notNull().default(false),
2090
+ /** 是否已发布(草稿/发布) */
2091
+ isPublished: boolean("is_published").notNull().default(true),
2092
+ /** 是否已归档 */
2093
+ isArchived: boolean("is_archived").notNull().default(false),
2094
+ /** 是否已删除(软删除) */
2095
+ isDeleted: boolean("is_deleted").notNull().default(false),
2096
+ // ========== 权限和所有权 ==========
2097
+ /** 创建者ID */
2098
+ createdBy: varchar("created_by", { length: 255 }).notNull(),
2099
+ /** 最后更新者ID */
2100
+ updatedBy: varchar("updated_by", { length: 255 }),
2101
+ /** 所属组织/租户ID(多租户支持) */
2102
+ organizationId: varchar("organization_id", { length: 255 }),
2103
+ // ========== 统计信息 ==========
2104
+ /** 使用次数 */
2105
+ usageCount: integer("usage_count").notNull().default(0),
2106
+ /** 最后使用时间 */
2107
+ lastUsedAt: timestamp("last_used_at"),
2108
+ /** 浏览次数 */
2109
+ viewCount: integer("view_count").notNull().default(0),
2110
+ // ========== 版本控制 ==========
2111
+ /** 配置版本号 */
2112
+ version: integer("version").notNull().default(1),
2113
+ /** 父配置ID(用于版本追踪) */
2114
+ parentId: uuid("parent_id"),
2115
+ // ========== 自定义字段 ==========
2116
+ /** 自定义元数据(扩展字段) */
2117
+ metadata: json("metadata").$type(),
2118
+ /** 配置来源(web/api/import) */
2119
+ source: varchar("source", { length: 50 }),
2120
+ // ========== 时间戳 ==========
2121
+ /** 创建时间 */
2122
+ createdAt: timestamp("created_at").defaultNow().notNull(),
2123
+ /** 更新时间 */
2124
+ updatedAt: timestamp("updated_at").defaultNow().notNull(),
2125
+ /** 发布时间 */
2126
+ publishedAt: timestamp("published_at"),
2127
+ /** 归档时间 */
2128
+ archivedAt: timestamp("archived_at"),
2129
+ /** 删除时间 */
2130
+ deletedAt: timestamp("deleted_at")
2131
+ },
2132
+ (table) => ({
2133
+ // ========== 索引设计 ==========
2134
+ /** 名称搜索索引 */
2135
+ nameIndex: index("test_configs_name_idx").on(table.name),
2136
+ /** 创建者索引 */
2137
+ createdByIndex: index("test_configs_created_by_idx").on(table.createdBy),
2138
+ /** 组织索引(多租户) */
2139
+ organizationIndex: index("test_configs_organization_idx").on(table.organizationId),
2140
+ /** 默认配置索引 */
2141
+ isDefaultIndex: index("test_configs_is_default_idx").on(table.isDefault),
2142
+ /** 发布状态索引 */
2143
+ isPublishedIndex: index("test_configs_is_published_idx").on(table.isPublished),
2144
+ /** 删除状态索引 */
2145
+ isDeletedIndex: index("test_configs_is_deleted_idx").on(table.isDeleted),
2146
+ /** 创建时间索引 */
2147
+ createdAtIndex: index("test_configs_created_at_idx").on(table.createdAt),
2148
+ /** 最后使用时间索引 */
2149
+ lastUsedAtIndex: index("test_configs_last_used_at_idx").on(table.lastUsedAt),
2150
+ /** 组合索引:组织+删除状态+发布状态 */
2151
+ orgDeletedPublishedIndex: index("test_configs_org_deleted_published_idx").on(
2152
+ table.organizationId,
2153
+ table.isDeleted,
2154
+ table.isPublished
2155
+ ),
2156
+ /** 组合索引:创建者+删除状态 */
2157
+ createdByDeletedIndex: index("test_configs_created_by_deleted_idx").on(
2158
+ table.createdBy,
2159
+ table.isDeleted
2160
+ )
2161
+ })
2162
+ );
2163
+ var testYourselfConfigUsage = pgTable(
2164
+ "test_yourself_config_usage",
2165
+ {
2166
+ /** 记录ID */
2167
+ id: uuid("id").primaryKey().defaultRandom(),
2168
+ /** 配置ID */
2169
+ configId: uuid("config_id").references(() => testYourselfConfigs.id, { onDelete: "cascade" }).notNull(),
2170
+ /** 用户ID */
2171
+ userId: varchar("user_id", { length: 255 }),
2172
+ /** 设备指纹 */
2173
+ fingerprint: text("fingerprint"),
2174
+ /** 测试结果ID */
2175
+ resultId: varchar("result_id", { length: 255 }),
2176
+ /** IP地址 */
2177
+ ipAddress: varchar("ip_address", { length: 45 }),
2178
+ /** User Agent */
2179
+ userAgent: text("user_agent"),
2180
+ /** 来源页面 */
2181
+ referer: text("referer"),
2182
+ /** 使用时间 */
2183
+ usedAt: timestamp("used_at").defaultNow().notNull(),
2184
+ /** 完成时间(毫秒) */
2185
+ completionTime: integer("completion_time"),
2186
+ /** 额外数据 */
2187
+ metadata: json("metadata").$type()
2188
+ },
2189
+ (table) => ({
2190
+ /** 配置ID索引 */
2191
+ configIndex: index("test_usage_config_idx").on(table.configId),
2192
+ /** 用户ID索引 */
2193
+ userIndex: index("test_usage_user_idx").on(table.userId),
2194
+ /** 使用时间索引 */
2195
+ usedAtIndex: index("test_usage_used_at_idx").on(table.usedAt),
2196
+ /** 指纹索引 */
2197
+ fingerprintIndex: index("test_usage_fingerprint_idx").on(table.fingerprint)
2198
+ })
2199
+ );
2200
+ var testYourselfConfigShares = pgTable(
2201
+ "test_yourself_config_shares",
2202
+ {
2203
+ /** 分享ID */
2204
+ id: uuid("id").primaryKey().defaultRandom(),
2205
+ /** 分享代码(短链接标识) */
2206
+ shareCode: varchar("share_code", { length: 20 }).notNull().unique(),
2207
+ /** 配置ID */
2208
+ configId: uuid("config_id").references(() => testYourselfConfigs.id, { onDelete: "cascade" }).notNull(),
2209
+ /** 分享标题 */
2210
+ title: varchar("title", { length: 255 }),
2211
+ /** 分享描述 */
2212
+ description: text("description"),
2213
+ /** 访问密码 */
2214
+ password: varchar("password", { length: 100 }),
2215
+ /** 最大访问次数 */
2216
+ maxAccess: integer("max_access"),
2217
+ /** 当前访问次数 */
2218
+ accessCount: integer("access_count").notNull().default(0),
2219
+ /** 是否启用 */
2220
+ isActive: boolean("is_active").notNull().default(true),
2221
+ /** 过期时间 */
2222
+ expiresAt: timestamp("expires_at"),
2223
+ /** 创建者ID */
2224
+ createdBy: varchar("created_by", { length: 255 }).notNull(),
2225
+ /** 创建时间 */
2226
+ createdAt: timestamp("created_at").defaultNow().notNull(),
2227
+ /** 更新时间 */
2228
+ updatedAt: timestamp("updated_at").defaultNow().notNull()
2229
+ },
2230
+ (table) => ({
2231
+ /** 分享代码索引 */
2232
+ shareCodeIndex: index("test_shares_share_code_idx").on(table.shareCode),
2233
+ /** 配置ID索引 */
2234
+ configIndex: index("test_shares_config_idx").on(table.configId),
2235
+ /** 创建者索引 */
2236
+ createdByIndex: index("test_shares_created_by_idx").on(table.createdBy),
2237
+ /** 活跃状态索引 */
2238
+ isActiveIndex: index("test_shares_is_active_idx").on(table.isActive)
2239
+ })
2240
+ );
2241
+
2242
+ // src/business/testYourself/server/DatabaseConfigAdapter.ts
2243
+ var DatabaseConfigAdapter = class {
2244
+ constructor(options) {
2245
+ this.db = options.db;
2246
+ this.userId = options.userId || "system";
2247
+ this.organizationId = options.organizationId;
2248
+ this.softDelete = options.softDelete !== false;
2249
+ }
2250
+ /**
2251
+ * 设置当前用户ID
2252
+ */
2253
+ setUserId(userId) {
2254
+ this.userId = userId;
2255
+ }
2256
+ /**
2257
+ * 设置组织ID
2258
+ */
2259
+ setOrganizationId(organizationId) {
2260
+ this.organizationId = organizationId;
2261
+ }
2262
+ /**
2263
+ * 将数据库记录转换为 SavedConfig
2264
+ */
2265
+ toSavedConfig(dbConfig) {
2266
+ return {
2267
+ id: dbConfig.id,
2268
+ name: dbConfig.name,
2269
+ description: dbConfig.description || void 0,
2270
+ config: dbConfig.config,
2271
+ createdAt: dbConfig.createdAt.getTime(),
2272
+ updatedAt: dbConfig.updatedAt.getTime(),
2273
+ isDefault: dbConfig.isDefault
2274
+ };
2275
+ }
2276
+ /**
2277
+ * 保存配置
2278
+ */
2279
+ async saveConfig(config) {
2280
+ try {
2281
+ const existing = await this.db.select().from(testYourselfConfigs).where(eq(testYourselfConfigs.id, config.id)).limit(1);
2282
+ const resultCount = config.config.results?.length || 0;
2283
+ if (existing && existing.length > 0) {
2284
+ await this.db.update(testYourselfConfigs).set({
2285
+ name: config.name,
2286
+ description: config.description || null,
2287
+ config: config.config,
2288
+ resultCount,
2289
+ isDefault: config.isDefault || false,
2290
+ updatedBy: this.userId,
2291
+ updatedAt: /* @__PURE__ */ new Date(),
2292
+ version: sql`${testYourselfConfigs.version} + 1`
2293
+ // 版本号递增
2294
+ }).where(eq(testYourselfConfigs.id, config.id));
2295
+ console.log("\u2705 [DatabaseAdapter] \u914D\u7F6E\u5DF2\u66F4\u65B0:", config.id);
2296
+ } else {
2297
+ await this.db.insert(testYourselfConfigs).values({
2298
+ id: config.id,
2299
+ name: config.name,
2300
+ description: config.description || null,
2301
+ config: config.config,
2302
+ resultCount,
2303
+ isDefault: config.isDefault || false,
2304
+ createdBy: this.userId,
2305
+ organizationId: this.organizationId,
2306
+ createdAt: new Date(config.createdAt),
2307
+ updatedAt: new Date(config.updatedAt)
2308
+ });
2309
+ console.log("\u2705 [DatabaseAdapter] \u914D\u7F6E\u5DF2\u521B\u5EFA:", config.id);
2310
+ }
2311
+ } catch (error) {
2312
+ console.error("\u274C [DatabaseAdapter] \u4FDD\u5B58\u914D\u7F6E\u5931\u8D25:", error);
2313
+ throw error;
2314
+ }
2315
+ }
2316
+ /**
2317
+ * 获取配置
2318
+ */
2319
+ async getConfig(id) {
2320
+ try {
2321
+ const conditions = [eq(testYourselfConfigs.id, id)];
2322
+ if (this.softDelete) {
2323
+ conditions.push(eq(testYourselfConfigs.isDeleted, false));
2324
+ }
2325
+ const result = await this.db.select().from(testYourselfConfigs).where(and(...conditions)).limit(1);
2326
+ if (!result || result.length === 0) {
2327
+ return null;
2328
+ }
2329
+ return this.toSavedConfig(result[0]);
2330
+ } catch (error) {
2331
+ console.error("\u274C [DatabaseAdapter] \u83B7\u53D6\u914D\u7F6E\u5931\u8D25:", error);
2332
+ throw error;
2333
+ }
2334
+ }
2335
+ /**
2336
+ * 获取所有配置列表
2337
+ */
2338
+ async getAllConfigs() {
2339
+ try {
2340
+ const conditions = [];
2341
+ if (this.softDelete) {
2342
+ conditions.push(eq(testYourselfConfigs.isDeleted, false));
2343
+ }
2344
+ if (this.organizationId) {
2345
+ conditions.push(
2346
+ or(
2347
+ eq(testYourselfConfigs.organizationId, this.organizationId),
2348
+ sql`${testYourselfConfigs.organizationId} IS NULL`
2349
+ // 包含全局配置
2350
+ )
2351
+ );
2352
+ }
2353
+ const result = await this.db.select().from(testYourselfConfigs).where(conditions.length > 0 ? and(...conditions) : void 0).orderBy(desc(testYourselfConfigs.createdAt));
2354
+ return result.map((config) => this.toSavedConfig(config));
2355
+ } catch (error) {
2356
+ console.error("\u274C [DatabaseAdapter] \u83B7\u53D6\u914D\u7F6E\u5217\u8868\u5931\u8D25:", error);
2357
+ throw error;
2358
+ }
2359
+ }
2360
+ /**
2361
+ * 删除配置
2362
+ */
2363
+ async deleteConfig(id) {
2364
+ try {
2365
+ if (this.softDelete) {
2366
+ await this.db.update(testYourselfConfigs).set({
2367
+ isDeleted: true,
2368
+ deletedAt: /* @__PURE__ */ new Date(),
2369
+ updatedBy: this.userId,
2370
+ updatedAt: /* @__PURE__ */ new Date()
2371
+ }).where(eq(testYourselfConfigs.id, id));
2372
+ console.log("\u{1F5D1}\uFE0F [DatabaseAdapter] \u914D\u7F6E\u5DF2\u8F6F\u5220\u9664:", id);
2373
+ } else {
2374
+ await this.db.delete(testYourselfConfigs).where(eq(testYourselfConfigs.id, id));
2375
+ console.log("\u{1F5D1}\uFE0F [DatabaseAdapter] \u914D\u7F6E\u5DF2\u786C\u5220\u9664:", id);
2376
+ }
2377
+ } catch (error) {
2378
+ console.error("\u274C [DatabaseAdapter] \u5220\u9664\u914D\u7F6E\u5931\u8D25:", error);
2379
+ throw error;
2380
+ }
2381
+ }
2382
+ /**
2383
+ * 更新配置
2384
+ */
2385
+ async updateConfig(id, config) {
2386
+ try {
2387
+ const resultCount = config.config.results?.length || 0;
2388
+ await this.db.update(testYourselfConfigs).set({
2389
+ name: config.name,
2390
+ description: config.description || null,
2391
+ config: config.config,
2392
+ resultCount,
2393
+ isDefault: config.isDefault || false,
2394
+ updatedBy: this.userId,
2395
+ updatedAt: new Date(config.updatedAt),
2396
+ version: sql`${testYourselfConfigs.version} + 1`
2397
+ }).where(eq(testYourselfConfigs.id, id));
2398
+ console.log("\u2705 [DatabaseAdapter] \u914D\u7F6E\u5DF2\u66F4\u65B0:", id);
2399
+ } catch (error) {
2400
+ console.error("\u274C [DatabaseAdapter] \u66F4\u65B0\u914D\u7F6E\u5931\u8D25:", error);
2401
+ throw error;
2402
+ }
2403
+ }
2404
+ /**
2405
+ * 设置默认配置
2406
+ */
2407
+ async setDefaultConfig(id) {
2408
+ try {
2409
+ await this.db.transaction(async (tx) => {
2410
+ const conditions = [eq(testYourselfConfigs.isDefault, true)];
2411
+ if (this.organizationId) {
2412
+ conditions.push(
2413
+ eq(testYourselfConfigs.organizationId, this.organizationId)
2414
+ );
2415
+ }
2416
+ await tx.update(testYourselfConfigs).set({
2417
+ isDefault: false,
2418
+ updatedBy: this.userId,
2419
+ updatedAt: /* @__PURE__ */ new Date()
2420
+ }).where(and(...conditions));
2421
+ await tx.update(testYourselfConfigs).set({
2422
+ isDefault: true,
2423
+ updatedBy: this.userId,
2424
+ updatedAt: /* @__PURE__ */ new Date()
2425
+ }).where(eq(testYourselfConfigs.id, id));
2426
+ });
2427
+ console.log("\u2705 [DatabaseAdapter] \u9ED8\u8BA4\u914D\u7F6E\u5DF2\u8BBE\u7F6E:", id);
2428
+ } catch (error) {
2429
+ console.error("\u274C [DatabaseAdapter] \u8BBE\u7F6E\u9ED8\u8BA4\u914D\u7F6E\u5931\u8D25:", error);
2430
+ throw error;
2431
+ }
2432
+ }
2433
+ /**
2434
+ * 获取默认配置
2435
+ */
2436
+ async getDefaultConfig() {
2437
+ try {
2438
+ const conditions = [eq(testYourselfConfigs.isDefault, true)];
2439
+ if (this.softDelete) {
2440
+ conditions.push(eq(testYourselfConfigs.isDeleted, false));
2441
+ }
2442
+ if (this.organizationId) {
2443
+ conditions.push(
2444
+ eq(testYourselfConfigs.organizationId, this.organizationId)
2445
+ );
2446
+ }
2447
+ const result = await this.db.select().from(testYourselfConfigs).where(and(...conditions)).limit(1);
2448
+ if (!result || result.length === 0) {
2449
+ return null;
2450
+ }
2451
+ return this.toSavedConfig(result[0]);
2452
+ } catch (error) {
2453
+ console.error("\u274C [DatabaseAdapter] \u83B7\u53D6\u9ED8\u8BA4\u914D\u7F6E\u5931\u8D25:", error);
2454
+ throw error;
2455
+ }
2456
+ }
2457
+ /**
2458
+ * 恢复已删除的配置(软删除时可用)
2459
+ */
2460
+ async restoreConfig(id) {
2461
+ if (!this.softDelete) {
2462
+ throw new Error("\u6062\u590D\u529F\u80FD\u4EC5\u5728\u542F\u7528\u8F6F\u5220\u9664\u65F6\u53EF\u7528");
2463
+ }
2464
+ try {
2465
+ await this.db.update(testYourselfConfigs).set({
2466
+ isDeleted: false,
2467
+ deletedAt: null,
2468
+ updatedBy: this.userId,
2469
+ updatedAt: /* @__PURE__ */ new Date()
2470
+ }).where(eq(testYourselfConfigs.id, id));
2471
+ console.log("\u267B\uFE0F [DatabaseAdapter] \u914D\u7F6E\u5DF2\u6062\u590D:", id);
2472
+ } catch (error) {
2473
+ console.error("\u274C [DatabaseAdapter] \u6062\u590D\u914D\u7F6E\u5931\u8D25:", error);
2474
+ throw error;
2475
+ }
2476
+ }
2477
+ /**
2478
+ * 归档配置
2479
+ */
2480
+ async archiveConfig(id) {
2481
+ try {
2482
+ await this.db.update(testYourselfConfigs).set({
2483
+ isArchived: true,
2484
+ archivedAt: /* @__PURE__ */ new Date(),
2485
+ updatedBy: this.userId,
2486
+ updatedAt: /* @__PURE__ */ new Date()
2487
+ }).where(eq(testYourselfConfigs.id, id));
2488
+ console.log("\u{1F4E6} [DatabaseAdapter] \u914D\u7F6E\u5DF2\u5F52\u6863:", id);
2489
+ } catch (error) {
2490
+ console.error("\u274C [DatabaseAdapter] \u5F52\u6863\u914D\u7F6E\u5931\u8D25:", error);
2491
+ throw error;
2492
+ }
2493
+ }
2494
+ /**
2495
+ * 取消归档
2496
+ */
2497
+ async unarchiveConfig(id) {
2498
+ try {
2499
+ await this.db.update(testYourselfConfigs).set({
2500
+ isArchived: false,
2501
+ archivedAt: null,
2502
+ updatedBy: this.userId,
2503
+ updatedAt: /* @__PURE__ */ new Date()
2504
+ }).where(eq(testYourselfConfigs.id, id));
2505
+ console.log("\u{1F4C2} [DatabaseAdapter] \u914D\u7F6E\u5DF2\u53D6\u6D88\u5F52\u6863:", id);
2506
+ } catch (error) {
2507
+ console.error("\u274C [DatabaseAdapter] \u53D6\u6D88\u5F52\u6863\u5931\u8D25:", error);
2508
+ throw error;
2509
+ }
2510
+ }
2511
+ /**
2512
+ * 记录使用次数
2513
+ */
2514
+ async incrementUsageCount(id) {
2515
+ try {
2516
+ await this.db.update(testYourselfConfigs).set({
2517
+ usageCount: sql`${testYourselfConfigs.usageCount} + 1`,
2518
+ lastUsedAt: /* @__PURE__ */ new Date()
2519
+ }).where(eq(testYourselfConfigs.id, id));
2520
+ } catch (error) {
2521
+ console.error("\u274C [DatabaseAdapter] \u66F4\u65B0\u4F7F\u7528\u6B21\u6570\u5931\u8D25:", error);
2522
+ }
2523
+ }
2524
+ };
2525
+ function createDatabaseConfigAdapter(options) {
2526
+ return new DatabaseConfigAdapter(options);
2527
+ }
2528
+
2529
+ export { ConfigList, ConfigManager, ConfigService, DEFAULT_RESULTS, DatabaseConfigAdapter, TestYourself, createConfigService, createDatabaseConfigAdapter, generateDeviceHash, getDefaultConfigService, getDeviceFingerprint, selectResultIndex, testYourselfConfigShares, testYourselfConfigUsage, testYourselfConfigs, tryGetIPAddress };
2530
+ //# sourceMappingURL=index.mjs.map
2531
+ //# sourceMappingURL=index.mjs.map