apps-key-guard 0.0.1 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -10,6 +10,10 @@
10
10
  - 支持密钥过期检查
11
11
  - 完全可自定义样式
12
12
  - TypeScript 支持
13
+ - 悬浮窗显示用户信息(应用名称、密钥、等级、过期时间)
14
+ - 密钥显示/隐藏切换(小眼睛图标)
15
+ - 一键退出登录功能
16
+ - 自动安全防护(关闭面板时隐藏密钥)
13
17
 
14
18
  ## 安装
15
19
 
@@ -81,11 +85,12 @@ function App() {
81
85
  | `appName` | `string` | 是 | - | 应用名称,用于标识应用 |
82
86
  | `apiBaseUrl` | `string` | 否 | `http://localhost:8080` | API 基础 URL |
83
87
  | `children` | `ReactNode` | 是 | - | 验证成功后要渲染的子组件 |
84
- | `onValidated` | `(level: string) => void` | 否 | - | 验证成功后的回调函数,返回密钥等级 |
88
+ | `onValidated` | `(level: Level) => void` | 否 | - | 验证成功后的回调函数,返回密钥等级 |
85
89
  | `customStyle` | `CSSProperties` | 否 | - | 自定义容器样式 |
86
90
  | `title` | `string` | 否 | `访问验证` | 标题文本 |
87
91
  | `placeholder` | `string` | 否 | `请输入访问密钥` | 输入框占位符 |
88
92
  | `buttonText` | `string` | 否 | `验证` | 按钮文本 |
93
+ | `showFloatingButton` | `boolean` | 否 | `true` | 是否显示悬浮按钮 |
89
94
 
90
95
  ## 工作原理
91
96
 
@@ -95,6 +100,22 @@ function App() {
95
100
  4. 验证失败时,显示密钥输入表单
96
101
  5. 用户输入密钥后,调用后端 API 进行验证
97
102
  6. 验证成功后,将密钥存储到 localStorage,并渲染子组件
103
+ 7. 验证成功后,右下角显示悬浮按钮,点击可查看密钥信息
104
+ 8. 用户可以通过悬浮窗中的"退出登录"按钮清除本地密钥
105
+
106
+ ## 悬浮窗功能
107
+
108
+ 验证成功后,页面右下角会显示一个悬浮按钮。点击按钮可以打开信息面板,显示:
109
+
110
+ - **应用名称**: 当前访问的应用
111
+ - **密钥**: 默认隐藏显示(用 • 符号遮蔽),点击小眼睛图标可切换显示/隐藏
112
+ - **等级**: 密钥等级(游客/VIP/高级 VIP)
113
+ - **过期时间**: 密钥的过期时间或"永久有效"
114
+ - **退出登录**: 清除本地存储的密钥并返回验证页面
115
+
116
+ **安全性**:关闭面板时会自动重置密钥显示状态为隐藏,保护用户隐私。
117
+
118
+ 可以通过 `showFloatingButton={false}` 禁用悬浮窗功能。
98
119
 
99
120
  ## 后端 API 要求
100
121
 
package/dist/index.d.mts CHANGED
@@ -15,7 +15,11 @@ interface KeyGuardProps {
15
15
  title?: string;
16
16
  placeholder?: string;
17
17
  buttonText?: string;
18
+ showFloatingButton?: boolean;
19
+ authorWechat?: string;
20
+ authorQrCode?: string;
21
+ allowGuestAccess?: boolean;
18
22
  }
19
23
  declare const KeyGuard: React.FC<KeyGuardProps>;
20
24
 
21
- export { KeyGuard, type KeyGuardProps, KeyGuard as default };
25
+ export { KeyGuard, type KeyGuardProps, Level, KeyGuard as default };
package/dist/index.d.ts CHANGED
@@ -15,7 +15,11 @@ interface KeyGuardProps {
15
15
  title?: string;
16
16
  placeholder?: string;
17
17
  buttonText?: string;
18
+ showFloatingButton?: boolean;
19
+ authorWechat?: string;
20
+ authorQrCode?: string;
21
+ allowGuestAccess?: boolean;
18
22
  }
19
23
  declare const KeyGuard: React.FC<KeyGuardProps>;
20
24
 
21
- export { KeyGuard, type KeyGuardProps, KeyGuard as default };
25
+ export { KeyGuard, type KeyGuardProps, Level, KeyGuard as default };
package/dist/index.js CHANGED
@@ -31,12 +31,20 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
33
  KeyGuard: () => KeyGuard,
34
+ Level: () => Level,
34
35
  default: () => index_default
35
36
  });
36
37
  module.exports = __toCommonJS(index_exports);
37
38
  var import_react = require("react");
38
39
  var import_axios = __toESM(require("axios"));
39
40
  var import_jsx_runtime = require("react/jsx-runtime");
41
+ var Level = /* @__PURE__ */ ((Level2) => {
42
+ Level2["None"] = "none";
43
+ Level2["Guest"] = "guest";
44
+ Level2["VIP"] = "vip";
45
+ Level2["SuperVIP"] = "super_vip";
46
+ return Level2;
47
+ })(Level || {});
40
48
  var STORAGE_KEY_PREFIX = "apps_admin_key_";
