best-unit 1.2.19 → 1.2.21

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 (49) hide show
  1. package/BEST_UNIT_USAGE.md +402 -0
  2. package/dist/best-unit.cjs +13 -13
  3. package/dist/best-unit.js +1352 -1328
  4. package/index.html +13 -0
  5. package/package.json +1 -4
  6. package/src/api/axiosInstance.ts +111 -0
  7. package/src/api/index.ts +136 -0
  8. package/src/api/proxy.ts +11 -0
  9. package/src/components/business/recharge-sdk/components/offline-transfer-form/index.tsx +158 -0
  10. package/src/components/business/recharge-sdk/components/offline-transfer-form/theme.tsx +238 -0
  11. package/src/components/business/recharge-sdk/components/online-recharge-form/index.tsx +199 -0
  12. package/src/components/business/recharge-sdk/components/online-recharge-form/theme.tsx +159 -0
  13. package/src/components/business/recharge-sdk/components/recharge/index.tsx +152 -0
  14. package/src/components/business/recharge-sdk/components/recharge/theme.tsx +68 -0
  15. package/src/components/business/recharge-sdk/index.tsx +37 -0
  16. package/src/components/business/refresh-button/index.tsx +99 -0
  17. package/src/components/business/refresh-button/theme.tsx +58 -0
  18. package/src/components/business/statistical-balance/index.tsx +190 -0
  19. package/src/components/business/statistical-balance/theme.tsx +117 -0
  20. package/src/components/common/button/index.tsx +17 -0
  21. package/src/components/common/button/theme.tsx +56 -0
  22. package/src/components/common/hover-popover/index.tsx +182 -0
  23. package/src/components/common/hover-popover/theme.tsx +39 -0
  24. package/src/components/common/message/index.tsx +321 -0
  25. package/src/components/common/message/theme.tsx +25 -0
  26. package/src/components/common/modal/index.tsx +99 -0
  27. package/src/components/common/modal/theme.tsx +99 -0
  28. package/src/components/common/select/index.tsx +229 -0
  29. package/src/components/common/select/theme.tsx +104 -0
  30. package/src/components/common/upload/index.tsx +140 -0
  31. package/src/components/common/upload/theme.tsx +95 -0
  32. package/src/demo/App.tsx +685 -0
  33. package/src/demo/index.tsx +4 -0
  34. package/src/demo/testBalanceData.tsx +79 -0
  35. package/src/demo/theme-config-example.tsx +1 -0
  36. package/src/local/en.ts +64 -0
  37. package/src/local/index.ts +36 -0
  38. package/src/local/zh.ts +63 -0
  39. package/src/main.ts +26 -0
  40. package/src/types/global.d.ts +146 -0
  41. package/src/types/index.ts +31 -0
  42. package/src/types/preact-custom-element.d.ts +1 -0
  43. package/src/utils/business/index.ts +132 -0
  44. package/src/utils/common/index.ts +8 -0
  45. package/src/vite-env.d.ts +8 -0
  46. package/tsconfig.app.json +33 -0
  47. package/tsconfig.json +15 -0
  48. package/tsconfig.node.json +24 -0
  49. package/vite.config.ts +24 -0
