maooo-skyline-ui-system 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (117) hide show
  1. package/README.md +157 -0
  2. package/miniprogram_dist/button/button.js +43 -0
  3. package/miniprogram_dist/button/button.json +5 -0
  4. package/miniprogram_dist/button/button.wxml +12 -0
  5. package/miniprogram_dist/button/button.wxs +23 -0
  6. package/miniprogram_dist/button/button.wxss +133 -0
  7. package/miniprogram_dist/calendar/calendar.js +549 -0
  8. package/miniprogram_dist/calendar/calendar.json +8 -0
  9. package/miniprogram_dist/calendar/calendar.wxml +79 -0
  10. package/miniprogram_dist/calendar/calendar.wxss +265 -0
  11. package/miniprogram_dist/card/card.js +22 -0
  12. package/miniprogram_dist/card/card.json +5 -0
  13. package/miniprogram_dist/card/card.wxml +9 -0
  14. package/miniprogram_dist/card/card.wxss +34 -0
  15. package/miniprogram_dist/countdown/countdown.js +213 -0
  16. package/miniprogram_dist/countdown/countdown.json +3 -0
  17. package/miniprogram_dist/countdown/countdown.wxml +9 -0
  18. package/miniprogram_dist/countdown/countdown.wxss +33 -0
  19. package/miniprogram_dist/form/form.js +12 -0
  20. package/miniprogram_dist/form/form.json +5 -0
  21. package/miniprogram_dist/form/form.wxml +12 -0
  22. package/miniprogram_dist/form/form.wxss +35 -0
  23. package/miniprogram_dist/form-group/form-group.js +53 -0
  24. package/miniprogram_dist/form-group/form-group.json +5 -0
  25. package/miniprogram_dist/form-group/form-group.wxml +11 -0
  26. package/miniprogram_dist/form-group/form-group.wxss +50 -0
  27. package/miniprogram_dist/form-input/form-input.js +48 -0
  28. package/miniprogram_dist/form-input/form-input.json +8 -0
  29. package/miniprogram_dist/form-input/form-input.wxml +28 -0
  30. package/miniprogram_dist/form-input/form-input.wxss +3 -0
  31. package/miniprogram_dist/form-textarea/form-textarea.js +34 -0
  32. package/miniprogram_dist/form-textarea/form-textarea.json +8 -0
  33. package/miniprogram_dist/form-textarea/form-textarea.wxml +20 -0
  34. package/miniprogram_dist/form-textarea/form-textarea.wxss +3 -0
  35. package/miniprogram_dist/icon/_iconfont/iconfont.js +63 -0
  36. package/miniprogram_dist/icon/_iconfont/iconfont.json +4 -0
  37. package/miniprogram_dist/icon/_iconfont/iconfont.wxml +8 -0
  38. package/miniprogram_dist/icon/_iconfont/iconfont.wxss +3 -0
  39. package/miniprogram_dist/icon/icon.js +41 -0
  40. package/miniprogram_dist/icon/icon.json +7 -0
  41. package/miniprogram_dist/icon/icon.wxml +3 -0
  42. package/miniprogram_dist/icon/icon.wxss +13 -0
  43. package/miniprogram_dist/image-cropper/image-cropper.js +703 -0
  44. package/miniprogram_dist/image-cropper/image-cropper.json +3 -0
  45. package/miniprogram_dist/image-cropper/image-cropper.wxml +58 -0
  46. package/miniprogram_dist/image-cropper/image-cropper.wxss +195 -0
  47. package/miniprogram_dist/index.js +4 -0
  48. package/miniprogram_dist/input/input.js +119 -0
  49. package/miniprogram_dist/input/input.json +7 -0
  50. package/miniprogram_dist/input/input.wxml +38 -0
  51. package/miniprogram_dist/input/input.wxss +52 -0
  52. package/miniprogram_dist/media-upload/media-upload.js +1108 -0
  53. package/miniprogram_dist/media-upload/media-upload.json +7 -0
  54. package/miniprogram_dist/media-upload/media-upload.wxml +98 -0
  55. package/miniprogram_dist/media-upload/media-upload.wxss +149 -0
  56. package/miniprogram_dist/navbar/navbar.js +191 -0
  57. package/miniprogram_dist/navbar/navbar.json +6 -0
  58. package/miniprogram_dist/navbar/navbar.wxml +25 -0
  59. package/miniprogram_dist/navbar/navbar.wxss +60 -0
  60. package/miniprogram_dist/notify/notify.js +342 -0
  61. package/miniprogram_dist/notify/notify.json +5 -0
  62. package/miniprogram_dist/notify/notify.wxml +27 -0
  63. package/miniprogram_dist/notify/notify.wxss +246 -0
  64. package/miniprogram_dist/notify/service.js +69 -0
  65. package/miniprogram_dist/oss-upload/oss-upload.js +505 -0
  66. package/miniprogram_dist/oss-upload/oss-upload.json +6 -0
  67. package/miniprogram_dist/oss-upload/oss-upload.wxml +20 -0
  68. package/miniprogram_dist/oss-upload/oss-upload.wxss +5 -0
  69. package/miniprogram_dist/popup/popup.js +36 -0
  70. package/miniprogram_dist/popup/popup.json +5 -0
  71. package/miniprogram_dist/popup/popup.wxml +14 -0
  72. package/miniprogram_dist/popup/popup.wxss +138 -0
  73. package/miniprogram_dist/popup-page/popup-page.js +341 -0
  74. package/miniprogram_dist/popup-page/popup-page.json +8 -0
  75. package/miniprogram_dist/popup-page/popup-page.wxml +65 -0
  76. package/miniprogram_dist/popup-page/popup-page.wxss +92 -0
  77. package/miniprogram_dist/progress/progress.js +161 -0
  78. package/miniprogram_dist/progress/progress.json +5 -0
  79. package/miniprogram_dist/progress/progress.wxml +16 -0
  80. package/miniprogram_dist/progress/progress.wxss +81 -0
  81. package/miniprogram_dist/rate/rate.js +127 -0
  82. package/miniprogram_dist/rate/rate.json +5 -0
  83. package/miniprogram_dist/rate/rate.wxml +32 -0
  84. package/miniprogram_dist/rate/rate.wxs +21 -0
  85. package/miniprogram_dist/rate/rate.wxss +126 -0
  86. package/miniprogram_dist/stepper/stepper.js +153 -0
  87. package/miniprogram_dist/stepper/stepper.json +5 -0
  88. package/miniprogram_dist/stepper/stepper.wxml +19 -0
  89. package/miniprogram_dist/stepper/stepper.wxss +66 -0
  90. package/miniprogram_dist/switch/switch.js +29 -0
  91. package/miniprogram_dist/switch/switch.json +3 -0
  92. package/miniprogram_dist/switch/switch.wxml +10 -0
  93. package/miniprogram_dist/switch/switch.wxss +71 -0
  94. package/miniprogram_dist/textarea/textarea.js +83 -0
  95. package/miniprogram_dist/textarea/textarea.json +5 -0
  96. package/miniprogram_dist/textarea/textarea.wxml +14 -0
  97. package/miniprogram_dist/textarea/textarea.wxss +33 -0
  98. package/miniprogram_dist/theme/theme.wxss +64 -0
  99. package/miniprogram_dist/toast/toast.js +51 -0
  100. package/miniprogram_dist/toast/toast.json +5 -0
  101. package/miniprogram_dist/toast/toast.wxml +13 -0
  102. package/miniprogram_dist/toast/toast.wxss +143 -0
  103. package/miniprogram_dist/upload/upload.js +276 -0
  104. package/miniprogram_dist/upload/upload.json +3 -0
  105. package/miniprogram_dist/upload/upload.wxml +36 -0
  106. package/miniprogram_dist/upload/upload.wxss +136 -0
  107. package/miniprogram_dist/utils/class.js +37 -0
  108. package/miniprogram_dist/utils/device.js +67 -0
  109. package/miniprogram_dist/utils/dom.js +51 -0
  110. package/miniprogram_dist/utils/event.js +28 -0
  111. package/miniprogram_dist/utils/form.js +39 -0
  112. package/miniprogram_dist/utils/function.js +63 -0
  113. package/miniprogram_dist/utils/index.js +21 -0
  114. package/miniprogram_dist/utils/props.js +51 -0
  115. package/miniprogram_dist/utils/style.js +52 -0
  116. package/miniprogram_dist/utils/theme.js +30 -0
  117. package/package.json +31 -0