41
49
  var KeyGuard = ({
42
50
  appName,
@@ -46,14 +54,24 @@ var KeyGuard = ({
46
54
  customStyle,
47
55
  title = "\u8BBF\u95EE\u9A8C\u8BC1",
48
56
  placeholder = "\u8BF7\u8F93\u5165\u8BBF\u95EE\u5BC6\u94A5",
49
- buttonText = "\u9A8C\u8BC1"
57
+ buttonText = "\u9A8C\u8BC1",
58
+ showFloatingButton = true,
59
+ authorWechat = "zhoulixiang0305",
60
+ authorQrCode = "https://i.postimg.cc/bJz33sy2/saler.jpg",
61
+ allowGuestAccess = false
50
62
  }) => {
51
63
  const [isValidated, setIsValidated] = (0, import_react.useState)(false);
52
64
  const [keyValue, setKeyValue] = (0, import_react.useState)("");
53
65
  const [error, setError] = (0, import_react.useState)("");
54
66
  const [loading, setLoading] = (0, import_react.useState)(false);
55
67
  const [initializing, setInitializing] = (0, import_react.useState)(true);
68
+ const [userInfo, setUserInfo] = (0, import_react.useState)(null);
69
+ const [showPanel, setShowPanel] = (0, import_react.useState)(false);
70
+ const [showKey, setShowKey] = (0, import_react.useState)(false);
71
+ const [showContactDialog, setShowContactDialog] = (0, import_react.useState)(false);
72
+ const [guestButtonHover, setGuestButtonHover] = (0, import_react.useState)(false);
56
73
  const storageKey = `${STORAGE_KEY_PREFIX}${appName}`;
74
+ const storageInfoKey = `${STORAGE_KEY_PREFIX}${appName}_info`;
57
75
  const verifyKey = async (key) => {
58
76
  try {
59
77
  const response = await import_axios.default.post(
@@ -64,8 +82,15 @@ var KeyGuard = ({
64
82
  }
65
83
  );
66
84
  const { data } = response.data;
67
- if (data.valid && data.level && data.level !== "none") {
85
+ if (data.valid && data.level && data.level !== "none" /* None */) {
68
86
  localStorage.setItem(storageKey, key);
87
+ const info = {
88
+ keyValue: key,
89
+ level: data.level,
90
+ expiresAt: data.expires_at
91
+ };
92
+ setUserInfo(info);
93
+ localStorage.setItem(storageInfoKey, JSON.stringify(info));
69
94
  if (onValidated && data.level) {
70
95
  onValidated(data.level);
71
96
  }
@@ -86,12 +111,22 @@ var KeyGuard = ({
86
111
  (0, import_react.useEffect)(() => {
87
112
  const checkStoredKey = async () => {
88
113
  const storedKey = localStorage.getItem(storageKey);
114
+ const storedInfo = localStorage.getItem(storageInfoKey);
89
115
  if (storedKey) {
116
+ if (storedInfo) {
117
+ try {
118
+ const info = JSON.parse(storedInfo);
119
+ setUserInfo(info);
120
+ } catch (e) {
121
+ }
122
+ }
90
123
  const isValid = await verifyKey(storedKey);
91
124
  if (isValid) {
92
125
  setIsValidated(true);
93
126
  } else {
94
127
  localStorage.removeItem(storageKey);
128
+ localStorage.removeItem(storageInfoKey);
129
+ setUserInfo(null);
95
130
  }
96
131
  }
97
132
  setInitializing(false);
@@ -112,11 +147,235 @@ var KeyGuard = ({
112
147
  }
113
148
  setLoading(false);
114
149
  };
150
+ const handleGuestAccess = () => {
151
+ const guestInfo = {
152
+ keyValue: "guest",
153
+ level: "guest" /* Guest */
154
+ };
155
+ setUserInfo(guestInfo);
156
+ setIsValidated(true);
157
+ if (onValidated) {
158
+ onValidated("guest" /* Guest */);
159
+ }
160
+ };
161
+ const handleLogout = () => {
162
+ localStorage.removeItem(storageKey);
163
+ localStorage.removeItem(storageInfoKey);
164
+ setUserInfo(null);
165
+ setIsValidated(false);
166
+ setKeyValue("");
167
+ setShowPanel(false);
168
+ setShowKey(false);
169
+ };
170
+ const handleClosePanel = () => {
171
+ setShowPanel(false);
172
+ setShowKey(false);
173
+ setShowContactDialog(false);
174
+ };
175
+ const formatLevel = (level) => {
176
+ const levelMap = {
177
+ ["none" /* None */]: "\u65E0\u6743\u9650",
178
+ ["guest" /* Guest */]: "\u6E38\u5BA2",
179
+ ["vip" /* VIP */]: "VIP",
180
+ ["super_vip" /* SuperVIP */]: "\u9AD8\u7EA7 VIP"
181
+ };
182
+ return levelMap[level] || level;
183
+ };
184
+ const formatExpiresAt = (expiresAt) => {
185
+ if (!expiresAt) return "\u6C38\u4E45\u6709\u6548";
186
+ try {
187
+ const date = new Date(expiresAt);
188
+ return date.toLocaleString("zh-CN");
189
+ } catch (e) {
190
+ return expiresAt;
191
+ }
192
+ };
115
193
  if (initializing) {
116
194
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { ...defaultContainerStyle, ...customStyle }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: defaultCardStyle, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: loadingStyle, children: "\u9A8C\u8BC1\u4E2D..." }) }) });
117
195
  }
118
196
  if (isValidated) {
119
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children });
197
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
198
+ children,
199
+ showFloatingButton && userInfo && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
200
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
201
+ "button",
202
+ {
203
+ onClick: () => setShowPanel(!showPanel),
204
+ style: floatingButtonStyle,
205
+ "aria-label": "\u7528\u6237\u4FE1\u606F",
206
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
207
+ "svg",
208
+ {
209
+ width: "24",
210
+ height: "24",
211
+ viewBox: "0 0 24 24",
212
+ fill: "none",
213
+ stroke: "currentColor",
214
+ strokeWidth: "2",
215
+ children: [
216
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2" }),
217
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "12", cy: "7", r: "4" })
218
+ ]
219
+ }
220
+ )
221
+ }
222
+ ),
223
+ showPanel && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
224
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: overlayStyle, onClick: handleClosePanel }),
225
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: panelStyle, children: [
226
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: panelHeaderStyle, children: [
227
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { style: panelTitleStyle, children: "\u5BC6\u94A5\u4FE1\u606F" }),
228
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
229
+ "button",
230
+ {
231
+ onClick: handleClosePanel,
232
+ style: closeButtonStyle,
233
+ "aria-label": "\u5173\u95ED",
234
+ children: "\u2715"
235
+ }
236
+ )
237
+ ] }),
238
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: panelBodyStyle, children: [
239
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: infoItemStyle, children: [
240
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: infoLabelStyle, children: "\u5E94\u7528\u540D\u79F0" }),
241
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: infoValueStyle, children: appName })
242
+ ] }),
243
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: infoItemStyle, children: [
244
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: infoLabelStyle, children: "\u5BC6\u94A5" }),
245
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: keyDisplayContainerStyle, children: [
246
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: infoValueStyle, children: showKey ? userInfo.keyValue : "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022" }),
247
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
248
+ "button",
249
+ {
250
+ onClick: () => setShowKey(!showKey),
251
+ style: eyeButtonStyle,
252
+ "aria-label": showKey ? "\u9690\u85CF\u5BC6\u94A5" : "\u663E\u793A\u5BC6\u94A5",
253
+ children: showKey ? (
254
+ // 睁眼图标
255
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
256
+ "svg",
257
+ {
258
+ width: "18",
259
+ height: "18",
260
+ viewBox: "0 0 24 24",
261
+ fill: "none",
262
+ stroke: "currentColor",
263
+ strokeWidth: "2",
264
+ strokeLinecap: "round",
265
+ strokeLinejoin: "round",
266
+ children: [
267
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z" }),
268
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "12", cy: "12", r: "3" })
269
+ ]
270
+ }
271
+ )
272
+ ) : (
273
+ // 闭眼图标
274
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
275
+ "svg",
276
+ {
277
+ width: "18",
278
+ height: "18",
279
+ viewBox: "0 0 24 24",
280
+ fill: "none",
281
+ stroke: "currentColor",
282
+ strokeWidth: "2",
283
+ strokeLinecap: "round",
284
+ strokeLinejoin: "round",
285
+ children: [
286
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19m-6.72-1.07a3 3 0 1 1-4.24-4.24" }),
287
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "1", y1: "1", x2: "23", y2: "23" })
288
+ ]
289
+ }
290
+ )
291
+ )
292
+ }
293
+ )
294
+ ] })
295
+ ] }),
296
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: infoItemStyle, children: [
297
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: infoLabelStyle, children: "\u7B49\u7EA7" }),
298
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: levelBadgeStyle(userInfo.level), children: formatLevel(userInfo.level) })
299
+ ] }),
300
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: infoItemStyle, children: [
301
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: infoLabelStyle, children: "\u8FC7\u671F\u65F6\u95F4" }),
302
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: infoValueStyle, children: formatExpiresAt(userInfo.expiresAt) })
303
+ ] })
304
+ ] }),
305
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: panelFooterStyle, children: [
306
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
307
+ "button",
308
+ {
309
+ onClick: () => setShowContactDialog(true),
310
+ style: contactButtonStyle,
311
+ children: "\u8054\u7CFB\u4F5C\u8005"
312
+ }
313
+ ),
314
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { onClick: handleLogout, style: logoutButtonStyle, children: "\u9000\u51FA\u767B\u5F55" })
315
+ ] })
316
+ ] })
317
+ ] }),
318
+ showContactDialog && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
319
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
320
+ "div",
321
+ {
322
+ style: overlayStyle,
323
+ onClick: () => setShowContactDialog(false)
324
+ }
325
+ ),
326
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: contactDialogStyle, children: [
327
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: panelHeaderStyle, children: [
328
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { style: panelTitleStyle, children: "\u8054\u7CFB\u4F5C\u8005" }),
329
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
330
+ "button",
331
+ {
332
+ onClick: () => setShowContactDialog(false),
333
+ style: closeButtonStyle,
334
+ "aria-label": "\u5173\u95ED",
335
+ children: "\u2715"
336
+ }
337
+ )
338
+ ] }),
339
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: contactDialogBodyStyle, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: contactInfoStyle, children: [
340
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: wechatInfoStyle, children: [
341
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: wechatLabelStyle, children: "\u5FAE\u4FE1\u53F7\uFF1A" }),
342
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: wechatValueStyle, children: authorWechat }),
343
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
344
+ "button",
345
+ {
346
+ onClick: () => {
347
+ navigator.clipboard.writeText(authorWechat).then(() => {
348
+ alert("\u5FAE\u4FE1\u53F7\u5DF2\u590D\u5236\u5230\u526A\u8D34\u677F");
349
+ }).catch(() => {
350
+ alert("\u590D\u5236\u5931\u8D25\uFF0C\u8BF7\u624B\u52A8\u590D\u5236");
351
+ });
352
+ },
353
+ style: copyButtonStyle,
354
+ "aria-label": "\u590D\u5236\u5FAE\u4FE1\u53F7",
355
+ children: "\u590D\u5236"
356
+ }
357
+ )
358
+ ] }),
359
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: qrCodeContainerStyle, children: [
360
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: qrCodeLabelStyle, children: "\u626B\u7801\u6DFB\u52A0\u5FAE\u4FE1" }),
361
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: qrCodePlaceholderStyle, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
362
+ "img",
363
+ {
364
+ src: authorQrCode,
365
+ alt: "\u5FAE\u4FE1\u4E8C\u7EF4\u7801",
366
+ style: qrCodeImageStyle,
367
+ onError: (e) => {
368
+ const target = e.target;
369
+ target.src = "https://via.placeholder.com/200x200?text=\u4E8C\u7EF4\u7801\u52A0\u8F7D\u5931\u8D25";
370
+ }
371
+ }
372
+ ) })
373
+ ] })
374
+ ] }) })
375
+ ] })
376
+ ] })
377
+ ] })
378
+ ] });
120
379
  }
