@svton/taro-ui 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,727 @@
1
+ var __create = Object.create;
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __getProtoOf = Object.getPrototypeOf;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
20
+ // If the importer is in node compatibility mode or this is not an ESM
21
+ // file that has been converted to a CommonJS file using a Babel-
22
+ // compatible transform (i.e. "__esModule" has not been set), then set
23
+ // "default" to the CommonJS "module.exports" for node compatibility.
24
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
25
+ mod
26
+ ));
27
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
28
+
29
+ // src/index.ts
30
+ var index_exports = {};
31
+ __export(index_exports, {
32
+ Button: () => Button,
33
+ ContentActionBar: () => ContentActionBar,
34
+ ImageGrid: () => ImageGrid,
35
+ ImageUploader: () => ImageUploader,
36
+ List: () => List,
37
+ NavBar: () => CustomNavBar,
38
+ StatusBar: () => StatusBar,
39
+ TabBar: () => TabBar,
40
+ Tabs: () => Tabs,
41
+ TabsDefault: () => Tabs_default,
42
+ systemInfoManager: () => systemInfoManager,
43
+ useScrollOpacity: () => useScrollOpacity
44
+ });
45
+ module.exports = __toCommonJS(index_exports);
46
+
47
+ // src/components/TabBar/index.tsx
48
+ var import_react = __toESM(require("react"));
49
+ var import_components = require("@tarojs/components");
50
+ var import_hooks = require("@svton/hooks");
51
+ function TabBar(props) {
52
+ var _a;
53
+ const {
54
+ items,
55
+ activeKey: controlledActiveKey,
56
+ defaultActiveKey,
57
+ onChange,
58
+ className = "",
59
+ style,
60
+ indicatorWidth = 48,
61
+ showIndicator = true,
62
+ sticky = true
63
+ } = props;
64
+ const [internalActiveKey, setInternalActiveKey] = (0, import_react.useState)(defaultActiveKey || ((_a = items[0]) == null ? void 0 : _a.key));
65
+ const activeKey = controlledActiveKey !== void 0 ? controlledActiveKey : internalActiveKey;
66
+ const activeIndex = items.findIndex((item) => item.key === activeKey);
67
+ const indicatorLeft = items.length > 0 ? `${(activeIndex + 0.5) * (100 / items.length)}%` : "50%";
68
+ const handleTabChange = (0, import_hooks.usePersistFn)((key, disabled) => {
69
+ if (disabled) return;
70
+ if (controlledActiveKey === void 0) {
71
+ setInternalActiveKey(key);
72
+ }
73
+ onChange == null ? void 0 : onChange(key);
74
+ });
75
+ (0, import_react.useEffect)(() => {
76
+ if (controlledActiveKey !== void 0) {
77
+ setInternalActiveKey(controlledActiveKey);
78
+ }
79
+ }, [controlledActiveKey]);
80
+ return /* @__PURE__ */ import_react.default.createElement(import_components.View, { className: `svton-tab-bar ${sticky ? "sticky" : ""} ${className}`, style }, /* @__PURE__ */ import_react.default.createElement(import_components.View, { className: "svton-tab-bar__list" }, items.map((item) => /* @__PURE__ */ import_react.default.createElement(
81
+ import_components.View,
82
+ {
83
+ key: item.key,
84
+ className: `svton-tab-bar__item ${activeKey === item.key ? "active" : ""} ${item.disabled ? "disabled" : ""}`,
85
+ onClick: () => handleTabChange(item.key, item.disabled)
86
+ },
87
+ item.render ? item.render() : /* @__PURE__ */ import_react.default.createElement(import_components.Text, { className: "svton-tab-bar__text" }, item.label)
88
+ ))), showIndicator && items.length > 0 && /* @__PURE__ */ import_react.default.createElement(import_components.View, { className: "svton-tab-bar__indicator-wrapper" }, /* @__PURE__ */ import_react.default.createElement(
89
+ import_components.View,
90
+ {
91
+ className: "svton-tab-bar__indicator",
92
+ style: {
93
+ left: indicatorLeft,
94
+ width: `${indicatorWidth}px`,
95
+ marginLeft: `-${indicatorWidth / 2}px`
96
+ }
97
+ }
98
+ )));
99
+ }
100
+
101
+ // src/components/Button/index.tsx
102
+ var import_react2 = __toESM(require("react"));
103
+ var import_components2 = require("@tarojs/components");
104
+ var import_hooks2 = require("@svton/hooks");
105
+ function Button(props) {
106
+ const {
107
+ type = "default",
108
+ size = "medium",
109
+ loading = false,
110
+ disabled = false,
111
+ block = false,
112
+ children,
113
+ className = "",
114
+ style,
115
+ onClick
116
+ } = props;
117
+ const handleClick = (0, import_hooks2.usePersistFn)(() => {
118
+ if (disabled || loading) return;
119
+ onClick == null ? void 0 : onClick();
120
+ });
121
+ return /* @__PURE__ */ import_react2.default.createElement(
122
+ import_components2.View,
123
+ {
124
+ className: `svton-button svton-button--${type} svton-button--${size} ${block ? "svton-button--block" : ""} ${disabled ? "svton-button--disabled" : ""} ${loading ? "svton-button--loading" : ""} ${className}`,
125
+ style,
126
+ onClick: handleClick
127
+ },
128
+ loading && /* @__PURE__ */ import_react2.default.createElement(import_components2.View, { className: "svton-button__loading" }, /* @__PURE__ */ import_react2.default.createElement(import_components2.Text, { className: "svton-button__loading-icon" }, "\u23F3")),
129
+ /* @__PURE__ */ import_react2.default.createElement(import_components2.Text, { className: "svton-button__text" }, children)
130
+ );
131
+ }
132
+
133
+ // src/components/List/index.tsx
134
+ var import_react3 = __toESM(require("react"));
135
+ var import_components3 = require("@tarojs/components");
136
+ var import_taro = __toESM(require("@tarojs/taro"));
137
+ function List(props) {
138
+ const {
139
+ data,
140
+ renderItem,
141
+ keyExtractor = (_, index) => String(index),
142
+ loading = false,
143
+ hasMore = true,
144
+ onRefresh,
145
+ onLoadMore,
146
+ renderEmpty,
147
+ emptyText = "\u6682\u65E0\u6570\u636E",
148
+ loadingText = "\u52A0\u8F7D\u4E2D...",
149
+ noMoreText = "\u6CA1\u6709\u66F4\u591A\u4E86",
150
+ className = "",
151
+ style,
152
+ enableRefresh = true,
153
+ enableLoadMore = true,
154
+ header,
155
+ footer
156
+ } = props;
157
+ (0, import_taro.usePullDownRefresh)(async () => {
158
+ if (!enableRefresh || !onRefresh) {
159
+ import_taro.default.stopPullDownRefresh();
160
+ return;
161
+ }
162
+ try {
163
+ await (onRefresh == null ? void 0 : onRefresh());
164
+ } finally {
165
+ import_taro.default.stopPullDownRefresh();
166
+ }
167
+ });
168
+ (0, import_taro.useReachBottom)(async () => {
169
+ if (!enableLoadMore || !hasMore || loading || !onLoadMore) return;
170
+ await (onLoadMore == null ? void 0 : onLoadMore());
171
+ });
172
+ const renderEmptyContent = () => {
173
+ if (renderEmpty) {
174
+ return renderEmpty();
175
+ }
176
+ return /* @__PURE__ */ import_react3.default.createElement(import_components3.View, { className: "svton-list__empty" }, /* @__PURE__ */ import_react3.default.createElement(import_components3.Text, { className: "svton-list__empty-text" }, emptyText));
177
+ };
178
+ const renderLoadingTip = () => {
179
+ if (!loading && !hasMore) {
180
+ return /* @__PURE__ */ import_react3.default.createElement(import_components3.View, { className: "svton-list__tip svton-list__tip--no-more" }, /* @__PURE__ */ import_react3.default.createElement(import_components3.Text, null, noMoreText));
181
+ }
182
+ if (loading && data.length > 0) {
183
+ return /* @__PURE__ */ import_react3.default.createElement(import_components3.View, { className: "svton-list__tip svton-list__tip--loading" }, /* @__PURE__ */ import_react3.default.createElement(import_components3.Text, null, loadingText));
184
+ }
185
+ return null;
186
+ };
187
+ return /* @__PURE__ */ import_react3.default.createElement(import_components3.ScrollView, { className: `svton-list ${className}`, style, scrollY: true, enableBackToTop: true }, header && /* @__PURE__ */ import_react3.default.createElement(import_components3.View, { className: "svton-list__header" }, header), data.length === 0 && !loading ? renderEmptyContent() : /* @__PURE__ */ import_react3.default.createElement(import_components3.View, { className: "svton-list__content" }, data.map((item, index) => /* @__PURE__ */ import_react3.default.createElement(import_components3.View, { key: keyExtractor(item, index), className: "svton-list__item" }, renderItem(item, index)))), renderLoadingTip(), footer && /* @__PURE__ */ import_react3.default.createElement(import_components3.View, { className: "svton-list__footer" }, footer));
188
+ }
189
+
190
+ // src/components/NavBar/index.tsx
191
+ var import_react5 = __toESM(require("react"));
192
+ var import_components5 = require("@tarojs/components");
193
+ var import_taro3 = __toESM(require("@tarojs/taro"));
194
+
195
+ // src/utils/systemInfo.ts
196
+ var import_taro2 = __toESM(require("@tarojs/taro"));
197
+ var SystemInfoManager = class {
198
+ info = null;
199
+ async init() {
200
+ try {
201
+ const systemInfo = await import_taro2.default.getSystemInfo();
202
+ const statusBarHeight = systemInfo.statusBarHeight || 44;
203
+ const windowWidth = systemInfo.windowWidth || 375;
204
+ const windowHeight = systemInfo.windowHeight || 667;
205
+ const safeArea = systemInfo.safeArea || {
206
+ top: statusBarHeight,
207
+ right: windowWidth,
208
+ bottom: windowHeight,
209
+ left: 0
210
+ };
211
+ const safeAreaInsets = {
212
+ top: safeArea.top,
213
+ right: windowWidth - safeArea.right,
214
+ bottom: windowHeight - safeArea.bottom,
215
+ left: safeArea.left
216
+ };
217
+ let menuButton;
218
+ let navBarHeight = statusBarHeight + 44;
219
+ try {
220
+ if (import_taro2.default.getEnv() === import_taro2.default.ENV_TYPE.WEAPP) {
221
+ menuButton = import_taro2.default.getMenuButtonBoundingClientRect();
222
+ navBarHeight = menuButton.bottom + (menuButton.top - statusBarHeight);
223
+ }
224
+ } catch (e) {
225
+ console.warn("\u83B7\u53D6\u80F6\u56CA\u6309\u94AE\u4FE1\u606F\u5931\u8D25", e);
226
+ }
227
+ this.info = {
228
+ statusBarHeight,
229
+ safeAreaInsets,
230
+ menuButton,
231
+ navBarHeight,
232
+ windowWidth,
233
+ windowHeight
234
+ };
235
+ this.setCSSVariables();
236
+ console.log("\u7CFB\u7EDF\u4FE1\u606F\u521D\u59CB\u5316\u6210\u529F:", this.info);
237
+ return this.info;
238
+ } catch (error) {
239
+ console.error("\u83B7\u53D6\u7CFB\u7EDF\u4FE1\u606F\u5931\u8D25", error);
240
+ this.info = {
241
+ statusBarHeight: 44,
242
+ safeAreaInsets: { top: 44, right: 0, bottom: 0, left: 0 },
243
+ navBarHeight: 88,
244
+ windowWidth: 375,
245
+ windowHeight: 667
246
+ };
247
+ this.setCSSVariables();
248
+ return this.info;
249
+ }
250
+ }
251
+ setCSSVariables() {
252
+ if (!this.info) return;
253
+ try {
254
+ if (typeof document !== "undefined" && document.documentElement) {
255
+ const root = document.documentElement;
256
+ root.style.setProperty("--status-bar-height", `${this.info.statusBarHeight}px`);
257
+ root.style.setProperty("--safe-area-top", `${this.info.safeAreaInsets.top}px`);
258
+ root.style.setProperty("--safe-area-right", `${this.info.safeAreaInsets.right}px`);
259
+ root.style.setProperty("--safe-area-bottom", `${this.info.safeAreaInsets.bottom}px`);
260
+ root.style.setProperty("--safe-area-left", `${this.info.safeAreaInsets.left}px`);
261
+ root.style.setProperty("--nav-bar-height", `${this.info.navBarHeight}px`);
262
+ if (this.info.menuButton) {
263
+ root.style.setProperty("--menu-button-width", `${this.info.menuButton.width}px`);
264
+ root.style.setProperty("--menu-button-height", `${this.info.menuButton.height}px`);
265
+ root.style.setProperty("--menu-button-top", `${this.info.menuButton.top}px`);
266
+ root.style.setProperty("--menu-button-right", `${this.info.menuButton.right}px`);
267
+ root.style.setProperty("--menu-button-bottom", `${this.info.menuButton.bottom}px`);
268
+ root.style.setProperty("--menu-button-left", `${this.info.menuButton.left}px`);
269
+ }
270
+ console.log("CSS \u53D8\u91CF\u8BBE\u7F6E\u6210\u529F");
271
+ }
272
+ } catch (e) {
273
+ console.warn("\u8BBE\u7F6E CSS \u53D8\u91CF\u5931\u8D25", e);
274
+ }
275
+ }
276
+ getInfo() {
277
+ return this.info;
278
+ }
279
+ getStatusBarHeight() {
280
+ var _a;
281
+ return ((_a = this.info) == null ? void 0 : _a.statusBarHeight) || 44;
282
+ }
283
+ getNavBarHeight() {
284
+ var _a;
285
+ return ((_a = this.info) == null ? void 0 : _a.navBarHeight) || 88;
286
+ }
287
+ getSafeAreaInsets() {
288
+ var _a;
289
+ return ((_a = this.info) == null ? void 0 : _a.safeAreaInsets) || { top: 44, right: 0, bottom: 0, left: 0 };
290
+ }
291
+ };
292
+ var systemInfoManager = new SystemInfoManager();
293
+
294
+ // src/components/StatusBar/index.tsx
295
+ var import_react4 = __toESM(require("react"));
296
+ var import_components4 = require("@tarojs/components");
297
+ function StatusBar({ backgroundColor, className = "" }) {
298
+ const [height, setHeight] = (0, import_react4.useState)(44);
299
+ (0, import_react4.useEffect)(() => {
300
+ const info = systemInfoManager.getInfo();
301
+ if (info) {
302
+ setHeight(info.statusBarHeight);
303
+ }
304
+ }, []);
305
+ return /* @__PURE__ */ import_react4.default.createElement(
306
+ import_components4.View,
307
+ {
308
+ className: `status-bar ${className}`,
309
+ style: {
310
+ height: `${height}px`,
311
+ backgroundColor: backgroundColor || "transparent"
312
+ }
313
+ }
314
+ );
315
+ }
316
+
317
+ // src/components/NavBar/index.tsx
318
+ var BACK_ICON_SVG = "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KICA8cGF0aCBkPSJNMTUgMThMOSAxMkwxNSA2IiBzdHJva2U9IiMzMzMzMzMiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIi8+Cjwvc3ZnPgo=";
319
+ function CustomNavBar({
320
+ title = "",
321
+ showBack = false,
322
+ showClose = false,
323
+ backgroundColor = "#ffffff",
324
+ textColor = "#333333",
325
+ onBack,
326
+ onClose,
327
+ rightContent,
328
+ fixed = false,
329
+ scrollOpacity = 1
330
+ }) {
331
+ const [navBarContentHeight, setNavBarContentHeight] = (0, import_react5.useState)(44);
332
+ const [statusBarHeight, setStatusBarHeight] = (0, import_react5.useState)(44);
333
+ const [menuButtonLeft, setMenuButtonLeft] = (0, import_react5.useState)(0);
334
+ const getBackgroundColor = () => {
335
+ if (scrollOpacity >= 1) {
336
+ return backgroundColor;
337
+ }
338
+ const hex = backgroundColor.replace("#", "");
339
+ const r = parseInt(hex.substring(0, 2), 16);
340
+ const g = parseInt(hex.substring(2, 4), 16);
341
+ const b = parseInt(hex.substring(4, 6), 16);
342
+ return `rgba(${r}, ${g}, ${b}, ${scrollOpacity})`;
343
+ };
344
+ (0, import_react5.useEffect)(() => {
345
+ const info = systemInfoManager.getInfo();
346
+ if (info) {
347
+ setNavBarContentHeight(info.navBarHeight - info.statusBarHeight);
348
+ setStatusBarHeight(info.statusBarHeight);
349
+ if (info.menuButton) {
350
+ setMenuButtonLeft(info.menuButton.left);
351
+ }
352
+ }
353
+ }, []);
354
+ const handleBack = () => {
355
+ if (onBack) {
356
+ onBack();
357
+ } else {
358
+ const pages = import_taro3.default.getCurrentPages();
359
+ if (pages.length > 1) {
360
+ import_taro3.default.navigateBack().catch((err) => {
361
+ console.warn("\u8FD4\u56DE\u5931\u8D25:", err);
362
+ import_taro3.default.switchTab({ url: "/pages/index/index" }).catch(() => {
363
+ console.error("\u8DF3\u8F6C\u9996\u9875\u5931\u8D25");
364
+ });
365
+ });
366
+ } else {
367
+ import_taro3.default.switchTab({ url: "/pages/index/index" }).catch(() => {
368
+ console.error("\u8DF3\u8F6C\u9996\u9875\u5931\u8D25");
369
+ });
370
+ }
371
+ }
372
+ };
373
+ const handleClose = () => {
374
+ if (onClose) {
375
+ onClose();
376
+ } else {
377
+ const pages = import_taro3.default.getCurrentPages();
378
+ if (pages.length > 1) {
379
+ import_taro3.default.navigateBack().catch((err) => {
380
+ console.warn("\u5173\u95ED\u5931\u8D25:", err);
381
+ import_taro3.default.switchTab({ url: "/pages/index/index" }).catch(() => {
382
+ console.error("\u8DF3\u8F6C\u9996\u9875\u5931\u8D25");
383
+ });
384
+ });
385
+ } else {
386
+ import_taro3.default.switchTab({ url: "/pages/index/index" }).catch(() => {
387
+ console.error("\u8DF3\u8F6C\u9996\u9875\u5931\u8D25");
388
+ });
389
+ }
390
+ }
391
+ };
392
+ const getRightSafeDistance = () => {
393
+ if (menuButtonLeft > 0) {
394
+ const info = systemInfoManager.getInfo();
395
+ if (info) {
396
+ return info.windowWidth - menuButtonLeft + 8;
397
+ }
398
+ }
399
+ return 100;
400
+ };
401
+ const navBarClass = `custom-nav-bar ${fixed ? "fixed" : ""}`;
402
+ const actualBgColor = getBackgroundColor();
403
+ const totalHeight = statusBarHeight + navBarContentHeight;
404
+ const navBarContent = /* @__PURE__ */ import_react5.default.createElement(import_components5.View, { className: navBarClass, style: { backgroundColor: actualBgColor } }, /* @__PURE__ */ import_react5.default.createElement(StatusBar, { backgroundColor: actualBgColor }), /* @__PURE__ */ import_react5.default.createElement(
405
+ import_components5.View,
406
+ {
407
+ className: "nav-bar-content",
408
+ style: {
409
+ height: `${navBarContentHeight}px`,
410
+ backgroundColor: actualBgColor
411
+ }
412
+ },
413
+ (showBack || showClose) && /* @__PURE__ */ import_react5.default.createElement(import_components5.View, { className: "nav-left" }, showBack && /* @__PURE__ */ import_react5.default.createElement(import_components5.View, { className: "nav-btn", onClick: handleBack }, /* @__PURE__ */ import_react5.default.createElement(
414
+ import_components5.Image,
415
+ {
416
+ className: "nav-icon-img back-icon-img",
417
+ src: BACK_ICON_SVG,
418
+ mode: "aspectFit"
419
+ }
420
+ )), showClose && /* @__PURE__ */ import_react5.default.createElement(import_components5.View, { className: "nav-btn", onClick: handleClose }, /* @__PURE__ */ import_react5.default.createElement(import_components5.Text, { className: "nav-icon close-icon", style: { color: textColor } }, "\u2715"))),
421
+ title && /* @__PURE__ */ import_react5.default.createElement(import_components5.View, { className: "nav-title" }, /* @__PURE__ */ import_react5.default.createElement(import_components5.Text, { className: "title-text", style: { color: textColor } }, title)),
422
+ rightContent && /* @__PURE__ */ import_react5.default.createElement(import_components5.View, { className: "nav-right", style: { right: `${getRightSafeDistance()}px` } }, rightContent)
423
+ ));
424
+ if (fixed) {
425
+ return /* @__PURE__ */ import_react5.default.createElement(import_react5.default.Fragment, null, /* @__PURE__ */ import_react5.default.createElement(
426
+ import_components5.View,
427
+ {
428
+ className: "nav-bar-placeholder",
429
+ style: { height: `${totalHeight}px` }
430
+ }
431
+ ), navBarContent);
432
+ }
433
+ return navBarContent;
434
+ }
435
+
436
+ // src/components/ImageUploader/index.tsx
437
+ var import_react6 = __toESM(require("react"));
438
+ var import_components6 = require("@tarojs/components");
439
+ var import_taro4 = __toESM(require("@tarojs/taro"));
440
+ function ImageUploader({
441
+ value = [],
442
+ onChange,
443
+ maxCount = 9,
444
+ uploadUrl = process.env.TARO_APP_API + "/upload/image"
445
+ }) {
446
+ const [uploading, setUploading] = (0, import_react6.useState)(false);
447
+ const handleChooseImage = async () => {
448
+ try {
449
+ const res = await import_taro4.default.chooseImage({
450
+ count: maxCount - value.length,
451
+ sizeType: ["compressed"],
452
+ sourceType: ["album", "camera"]
453
+ });
454
+ setUploading(true);
455
+ const uploadPromises = res.tempFilePaths.map(async (filePath) => {
456
+ const token = import_taro4.default.getStorageSync("token");
457
+ const uploadRes = await import_taro4.default.uploadFile({
458
+ url: uploadUrl,
459
+ filePath,
460
+ name: "file",
461
+ header: {
462
+ Authorization: `Bearer ${token}`
463
+ }
464
+ });
465
+ const data = JSON.parse(uploadRes.data);
466
+ return data.url;
467
+ });
468
+ const urls = await Promise.all(uploadPromises);
469
+ const newImages = [...value, ...urls];
470
+ onChange == null ? void 0 : onChange(newImages);
471
+ import_taro4.default.showToast({
472
+ title: "\u4E0A\u4F20\u6210\u529F",
473
+ icon: "success"
474
+ });
475
+ } catch (error) {
476
+ import_taro4.default.showToast({
477
+ title: error.errMsg || "\u4E0A\u4F20\u5931\u8D25",
478
+ icon: "none"
479
+ });
480
+ } finally {
481
+ setUploading(false);
482
+ }
483
+ };
484
+ const handlePreview = (index) => {
485
+ import_taro4.default.previewImage({
486
+ urls: value,
487
+ current: value[index]
488
+ });
489
+ };
490
+ const handleDelete = (index) => {
491
+ import_taro4.default.showModal({
492
+ title: "\u63D0\u793A",
493
+ content: "\u786E\u5B9A\u5220\u9664\u8FD9\u5F20\u56FE\u7247\u5417\uFF1F",
494
+ success: (res) => {
495
+ if (res.confirm) {
496
+ const newImages = value.filter((_, i) => i !== index);
497
+ onChange == null ? void 0 : onChange(newImages);
498
+ }
499
+ }
500
+ });
501
+ };
502
+ return /* @__PURE__ */ import_react6.default.createElement(import_components6.View, { className: "image-uploader" }, /* @__PURE__ */ import_react6.default.createElement(import_components6.View, { className: "image-list" }, value.map((url, index) => /* @__PURE__ */ import_react6.default.createElement(import_components6.View, { key: index, className: "image-item" }, /* @__PURE__ */ import_react6.default.createElement(
503
+ import_components6.Image,
504
+ {
505
+ src: url,
506
+ mode: "aspectFill",
507
+ className: "image",
508
+ onClick: () => handlePreview(index)
509
+ }
510
+ ), /* @__PURE__ */ import_react6.default.createElement(import_components6.View, { className: "delete-btn", onClick: () => handleDelete(index) }, "\xD7"))), value.length < maxCount && /* @__PURE__ */ import_react6.default.createElement(
511
+ import_components6.View,
512
+ {
513
+ className: `add-btn ${uploading ? "disabled" : ""}`,
514
+ onClick: uploading ? void 0 : handleChooseImage
515
+ },
516
+ uploading ? /* @__PURE__ */ import_react6.default.createElement(import_components6.View, { className: "loading" }, "\u4E0A\u4F20\u4E2D...") : /* @__PURE__ */ import_react6.default.createElement(import_components6.View, { className: "add-icon" }, "+")
517
+ )), /* @__PURE__ */ import_react6.default.createElement(import_components6.View, { className: "tip" }, "\u6700\u591A\u4E0A\u4F20 ", maxCount, " \u5F20\u56FE\u7247"));
518
+ }
519
+
520
+ // src/components/ImageGrid/index.tsx
521
+ var import_react7 = __toESM(require("react"));
522
+ var import_components7 = require("@tarojs/components");
523
+ var import_taro5 = __toESM(require("@tarojs/taro"));
524
+ function ImageGrid({ images, maxCount = 9, onImageClick }) {
525
+ const displayImages = images.slice(0, maxCount);
526
+ const count = displayImages.length;
527
+ const getGridClass = () => {
528
+ if (count === 1) return "grid-single";
529
+ if (count === 2 || count === 4) return "grid-2";
530
+ return "grid-3";
531
+ };
532
+ const handleImageClick = (index) => {
533
+ if (onImageClick) {
534
+ onImageClick(index);
535
+ } else {
536
+ import_taro5.default.previewImage({
537
+ urls: images,
538
+ current: images[index]
539
+ });
540
+ }
541
+ };
542
+ return /* @__PURE__ */ import_react7.default.createElement(import_components7.View, { className: `image-grid ${getGridClass()}` }, displayImages.map((url, index) => /* @__PURE__ */ import_react7.default.createElement(import_components7.View, { key: index, className: "image-item", onClick: () => handleImageClick(index) }, /* @__PURE__ */ import_react7.default.createElement(
543
+ import_components7.Image,
544
+ {
545
+ src: url,
546
+ mode: count === 1 ? "widthFix" : "aspectFill",
547
+ className: "image",
548
+ lazyLoad: true
549
+ }
550
+ ), index === maxCount - 1 && images.length > maxCount && /* @__PURE__ */ import_react7.default.createElement(import_components7.View, { className: "image-overlay" }, /* @__PURE__ */ import_react7.default.createElement(import_components7.View, { className: "image-count" }, "+", images.length - maxCount + 1)))));
551
+ }
552
+
553
+ // src/components/Tabs/index.tsx
554
+ var import_components8 = require("@tarojs/components");
555
+ function Tabs({ activeKey, items, onChange, className = "" }) {
556
+ const handleItemClick = (e) => {
557
+ const key = e.currentTarget.dataset.key;
558
+ if (key && key !== activeKey) {
559
+ onChange(key);
560
+ }
561
+ };
562
+ return /* @__PURE__ */ React.createElement(import_components8.View, { className: `svton-tabs ${className}` }, items.map((item) => /* @__PURE__ */ React.createElement(
563
+ import_components8.View,
564
+ {
565
+ key: item.key,
566
+ "data-key": item.key,
567
+ className: `svton-tabs__item ${activeKey === item.key ? "svton-tabs__item--active" : ""}`,
568
+ onClick: handleItemClick
569
+ },
570
+ /* @__PURE__ */ React.createElement(import_components8.Text, { className: "svton-tabs__label" }, item.label),
571
+ item.count !== void 0 && /* @__PURE__ */ React.createElement(import_components8.Text, { className: "svton-tabs__count" }, "(", item.count, ")")
572
+ )));
573
+ }
574
+ var Tabs_default = Tabs;
575
+
576
+ // src/components/ContentActionBar/index.tsx
577
+ var import_react8 = __toESM(require("react"));
578
+ var import_components9 = require("@tarojs/components");
579
+ var ICONS = {
580
+ // 点赞图标(未点赞) - 空心心形
581
+ like: "https://miaoduo.fbcontent.cn/private/resource/image/19a9ba374ebbee0-0aa9c734-e868-4861-9bfd-37de7ed3a123.svg",
582
+ // 点赞图标(已点赞) - 实心红色心形
583
+ liked: "https://miaoduo.fbcontent.cn/private/resource/image/19a9d0909e270e7-fdf153f2-b3d2-4800-a63a-213c242beed8.svg",
584
+ // 收藏图标(未收藏) - 星形
585
+ favorite: "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KICA8cGF0aCBkPSJNMTIgMi41TDE1LjA5IDguMjZMMjEuMTggOS4yN0wxNi41OSAxMy45N0wxNy42NCAyMEwxMiAxNy4yN0w2LjM2IDIwTDcuNDEgMTMuOTdMMi44MiA5LjI3TDguOTEgOC4yNkwxMiAyLjV6IiBzdHJva2U9IiMzMzMiIHN0cm9rZS13aWR0aD0iMS41IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz4KPC9zdmc+",
586
+ // 收藏图标(已收藏) - 金黄色星形
587
+ favorited: "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KICA8cGF0aCBkPSJNMTIgMi41TDE1LjA5IDguMjZMMjEuMTggOS4yN0wxNi41OSAxMy45N0wxNy42NCAyMEwxMiAxNy4yN0w2LjM2IDIwTDcuNDEgMTMuOTdMMi44MiA5LjI3TDguOTEgOC4yNkwxMiAyLjV6IiBmaWxsPSIjRkZDQzAwIiBzdHJva2U9IiNGRkNDMDAiIHN0cm9rZS13aWR0aD0iMS41IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz4KPC9zdmc+",
588
+ // 分享图标
589
+ share: "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KICA8Y2lyY2xlIGN4PSIxOCIgY3k9IjUiIHI9IjMiIHN0cm9rZT0iIzMzMyIgc3Ryb2tlLXdpZHRoPSIxLjUiLz4KICA8Y2lyY2xlIGN4PSI2IiBjeT0iMTIiIHI9IjMiIHN0cm9rZT0iIzMzMyIgc3Ryb2tlLXdpZHRoPSIxLjUiLz4KICA8Y2lyY2xlIGN4PSIxOCIgY3k9IjE5IiByPSIzIiBzdHJva2U9IiMzMzMiIHN0cm9rZS13aWR0aD0iMS41Ii8+CiAgPHBhdGggZD0iTTguNTkgMTMuNTFMMTUuNDIgMTcuNDlNMTUuNDEgNi41MUw4LjU5IDEwLjQ5IiBzdHJva2U9IiMzMzMiIHN0cm9rZS13aWR0aD0iMS41Ii8+Cjwvc3ZnPg=="
590
+ };
591
+ function ContentActionBar({
592
+ onComment,
593
+ onLike,
594
+ onFavorite,
595
+ onShare,
596
+ liked = false,
597
+ favorited = false,
598
+ placeholder = "\u8BF4\u70B9\u4EC0\u4E48...",
599
+ maxLength = 500,
600
+ disabled = false
601
+ }) {
602
+ const [isExpanded, setIsExpanded] = (0, import_react8.useState)(false);
603
+ const [inputValue, setInputValue] = (0, import_react8.useState)("");
604
+ const [submitting, setSubmitting] = (0, import_react8.useState)(false);
605
+ const handleExpand = () => {
606
+ if (disabled) return;
607
+ setIsExpanded(true);
608
+ };
609
+ const handleCollapse = () => {
610
+ setIsExpanded(false);
611
+ setInputValue("");
612
+ };
613
+ const handleSubmit = async () => {
614
+ if (!inputValue.trim() || submitting || disabled) return;
615
+ setSubmitting(true);
616
+ try {
617
+ await (onComment == null ? void 0 : onComment(inputValue.trim()));
618
+ handleCollapse();
619
+ } catch (error) {
620
+ console.error("\u8BC4\u8BBA\u5931\u8D25:", error);
621
+ } finally {
622
+ setSubmitting(false);
623
+ }
624
+ };
625
+ const handleLike = (e) => {
626
+ e.stopPropagation();
627
+ if (disabled) return;
628
+ onLike == null ? void 0 : onLike();
629
+ };
630
+ const handleFavorite = (e) => {
631
+ e.stopPropagation();
632
+ if (disabled) return;
633
+ onFavorite == null ? void 0 : onFavorite();
634
+ };
635
+ const handleShare = (e) => {
636
+ e.stopPropagation();
637
+ if (disabled) return;
638
+ onShare == null ? void 0 : onShare();
639
+ };
640
+ return /* @__PURE__ */ import_react8.default.createElement(import_components9.View, { className: "content-action-bar" }, !isExpanded ? (
641
+ // 收起状态:输入框占位 + 操作按钮
642
+ /* @__PURE__ */ import_react8.default.createElement(import_components9.View, { className: "action-bar-collapsed" }, /* @__PURE__ */ import_react8.default.createElement(import_components9.View, { className: "input-placeholder", onClick: handleExpand }, /* @__PURE__ */ import_react8.default.createElement(import_components9.Text, { className: "placeholder-text" }, placeholder)), /* @__PURE__ */ import_react8.default.createElement(import_components9.View, { className: "action-buttons" }, /* @__PURE__ */ import_react8.default.createElement(
643
+ import_components9.View,
644
+ {
645
+ className: `action-btn ${liked ? "active" : ""}`,
646
+ onClick: handleLike
647
+ },
648
+ /* @__PURE__ */ import_react8.default.createElement(
649
+ import_components9.Image,
650
+ {
651
+ className: `action-icon-img ${liked ? "liked" : ""}`,
652
+ src: liked ? ICONS.liked : ICONS.like,
653
+ mode: "aspectFit"
654
+ }
655
+ )
656
+ ), /* @__PURE__ */ import_react8.default.createElement(
657
+ import_components9.View,
658
+ {
659
+ className: `action-btn ${favorited ? "active" : ""}`,
660
+ onClick: handleFavorite
661
+ },
662
+ /* @__PURE__ */ import_react8.default.createElement(
663
+ import_components9.Image,
664
+ {
665
+ className: `action-icon-img ${favorited ? "favorited" : ""}`,
666
+ src: favorited ? ICONS.favorited : ICONS.favorite,
667
+ mode: "aspectFit"
668
+ }
669
+ )
670
+ ), /* @__PURE__ */ import_react8.default.createElement(import_components9.View, { className: "action-btn", onClick: handleShare }, /* @__PURE__ */ import_react8.default.createElement(
671
+ import_components9.Image,
672
+ {
673
+ className: "action-icon-img",
674
+ src: ICONS.share,
675
+ mode: "aspectFit"
676
+ }
677
+ ))))
678
+ ) : (
679
+ // 展开状态:多行输入框 + 发送按钮
680
+ /* @__PURE__ */ import_react8.default.createElement(import_components9.View, { className: "action-bar-expanded" }, /* @__PURE__ */ import_react8.default.createElement(
681
+ import_components9.Textarea,
682
+ {
683
+ className: "comment-textarea",
684
+ value: inputValue,
685
+ onInput: (e) => setInputValue(e.detail.value),
686
+ placeholder,
687
+ maxlength: maxLength,
688
+ autoHeight: true,
689
+ focus: true,
690
+ disabled
691
+ }
692
+ ), /* @__PURE__ */ import_react8.default.createElement(import_components9.View, { className: "expanded-actions" }, /* @__PURE__ */ import_react8.default.createElement(import_components9.Text, { className: "char-count" }, inputValue.length, "/", maxLength), /* @__PURE__ */ import_react8.default.createElement(import_components9.View, { className: "action-btns" }, /* @__PURE__ */ import_react8.default.createElement(import_components9.View, { className: "cancel-btn", onClick: handleCollapse }, /* @__PURE__ */ import_react8.default.createElement(import_components9.Text, { className: "btn-text" }, "\u53D6\u6D88")), /* @__PURE__ */ import_react8.default.createElement(
693
+ import_components9.View,
694
+ {
695
+ className: `send-btn ${inputValue.trim() && !submitting ? "active" : "disabled"}`,
696
+ onClick: handleSubmit
697
+ },
698
+ /* @__PURE__ */ import_react8.default.createElement(import_components9.Text, { className: "btn-text" }, submitting ? "\u53D1\u9001\u4E2D..." : "\u53D1\u9001")
699
+ ))))
700
+ ));
701
+ }
702
+
703
+ // src/hooks/useScrollOpacity.ts
704
+ var import_react9 = require("react");
705
+ function useScrollOpacity(threshold = 200) {
706
+ const [scrollOpacity, setScrollOpacity] = (0, import_react9.useState)(1);
707
+ const handleScroll = (0, import_react9.useCallback)((scrollTop) => {
708
+ const opacity = Math.max(1 - scrollTop / threshold, 0);
709
+ setScrollOpacity(opacity);
710
+ }, [threshold]);
711
+ return [scrollOpacity, handleScroll];
712
+ }
713
+ // Annotate the CommonJS export names for ESM import in node:
714
+ 0 && (module.exports = {
715
+ Button,
716
+ ContentActionBar,
717
+ ImageGrid,
718
+ ImageUploader,
719
+ List,
720
+ NavBar,
721
+ StatusBar,
722
+ TabBar,
723
+ Tabs,
724
+ TabsDefault,
725
+ systemInfoManager,
726
+ useScrollOpacity
727
+ });