@@ -0,0 +1,182 @@
1
+ import { useState, useRef } from "preact/hooks";
2
+ import type { FunctionalComponent, JSX } from "preact";
3
+ import { getHoverPopoverTheme } from "./theme";
4
+ import { getInitParams } from "@/utils/business";
5
+ import { Size } from "@/types";
6
+
7
+ const size = getInitParams<Size>("size");
8
+ export type PopoverPosition = "top" | "bottom" | "leftTop" | "rightTop";
9
+ interface HoverPopoverProps {
10
+ popover: JSX.Element;
11
+ children: JSX.Element;
12
+ popoverWidth?: number;
13
+ popoverMinWidth?: number;
14
+ offsetY?: number; // 弹层与目标元素的垂直间距
15
+ offsetX?: number; // 弹层与目标元素的水平间距
16
+ popoverPosition?: "top" | "bottom" | "leftTop" | "rightTop";
17
+ }
18
+
19
+ /**
20
+ * 通用 HoverPopover 组件,支持上下、左上、右上浮层、箭头、位置自适应
21
+ */
22
+ const HoverPopover: FunctionalComponent<HoverPopoverProps> = ({
23
+ popover,
24
+ children,
25
+ popoverWidth = size === Size.SMALL ? 200 : 300,
26
+ popoverMinWidth = size === Size.SMALL ? 150 : 200,
27
+ offsetY = size === Size.SMALL ? 8 : 16,
28
+ offsetX = size === Size.SMALL ? 8 : 16,
29
+ popoverPosition = "top",
30
+ }) => {
31
+ const [show, setShow] = useState(false);
32
+ const [position, setPosition] = useState<
33
+ "top" | "bottom" | "leftTop" | "rightTop"
34
+ >(popoverPosition);
35
+ const ref = useRef<HTMLDivElement>(null);
36
+ const timerRef = useRef<number | null>(null);
37
+ const theme = getHoverPopoverTheme();
38
+
39
+ const handleMouseEnter = () => {
40
+ if (timerRef.current) {
41
+ clearTimeout(timerRef.current);
42
+ timerRef.current = null;
43
+ }
44
+ if (popoverPosition === "top" || popoverPosition === "bottom") {
45
+ if (ref.current) {
46
+ const rect = ref.current.getBoundingClientRect();
47
+ if (popoverPosition === "top" && rect.top < 100) {
48
+ setPosition("bottom");
49
+ } else if (
50
+ popoverPosition === "bottom" &&
51
+ window.innerHeight - rect.bottom < 100
52
+ ) {
53
+ setPosition("top");
54
+ } else {
55
+ setPosition(popoverPosition);
56
+ }
57
+ } else {
58
+ setPosition(popoverPosition);
59
+ }
60
+ } else {
61
+ setPosition(popoverPosition);
62
+ }
63
+ setShow(true);
64
+ };
65
+
66
+ const handleMouseLeave = () => {
67
+ // 延迟关闭,防止鼠标快速移动到弹窗内容时闪烁
68
+ timerRef.current = window.setTimeout(() => {
69
+ setShow(false);
70
+ }, 120);
71
+ };
72
+
73
+ // 弹层定位样式
74
+ let popoverStyle: any = {
75
+ position: "absolute",
76
+ zIndex: 999,
77
+ borderRadius: 6,
78
+ fontSize: size === Size.SMALL ? 12 : 15,
79
+ minWidth: popoverMinWidth,
80
+ width: popoverWidth,
81
+ padding: size === Size.SMALL ? "6px 12px" : "8px 14px",
82
+ pointerEvents: "auto",
83
+ textAlign: "center",
84
+ animation: "fadeInUp 0.3s",
85
+ ...theme.popover,
86
+ };
87
+ let arrowStyle: any = {
88
+ position: "absolute",
89
+ zIndex: 11,
90
+ width: 0,
91
+ height: 0,
92
+ };
93
+ if (position === "top") {
94
+ popoverStyle = {
95
+ ...popoverStyle,
96
+ left: "50%",
97
+ top: -48,
98
+ transform: "translateX(-50%)",
99
+ };
100
+ arrowStyle = {
101
+ ...arrowStyle,
102
+ left: "50%",
103
+ bottom: -8,
104
+ transform: "translateX(-50%)",
105
+ borderLeft: "8px solid transparent",
106
+ borderRight: "8px solid transparent",
107
+ borderTop: `8px solid ${theme.arrow.top}`,
108
+ };
109
+ } else if (position === "bottom") {
110
+ popoverStyle = {
111
+ ...popoverStyle,
112
+ left: "50%",
113
+ top: "100%",
114
+ marginTop: offsetY,
115
+ transform: "translateX(-50%)",
116
+ };
117
+ arrowStyle = {
118
+ ...arrowStyle,
119
+ left: "50%",
120
+ top: -8,
121
+ transform: "translateX(-50%)",
122
+ borderLeft: "8px solid transparent",
123
+ borderRight: "8px solid transparent",
124
+ borderBottom: `8px solid ${theme.arrow.bottom}`,
125
+ };
126
+ } else if (position === "leftTop") {
127
+ popoverStyle = {
128
+ ...popoverStyle,
129
+ right: "100%",
130
+ top: 0,
131
+ marginRight: offsetX,
132
+ transform: "none",
133
+ };
134
+ arrowStyle = {
135
+ ...arrowStyle,
136
+ right: -8,
137
+ top: size === Size.SMALL ? 8 : 12,
138
+ borderTop: "8px solid transparent",
139
+ borderBottom: "8px solid transparent",
140
+ borderLeft: `8px solid ${theme.arrow.left}`,
141
+ };
142
+ } else if (position === "rightTop") {
143
+ popoverStyle = {
144
+ ...popoverStyle,
145
+ left: "100%",
146
+ top: 0,
147
+ marginLeft: offsetX,
148
+ transform: "translateY(0)", // 右上角对齐
149
+ };
150
+ arrowStyle = {
151
+ ...arrowStyle,
152
+ left: -8,
153
+ top: 12,
154
+ borderTop: "8px solid transparent",
155
+ borderBottom: "8px solid transparent",
156
+ borderRight: `8px solid ${theme.arrow.right}`,
157
+ };
158
+ }
159
+
160
+ return (
161
+ <div
162
+ ref={ref}
163
+ style={{ position: "relative", display: "inline-block" }}
164
+ onMouseEnter={handleMouseEnter}
165
+ onMouseLeave={handleMouseLeave}
166
+ >
167
+ {children}
168
+ {show && (
169
+ <div
170
+ style={popoverStyle}
171
+ onMouseEnter={handleMouseEnter}
172
+ onMouseLeave={handleMouseLeave}
173
+ >
174
+ {popover}
175
+ <div style={arrowStyle} />
176
+ </div>
177
+ )}
178
+ </div>
179
+ );
180
+ };
181
+
182
+ export default HoverPopover;
@@ -0,0 +1,39 @@
1
+ import { Theme } from "@/types";
2
+ import { getInitParams } from "@/utils/business";
3
+
4
+ export const hoverPopoverThemes = {
5
+ white: {
6
+ popover: {
7
+ background: "#fff",
8
+ color: "#222",
9
+ boxShadow: "0 4px 16px rgba(0,0,0,0.12)",
10
+ border: "none",
11
+ },
12
+ arrow: {
13
+ top: "#fff",
14
+ bottom: "#fff",
15
+ left: "#fff",
16
+ right: "#fff",
17
+ },
18
+ },
19
+ dark: {
20
+ popover: {
21
+ background: "#23262F",
22
+ color: "#fff",
23
+ boxShadow: "0 4px 16px rgba(0,0,0,0.32)",
24
+ border: "1px solid #444C5C",
25
+ },
26
+ arrow: {
27
+ top: "#23262F",
28
+ bottom: "#23262F",
29
+ left: "#23262F",
30
+ right: "#23262F",
31
+ },
32
+ },
33
+ };
34
+
35
+ export function getHoverPopoverTheme() {
36
+ const theme = getInitParams<Theme>("theme");
37
+ const whiteTheme = theme === Theme.WHITE;
38
+ return whiteTheme ? hoverPopoverThemes.white : hoverPopoverThemes.dark;
39
+ }
@@ -0,0 +1,321 @@
1
+ import type { FunctionalComponent } from "preact";
2
+ import { useState, useEffect } from "preact/hooks";
3
+ import { getMessageTheme } from "./theme";
4
+
5
+ export type MessageType = "success" | "error" | "warning" | "info";
6
+
7
+ interface MessageProps {
8
+ type: MessageType;
9
+ content: string;
10
+ duration?: number;
11
+ onClose?: () => void;
12
+ closable?: boolean;
13
+ }
14
+
15
+ const MessageItem: FunctionalComponent<MessageProps> = ({
16
+ type,
17
+ content,
18
+ duration = 3000,
19
+ onClose,
20
+ closable = false,
21
+ }) => {
22
+ const [visible, setVisible] = useState(true);
23
+ const theme = getMessageTheme();
24
+
25
+ useEffect(() => {
26
+ if (duration > 0) {
27
+ const timer = setTimeout(() => {
28
+ setVisible(false);
29
+ setTimeout(() => onClose?.(), 300);
30
+ }, duration);
31
+ return () => clearTimeout(timer);
32
+ }
33
+ }, [duration, onClose]);
34
+
35
+ const getIcon = () => {
36
+ switch (type) {
37
+ case "error":
38
+ return (
39
+ <svg width="16" height="16" viewBox="0 0 16 16" fill="none">
40
+ <circle cx="8" cy="8" r="8" fill="#ff4d4f" />
41
+ <path
42
+ d="M10.5 5.5l-5 5m0-5l5 5"
43
+ stroke="white"
44
+ strokeWidth="1.5"
45
+ strokeLinecap="round"
46
+ />
47
+ </svg>
48
+ );
49
+ case "success":
50
+ return (
51
+ <svg width="16" height="16" viewBox="0 0 16 16" fill="none">
52
+ <circle cx="8" cy="8" r="8" fill="#52c41a" />
53
+ <path
54
+ d="M5 8.5l2.2 2.2 3.8-3.4"
55
+ stroke="white"
56
+ strokeWidth="1.8"
57
+ strokeLinecap="round"
58
+ strokeLinejoin="round"
59
+ fill="none"
60
+ />
61
+ </svg>
62
+ );
63
+ case "warning":
64
+ return (
65
+ <svg width="16" height="16" viewBox="0 0 16 16" fill="none">
66
+ <circle cx="8" cy="8" r="8" fill="#faad14" />
67
+ <path
68
+ d="M8 4.5v5"
69
+ stroke="white"
70
+ strokeWidth="1.5"
71
+ strokeLinecap="round"
72
+ />
73
+ <circle cx="8" cy="12.5" r="1" fill="white" />
74
+ </svg>
75
+ );
76
+ case "info":
77
+ return (
78
+ <svg width="16" height="16" viewBox="0 0 16 16" fill="none">
79
+ <circle cx="8" cy="8" r="8" fill="#1677ff" />
80
+ <rect
81
+ x="7.25"
82
+ y="7"
83
+ width="1.5"
84
+ height="6"
85
+ rx="0.75"
86
+ fill="white"
87
+ />
88
+ <rect
89
+ x="7.25"
90
+ y="5"
91
+ width="1.5"
92
+ height="1.5"
93
+ rx="0.75"
94
+ fill="white"
95
+ />
96
+ </svg>
97
+ );
98
+ default:
99
+ return null;
100
+ }
101
+ };
102
+
103
+ return (
104
+ <div
105
+ style={{
106
+ position: "fixed",
107
+ top: "20px",
108
+ left: "50%",
109
+ zIndex: 9999,
110
+ background: theme.background,
111
+ border: `1px solid ${theme.border}`,
112
+ borderRadius: "12px",
113
+ padding: "8px 16px",
114
+ display: "flex",
115
+ alignItems: "center",
116
+ gap: "8px",
117
+ boxShadow: theme.boxShadow,
118
+ minWidth: content.length < 20 ? "auto" : "300px",
119
+ maxWidth: "500px",
120
+ opacity: visible ? 1 : 0,
121
+ color: theme.color,
122
+ transform: visible
123
+ ? "translateX(-50%) translateY(0)"
124
+ : "translateX(-50%) translateY(-20px)",
125
+ transition: "all 0.3s ease",
126
+ }}
127
+ >
128
+ <div style={{ flexShrink: 0 }}>{getIcon()}</div>
129
+ <div
130
+ style={{
131
+ color: theme.color,
132
+ fontSize: "14px",
133
+ lineHeight: "1.5",
134
+ flex: 1,
135
+ }}
136
+ >
137
+ {content}
138
+ </div>
139
+ {closable && (
140
+ <button
141
+ onClick={() => {
142
+ setVisible(false);
143
+ setTimeout(() => onClose?.(), 300);
144
+ }}
145
+ style={{
146
+ background: "none",
147
+ border: "none",
148
+ cursor: "pointer",
149
+ padding: "4px",
150
+ color: theme.closeColor,
151
+ fontSize: "12px",
152
+ flexShrink: 0,
153
+ }}
154
+ >
155
+
156
+ </button>
157
+ )}
158
+ </div>
159
+ );
160
+ };
161
+
162
+ // 支持对象参数
163
+ interface MessageOptions {
164
+ content: string;
165
+ duration?: number;
166
+ closable?: boolean;
167
+ }
168
+
169
+ type MessageArg = string | MessageOptions;
170
+
171
+ // 简单的消息管理器
172
+ class MessageManager {
173
+ private parseArg(arg: MessageArg): MessageOptions {
174
+ if (typeof arg === "string") {
175
+ return { content: arg };
176
+ }
177
+ return arg;
178
+ }
179
+
180
+ show(type: MessageType, arg: MessageArg) {
181
+ const { content, duration, closable } = this.parseArg(arg);
182
+ // 创建消息元素
183
+ const theme = getMessageTheme();
184
+ const messageDiv = document.createElement("div");
185
+ messageDiv.style.cssText = `
186
+ position: fixed;
187
+ top: 20px;
188
+ left: 50%;
189
+ transform: translateX(-50%);
190
+ z-index: 9999;
191
+ background: ${theme.background};
192
+ border: 1px solid ${theme.border};
193
+ border-radius: 12px;
194
+ padding: 8px 16px;
195
+ box-shadow: ${theme.boxShadow};
196
+ min-width: ${content.length < 20 ? "auto" : "300px"};
197
+ max-width: 500px;
198
+ display: flex;
199
+ align-items: center;
200
+ gap: 8px;
201
+ font-size: 14px;
202
+ color: ${theme.color};
203
+ `;
204
+
205
+ // 添加图标
206
+ if (type === "error") {
207
+ const iconSvg = document.createElementNS(
208
+ "http://www.w3.org/2000/svg",
209
+ "svg"
210
+ );
211
+ iconSvg.setAttribute("width", "16");
212
+ iconSvg.setAttribute("height", "16");
213
+ iconSvg.setAttribute("viewBox", "0 0 16 16");
214
+ iconSvg.innerHTML = `
215
+ <circle cx="8" cy="8" r="8" fill="#ff4d4f"/>
216
+ <path d="M10.5 5.5l-5 5m0-5l5 5" stroke="white" stroke-width="1.5" stroke-linecap="round"/>
217
+ `;
218
+ messageDiv.appendChild(iconSvg);
219
+ }
220
+ if (type === "success") {
221
+ const iconSvg = document.createElementNS(
222
+ "http://www.w3.org/2000/svg",
223
+ "svg"
224
+ );
225
+ iconSvg.setAttribute("width", "16");
226
+ iconSvg.setAttribute("height", "16");
227
+ iconSvg.setAttribute("viewBox", "0 0 16 16");
228
+ iconSvg.innerHTML = `
229
+ <circle cx="8" cy="8" r="8" fill="#52c41a"/>
230
+ <path d="M5 8.5l2.2 2.2 3.8-3.4" stroke="white" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" fill="none"/>
231
+ `;
232
+ messageDiv.appendChild(iconSvg);
233
+ }
234
+ if (type === "warning") {
235
+ const iconSvg = document.createElementNS(
236
+ "http://www.w3.org/2000/svg",
237
+ "svg"
238
+ );
239
+ iconSvg.setAttribute("width", "16");
240
+ iconSvg.setAttribute("height", "16");
241
+ iconSvg.setAttribute("viewBox", "0 0 16 16");
242
+ iconSvg.innerHTML = `
243
+ <circle cx="8" cy="8" r="8" fill="#faad14"/>
244
+ <path d="M8 4.5v5" stroke="white" stroke-width="1.5" stroke-linecap="round"/>
245
+ <circle cx="8" cy="12.5" r="1" fill="white"/>
246
+ `;
247
+ messageDiv.appendChild(iconSvg);
248
+ }
249
+ if (type === "info") {
250
+ const iconSvg = document.createElementNS(
251
+ "http://www.w3.org/2000/svg",
252
+ "svg"
253
+ );
254
+ iconSvg.setAttribute("width", "16");
255
+ iconSvg.setAttribute("height", "16");
256
+ iconSvg.setAttribute("viewBox", "0 0 16 16");
257
+ iconSvg.innerHTML = `
258
+ <circle cx="8" cy="8" r="8" fill="#1677ff"/>
259
+ <rect x="7.25" y="7" width="1.5" height="6" rx="0.75" fill="white"/>
260
+ <rect x="7.25" y="5" width="1.5" height="1.5" rx="0.75" fill="white"/>
261
+ `;
262
+ messageDiv.appendChild(iconSvg);
263
+ }
264
+
265
+ // 添加文本
266
+ const textDiv = document.createElement("div");
267
+ textDiv.textContent = content;
268
+ textDiv.style.flex = "1";
269
+ textDiv.style.color = theme.color;
270
+ messageDiv.appendChild(textDiv);
271
+
272
+ // 添加关闭按钮(如果启用)
273
+ if (closable) {
274
+ const closeBtn = document.createElement("button");
275
+ closeBtn.textContent = "✕";
276
+ closeBtn.style.cssText = `
277
+ background: none;
278
+ border: none;
279
+ cursor: pointer;
280
+ padding: 4px;
281
+ color: ${theme.closeColor};
282
+ font-size: 12px;
283
+ `;
284
+ closeBtn.onclick = () => {
285
+ document.body.removeChild(messageDiv);
286
+ };
287
+ messageDiv.appendChild(closeBtn);
288
+ }
289
+
290
+ // 添加到页面
291
+ document.body.appendChild(messageDiv);
292
+
293
+ // 自动关闭
294
+ if (duration !== 0) {
295
+ setTimeout(() => {
296
+ if (document.body.contains(messageDiv)) {
297
+ document.body.removeChild(messageDiv);
298
+ }
299
+ }, duration || 3000);
300
+ }
301
+ }
302
+
303
+ success(arg: MessageArg) {
304
+ this.show("success", arg);
305
+ }
306
+
307
+ error(arg: MessageArg) {
308
+ this.show("error", arg);
309
+ }
310
+
311
+ warning(arg: MessageArg) {
312
+ this.show("warning", arg);
313
+ }
314
+
315
+ info(arg: MessageArg) {
316
+ this.show("info", arg);
317
+ }
318
+ }
319
+
320
+ export const message = new MessageManager();
321
+ export default MessageItem;
@@ -0,0 +1,25 @@
1
+ import { Theme } from "@/types";
2
+ import { getInitParams } from "@/utils/business";
3
+
4
+ export const messageThemes = {
5
+ white: {
6
+ background: "#fff",
7
+ border: "#d9d9d9",
8
+ color: "#222",
9
+ boxShadow: "0 6px 16px rgba(0, 0, 0, 0.12)",
10
+ closeColor: "#999",
11
+ },
12
+ dark: {
13
+ background: "#23262F",
14
+ border: "#444C5C",
15
+ color: "#F5F6FA",
16
+ boxShadow: "0 6px 16px rgba(0,0,0,0.32)",
17
+ closeColor: "#B5B8BE",
18
+ },
19
+ };
20
+
21
+ export function getMessageTheme() {
22
+ const theme = getInitParams<Theme>("theme");
23
+ const whiteTheme = theme === Theme.WHITE;
24
+ return whiteTheme ? messageThemes.white : messageThemes.dark;
25
+ }
@@ -0,0 +1,99 @@
1
+ import type { ComponentChildren } from "preact";
2
+ import { useState } from "preact/hooks";
3
+ import { getModalTheme } from "./theme";
4
+ import { getInitParams } from "@/utils/business";
5
+ import { Size } from "@/types";
6
+ const size = getInitParams<Size>("size");
7
+ interface ModalProps {
8
+ visible: boolean;
9
+ onClose: () => void;
10
+ title?: string;
11
+ children: ComponentChildren;
12
+ width?: number | string;
13
+ maxWidth?: number | string;
14
+ showClose?: boolean;
15
+ maskClosable?: boolean;
16
+ }
17
+
18
+ export function Modal({
19
+ visible,
20
+ onClose,
21
+ title,
22
+ children,
23
+ width = size === Size.SMALL ? 300 : 400,
24
+ maxWidth = size === Size.SMALL ? 300 : 400,
25
+ showClose = true,
26
+ maskClosable = true,
27
+ }: ModalProps) {
28
+ const [maskMouseDown, setMaskMouseDown] = useState(false);
29
+ const theme = getModalTheme();
30
+
31
+ // 只有mousedown和mouseup都在mask上才关闭弹窗
32
+ const handleMaskMouseDown = (e: any) => {
33
+ if (e.target === e.currentTarget) {
34
+ setMaskMouseDown(true);
35
+ } else {
36
+ setMaskMouseDown(false);
37
+ }
38
+ };
39
+
40
+ const handleMaskMouseUp = (e: any) => {
41
+ if (e.target === e.currentTarget && maskMouseDown && maskClosable) {
42
+ onClose();
43
+ }
44
+ setMaskMouseDown(false);
45
+ };
46
+
47
+ if (!visible) return null;
48
+
49
+ return (
50
+ <div
51
+ style={{
52
+ position: "fixed",
53
+ top: 0,
54
+ left: 0,
55
+ right: 0,
56
+ bottom: 0,
57
+ background: theme.mask,
58
+ display: "flex",
59
+ alignItems: "center",
60
+ justifyContent: "center",
61
+ zIndex: 9999,
62
+ }}
63
+ onMouseDown={handleMaskMouseDown}
64
+ onMouseUp={handleMaskMouseUp}
65
+ >
66
+ <div
67
+ style={{
68
+ background: theme.modalBg,
69
+ padding: size === Size.SMALL ? 24 : 32,
70
+ borderRadius: size === Size.SMALL ? 8 : 12,
71
+ minWidth: width,
72
+ maxWidth: maxWidth,
73
+ color: theme.modalColor,
74
+ boxShadow: theme.modalBoxShadow,
75
+ position: "relative",
76
+ }}
77
+ onClick={(e) => e.stopPropagation()}
78
+ >
79
+ {/* 关闭按钮 */}
80
+ {showClose && (
81
+ <button
82
+ type="button"
83
+ onClick={onClose}
84
+ style={theme.closeBtn}
85
+ aria-label="关闭"
86
+ >
87
+ ×
88
+ </button>
89
+ )}
90
+
91
+ {/* 标题 */}
92
+ {title && <div style={theme.title}>{title}</div>}
93
+
94
+ {/* 内容 */}
95
+ {children}
96
+ </div>
97
+ </div>
98
+ );
99
+ }