121
380
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { ...defaultContainerStyle, ...customStyle }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: defaultCardStyle, children: [
122
381
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { style: titleStyle, children: title }),
@@ -134,7 +393,22 @@ var KeyGuard = ({
134
393
  ),
135
394
  error && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: errorStyle, children: error }),
136
395
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "submit", style: buttonStyle, disabled: loading, children: loading ? "\u9A8C\u8BC1\u4E2D..." : buttonText })
137
- ] })
396
+ ] }),
397
+ allowGuestAccess && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: guestAccessContainerStyle, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
398
+ "button",
399
+ {
400
+ type: "button",
401
+ onClick: handleGuestAccess,
402
+ onMouseEnter: () => setGuestButtonHover(true),
403
+ onMouseLeave: () => setGuestButtonHover(false),
404
+ style: {
405
+ ...guestAccessButtonStyle,
406
+ color: guestButtonHover ? "#007bff" : "#6c757d"
407
+ },
408
+ disabled: loading,
409
+ children: "\u4EE5\u6E38\u5BA2\u8EAB\u4EFD\u8BBF\u95EE"
410
+ }
411
+ ) })
138
412
  ] }) });
139
413
  };
140
414
  var defaultContainerStyle = {
@@ -193,8 +467,263 @@ var loadingStyle = {
193
467
  color: "#666",
194
468
  fontSize: "1rem"
195
469
  };
470
+ var floatingButtonStyle = {
471
+ position: "fixed",
472
+ bottom: "2rem",
473
+ right: "2rem",
474
+ width: "56px",
475
+ height: "56px",
476
+ borderRadius: "50%",
477
+ backgroundColor: "#007bff",
478
+ color: "white",
479
+ border: "none",
480
+ boxShadow: "0 4px 12px rgba(0, 123, 255, 0.4)",
481
+ cursor: "pointer",
482
+ display: "flex",
483
+ alignItems: "center",
484
+ justifyContent: "center",
485
+ transition: "all 0.3s ease",
486
+ zIndex: 1e3
487
+ };
488
+ var overlayStyle = {
489
+ position: "fixed",
490
+ top: 0,
491
+ left: 0,
492
+ right: 0,
493
+ bottom: 0,
494
+ backgroundColor: "rgba(0, 0, 0, 0.5)",
495
+ zIndex: 1001,
496
+ animation: "fadeIn 0.2s ease"
497
+ };
498
+ var panelStyle = {
499
+ position: "fixed",
500
+ top: "50%",
501
+ left: "50%",
502
+ transform: "translate(-50%, -50%)",
503
+ backgroundColor: "white",
504
+ borderRadius: "12px",
505
+ boxShadow: "0 8px 32px rgba(0, 0, 0, 0.2)",
506
+ width: "90%",
507
+ maxWidth: "400px",
508
+ zIndex: 1002,
509
+ animation: "slideIn 0.3s ease"
510
+ };
511
+ var panelHeaderStyle = {
512
+ display: "flex",
513
+ justifyContent: "space-between",
514
+ alignItems: "center",
515
+ padding: "1.5rem",
516
+ borderBottom: "1px solid #e9ecef"
517
+ };
518
+ var panelTitleStyle = {
519
+ margin: 0,
520
+ fontSize: "1.25rem",
521
+ fontWeight: "600",
522
+ color: "#333"
523
+ };
524
+ var closeButtonStyle = {
525
+ background: "none",
526
+ border: "none",
527
+ fontSize: "1.5rem",
528
+ color: "#999",
529
+ cursor: "pointer",
530
+ padding: "0",
531
+ width: "32px",
532
+ height: "32px",
533
+ display: "flex",
534
+ alignItems: "center",
535
+ justifyContent: "center",
536
+ borderRadius: "4px",
537
+ transition: "background-color 0.2s"
538
+ };
539
+ var panelBodyStyle = {
540
+ padding: "1.5rem",
541
+ display: "flex",
542
+ flexDirection: "column",
543
+ gap: "1rem"
544
+ };
545
+ var infoItemStyle = {
546
+ display: "flex",
547
+ justifyContent: "space-between",
548
+ alignItems: "center",
549
+ padding: "0.75rem",
550
+ backgroundColor: "#f8f9fa",
551
+ borderRadius: "6px"
552
+ };
553
+ var keyDisplayContainerStyle = {
554
+ display: "flex",
555
+ alignItems: "center",
556
+ gap: "0.5rem"
557
+ };
558
+ var eyeButtonStyle = {
559
+ background: "none",
560
+ border: "none",
561
+ color: "#6c757d",
562
+ cursor: "pointer",
563
+ padding: "0.25rem",
564
+ display: "flex",
565
+ alignItems: "center",
566
+ justifyContent: "center",
567
+ borderRadius: "4px",
568
+ transition: "all 0.2s"
569
+ };
570
+ var infoLabelStyle = {
571
+ fontSize: "0.875rem",
572
+ color: "#6c757d",
573
+ fontWeight: "500"
574
+ };
575
+ var infoValueStyle = {
576
+ fontSize: "0.875rem",
577
+ color: "#333",
578
+ fontWeight: "500"
579
+ };
580
+ var levelBadgeStyle = (level) => {
581
+ const colors = {
582
+ ["none" /* None */]: { bg: "#6c757d", color: "white" },
583
+ ["guest" /* Guest */]: { bg: "#17a2b8", color: "white" },
584
+ ["vip" /* VIP */]: { bg: "#ffc107", color: "#333" },
585
+ ["super_vip" /* SuperVIP */]: { bg: "#dc3545", color: "white" }
586
+ };
587
+ const { bg, color } = colors[level] || colors["none" /* None */];
588
+ return {
589
+ padding: "0.25rem 0.75rem",
590
+ borderRadius: "12px",
591
+ backgroundColor: bg,
592
+ color,
593
+ fontSize: "0.75rem",
594
+ fontWeight: "600",
595
+ textTransform: "uppercase"
596
+ };
597
+ };
598
+ var panelFooterStyle = {
599
+ padding: "1.5rem",
600
+ borderTop: "1px solid #e9ecef"
601
+ };
602
+ var contactButtonStyle = {
603
+ width: "100%",
604
+ padding: "0.75rem",
605
+ fontSize: "1rem",
606
+ fontWeight: "500",
607
+ color: "white",
608
+ backgroundColor: "#28a745",
609
+ border: "none",
610
+ borderRadius: "6px",
611
+ cursor: "pointer",
612
+ transition: "background-color 0.2s",
613
+ marginBottom: "0.75rem"
614
+ };
615
+ var logoutButtonStyle = {
616
+ width: "100%",
617
+ padding: "0.75rem",
618
+ fontSize: "1rem",
619
+ fontWeight: "500",
620
+ color: "white",
621
+ backgroundColor: "#dc3545",
622
+ border: "none",
623
+ borderRadius: "6px",
624
+ cursor: "pointer",
625
+ transition: "background-color 0.2s"
626
+ };
627
+ var contactDialogStyle = {
628
+ position: "fixed",
629
+ top: "50%",
630
+ left: "50%",
631
+ transform: "translate(-50%, -50%)",
632
+ backgroundColor: "white",
633
+ borderRadius: "12px",
634
+ boxShadow: "0 8px 32px rgba(0, 0, 0, 0.2)",
635
+ width: "90%",
636
+ maxWidth: "400px",
637
+ zIndex: 1003,
638
+ animation: "slideIn 0.3s ease"
639
+ };
640
+ var contactDialogBodyStyle = {
641
+ padding: "1.5rem",
642
+ display: "flex",
643
+ flexDirection: "column",
644
+ gap: "1.5rem"
645
+ };
646
+ var contactInfoStyle = {
647
+ display: "flex",
648
+ flexDirection: "column",
649
+ gap: "1.5rem"
650
+ };
651
+ var wechatInfoStyle = {
652
+ display: "flex",
653
+ alignItems: "center",
654
+ gap: "0.75rem",
655
+ padding: "1rem",
656
+ backgroundColor: "#f8f9fa",
657
+ borderRadius: "8px",
658
+ flexWrap: "wrap"
659
+ };
660
+ var wechatLabelStyle = {
661
+ fontSize: "0.875rem",
662
+ color: "#6c757d",
663
+ fontWeight: "500"
664
+ };
665
+ var wechatValueStyle = {
666
+ fontSize: "1rem",
667
+ color: "#333",
668
+ fontWeight: "600",
669
+ flex: 1,
670
+ minWidth: "120px"
671
+ };
672
+ var copyButtonStyle = {
673
+ padding: "0.5rem 1rem",
674
+ fontSize: "0.875rem",
675
+ fontWeight: "500",
676
+ color: "#007bff",
677
+ backgroundColor: "white",
678
+ border: "1px solid #007bff",
679
+ borderRadius: "4px",
680
+ cursor: "pointer",
681
+ transition: "all 0.2s"
682
+ };
683
+ var qrCodeContainerStyle = {
684
+ display: "flex",
685
+ flexDirection: "column",
686
+ alignItems: "center",
687
+ gap: "1rem"
688
+ };
689
+ var qrCodeLabelStyle = {
690
+ fontSize: "0.875rem",
691
+ color: "#6c757d",
692
+ fontWeight: "500"
693
+ };
694
+ var qrCodePlaceholderStyle = {
695
+ display: "flex",
696
+ flexDirection: "column",
697
+ alignItems: "center",
698
+ gap: "0.5rem",
699
+ padding: "1rem",
700
+ backgroundColor: "#f8f9fa",
701
+ borderRadius: "8px",
702
+ width: "100%"
703
+ };
704
+ var qrCodeImageStyle = {
705
+ width: "200px",
706
+ height: "200px",
707
+ borderRadius: "8px",
708
+ border: "1px solid #e9ecef"
709
+ };
710
+ var guestAccessContainerStyle = {
711
+ textAlign: "center",
712
+ paddingTop: "0.75rem"
713
+ };
714
+ var guestAccessButtonStyle = {
715
+ background: "none",
716
+ border: "none",
717
+ color: "#6c757d",
718
+ fontSize: "0.75rem",
719
+ cursor: "pointer",
720
+ textDecoration: "none",
721
+ padding: "0.25rem 0.5rem",
722
+ transition: "color 0.2s"
723
+ };
196
724
  var index_default = KeyGuard;
197
725
  // Annotate the CommonJS export names for ESM import in node:
198
726
  0 && (module.exports = {
199
- KeyGuard
727
+ KeyGuard,
728
+ Level
200
729
  });
package/dist/index.mjs CHANGED
@@ -2,6 +2,13 @@
2
2
  import { useState, useEffect } from "react";
3
3
  import axios from "axios";
4
4
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
5
+ var Level = /* @__PURE__ */ ((Level2) => {
6
+ Level2["None"] = "none";
7
+ Level2["Guest"] = "guest";
8
+ Level2["VIP"] = "vip";
9
+ Level2["SuperVIP"] = "super_vip";
10
+ return Level2;
11
+ })(Level || {});
5
12
  var STORAGE_KEY_PREFIX = "apps_admin_key_";
6
13
  var KeyGuard = ({
7
14
  appName,
@@ -11,14 +18,24 @@ var KeyGuard = ({
11
18
  customStyle,
12
19
  title = "\u8BBF\u95EE\u9A8C\u8BC1",
13
20
  placeholder = "\u8BF7\u8F93\u5165\u8BBF\u95EE\u5BC6\u94A5",
14
- buttonText = "\u9A8C\u8BC1"
21
+ buttonText = "\u9A8C\u8BC1",
22
+ showFloatingButton = true,
23
+ authorWechat = "zhoulixiang0305",
24
+ authorQrCode = "https://i.postimg.cc/bJz33sy2/saler.jpg",
25
+ allowGuestAccess = false
15
26
  }) => {
16
27
  const [isValidated, setIsValidated] = useState(false);
17
28
  const [keyValue, setKeyValue] = useState("");
18
29
  const [error, setError] = useState("");
19
30
  const [loading, setLoading] = useState(false);
20
31
  const [initializing, setInitializing] = useState(true);
32
+ const [userInfo, setUserInfo] = useState(null);
33
+ const [showPanel, setShowPanel] = useState(false);
34
+ const [showKey, setShowKey] = useState(false);
35
+ const [showContactDialog, setShowContactDialog] = useState(false);
36
+ const [guestButtonHover, setGuestButtonHover] = useState(false);
21
37
  const storageKey = `${STORAGE_KEY_PREFIX}${appName}`;
38
+ const storageInfoKey = `${STORAGE_KEY_PREFIX}${appName}_info`;
22
39
  const verifyKey = async (key) => {
23
40
  try {
24
41
  const response = await axios.post(
@@ -29,8 +46,15 @@ var KeyGuard = ({
29
46
  }
30
47
  );
31
48
  const { data } = response.data;
32
- if (data.valid && data.level && data.level !== "none") {
49
+ if (data.valid && data.level && data.level !== "none" /* None */) {
33
50
  localStorage.setItem(storageKey, key);
51
+ const info = {
52
+ keyValue: key,
53
+ level: data.level,
54
+ expiresAt: data.expires_at
55
+ };
56
+ setUserInfo(info);
57
+ localStorage.setItem(storageInfoKey, JSON.stringify(info));
34
58
  if (onValidated && data.level) {
35
59
  onValidated(data.level);
36
60
  }
@@ -51,12 +75,22 @@ var KeyGuard = ({
51
75
  useEffect(() => {
52
76
  const checkStoredKey = async () => {
53
77
  const storedKey = localStorage.getItem(storageKey);
78
+ const storedInfo = localStorage.getItem(storageInfoKey);
54
79
  if (storedKey) {
80
+ if (storedInfo) {
81
+ try {
82
+ const info = JSON.parse(storedInfo);
83
+ setUserInfo(info);
84
+ } catch (e) {
85
+ }
86
+ }
55
87
  const isValid = await verifyKey(storedKey);
56
88
  if (isValid) {
57
89
  setIsValidated(true);
58
90
  } else {
59
91
  localStorage.removeItem(storageKey);
92
+ localStorage.removeItem(storageInfoKey);
93
+ setUserInfo(null);
60
94
  }
61
95
  }
62
96
  setInitializing(false);
@@ -77,11 +111,235 @@ var KeyGuard = ({
77
111
  }
78
112
  setLoading(false);
79
113
  };
114
+ const handleGuestAccess = () => {
115
+ const guestInfo = {
116
+ keyValue: "guest",
117
+ level: "guest" /* Guest */
118
+ };
119
+ setUserInfo(guestInfo);
120
+ setIsValidated(true);
121
+ if (onValidated) {
122
+ onValidated("guest" /* Guest */);
123
+ }
124
+ };
125
+ const handleLogout = () => {
126
+ localStorage.removeItem(storageKey);
127
+ localStorage.removeItem(storageInfoKey);
128
+ setUserInfo(null);
129
+ setIsValidated(false);
130
+ setKeyValue("");
131
+ setShowPanel(false);
132
+ setShowKey(false);
133
+ };
134
+ const handleClosePanel = () => {
135
+ setShowPanel(false);
136
+ setShowKey(false);
137
+ setShowContactDialog(false);
138
+ };
139
+ const formatLevel = (level) => {
140
+ const levelMap = {
141
+ ["none" /* None */]: "\u65E0\u6743\u9650",
142
+ ["guest" /* Guest */]: "\u6E38\u5BA2",
143
+ ["vip" /* VIP */]: "VIP",
144
+ ["super_vip" /* SuperVIP */]: "\u9AD8\u7EA7 VIP"
145
+ };
146
+ return levelMap[level] || level;
147
+ };
148
+ const formatExpiresAt = (expiresAt) => {
149
+ if (!expiresAt) return "\u6C38\u4E45\u6709\u6548";
150
+ try {
151
+ const date = new Date(expiresAt);
152
+ return date.toLocaleString("zh-CN");
153
+ } catch (e) {
154
+ return expiresAt;
155
+ }
156
+ };
80
157
  if (initializing) {
81
158
  return /* @__PURE__ */ jsx("div", { style: { ...defaultContainerStyle, ...customStyle }, children: /* @__PURE__ */ jsx("div", { style: defaultCardStyle, children: /* @__PURE__ */ jsx("div", { style: loadingStyle, children: "\u9A8C\u8BC1\u4E2D..." }) }) });
82
159
  }
83
160
  if (isValidated) {
84
- return /* @__PURE__ */ jsx(Fragment, { children });
161
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
162
+ children,
163
+ showFloatingButton && userInfo && /* @__PURE__ */ jsxs(Fragment, { children: [
164
+ /* @__PURE__ */ jsx(
165
+ "button",
166
+ {
167
+ onClick: () => setShowPanel(!showPanel),
168
+ style: floatingButtonStyle,
169
+ "aria-label": "\u7528\u6237\u4FE1\u606F",
170
+ children: /* @__PURE__ */ jsxs(
171
+ "svg",
172
+ {
173
+ width: "24",
174
+ height: "24",
175
+ viewBox: "0 0 24 24",
176
+ fill: "none",
177
+ stroke: "currentColor",
178
+ strokeWidth: "2",
179
+ children: [
180
+ /* @__PURE__ */ jsx("path", { d: "M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2" }),
181
+ /* @__PURE__ */ jsx("circle", { cx: "12", cy: "7", r: "4" })
182
+ ]
183
+ }
184
+ )
185
+ }
186
+ ),
187
+ showPanel && /* @__PURE__ */ jsxs(Fragment, { children: [
188
+ /* @__PURE__ */ jsx("div", { style: overlayStyle, onClick: handleClosePanel }),
189
+ /* @__PURE__ */ jsxs("div", { style: panelStyle, children: [
190
+ /* @__PURE__ */ jsxs("div", { style: panelHeaderStyle, children: [
191
+ /* @__PURE__ */ jsx("h3", { style: panelTitleStyle, children: "\u5BC6\u94A5\u4FE1\u606F" }),
192
+ /* @__PURE__ */ jsx(
193
+ "button",
194
+ {
195
+ onClick: handleClosePanel,
196
+ style: closeButtonStyle,
197
+ "aria-label": "\u5173\u95ED",
198
+ children: "\u2715"
199
+ }
200
+ )
201
+ ] }),
202
+ /* @__PURE__ */ jsxs("div", { style: panelBodyStyle, children: [
203
+ /* @__PURE__ */ jsxs("div", { style: infoItemStyle, children: [
204
+ /* @__PURE__ */ jsx("span", { style: infoLabelStyle, children: "\u5E94\u7528\u540D\u79F0" }),
205
+ /* @__PURE__ */ jsx("span", { style: infoValueStyle, children: appName })
206
+ ] }),
207
+ /* @__PURE__ */ jsxs("div", { style: infoItemStyle, children: [
208
+ /* @__PURE__ */ jsx("span", { style: infoLabelStyle, children: "\u5BC6\u94A5" }),
209
+ /* @__PURE__ */ jsxs("div", { style: keyDisplayContainerStyle, children: [
210
+ /* @__PURE__ */ jsx("span", { style: infoValueStyle, children: showKey ? userInfo.keyValue : "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022" }),
211
+ /* @__PURE__ */ jsx(
212
+ "button",
213
+ {
214
+ onClick: () => setShowKey(!showKey),
215
+ style: eyeButtonStyle,
216
+ "aria-label": showKey ? "\u9690\u85CF\u5BC6\u94A5" : "\u663E\u793A\u5BC6\u94A5",
217
+ children: showKey ? (
218
+ // 睁眼图标
219
+ /* @__PURE__ */ jsxs(
220
+ "svg",
221
+ {
222
+ width: "18",
223
+ height: "18",
224
+ viewBox: "0 0 24 24",
225
+ fill: "none",
226
+ stroke: "currentColor",
227
+ strokeWidth: "2",
228
+ strokeLinecap: "round",
229
+ strokeLinejoin: "round",
230
+ children: [
231
+ /* @__PURE__ */ jsx("path", { d: "M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z" }),
232
+ /* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "3" })
233
+ ]
234
+ }
235
+ )
236
+ ) : (
237
+ // 闭眼图标
238
+ /* @__PURE__ */ jsxs(
239
+ "svg",
240
+ {
241
+ width: "18",
242
+ height: "18",
243
+ viewBox: "0 0 24 24",
244
+ fill: "none",
245
+ stroke: "currentColor",
246
+ strokeWidth: "2",
247
+ strokeLinecap: "round",
248
+ strokeLinejoin: "round",
249
+ children: [
250
+ /* @__PURE__ */ jsx("path", { d: "M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19m-6.72-1.07a3 3 0 1 1-4.24-4.24" }),
251
+ /* @__PURE__ */ jsx("line", { x1: "1", y1: "1", x2: "23", y2: "23" })
252
+ ]
253
+ }
254
+ )
255
+ )
256
+ }
257
+ )
258
+ ] })
259
+ ] }),
260
+ /* @__PURE__ */ jsxs("div", { style: infoItemStyle, children: [
261
+ /* @__PURE__ */ jsx("span", { style: infoLabelStyle, children: "\u7B49\u7EA7" }),
262
+ /* @__PURE__ */ jsx("span", { style: levelBadgeStyle(userInfo.level), children: formatLevel(userInfo.level) })
263
+ ] }),
264
+ /* @__PURE__ */ jsxs("div", { style: infoItemStyle, children: [
265
+ /* @__PURE__ */ jsx("span", { style: infoLabelStyle, children: "\u8FC7\u671F\u65F6\u95F4" }),
266
+ /* @__PURE__ */ jsx("span", { style: infoValueStyle, children: formatExpiresAt(userInfo.expiresAt) })
267
+ ] })
268
+ ] }),
269
+ /* @__PURE__ */ jsxs("div", { style: panelFooterStyle, children: [
270
+ /* @__PURE__ */ jsx(
271
+ "button",
272
+ {
273
+ onClick: () => setShowContactDialog(true),
274
+ style: contactButtonStyle,
275
+ children: "\u8054\u7CFB\u4F5C\u8005"
276
+ }
277
+ ),
278
+ /* @__PURE__ */ jsx("button", { onClick: handleLogout, style: logoutButtonStyle, children: "\u9000\u51FA\u767B\u5F55" })
279
+ ] })
280
+ ] })
281
+ ] }),
282
+ showContactDialog && /* @__PURE__ */ jsxs(Fragment, { children: [
283
+ /* @__PURE__ */ jsx(
284
+ "div",
285
+ {
286
+ style: overlayStyle,
287
+ onClick: () => setShowContactDialog(false)
288
+ }
289
+ ),
290
+ /* @__PURE__ */ jsxs("div", { style: contactDialogStyle, children: [
291
+ /* @__PURE__ */ jsxs("div", { style: panelHeaderStyle, children: [
292
+ /* @__PURE__ */ jsx("h3", { style: panelTitleStyle, children: "\u8054\u7CFB\u4F5C\u8005" }),
293
+ /* @__PURE__ */ jsx(
294
+ "button",
295
+ {
296
+ onClick: () => setShowContactDialog(false),
297
+ style: closeButtonStyle,
298
+ "aria-label": "\u5173\u95ED",
299
+ children: "\u2715"
300
+ }
301
+ )
302
+ ] }),
303
+ /* @__PURE__ */ jsx("div", { style: contactDialogBodyStyle, children: /* @__PURE__ */ jsxs("div", { style: contactInfoStyle, children: [
304
+ /* @__PURE__ */ jsxs("div", { style: wechatInfoStyle, children: [
305
+ /* @__PURE__ */ jsx("span", { style: wechatLabelStyle, children: "\u5FAE\u4FE1\u53F7\uFF1A" }),
306
+ /* @__PURE__ */ jsx("span", { style: wechatValueStyle, children: authorWechat }),
307
+ /* @__PURE__ */ jsx(
308
+ "button",
309
+ {
310
+ onClick: () => {
311
+ navigator.clipboard.writeText(authorWechat).then(() => {
312
+ alert("\u5FAE\u4FE1\u53F7\u5DF2\u590D\u5236\u5230\u526A\u8D34\u677F");
313
+ }).catch(() => {
314
+ alert("\u590D\u5236\u5931\u8D25\uFF0C\u8BF7\u624B\u52A8\u590D\u5236");
315
+ });
316
+ },
317
+ style: copyButtonStyle,
318
+ "aria-label": "\u590D\u5236\u5FAE\u4FE1\u53F7",
319
+ children: "\u590D\u5236"
320
+ }
321
+ )
322
+ ] }),
323
+ /* @__PURE__ */ jsxs("div", { style: qrCodeContainerStyle, children: [
324
+ /* @__PURE__ */ jsx("div", { style: qrCodeLabelStyle, children: "\u626B\u7801\u6DFB\u52A0\u5FAE\u4FE1" }),
325
+ /* @__PURE__ */ jsx("div", { style: qrCodePlaceholderStyle, children: /* @__PURE__ */ jsx(
326
+ "img",
327
+ {
328
+ src: authorQrCode,
329
+ alt: "\u5FAE\u4FE1\u4E8C\u7EF4\u7801",
330
+ style: qrCodeImageStyle,
331
+ onError: (e) => {
332
+ const target = e.target;
333
+ target.src = "https://via.placeholder.com/200x200?text=\u4E8C\u7EF4\u7801\u52A0\u8F7D\u5931\u8D25";
334
+ }
335
+ }
336
+ ) })
337
+ ] })
338
+ ] }) })
339
+ ] })
340
+ ] })
341
+ ] })
342
+ ] });
85
343
  }
86
344
  return /* @__PURE__ */ jsx("div", { style: { ...defaultContainerStyle, ...customStyle }, children: /* @__PURE__ */ jsxs("div", { style: defaultCardStyle, children: [
87
345
  /* @__PURE__ */ jsx("h2", { style: titleStyle, children: title }),
@@ -99,7 +357,22 @@ var KeyGuard = ({
99
357
  ),
100
358
  error && /* @__PURE__ */ jsx("div", { style: errorStyle, children: error }),
101
359
  /* @__PURE__ */ jsx("button", { type: "submit", style: buttonStyle, disabled: loading, children: loading ? "\u9A8C\u8BC1\u4E2D..." : buttonText })
102
- ] })
360
+ ] }),
361
+ allowGuestAccess && /* @__PURE__ */ jsx("div", { style: guestAccessContainerStyle, children: /* @__PURE__ */ jsx(
362
+ "button",
363
+ {
364
+ type: "button",
365
+ onClick: handleGuestAccess,
366
+ onMouseEnter: () => setGuestButtonHover(true),
367
+ onMouseLeave: () => setGuestButtonHover(false),
368
+ style: {
369
+ ...guestAccessButtonStyle,
370
+ color: guestButtonHover ? "#007bff" : "#6c757d"
371
+ },
372
+ disabled: loading,
373
+ children: "\u4EE5\u6E38\u5BA2\u8EAB\u4EFD\u8BBF\u95EE"
374
+ }
375
+ ) })
103
376
  ] }) });
104
377
  };
105
378
  var defaultContainerStyle = {
@@ -158,8 +431,263 @@ var loadingStyle = {
158
431
  color: "#666",
159
432
  fontSize: "1rem"
160
433
  };
434
+ var floatingButtonStyle = {
435
+ position: "fixed",
436
+ bottom: "2rem",
437
+ right: "2rem",
438
+ width: "56px",
439
+ height: "56px",
440
+ borderRadius: "50%",
441
+ backgroundColor: "#007bff",
442
+ color: "white",
443
+ border: "none",
444
+ boxShadow: "0 4px 12px rgba(0, 123, 255, 0.4)",
445
+ cursor: "pointer",
446
+ display: "flex",
447
+ alignItems: "center",
448
+ justifyContent: "center",
449
+ transition: "all 0.3s ease",
450
+ zIndex: 1e3
451
+ };
452
+ var overlayStyle = {
453
+ position: "fixed",
454
+ top: 0,
455
+ left: 0,
456
+ right: 0,
457
+ bottom: 0,
458
+ backgroundColor: "rgba(0, 0, 0, 0.5)",
459
+ zIndex: 1001,
460
+ animation: "fadeIn 0.2s ease"
461
+ };
462
+ var panelStyle = {
463
+ position: "fixed",
464
+ top: "50%",
465
+ left: "50%",
466
+ transform: "translate(-50%, -50%)",
467
+ backgroundColor: "white",
468
+ borderRadius: "12px",
469
+ boxShadow: "0 8px 32px rgba(0, 0, 0, 0.2)",
470
+ width: "90%",
471
+ maxWidth: "400px",
472
+ zIndex: 1002,
473
+ animation: "slideIn 0.3s ease"
474
+ };
475
+ var panelHeaderStyle = {
476
+ display: "flex",
477
+ justifyContent: "space-between",
478
+ alignItems: "center",
479
+ padding: "1.5rem",
480
+ borderBottom: "1px solid #e9ecef"
481
+ };
482
+ var panelTitleStyle = {
483
+ margin: 0,
484
+ fontSize: "1.25rem",
485
+ fontWeight: "600",
486
+ color: "#333"
487
+ };
488
+ var closeButtonStyle = {
489
+ background: "none",
490
+ border: "none",
491
+ fontSize: "1.5rem",
492
+ color: "#999",
493
+ cursor: "pointer",
494
+ padding: "0",
495
+ width: "32px",
496
+ height: "32px",
497
+ display: "flex",
498
+ alignItems: "center",
499
+ justifyContent: "center",
500
+ borderRadius: "4px",
501
+ transition: "background-color 0.2s"
502
+ };
503
+ var panelBodyStyle = {
504
+ padding: "1.5rem",
505
+ display: "flex",
506
+ flexDirection: "column",
507
+ gap: "1rem"
508
+ };
509
+ var infoItemStyle = {
510
+ display: "flex",
511
+ justifyContent: "space-between",
512
+ alignItems: "center",
513
+ padding: "0.75rem",
514
+ backgroundColor: "#f8f9fa",
515
+ borderRadius: "6px"
516
+ };
517
+ var keyDisplayContainerStyle = {
518
+ display: "flex",
519
+ alignItems: "center",
520
+ gap: "0.5rem"
521
+ };
522
+ var eyeButtonStyle = {
523
+ background: "none",
524
+ border: "none",
525
+ color: "#6c757d",
526
+ cursor: "pointer",
527
+ padding: "0.25rem",
528
+ display: "flex",
529
+ alignItems: "center",
530
+ justifyContent: "center",
531
+ borderRadius: "4px",
532
+ transition: "all 0.2s"
533
+ };
534
+ var infoLabelStyle = {
535
+ fontSize: "0.875rem",
536
+ color: "#6c757d",
537
+ fontWeight: "500"
538
+ };
539
+ var infoValueStyle = {
540
+ fontSize: "0.875rem",
541
+ color: "#333",
542
+ fontWeight: "500"
543
+ };
544
+ var levelBadgeStyle = (level) => {
545
+ const colors = {
546
+ ["none" /* None */]: { bg: "#6c757d", color: "white" },
547
+ ["guest" /* Guest */]: { bg: "#17a2b8", color: "white" },
548
+ ["vip" /* VIP */]: { bg: "#ffc107", color: "#333" },
549
+ ["super_vip" /* SuperVIP */]: { bg: "#dc3545", color: "white" }
550
+ };
551
+ const { bg, color } = colors[level] || colors["none" /* None */];
552
+ return {
553
+ padding: "0.25rem 0.75rem",
554
+ borderRadius: "12px",
555
+ backgroundColor: bg,
556
+ color,
557
+ fontSize: "0.75rem",
558
+ fontWeight: "600",
559
+ textTransform: "uppercase"
560
+ };
561
+ };
562
+ var panelFooterStyle = {
563
+ padding: "1.5rem",
564
+ borderTop: "1px solid #e9ecef"
565
+ };
566
+ var contactButtonStyle = {
567
+ width: "100%",
568
+ padding: "0.75rem",
569
+ fontSize: "1rem",
570
+ fontWeight: "500",
571
+ color: "white",
572
+ backgroundColor: "#28a745",
573
+ border: "none",
574
+ borderRadius: "6px",
575
+ cursor: "pointer",
576
+ transition: "background-color 0.2s",
577
+ marginBottom: "0.75rem"
578
+ };
579
+ var logoutButtonStyle = {
580
+ width: "100%",
581
+ padding: "0.75rem",
582
+ fontSize: "1rem",
583
+ fontWeight: "500",
584
+ color: "white",
585
+ backgroundColor: "#dc3545",
586
+ border: "none",
587
+ borderRadius: "6px",
588
+ cursor: "pointer",
589
+ transition: "background-color 0.2s"
590
+ };
591
+ var contactDialogStyle = {
592
+ position: "fixed",
593
+ top: "50%",
594
+ left: "50%",
595
+ transform: "translate(-50%, -50%)",
596
+ backgroundColor: "white",
597
+ borderRadius: "12px",
598
+ boxShadow: "0 8px 32px rgba(0, 0, 0, 0.2)",
599
+ width: "90%",
600
+ maxWidth: "400px",
601
+ zIndex: 1003,
602
+ animation: "slideIn 0.3s ease"
603
+ };
604
+ var contactDialogBodyStyle = {
605
+ padding: "1.5rem",
606
+ display: "flex",
607
+ flexDirection: "column",
608
+ gap: "1.5rem"
609
+ };
610
+ var contactInfoStyle = {
611
+ display: "flex",
612
+ flexDirection: "column",
613
+ gap: "1.5rem"
614
+ };
615
+ var wechatInfoStyle = {
616
+ display: "flex",
617
+ alignItems: "center",
618
+ gap: "0.75rem",
619
+ padding: "1rem",
620
+ backgroundColor: "#f8f9fa",
621
+ borderRadius: "8px",
622
+ flexWrap: "wrap"
623
+ };
624
+ var wechatLabelStyle = {
625
+ fontSize: "0.875rem",
626
+ color: "#6c757d",
627
+ fontWeight: "500"
628
+ };
629
+ var wechatValueStyle = {
630
+ fontSize: "1rem",
631
+ color: "#333",
632
+ fontWeight: "600",
633
+ flex: 1,
634
+ minWidth: "120px"
635
+ };
636
+ var copyButtonStyle = {
637
+ padding: "0.5rem 1rem",
638
+ fontSize: "0.875rem",
639
+ fontWeight: "500",
640
+ color: "#007bff",
641
+ backgroundColor: "white",
642
+ border: "1px solid #007bff",
643
+ borderRadius: "4px",
644
+ cursor: "pointer",
645
+ transition: "all 0.2s"
646
+ };
647
+ var qrCodeContainerStyle = {
648
+ display: "flex",
649
+ flexDirection: "column",
650
+ alignItems: "center",
651
+ gap: "1rem"
652
+ };
653
+ var qrCodeLabelStyle = {
654
+ fontSize: "0.875rem",
655
+ color: "#6c757d",
656
+ fontWeight: "500"
657
+ };
658
+ var qrCodePlaceholderStyle = {
659
+ display: "flex",
660
+ flexDirection: "column",
661
+ alignItems: "center",
662
+ gap: "0.5rem",
663
+ padding: "1rem",
664
+ backgroundColor: "#f8f9fa",
665
+ borderRadius: "8px",
666
+ width: "100%"
667
+ };
668
+ var qrCodeImageStyle = {
669
+ width: "200px",
670
+ height: "200px",
671
+ borderRadius: "8px",
672
+ border: "1px solid #e9ecef"
673
+ };
674
+ var guestAccessContainerStyle = {
675
+ textAlign: "center",
676
+ paddingTop: "0.75rem"
677
+ };
678
+ var guestAccessButtonStyle = {
679
+ background: "none",
680
+ border: "none",
681
+ color: "#6c757d",
682
+ fontSize: "0.75rem",
683
+ cursor: "pointer",
684
+ textDecoration: "none",
685
+ padding: "0.25rem 0.5rem",
686
+ transition: "color 0.2s"
687
+ };
161
688
  var index_default = KeyGuard;
162
689
  export {
163
690
  KeyGuard,
691
+ Level,
164
692
  index_default as default
165
693
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "apps-key-guard",
3
- "version": "0.0.1",
3
+ "version": "0.0.3",
4
4
  "description": "A React component for key-based access control with localStorage caching",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",