@@ -0,0 +1,549 @@
1
+ const DAY_MS = 24 * 60 * 60 * 1000;
2
+ const DEFAULT_MONTH_SPAN = 6;
3
+
4
+ const WEEKDAYS = {
5
+ monday: ["一", "二", "三", "四", "五", "六", "日"],
6
+ sunday: ["日", "一", "二", "三", "四", "五", "六"]
7
+ };
8
+
9
+ function pad2(value) {
10
+ return value < 10 ? `0${value}` : `${value}`;
11
+ }
12
+
13
+ function startOfLocalDay(date) {
14
+ return new Date(date.getFullYear(), date.getMonth(), date.getDate());
15
+ }
16
+
17
+ function formatDate(date) {
18
+ return `${date.getFullYear()}-${pad2(date.getMonth() + 1)}-${pad2(date.getDate())}`;
19
+ }
20
+
21
+ function parseLocalDate(value) {
22
+ if (typeof value === "number" && Number.isFinite(value)) {
23
+ const date = new Date(value);
24
+ if (Number.isNaN(date.getTime())) return null;
25
+ return startOfLocalDay(date);
26
+ }
27
+
28
+ if (typeof value !== "string") return null;
29
+ if (!value) return null;
30
+
31
+ const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(value);
32
+ if (!match) return null;
33
+
34
+ const year = Number(match[1]);
35
+ const month = Number(match[2]);
36
+ const day = Number(match[3]);
37
+ const date = new Date(year, month - 1, day);
38
+
39
+ if (
40
+ date.getFullYear() !== year ||
41
+ date.getMonth() !== month - 1 ||
42
+ date.getDate() !== day
43
+ ) {
44
+ return null;
45
+ }
46
+
47
+ return date;
48
+ }
49
+
50
+ function addMonths(date, count) {
51
+ return new Date(date.getFullYear(), date.getMonth() + count, date.getDate());
52
+ }
53
+
54
+ function getMonthStart(date) {
55
+ return new Date(date.getFullYear(), date.getMonth(), 1);
56
+ }
57
+
58
+ function getMonthEnd(date) {
59
+ return new Date(date.getFullYear(), date.getMonth() + 1, 0);
60
+ }
61
+
62
+ function compareMonth(a, b) {
63
+ const left = a.getFullYear() * 12 + a.getMonth();
64
+ const right = b.getFullYear() * 12 + b.getMonth();
65
+ return left === right ? 0 : left > right ? 1 : -1;
66
+ }
67
+
68
+ function normalizeMode(mode) {
69
+ return mode === "multiple" ? "multiple" : "single";
70
+ }
71
+
72
+ function normalizeTagMode(mode) {
73
+ return mode === "single" ? "single" : "multiple";
74
+ }
75
+
76
+ function normalizeWeekStart(weekStart) {
77
+ return weekStart === "sunday" ? "sunday" : "monday";
78
+ }
79
+
80
+ function normalizeCount(value) {
81
+ const count = Number(value);
82
+ if (!Number.isFinite(count) || count <= 0) return 0;
83
+ return Math.floor(count);
84
+ }
85
+
86
+ function normalizeValueArray(value) {
87
+ const source = Array.isArray(value) ? value : [];
88
+ const result = [];
89
+ const cache = {};
90
+
91
+ source.forEach((item) => {
92
+ const date = parseLocalDate(item);
93
+ if (!date) return;
94
+
95
+ const key = formatDate(date);
96
+ if (cache[key]) return;
97
+
98
+ cache[key] = true;
99
+ result.push(key);
100
+ });
101
+
102
+ return result;
103
+ }
104
+
105
+ function normalizeTagValue(value) {
106
+ const source = Array.isArray(value) ? value : [];
107
+ const result = [];
108
+ const cache = {};
109
+
110
+ source.forEach((item) => {
111
+ if (item === null || item === undefined) return;
112
+
113
+ const key = String(item);
114
+ if (cache[key]) return;
115
+
116
+ cache[key] = true;
117
+ result.push(key);
118
+ });
119
+
120
+ return result;
121
+ }
122
+
123
+ function toSet(values) {
124
+ const set = {};
125
+ values.forEach((value) => {
126
+ set[value] = true;
127
+ });
128
+ return set;
129
+ }
130
+
131
+ function formatPrice(price) {
132
+ const value = Number(price);
133
+ if (!Number.isFinite(value)) return "";
134
+
135
+ const yuan = value / 100;
136
+ if (value % 100 === 0) return `¥${yuan}`;
137
+
138
+ return `¥${yuan.toFixed(2).replace(/0+$/, "").replace(/\.$/, "")}`;
139
+ }
140
+
141
+ function hasValue(value) {
142
+ return value !== null && value !== undefined && value !== "";
143
+ }
144
+
145
+ function getDateSubText(item) {
146
+ if (!item) return "";
147
+ if (hasValue(item.priceText)) return String(item.priceText);
148
+ if (hasValue(item.price)) return formatPrice(item.price);
149
+ if (hasValue(item.text)) return String(item.text);
150
+ return "";
151
+ }
152
+
153
+ function getDateClassName(day) {
154
+ const classList = ["mo-calendar__day"];
155
+
156
+ if (day.empty) classList.push("is-empty");
157
+ if (day.disabled) classList.push("is-disabled");
158
+ if (day.selected) classList.push("is-selected");
159
+ if (day.today) classList.push("is-today");
160
+
161
+ return classList.join(" ");
162
+ }
163
+
164
+ function getDateContentClassName(day) {
165
+ const classList = ["mo-calendar__day-content"];
166
+
167
+ if (day.disabled) classList.push("is-disabled");
168
+ if (day.selected) classList.push("is-selected");
169
+ if (day.today) classList.push("is-today");
170
+
171
+ return classList.join(" ");
172
+ }
173
+
174
+ function getTagClassName(option) {
175
+ const classList = ["mo-calendar__tag"];
176
+
177
+ if (option.selected) classList.push("is-selected");
178
+ if (option.disabled) classList.push("is-disabled");
179
+
180
+ return classList.join(" ");
181
+ }
182
+
183
+ function getRangeDefaults(minDate, maxDate) {
184
+ const today = startOfLocalDay(new Date());
185
+ const parsedMin = parseLocalDate(minDate);
186
+ const parsedMax = parseLocalDate(maxDate);
187
+
188
+ const min = parsedMin || today;
189
+ let max = parsedMax || addMonths(today, DEFAULT_MONTH_SPAN);
190
+
191
+ if (max.getTime() < min.getTime()) {
192
+ max = min;
193
+ }
194
+
195
+ return {
196
+ min,
197
+ max,
198
+ invalidMin: hasValue(minDate) && !parsedMin,
199
+ invalidMax: hasValue(maxDate) && !parsedMax,
200
+ invalidRange: parsedMin && parsedMax && parsedMax.getTime() < parsedMin.getTime()
201
+ };
202
+ }
203
+
204
+ Component({
205
+ properties: {
206
+ show: {
207
+ type: Boolean,
208
+ value: false
209
+ },
210
+ title: {
211
+ type: String,
212
+ value: "选择服务日期"
213
+ },
214
+ value: {
215
+ type: Array,
216
+ value: [],
217
+ observer: "updateCalendarData"
218
+ },
219
+ mode: {
220
+ type: String,
221
+ value: "single",
222
+ observer: "updateCalendarData"
223
+ },
224
+ maxDateCount: {
225
+ type: Number,
226
+ value: 0
227
+ },
228
+ minDate: {
229
+ type: null,
230
+ value: "",
231
+ observer: "updateCalendarData"
232
+ },
233
+ maxDate: {
234
+ type: null,
235
+ value: "",
236
+ observer: "updateCalendarData"
237
+ },
238
+ weekStart: {
239
+ type: String,
240
+ value: "monday",
241
+ observer: "updateCalendarData"
242
+ },
243
+ dateItems: {
244
+ type: Array,
245
+ value: [],
246
+ observer: "updateCalendarData"
247
+ },
248
+ tagTitle: {
249
+ type: String,
250
+ value: "请选择服务时间(最多2次)"
251
+ },
252
+ tagOptions: {
253
+ type: Array,
254
+ value: [],
255
+ observer: "updateCalendarData"
256
+ },
257
+ tagValue: {
258
+ type: Array,
259
+ value: [],
260
+ observer: "updateCalendarData"
261
+ },
262
+ tagMode: {
263
+ type: String,
264
+ value: "multiple",
265
+ observer: "updateCalendarData"
266
+ },
267
+ tagMaxCount: {
268
+ type: Number,
269
+ value: 2
270
+ },
271
+ confirmText: {
272
+ type: String,
273
+ value: "确认"
274
+ },
275
+ closeOnMask: {
276
+ type: Boolean,
277
+ value: true
278
+ },
279
+ zIndex: {
280
+ type: Number,
281
+ value: 999
282
+ },
283
+ customClass: {
284
+ type: String,
285
+ value: ""
286
+ }
287
+ },
288
+ data: {
289
+ weekdays: WEEKDAYS.monday,
290
+ months: [],
291
+ normalizedValue: [],
292
+ normalizedTagValue: [],
293
+ normalizedTagOptions: [],
294
+ hasTags: false
295
+ },
296
+ lifetimes: {
297
+ attached() {
298
+ this.updateCalendarData();
299
+ }
300
+ },
301
+ methods: {
302
+ updateCalendarData() {
303
+ const range = getRangeDefaults(this.data.minDate, this.data.maxDate);
304
+ const weekStart = normalizeWeekStart(this.data.weekStart);
305
+ const value = normalizeValueArray(this.data.value);
306
+ const tagValue = normalizeTagValue(this.data.tagValue);
307
+ const dateItemMap = this.getDateItemMap();
308
+ const months = this.createMonths(range.min, range.max, weekStart, dateItemMap, value);
309
+ const normalizedTagOptions = this.createTagOptions(tagValue);
310
+
311
+ if (range.invalidMin) {
312
+ this.emitError("date", "minDate 必须是 YYYY-MM-DD 或毫秒时间戳");
313
+ }
314
+
315
+ if (range.invalidMax) {
316
+ this.emitError("date", "maxDate 必须是 YYYY-MM-DD 或毫秒时间戳");
317
+ }
318
+
319
+ if (range.invalidRange) {
320
+ this.emitError("date", "maxDate 不能早于 minDate");
321
+ }
322
+
323
+ this.setData({
324
+ weekdays: WEEKDAYS[weekStart],
325
+ months,
326
+ normalizedValue: value,
327
+ normalizedTagValue: tagValue,
328
+ normalizedTagOptions,
329
+ hasTags: normalizedTagOptions.length > 0
330
+ });
331
+ },
332
+ getDateItemMap() {
333
+ const source = Array.isArray(this.data.dateItems) ? this.data.dateItems : [];
334
+ const map = {};
335
+
336
+ source.forEach((item) => {
337
+ if (!item || !hasValue(item.date)) return;
338
+
339
+ const date = parseLocalDate(item.date);
340
+ if (!date) {
341
+ this.emitError("dateItems", "dateItems 中的 date 必须是 YYYY-MM-DD 或毫秒时间戳");
342
+ return;
343
+ }
344
+
345
+ map[formatDate(date)] = item;
346
+ });
347
+
348
+ return map;
349
+ },
350
+ createMonths(minDate, maxDate, weekStart, dateItemMap, value) {
351
+ const months = [];
352
+ const selectedSet = toSet(value);
353
+ const todayKey = formatDate(new Date());
354
+ const minTime = minDate.getTime();
355
+ const maxTime = maxDate.getTime();
356
+ let cursor = getMonthStart(minDate);
357
+ const endMonth = getMonthStart(maxDate);
358
+
359
+ while (compareMonth(cursor, endMonth) <= 0) {
360
+ months.push(this.createMonth(cursor, minTime, maxTime, weekStart, dateItemMap, selectedSet, todayKey));
361
+ cursor = new Date(cursor.getFullYear(), cursor.getMonth() + 1, 1);
362
+ }
363
+
364
+ return months;
365
+ },
366
+ createMonth(monthDate, minTime, maxTime, weekStart, dateItemMap, selectedSet, todayKey) {
367
+ const days = [];
368
+ const firstDay = getMonthStart(monthDate);
369
+ const lastDay = getMonthEnd(monthDate);
370
+ const offset = weekStart === "monday" ? (firstDay.getDay() + 6) % 7 : firstDay.getDay();
371
+ const monthKey = `${monthDate.getFullYear()}-${pad2(monthDate.getMonth() + 1)}`;
372
+
373
+ for (let index = 0; index < offset; index += 1) {
374
+ days.push({
375
+ key: `${monthKey}-empty-before-${index}`,
376
+ empty: true,
377
+ className: "mo-calendar__day is-empty"
378
+ });
379
+ }
380
+
381
+ for (let day = 1; day <= lastDay.getDate(); day += 1) {
382
+ const date = new Date(monthDate.getFullYear(), monthDate.getMonth(), day);
383
+ const dateKey = formatDate(date);
384
+ const sourceItem = dateItemMap[dateKey] || {};
385
+ const disabled = date.getTime() < minTime || date.getTime() > maxTime || !!sourceItem.disabled;
386
+ const tagText = hasValue(sourceItem.tagText) ? String(sourceItem.tagText) : "";
387
+ const dayItem = {
388
+ ...sourceItem,
389
+ key: dateKey,
390
+ date: dateKey,
391
+ day,
392
+ empty: false,
393
+ disabled,
394
+ selected: !!selectedSet[dateKey],
395
+ today: dateKey === todayKey,
396
+ tagText,
397
+ subText: getDateSubText(sourceItem)
398
+ };
399
+
400
+ dayItem.className = getDateClassName(dayItem);
401
+ dayItem.contentClassName = getDateContentClassName(dayItem);
402
+ days.push(dayItem);
403
+ }
404
+
405
+ while (days.length % 7 !== 0) {
406
+ days.push({
407
+ key: `${monthKey}-empty-after-${days.length}`,
408
+ empty: true,
409
+ className: "mo-calendar__day is-empty"
410
+ });
411
+ }
412
+
413
+ return {
414
+ key: monthKey,
415
+ title: `${monthDate.getFullYear()}年${monthDate.getMonth() + 1}月`,
416
+ days
417
+ };
418
+ },
419
+ createTagOptions(tagValue) {
420
+ const source = Array.isArray(this.data.tagOptions) ? this.data.tagOptions : [];
421
+ const selectedSet = toSet(tagValue);
422
+
423
+ return source
424
+ .filter((option) => option && (hasValue(option.value) || hasValue(option.label)))
425
+ .map((option) => {
426
+ const value = hasValue(option.value) ? String(option.value) : String(option.label);
427
+ const normalized = {
428
+ ...option,
429
+ value,
430
+ label: hasValue(option.label) ? String(option.label) : value,
431
+ disabled: !!option.disabled,
432
+ selected: !!selectedSet[value]
433
+ };
434
+
435
+ normalized.className = getTagClassName(normalized);
436
+ return normalized;
437
+ });
438
+ },
439
+ getDayFromEvent(event) {
440
+ const monthIndex = Number(event.currentTarget.dataset.monthIndex);
441
+ const dayIndex = Number(event.currentTarget.dataset.dayIndex);
442
+ const month = this.data.months[monthIndex];
443
+
444
+ if (!month || !month.days) return null;
445
+ return month.days[dayIndex] || null;
446
+ },
447
+ handleDayTap(event) {
448
+ const day = this.getDayFromEvent(event);
449
+ if (!day || day.empty) return;
450
+
451
+ this.triggerEvent("click", {
452
+ date: day.date,
453
+ item: day,
454
+ disabled: !!day.disabled
455
+ });
456
+
457
+ if (day.disabled) return;
458
+
459
+ const mode = normalizeMode(this.data.mode);
460
+ const current = normalizeValueArray(this.data.value);
461
+ let nextValue = [];
462
+
463
+ if (mode === "single") {
464
+ nextValue = [day.date];
465
+ } else {
466
+ const exists = current.includes(day.date);
467
+ nextValue = exists ? current.filter((date) => date !== day.date) : current.concat(day.date);
468
+
469
+ const max = normalizeCount(this.data.maxDateCount);
470
+ if (!exists && max > 0 && current.length >= max) {
471
+ this.emitError("date-limit", `最多可选择${max}个日期`, null, true);
472
+ return;
473
+ }
474
+ }
475
+
476
+ this.triggerEvent("change", {
477
+ value: nextValue,
478
+ date: day.date,
479
+ item: day
480
+ });
481
+ },
482
+ handleTagTap(event) {
483
+ const index = Number(event.currentTarget.dataset.index);
484
+ const option = this.data.normalizedTagOptions[index];
485
+ if (!option || option.disabled) return;
486
+
487
+ const mode = normalizeTagMode(this.data.tagMode);
488
+ const current = normalizeTagValue(this.data.tagValue);
489
+ let nextValue = [];
490
+
491
+ if (mode === "single") {
492
+ nextValue = [option.value];
493
+ } else {
494
+ const exists = current.includes(option.value);
495
+ nextValue = exists ? current.filter((value) => value !== option.value) : current.concat(option.value);
496
+
497
+ const max = normalizeCount(this.data.tagMaxCount);
498
+ if (!exists && max > 0 && current.length >= max) {
499
+ this.emitError("tag-limit", `最多可选择${max}个选项`, null, true);
500
+ return;
501
+ }
502
+ }
503
+
504
+ this.triggerEvent("tagchange", {
505
+ value: nextValue,
506
+ option
507
+ });
508
+ },
509
+ handleConfirm() {
510
+ const value = normalizeValueArray(this.data.value);
511
+ const tagValue = normalizeTagValue(this.data.tagValue);
512
+ const dateItemMap = this.getDateItemMap();
513
+ const tagMap = {};
514
+
515
+ this.data.normalizedTagOptions.forEach((option) => {
516
+ tagMap[option.value] = option;
517
+ });
518
+
519
+ this.triggerEvent("confirm", {
520
+ value,
521
+ tagValue,
522
+ dates: value.map((date) => ({
523
+ ...(dateItemMap[date] || {}),
524
+ date
525
+ })),
526
+ tags: tagValue.map((valueItem) => tagMap[valueItem]).filter(Boolean)
527
+ });
528
+ },
529
+ handleClose(event) {
530
+ this.triggerEvent("close", event.detail || {});
531
+ },
532
+ handleCloseTap() {
533
+ this.triggerEvent("close", {
534
+ source: "close"
535
+ });
536
+ },
537
+ emitError(source, message, error, force) {
538
+ const key = `${source}:${message}`;
539
+ if (!force && this._lastErrorKey === key) return;
540
+
541
+ this._lastErrorKey = key;
542
+ this.triggerEvent("error", {
543
+ source,
544
+ message,
545
+ error
546
+ });
547
+ }
548
+ }
549
+ });
@@ -0,0 +1,8 @@
1
+ {
2
+ "component": true,
3
+ "styleIsolation": "apply-shared",
4
+ "usingComponents": {
5
+ "mo-popup-page": "../popup-page/popup-page",
6
+ "mo-icon": "../icon/icon"
7
+ }
8
+ }
@@ -0,0 +1,79 @@
1
+ <mo-popup-page
2
+ show="{{show}}"
3
+ zIndex="{{zIndex}}"
4
+ closeOnMask="{{closeOnMask}}"
5
+ show-handle="{{false}}"
6
+ content-scrollable="{{false}}"
7
+ footer-mode="confirm"
8
+ confirmText="{{confirmText}}"
9
+ customClass="mo-calendar__popup mo-popup-page--content-flush"
10
+ bind:close="handleClose"
11
+ bind:confirm="handleConfirm"
12
+ >
13
+ <view class="mo-calendar {{customClass}}">
14
+ <view class="mo-calendar__header">
15
+ <view class="mo-calendar__header-side"></view>
16
+ <text class="mo-calendar__title">{{title}}</text>
17
+ <view class="mo-calendar__close" catchtap="handleCloseTap">
18
+ <mo-icon
19
+ name="close"
20
+ size="{{36}}"
21
+ color="#999999"
22
+ />
23
+ </view>
24
+ </view>
25
+
26
+ <view class="mo-calendar__week">
27
+ <text wx:for="{{weekdays}}" wx:key="*this" class="mo-calendar__week-text">{{item}}</text>
28
+ </view>
29
+
30
+ <scroll-view class="mo-calendar__month-scroll" scroll-y type="list">
31
+ <view class="mo-calendar__months">
32
+ <block wx:for="{{months}}" wx:for-item="month" wx:for-index="monthIndex" wx:key="key">
33
+ <view class="mo-calendar__month">
34
+ <view class="mo-calendar__month-header">
35
+ <text class="mo-calendar__month-title">{{month.title}}</text>
36
+ </view>
37
+ <view class="mo-calendar__days">
38
+ <view
39
+ wx:for="{{month.days}}"
40
+ wx:for-item="day"
41
+ wx:for-index="dayIndex"
42
+ wx:key="key"
43
+ class="{{day.className}}"
44
+ data-month-index="{{monthIndex}}"
45
+ data-day-index="{{dayIndex}}"
46
+ bindtap="handleDayTap"
47
+ >
48
+ <block wx:if="{{!day.empty}}">
49
+ <view class="{{day.contentClassName}}">
50
+ <text class="mo-calendar__day-number">{{day.day}}</text>
51
+ <text wx:if="{{day.subText}}" class="mo-calendar__day-sub">{{day.subText}}</text>
52
+ </view>
53
+ <text wx:if="{{day.tagText}}" class="mo-calendar__day-badge">{{day.tagText}}</text>
54
+ </block>
55
+ </view>
56
+ </view>
57
+ </view>
58
+ </block>
59
+ </view>
60
+ </scroll-view>
61
+
62
+ <view wx:if="{{hasTags}}" class="mo-calendar__tags">
63
+ <text class="mo-calendar__tag-title">{{tagTitle}}</text>
64
+ <scroll-view class="mo-calendar__tag-scroll" type="list" scroll-x enable-flex style="flex-direction: row;">
65
+ <view class="mo-calendar__tag-list">
66
+ <view
67
+ wx:for="{{normalizedTagOptions}}"
68
+ wx:key="value"
69
+ class="{{item.className}}"
70
+ data-index="{{index}}"
71
+ bindtap="handleTagTap"
72
+ >
73
+ <text class="mo-calendar__tag-label">{{item.label}}</text>
74
+ </view>
75
+ </view>
76
+ </scroll-view>
77
+ </view>
78
+ </view>
79
+ </mo-popup-page>