@pisell/utils 3.0.5 → 3.0.7

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 (157) hide show
  1. package/es/arrayUtils.d.ts +72 -0
  2. package/es/arrayUtils.js +142 -3
  3. package/es/common/index.d.ts +5 -0
  4. package/es/common/index.js +5 -0
  5. package/es/common/stateHelper.d.ts +18 -0
  6. package/es/common/stateHelper.js +25 -0
  7. package/es/common/types.d.ts +28 -0
  8. package/es/common/types.js +1 -0
  9. package/es/componentStorage.d.ts +36 -0
  10. package/es/componentStorage.js +142 -0
  11. package/es/format.d.ts +5 -1
  12. package/es/format.js +53 -8
  13. package/es/index.d.ts +6 -0
  14. package/es/index.js +6 -0
  15. package/es/jsBridge/index.d.ts +9 -105
  16. package/es/jsBridge/index.js +28 -12
  17. package/es/jsBridge/types.d.ts +149 -0
  18. package/es/jsBridge/types.js +1 -0
  19. package/es/locales.d.ts +2 -5
  20. package/es/locales.js +36 -7
  21. package/es/miniRedux.js +7 -2
  22. package/es/number-input/hooks/index.d.ts +4 -0
  23. package/es/number-input/hooks/index.js +5 -0
  24. package/es/number-input/hooks/useNumberInputState.d.ts +25 -0
  25. package/es/number-input/hooks/useNumberInputState.js +129 -0
  26. package/es/number-input/index.d.ts +14 -0
  27. package/es/number-input/index.js +21 -0
  28. package/es/number-input/types.d.ts +96 -0
  29. package/es/number-input/types.js +1 -0
  30. package/es/number-input/utils/formatter.d.ts +53 -0
  31. package/es/number-input/utils/formatter.js +82 -0
  32. package/es/number-input/utils/index.d.ts +7 -0
  33. package/es/number-input/utils/index.js +8 -0
  34. package/es/number-input/utils/validation.d.ts +27 -0
  35. package/es/number-input/utils/validation.js +67 -0
  36. package/es/otherUtils.d.ts +48 -0
  37. package/es/select/hooks/index.d.ts +5 -0
  38. package/es/select/hooks/index.js +6 -0
  39. package/es/select/hooks/useDebouncedSearch.d.ts +20 -0
  40. package/es/select/hooks/useDebouncedSearch.js +55 -0
  41. package/es/select/hooks/useSelectState.d.ts +19 -0
  42. package/es/select/hooks/useSelectState.js +117 -0
  43. package/es/select/index.d.ts +8 -0
  44. package/es/select/index.js +13 -0
  45. package/es/select/types.d.ts +256 -0
  46. package/es/select/types.js +1 -0
  47. package/es/select/utils/filterOptions.d.ts +22 -0
  48. package/es/select/utils/filterOptions.js +34 -0
  49. package/es/select/utils/helpers.d.ts +42 -0
  50. package/es/select/utils/helpers.js +61 -0
  51. package/es/select/utils/index.d.ts +7 -0
  52. package/es/select/utils/index.js +8 -0
  53. package/es/select/utils/sortOptions.d.ts +12 -0
  54. package/es/select/utils/sortOptions.js +32 -0
  55. package/es/select/utils/tagAggregation.d.ts +25 -0
  56. package/es/select/utils/tagAggregation.js +48 -0
  57. package/es/text-input/hooks/index.d.ts +4 -0
  58. package/es/text-input/hooks/index.js +5 -0
  59. package/es/text-input/hooks/useTextInputState.d.ts +24 -0
  60. package/es/text-input/hooks/useTextInputState.js +127 -0
  61. package/es/text-input/index.d.ts +14 -0
  62. package/es/text-input/index.js +20 -0
  63. package/es/text-input/types.d.ts +56 -0
  64. package/es/text-input/types.js +1 -0
  65. package/es/text-input/utils/index.d.ts +5 -0
  66. package/es/text-input/utils/index.js +7 -0
  67. package/es/text-input/utils/validation.d.ts +23 -0
  68. package/es/text-input/utils/validation.js +71 -0
  69. package/es/walletValidity.d.ts +55 -0
  70. package/es/walletValidity.js +143 -38
  71. package/es/weighing.d.ts +62 -0
  72. package/es/weighing.js +119 -0
  73. package/lib/arrayUtils.d.ts +72 -0
  74. package/lib/arrayUtils.js +75 -0
  75. package/lib/common/index.d.ts +5 -0
  76. package/lib/common/index.js +29 -0
  77. package/lib/common/stateHelper.d.ts +18 -0
  78. package/lib/common/stateHelper.js +34 -0
  79. package/lib/common/types.d.ts +28 -0
  80. package/lib/common/types.js +17 -0
  81. package/lib/componentStorage.d.ts +36 -0
  82. package/lib/componentStorage.js +144 -0
  83. package/lib/format.d.ts +5 -1
  84. package/lib/format.js +45 -5
  85. package/lib/index.d.ts +6 -0
  86. package/lib/index.js +13 -1
  87. package/lib/jsBridge/index.d.ts +9 -105
  88. package/lib/jsBridge/index.js +20 -1
  89. package/lib/jsBridge/types.d.ts +149 -0
  90. package/lib/jsBridge/types.js +17 -0
  91. package/lib/locales.d.ts +2 -5
  92. package/lib/locales.js +34 -7
  93. package/lib/miniRedux.js +5 -7
  94. package/lib/number-input/hooks/index.d.ts +4 -0
  95. package/lib/number-input/hooks/index.js +29 -0
  96. package/lib/number-input/hooks/useNumberInputState.d.ts +25 -0
  97. package/lib/number-input/hooks/useNumberInputState.js +83 -0
  98. package/lib/number-input/index.d.ts +14 -0
  99. package/lib/number-input/index.js +41 -0
  100. package/lib/number-input/types.d.ts +96 -0
  101. package/lib/number-input/types.js +17 -0
  102. package/lib/number-input/utils/formatter.d.ts +53 -0
  103. package/lib/number-input/utils/formatter.js +55 -0
  104. package/lib/number-input/utils/index.d.ts +7 -0
  105. package/lib/number-input/utils/index.js +39 -0
  106. package/lib/number-input/utils/validation.d.ts +27 -0
  107. package/lib/number-input/utils/validation.js +54 -0
  108. package/lib/otherUtils.d.ts +48 -0
  109. package/lib/select/hooks/index.d.ts +5 -0
  110. package/lib/select/hooks/index.js +34 -0
  111. package/lib/select/hooks/useDebouncedSearch.d.ts +20 -0
  112. package/lib/select/hooks/useDebouncedSearch.js +43 -0
  113. package/lib/select/hooks/useSelectState.d.ts +19 -0
  114. package/lib/select/hooks/useSelectState.js +97 -0
  115. package/lib/select/index.d.ts +8 -0
  116. package/lib/select/index.js +53 -0
  117. package/lib/select/types.d.ts +256 -0
  118. package/lib/select/types.js +17 -0
  119. package/lib/select/utils/filterOptions.d.ts +22 -0
  120. package/lib/select/utils/filterOptions.js +41 -0
  121. package/lib/select/utils/helpers.d.ts +42 -0
  122. package/lib/select/utils/helpers.js +51 -0
  123. package/lib/select/utils/index.d.ts +7 -0
  124. package/lib/select/utils/index.js +48 -0
  125. package/lib/select/utils/sortOptions.d.ts +12 -0
  126. package/lib/select/utils/sortOptions.js +41 -0
  127. package/lib/select/utils/tagAggregation.d.ts +25 -0
  128. package/lib/select/utils/tagAggregation.js +43 -0
  129. package/lib/text-input/hooks/index.d.ts +4 -0
  130. package/lib/text-input/hooks/index.js +29 -0
  131. package/lib/text-input/hooks/useTextInputState.d.ts +24 -0
  132. package/lib/text-input/hooks/useTextInputState.js +81 -0
  133. package/lib/text-input/index.d.ts +14 -0
  134. package/lib/text-input/index.js +35 -0
  135. package/lib/text-input/types.d.ts +56 -0
  136. package/lib/text-input/types.js +17 -0
  137. package/lib/text-input/utils/index.d.ts +5 -0
  138. package/lib/text-input/utils/index.js +32 -0
  139. package/lib/text-input/utils/validation.d.ts +23 -0
  140. package/lib/text-input/utils/validation.js +60 -0
  141. package/lib/walletValidity.d.ts +55 -0
  142. package/lib/walletValidity.js +120 -40
  143. package/lib/weighing.d.ts +62 -0
  144. package/lib/weighing.js +159 -0
  145. package/package.json +1 -1
  146. package/es/date.d.ts +0 -28
  147. package/es/document.d.ts +0 -13
  148. package/es/firebase/config/index.d.ts +0 -13
  149. package/es/firebase/index.d.ts +0 -28
  150. package/es/image.d.ts +0 -11
  151. package/es/typeUtils.d.ts +0 -42
  152. package/lib/date.d.ts +0 -28
  153. package/lib/document.d.ts +0 -13
  154. package/lib/firebase/config/index.d.ts +0 -13
  155. package/lib/firebase/index.d.ts +0 -28
  156. package/lib/image.d.ts +0 -11
  157. package/lib/typeUtils.d.ts +0 -42
@@ -1,3 +1,15 @@
1
+ function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
2
+ function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
3
+ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
4
+ function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
5
+ function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : String(i); }
6
+ function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
7
+ function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); }
8
+ function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
9
+ function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
10
+ function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); }
11
+ function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); }
12
+ function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
1
13
  import dayjs from 'dayjs';
2
14
 
3
15
  // 文案配置类型定义
@@ -6,12 +18,35 @@ import dayjs from 'dayjs';
6
18
  var CONSTANTS = {
7
19
  ALL_DAY_START: '00:00',
8
20
  ALL_DAY_END: '23:59',
9
- DEFAULT_FUTURE_YEARS: 10,
10
21
  SECONDS_SUFFIX: ':59'
11
22
  };
12
23
 
13
24
  // 日程项接口定义
14
25
 
26
+ // API 日期使用店铺的日历日期;不要把已格式化的 DD/MM/YYYY 文案重新解析为日期。
27
+ function getValidityDate(value) {
28
+ if (typeof value !== 'string') return null;
29
+ var match = value.match(/^(\d{4}-\d{2}-\d{2})(?:$|[ T])/);
30
+ if (!match) return null;
31
+ var date = dayjs(match[1]);
32
+ return date.isValid() && date.format('YYYY-MM-DD') === match[1] ? match[1] : null;
33
+ }
34
+
35
+ /** 仅计算展示日期的交集,不修改日程的重复起点或每日使用时段。 */
36
+ export function getWalletValidityRange(start, end, validity) {
37
+ var scheduleStart = getValidityDate(start);
38
+ var scheduleEnd = getValidityDate(end);
39
+ var passStart = getValidityDate(validity === null || validity === void 0 ? void 0 : validity.start_date);
40
+ var passEnd = getValidityDate(validity === null || validity === void 0 ? void 0 : validity.expire_date) || getValidityDate(validity === null || validity === void 0 ? void 0 : validity.expire_time);
41
+ var startDate = passStart && (!scheduleStart || passStart > scheduleStart) ? passStart : scheduleStart;
42
+ var endDate = passEnd && (!scheduleEnd || passEnd < scheduleEnd) ? passEnd : scheduleEnd;
43
+ return {
44
+ startDate: startDate,
45
+ endDate: endDate,
46
+ isEmpty: Boolean(startDate && endDate && startDate > endDate)
47
+ };
48
+ }
49
+
15
50
  // 多语言文案配置
16
51
  var textConfig = {
17
52
  'zh-HK': {
@@ -28,6 +63,7 @@ var textConfig = {
28
63
  year: '年',
29
64
  available: '可用',
30
65
  unlimited: '无限期',
66
+ noAvailableDates: '無可用日期',
31
67
  multipleRules: '多規則,點詳情',
32
68
  multipleSlots: ',多時段',
33
69
  until: '至',
@@ -56,6 +92,7 @@ var textConfig = {
56
92
  year: '年',
57
93
  available: '可用',
58
94
  unlimited: '无限期',
95
+ noAvailableDates: '无可用日期',
59
96
  multipleRules: '多规则,点详情',
60
97
  multipleSlots: ',多时段',
61
98
  until: '至',
@@ -84,6 +121,7 @@ var textConfig = {
84
121
  year: 'year',
85
122
  available: 'available',
86
123
  unlimited: 'unlimited',
124
+ noAvailableDates: 'No available dates',
87
125
  multipleRules: 'Multiple rules, see details',
88
126
  multipleSlots: ', multiple slots',
89
127
  until: 'Until ',
@@ -109,7 +147,9 @@ var textConfig = {
109
147
  */
110
148
  function getWeekdayText(dayNumbers, locale, separator) {
111
149
  var config = textConfig[locale];
112
- return dayNumbers.map(function (day) {
150
+ return _toConsumableArray(new Set(dayNumbers)).sort(function (a, b) {
151
+ return a - b;
152
+ }).map(function (day) {
113
153
  return config.weekdays[day];
114
154
  }).join(separator || config.separator);
115
155
  }
@@ -211,13 +251,20 @@ function isAllDaySchedule(startTime, endTime) {
211
251
  */
212
252
  function formatDateRange(startDate, endDate, isStarted, locale) {
213
253
  var isAllDay = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false;
254
+ var validity = arguments.length > 5 ? arguments[5] : undefined;
214
255
  var config = getConfig(locale);
215
- if (isStarted) {
216
- var endDateStr = formatDateTime(endDate, isAllDay, locale);
256
+ var range = getWalletValidityRange(startDate, endDate, validity);
257
+ if (range.isEmpty) return config.noAvailableDates;
258
+ var effectiveStart = range.startDate === getValidityDate(startDate) ? startDate : "".concat(range.startDate, " 00:00:00");
259
+ var effectiveEnd = range.endDate === getValidityDate(endDate) ? endDate : "".concat(range.endDate, " 23:59:59");
260
+ if (!range.endDate) return config.unlimited;
261
+ var hasStarted = range.startDate ? dayjs(effectiveStart).isBefore(dayjs()) : isStarted;
262
+ if (hasStarted || !range.startDate) {
263
+ var endDateStr = formatDateTime(effectiveEnd, isAllDay, locale);
217
264
  return "".concat(config.until).concat(endDateStr);
218
265
  } else {
219
- var startDateStr = formatDateTime(startDate, isAllDay, locale);
220
- var _endDateStr = formatDateTime(endDate, isAllDay, locale);
266
+ var startDateStr = formatDateTime(effectiveStart, isAllDay, locale);
267
+ var _endDateStr = formatDateTime(effectiveEnd, isAllDay, locale);
221
268
  return "".concat(startDateStr).concat(config.rangeSeparator).concat(_endDateStr);
222
269
  }
223
270
  }
@@ -230,15 +277,27 @@ function formatDateRange(startDate, endDate, isStarted, locale) {
230
277
  */
231
278
  export function getScheduleSummary(scheduleList) {
232
279
  var locale = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'zh-CN';
280
+ var validity = arguments.length > 2 ? arguments[2] : undefined;
233
281
  try {
282
+ locale = textConfig[locale] ? locale : 'en';
234
283
  // 输入验证
235
284
  if (!scheduleList || !Array.isArray(scheduleList)) {
236
285
  return '';
237
286
  }
238
287
 
239
- // 如果是多个日程,直接返回多规则提示
288
+ // 多规则摘要仍保留卡的到期边界。
240
289
  if (scheduleList.length > 1) {
241
- return getText(locale, 'multipleRules');
290
+ var range = getWalletValidityRange(null, null, validity);
291
+ if (!range.startDate && !range.endDate) return getText(locale, 'multipleRules');
292
+ var summaries = scheduleList.map(function (schedule) {
293
+ return getScheduleSummary([schedule], locale, validity);
294
+ }).filter(function (summary) {
295
+ return summary !== getText(locale, 'noAvailableDates');
296
+ });
297
+ if (summaries.length === 0) return getText(locale, 'noAvailableDates');
298
+ if (summaries.length === 1 && summaries[0]) return summaries[0];
299
+ var prefix = range.endDate ? "".concat(formatDateRange(range.startDate || '', range.endDate, false, locale, true)).concat(getText(locale, 'periodSeparator')) : '';
300
+ return "".concat(prefix).concat(getText(locale, 'multipleRules'));
242
301
  }
243
302
 
244
303
  // 如果没有日程,返回空字符串
@@ -262,12 +321,42 @@ export function getScheduleSummary(scheduleList) {
262
321
  startTime = dayjs("".concat(firstDesignation.start_date, " ").concat(firstDesignation.start_time));
263
322
  }
264
323
  }
324
+ var passStart = getValidityDate(validity === null || validity === void 0 ? void 0 : validity.start_date);
325
+ if (passStart && (!startTime || dayjs(passStart).isAfter(startTime))) {
326
+ startTime = dayjs(passStart);
327
+ }
265
328
 
266
329
  // 判断开始时间是否在当前时间之后
267
330
  var isStarted = startTime ? isTimeStarted(now, startTime) : false;
268
331
 
269
332
  // 根据不同类型和状态返回摘要
270
- return generateSummaryText(schedule, isStarted, locale);
333
+ var summary = generateSummaryText(schedule, isStarted, locale, validity);
334
+ // 包含日期可独立于循环日程的日期范围生效。
335
+ if (summary === getText(locale, 'noAvailableDates')) {
336
+ var _schedule$repeat_rule;
337
+ var included = (_schedule$repeat_rule = schedule.repeat_rule) === null || _schedule$repeat_rule === void 0 || (_schedule$repeat_rule = _schedule$repeat_rule.included_date) === null || _schedule$repeat_rule === void 0 ? void 0 : _schedule$repeat_rule.filter(function (item) {
338
+ return !getWalletValidityRange(item.start, item.end, validity).isEmpty;
339
+ });
340
+ if (included !== null && included !== void 0 && included.length) {
341
+ return getScheduleSummary([_objectSpread(_objectSpread({}, schedule), {}, {
342
+ start_time: null,
343
+ end_time: null,
344
+ type: 'designation',
345
+ repeat_type: 'none',
346
+ repeat_rule: null,
347
+ is_all: 1,
348
+ designation: included.map(function (item) {
349
+ return {
350
+ start_date: item.start,
351
+ end_date: item.end,
352
+ start_time: '00:00',
353
+ end_time: '23:59'
354
+ };
355
+ })
356
+ })], locale, validity);
357
+ }
358
+ }
359
+ return summary;
271
360
  } catch (error) {
272
361
  console.warn('Error in getScheduleSummary:', error);
273
362
  return '';
@@ -291,29 +380,45 @@ function isTimeStarted(now, startTime) {
291
380
  * @param locale 语言设置
292
381
  * @returns 生成的摘要文本
293
382
  */
294
- function generateSummaryText(schedule, isStarted, locale) {
383
+ function generateSummaryText(schedule, isStarted, locale, validity) {
295
384
  // 1. 多天多段 (designation类型且有多个日期段)
296
385
  if (schedule.type === 'designation' && schedule.designation && schedule.designation.length > 1) {
297
- return getText(locale, 'multipleRules');
386
+ var passRange = getWalletValidityRange(null, null, validity);
387
+ if (!passRange.startDate && !passRange.endDate) return getText(locale, 'multipleRules');
388
+ var designation = schedule.designation.filter(function (item) {
389
+ return !getWalletValidityRange(item.start_date, item.end_date, validity).isEmpty;
390
+ });
391
+ if (!designation.length) return getText(locale, 'noAvailableDates');
392
+ if (designation.length === 1) {
393
+ return generateSingleScheduleSummary(_objectSpread(_objectSpread({}, schedule), {}, {
394
+ designation: designation
395
+ }), isStarted, locale, validity);
396
+ }
397
+ var range = getWalletValidityRange(designation.map(function (item) {
398
+ return item.start_date;
399
+ }).sort()[0], designation.map(function (item) {
400
+ return item.end_date;
401
+ }).sort().pop(), validity);
402
+ var prefix = range.endDate ? "".concat(formatDateRange(range.startDate || '', range.endDate, isStarted, locale, true)).concat(getText(locale, 'periodSeparator')) : '';
403
+ return "".concat(prefix).concat(getText(locale, 'multipleRules'));
298
404
  }
299
405
 
300
406
  // 2. 单日多段 (time-slots类型)
301
407
  if (schedule.type === 'time-slots') {
302
- var endDate = getScheduleEndDate(schedule);
303
- if (!endDate) return '';
304
- var endDateStr = formatDateTime(endDate, true, locale);
305
- var prefix = '';
408
+ var _range = getWalletValidityRange(schedule.start_time, getScheduleEndDate(schedule), validity);
409
+ if (_range.isEmpty) return getText(locale, 'noAvailableDates');
410
+ var endDateStr = _range.endDate ? formatDateTime(_range.endDate, true, locale) : getText(locale, 'unlimited');
306
411
  var suffix = getText(locale, 'multipleSlots');
307
- return "".concat(prefix).concat(endDateStr).concat(suffix);
412
+ return "".concat(endDateStr).concat(suffix);
308
413
  }
309
414
 
310
415
  // 3. 循环日程
311
416
  if (schedule.repeat_type !== 'none' && schedule.repeat_rule) {
312
- return generateRecurringSummary(schedule, isStarted, locale);
417
+ return generateRecurringSummary(schedule, isStarted, locale, validity);
313
418
  }
314
419
 
315
420
  // 4. 单个日程(包括designation类型的单个日期段)
316
- return generateSingleScheduleSummary(schedule, isStarted, locale);
421
+ return generateSingleScheduleSummary(schedule, isStarted, locale, validity);
317
422
  }
318
423
 
319
424
  /**
@@ -323,29 +428,30 @@ function generateSummaryText(schedule, isStarted, locale) {
323
428
  * @param locale 语言设置
324
429
  * @returns 循环日程摘要文本
325
430
  */
326
- function generateRecurringSummary(schedule, isStarted, locale) {
431
+ function generateRecurringSummary(schedule, isStarted, locale, validity) {
327
432
  var repeat_type = schedule.repeat_type,
328
433
  repeat_rule = schedule.repeat_rule;
329
434
  if (!repeat_rule) return '';
330
435
  var frequency = repeat_rule.frequency,
331
436
  frequency_date = repeat_rule.frequency_date,
332
437
  end = repeat_rule.end;
438
+ var range = getWalletValidityRange(schedule.start_time, getScheduleEndDate(schedule), validity);
439
+ if (range.isEmpty) return getText(locale, 'noAvailableDates');
333
440
 
334
441
  // 特殊处理:当 end.type === 'never' 时的格式
335
- if (end.type === 'never') {
336
- return generateNeverEndingRecurringSummary(schedule, isStarted, locale, repeat_type, frequency, frequency_date);
442
+ if (end.type === 'never' && !range.endDate) {
443
+ return generateNeverEndingRecurringSummary(schedule, isStarted, locale, repeat_type, frequency, frequency_date, validity);
337
444
  }
338
445
 
339
446
  // 原有逻辑:有结束时间的循环日程
340
- var endDate = getScheduleEndDate(schedule);
447
+ var endDate = range.endDate;
341
448
  if (!endDate) return '';
342
- var endDateStr = formatDateTime(endDate, true, locale);
343
449
 
344
450
  // 按天循环
345
451
  if (repeat_type === 'daily') {
346
452
  var frequencyText = getDailyFrequencyText(frequency, locale);
347
- var dateRange = formatDateRange(schedule.start_time, endDate, isStarted, locale, true);
348
- return "".concat(dateRange, "\uFF0C").concat(frequencyText);
453
+ var dateRange = formatDateRange(schedule.start_time, endDate, isStarted, locale, true, validity);
454
+ return "".concat(dateRange).concat(getText(locale, 'timeSlotSeparator')).concat(frequencyText);
349
455
  }
350
456
 
351
457
  // 按周循环
@@ -353,8 +459,8 @@ function generateRecurringSummary(schedule, isStarted, locale) {
353
459
  var weekdaysText = getWeekdayText(frequency_date, locale, '/');
354
460
  var frequencyPrefix = getWeeklyFrequencyPrefix(frequency, locale);
355
461
  var config = getConfig(locale);
356
- var _dateRange = formatDateRange(schedule.start_time, endDate, isStarted, locale, true);
357
- return "".concat(_dateRange, "\uFF0C").concat(frequencyPrefix).concat(config.weekPrefix).concat(weekdaysText);
462
+ var _dateRange = formatDateRange(schedule.start_time, endDate, isStarted, locale, true, validity);
463
+ return "".concat(_dateRange).concat(config.timeSlotSeparator).concat(frequencyPrefix).concat(config.weekPrefix).concat(weekdaysText);
358
464
  }
359
465
  return '';
360
466
  }
@@ -369,15 +475,16 @@ function generateRecurringSummary(schedule, isStarted, locale) {
369
475
  * @param frequencyDate 频率日期数组(用于周循环)
370
476
  * @returns 永不结束的循环日程摘要文本
371
477
  */
372
- function generateNeverEndingRecurringSummary(schedule, isStarted, locale, repeatType, frequency, frequencyDate) {
478
+ function generateNeverEndingRecurringSummary(schedule, isStarted, locale, repeatType, frequency, frequencyDate, validity) {
373
479
  var config = getConfig(locale);
374
480
 
375
481
  // 按天循环
376
482
  if (repeatType === 'daily') {
377
483
  if (frequency === 1) {
378
484
  // 频率=1:中文"自 YYYY-MM-DD 起长期有效",英文"Valid from DD MMM YYYY, no expiry"
379
- if (schedule.start_time) {
380
- var startDateStr = formatDateTime(schedule.start_time, true, locale);
485
+ var range = getWalletValidityRange(schedule.start_time, null, validity);
486
+ if (range.startDate) {
487
+ var startDateStr = formatDateTime(range.startDate, true, locale);
381
488
  return "".concat(config.longTermValidFrom).concat(startDateStr).concat(config.noExpiry);
382
489
  }
383
490
  return '';
@@ -422,7 +529,7 @@ function generateNeverEndingRecurringSummary(schedule, isStarted, locale, repeat
422
529
  * @param locale 语言设置
423
530
  * @returns 单个日程摘要文本
424
531
  */
425
- function generateSingleScheduleSummary(schedule, isStarted, locale) {
532
+ function generateSingleScheduleSummary(schedule, isStarted, locale, validity) {
426
533
  var isAllDay = schedule.is_all === 1;
427
534
 
428
535
  // 处理designation类型的单个日期段
@@ -437,12 +544,12 @@ function generateSingleScheduleSummary(schedule, isStarted, locale) {
437
544
  var isDesignationAllDay = isAllDaySchedule(startTime, endTime);
438
545
  if (isDesignationAllDay || isAllDay) {
439
546
  // 全天日程
440
- return formatDateRange(startDate, endDate, isStarted, locale, true);
547
+ return formatDateRange(startDate, endDate, isStarted, locale, true, validity);
441
548
  } else {
442
549
  // 非全天日程
443
550
  var startDateTime = "".concat(startDate, " ").concat(startTime).concat(CONSTANTS.SECONDS_SUFFIX);
444
551
  var endDateTime = "".concat(endDate, " ").concat(endTime).concat(CONSTANTS.SECONDS_SUFFIX);
445
- return formatDateRange(startDateTime, endDateTime, isStarted, locale, false);
552
+ return formatDateRange(startDateTime, endDateTime, isStarted, locale, false, validity);
446
553
  }
447
554
  }
448
555
 
@@ -451,10 +558,10 @@ function generateSingleScheduleSummary(schedule, isStarted, locale) {
451
558
  // 全天日程
452
559
  var _endDate = getScheduleEndDate(schedule);
453
560
  if (!_endDate) return '';
454
- return formatDateRange(schedule.start_time, _endDate, isStarted, locale, true);
561
+ return formatDateRange(schedule.start_time, _endDate, isStarted, locale, true, validity);
455
562
  } else {
456
563
  // 非全天日程
457
- return formatDateRange(schedule.start_time, schedule.end_time, isStarted, locale, false);
564
+ return formatDateRange(schedule.start_time, schedule.end_time, isStarted, locale, false, validity);
458
565
  }
459
566
  }
460
567
 
@@ -468,9 +575,7 @@ function getScheduleEndDate(schedule) {
468
575
  // 循环日程
469
576
  if (schedule.repeat_rule) {
470
577
  if (schedule.repeat_rule.end.type === 'never') {
471
- // 无限期的情况,返回一个合理的未来日期作为显示
472
- var futureDate = dayjs().add(CONSTANTS.DEFAULT_FUTURE_YEARS, 'year');
473
- return "".concat(futureDate.format('YYYY-MM-DD'), " ").concat(CONSTANTS.ALL_DAY_END).concat(CONSTANTS.SECONDS_SUFFIX);
578
+ return null;
474
579
  } else if (schedule.repeat_rule.end.end_date) {
475
580
  return "".concat(schedule.repeat_rule.end.end_date, " ").concat(CONSTANTS.ALL_DAY_END).concat(CONSTANTS.SECONDS_SUFFIX);
476
581
  }
@@ -0,0 +1,62 @@
1
+ /** 商品配置与 NativeScale 使用的重量单位。 */
2
+ export declare type WeightUnitName = 'KILOGRAMS' | 'GRAMS' | 'POUNDS' | 'OUNCES';
3
+ /** 订单快照与 UI 使用的标准单位符号。 */
4
+ export declare type WeightUnitSymbol = 'kg' | 'g' | 'lb' | 'oz';
5
+ export declare type WeightUnit = WeightUnitName | WeightUnitSymbol;
6
+ /**
7
+ * Pisell 的标准重量单位。目录单价、缺省秤单位和缺省展示均以它为准。
8
+ * 后续标准单位变化时,应优先只修改此处。
9
+ */
10
+ export declare const STANDARD_WEIGHT_UNIT: WeightUnitName;
11
+ export declare const WEIGHT_PRECISION = 3;
12
+ export declare const UNIT_PRICE_PRECISION = 2;
13
+ export declare const MAX_RETAIL_SCALE_WEIGHT_KG = 1000;
14
+ export declare const WEIGHT_UNIT_SYMBOLS: Record<WeightUnitName, WeightUnitSymbol>;
15
+ export declare const WEIGHT_UNIT_NAMES: Record<WeightUnitSymbol, WeightUnitName>;
16
+ /** 单位对应的克数,作为不依赖 NativeScale 的目录价格换算基准。 */
17
+ export declare const WEIGHT_UNIT_GRAMS: Record<WeightUnit, number>;
18
+ export declare const STANDARD_WEIGHT_UNIT_SYMBOL: WeightUnitSymbol;
19
+ export declare const normalizeWeightUnit: (unit?: string | null, fallback?: WeightUnitName) => WeightUnitName;
20
+ export declare const getWeightUnitSymbol: (unit?: string | null) => string;
21
+ export declare const getWeightUnitGrams: (unit?: string | null) => number | undefined;
22
+ /**
23
+ * 将“每 sourceUnitValue 个 sourceUnit 的价格”换算到每 1 个目标单位的倍率。
24
+ * 这里只处理目录单价的单位关系;实际秤读数计价仍应使用 NativeScale。
25
+ */
26
+ export declare const getWeightUnitPriceConversionFactor: (params: {
27
+ sourceUnit?: string | null;
28
+ sourceUnitValue?: string | number | null;
29
+ targetUnit?: string | null;
30
+ }) => number | undefined;
31
+ export declare const isSupportedWeightUnit: (unit?: unknown) => unit is WeightUnit;
32
+ export declare const formatWeightValue: (value: unknown, precision?: number) => string;
33
+ export declare const formatUnitPriceValue: (value: unknown, precision?: number) => string;
34
+ export declare type ScaleReadingStatus = 'stable' | 'unstable' | 'overload' | 'negative' | string | null;
35
+ export declare const INVALID_SCALE_READING_STATUSES: readonly ["overload", "negative"];
36
+ export declare const isInvalidScaleReadingStatus: (status?: ScaleReadingStatus) => boolean;
37
+ export declare const getUsableScaleWeight: (status: ScaleReadingStatus | undefined, weight: number) => number;
38
+ export interface WeighingSnapshot {
39
+ open_sold_weight?: number | string | boolean;
40
+ mode?: 'auto' | 'manual' | string;
41
+ weight_source?: 'auto' | 'manual' | string;
42
+ weight?: string | number;
43
+ tare?: {
44
+ id?: string;
45
+ label?: string;
46
+ value?: string | number;
47
+ unit?: WeightUnit | string;
48
+ };
49
+ tare_name?: string;
50
+ tare_weight?: string | number;
51
+ net_weight?: string | number;
52
+ unit_price?: string | number;
53
+ unit?: WeightUnit | string;
54
+ unit_weight?: string | number;
55
+ unit_data_snapshot?: unknown;
56
+ scalePriceProduct?: unknown;
57
+ total_amount?: string | number;
58
+ }
59
+ /**
60
+ * 解析订单行称重快照:product_sku 是权威源,_extend.weighing 仅用于旧流程兼容。
61
+ */
62
+ export declare const resolveWeighingSnapshot: (dataSource?: Record<string, any> | null) => WeighingSnapshot | undefined;
package/es/weighing.js ADDED
@@ -0,0 +1,119 @@
1
+ /** 商品配置与 NativeScale 使用的重量单位。 */
2
+
3
+ /** 订单快照与 UI 使用的标准单位符号。 */
4
+
5
+ /**
6
+ * Pisell 的标准重量单位。目录单价、缺省秤单位和缺省展示均以它为准。
7
+ * 后续标准单位变化时,应优先只修改此处。
8
+ */
9
+ export var STANDARD_WEIGHT_UNIT = 'KILOGRAMS';
10
+ export var WEIGHT_PRECISION = 3;
11
+ export var UNIT_PRICE_PRECISION = 2;
12
+ export var MAX_RETAIL_SCALE_WEIGHT_KG = 1000;
13
+ export var WEIGHT_UNIT_SYMBOLS = {
14
+ KILOGRAMS: 'kg',
15
+ GRAMS: 'g',
16
+ POUNDS: 'lb',
17
+ OUNCES: 'oz'
18
+ };
19
+ export var WEIGHT_UNIT_NAMES = {
20
+ kg: 'KILOGRAMS',
21
+ g: 'GRAMS',
22
+ lb: 'POUNDS',
23
+ oz: 'OUNCES'
24
+ };
25
+
26
+ /** 单位对应的克数,作为不依赖 NativeScale 的目录价格换算基准。 */
27
+ export var WEIGHT_UNIT_GRAMS = {
28
+ KILOGRAMS: 1000,
29
+ GRAMS: 1,
30
+ POUNDS: 453.59237,
31
+ OUNCES: 28.349523125,
32
+ kg: 1000,
33
+ g: 1,
34
+ lb: 453.59237,
35
+ oz: 28.349523125
36
+ };
37
+ export var STANDARD_WEIGHT_UNIT_SYMBOL = WEIGHT_UNIT_SYMBOLS[STANDARD_WEIGHT_UNIT];
38
+ export var normalizeWeightUnit = function normalizeWeightUnit(unit) {
39
+ var fallback = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : STANDARD_WEIGHT_UNIT;
40
+ if (unit && unit in WEIGHT_UNIT_SYMBOLS) {
41
+ return unit;
42
+ }
43
+ if (unit && unit in WEIGHT_UNIT_NAMES) {
44
+ return WEIGHT_UNIT_NAMES[unit];
45
+ }
46
+ return fallback;
47
+ };
48
+ export var getWeightUnitSymbol = function getWeightUnitSymbol(unit) {
49
+ if (unit && unit in WEIGHT_UNIT_SYMBOLS) {
50
+ return WEIGHT_UNIT_SYMBOLS[unit];
51
+ }
52
+ if (unit && unit in WEIGHT_UNIT_NAMES) {
53
+ return unit;
54
+ }
55
+ return unit || STANDARD_WEIGHT_UNIT_SYMBOL;
56
+ };
57
+ export var getWeightUnitGrams = function getWeightUnitGrams(unit) {
58
+ return unit ? WEIGHT_UNIT_GRAMS[unit] : WEIGHT_UNIT_GRAMS[STANDARD_WEIGHT_UNIT];
59
+ };
60
+
61
+ /**
62
+ * 将“每 sourceUnitValue 个 sourceUnit 的价格”换算到每 1 个目标单位的倍率。
63
+ * 这里只处理目录单价的单位关系;实际秤读数计价仍应使用 NativeScale。
64
+ */
65
+ export var getWeightUnitPriceConversionFactor = function getWeightUnitPriceConversionFactor(params) {
66
+ var _params$sourceUnitVal;
67
+ var sourceGrams = getWeightUnitGrams(params.sourceUnit || STANDARD_WEIGHT_UNIT);
68
+ var targetGrams = getWeightUnitGrams(params.targetUnit || STANDARD_WEIGHT_UNIT);
69
+ var sourceUnitValue = Number((_params$sourceUnitVal = params.sourceUnitValue) !== null && _params$sourceUnitVal !== void 0 ? _params$sourceUnitVal : 1);
70
+ if (!sourceGrams || !targetGrams || !Number.isFinite(sourceUnitValue) || sourceUnitValue <= 0) {
71
+ return undefined;
72
+ }
73
+ return targetGrams / (sourceGrams * sourceUnitValue);
74
+ };
75
+ export var isSupportedWeightUnit = function isSupportedWeightUnit(unit) {
76
+ return typeof unit === 'string' && unit in WEIGHT_UNIT_GRAMS;
77
+ };
78
+ export var formatWeightValue = function formatWeightValue(value) {
79
+ var precision = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : WEIGHT_PRECISION;
80
+ var parsedValue = Number(value);
81
+ return (Number.isFinite(parsedValue) ? parsedValue : 0).toFixed(precision);
82
+ };
83
+ export var formatUnitPriceValue = function formatUnitPriceValue(value) {
84
+ var precision = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : UNIT_PRICE_PRECISION;
85
+ var parsedValue = Number(value);
86
+ return (Number.isFinite(parsedValue) ? parsedValue : 0).toFixed(precision);
87
+ };
88
+ export var INVALID_SCALE_READING_STATUSES = ['overload', 'negative'];
89
+ export var isInvalidScaleReadingStatus = function isInvalidScaleReadingStatus(status) {
90
+ return INVALID_SCALE_READING_STATUSES.includes(status);
91
+ };
92
+ export var getUsableScaleWeight = function getUsableScaleWeight(status, weight) {
93
+ return isInvalidScaleReadingStatus(status) ? 0 : weight;
94
+ };
95
+ var hasDisplayWeight = function hasDisplayWeight(value) {
96
+ return Boolean(value && (Number(value.weight) > 0 || Number(value.net_weight) > 0));
97
+ };
98
+ var resolveProductSkuSnapshot = function resolveProductSkuSnapshot(value) {
99
+ var snapshot = value;
100
+ return Number(snapshot === null || snapshot === void 0 ? void 0 : snapshot.open_sold_weight) === 1 && hasDisplayWeight(snapshot) ? snapshot : undefined;
101
+ };
102
+
103
+ /**
104
+ * 解析订单行称重快照:product_sku 是权威源,_extend.weighing 仅用于旧流程兼容。
105
+ */
106
+ export var resolveWeighingSnapshot = function resolveWeighingSnapshot(dataSource) {
107
+ var _dataSource$_origin, _dataSource$metadata, _dataSource$_origin2, _dataSource$_extend, _dataSource$metadata2;
108
+ if (!dataSource) return undefined;
109
+ var productSkuCandidates = [dataSource.product_sku, (_dataSource$_origin = dataSource._origin) === null || _dataSource$_origin === void 0 ? void 0 : _dataSource$_origin.product_sku, (_dataSource$metadata = dataSource.metadata) === null || _dataSource$metadata === void 0 || (_dataSource$metadata = _dataSource$metadata.origin) === null || _dataSource$metadata === void 0 ? void 0 : _dataSource$metadata.product_sku];
110
+ for (var _i = 0, _productSkuCandidates = productSkuCandidates; _i < _productSkuCandidates.length; _i++) {
111
+ var candidate = _productSkuCandidates[_i];
112
+ var snapshot = resolveProductSkuSnapshot(candidate);
113
+ if (snapshot) return snapshot;
114
+ }
115
+ var legacyCandidates = [(_dataSource$_origin2 = dataSource._origin) === null || _dataSource$_origin2 === void 0 || (_dataSource$_origin2 = _dataSource$_origin2._extend) === null || _dataSource$_origin2 === void 0 ? void 0 : _dataSource$_origin2.weighing, (_dataSource$_extend = dataSource._extend) === null || _dataSource$_extend === void 0 ? void 0 : _dataSource$_extend.weighing, (_dataSource$metadata2 = dataSource.metadata) === null || _dataSource$metadata2 === void 0 || (_dataSource$metadata2 = _dataSource$metadata2.origin) === null || _dataSource$metadata2 === void 0 || (_dataSource$metadata2 = _dataSource$metadata2._extend) === null || _dataSource$metadata2 === void 0 ? void 0 : _dataSource$metadata2.weighing];
116
+ return legacyCandidates.find(function (candidate) {
117
+ return hasDisplayWeight(candidate);
118
+ });
119
+ };
@@ -1 +1,73 @@
1
1
  export declare const uniqueByKey: <T>(arr: T[], key: keyof T) => T[];
2
+ /**
3
+ * 分组键的基础类型
4
+ * @description 分组条件计算后得到的标量值
5
+ */
6
+ export declare type GroupKey = string | number | boolean;
7
+ /**
8
+ * 分组规则
9
+ * @template T - 数据项类型
10
+ * @description
11
+ * - `string`:单字段路径,支持 `'a.b.c'` 点路径
12
+ * - `string[]`:多字段组合,按顺序拼接为组合键
13
+ * - `(item, index) => GroupKey | GroupKey[]`:完全自定义分组键
14
+ */
15
+ export declare type GroupBySpec<T> = string | string[] | ((item: T, index: number) => GroupKey | GroupKey[]);
16
+ /**
17
+ * 单个分组的结果模型
18
+ * @template T - 数据项类型
19
+ */
20
+ export interface GroupResult<T> {
21
+ /** 分组唯一标识(默认由各分组键以 `|` 拼接) */
22
+ groupId: string;
23
+ /** 分组键值数组,与 `GroupBySpec` 顺序对应 */
24
+ keys: GroupKey[];
25
+ /** 字段名 -> 键值映射,便于头部直接按字段名取值(自定义函数分组时为空对象) */
26
+ keyMap: Record<string, GroupKey>;
27
+ /** 该分组内的原始数据项(保持原数组中的相对顺序) */
28
+ items: T[];
29
+ /** 分组的最终顺序下标 */
30
+ index: number;
31
+ }
32
+ /**
33
+ * 分组可选项
34
+ * @template T - 数据项类型
35
+ */
36
+ export interface GroupByOptions<T> {
37
+ /** 自定义分组 id 生成(默认 `keys.join('|')`) */
38
+ getGroupId?: (keys: GroupKey[]) => string;
39
+ /** 分组之间的排序;不传则保持各分组首次出现的顺序 */
40
+ sortGroups?: (a: GroupResult<T>, b: GroupResult<T>) => number;
41
+ /** 分组内数据项的排序;不传则保持原始相对顺序 */
42
+ sortItems?: (a: T, b: T) => number;
43
+ }
44
+ /**
45
+ * 按点路径安全取值
46
+ * @param obj - 取值对象
47
+ * @param path - 字段路径,支持 `'a.b.c'`
48
+ * @returns 路径对应的值,路径不存在时返回 undefined
49
+ * @example
50
+ * getByPath({ a: { b: 1 } }, 'a.b') // 1
51
+ * getByPath({ id: 9 }, 'id') // 9
52
+ */
53
+ export declare const getByPath: (obj: unknown, path: string) => unknown;
54
+ /**
55
+ * 将数组按指定条件分组
56
+ * @template T - 数据项类型
57
+ * @param dataSource - 待分组的数据数组
58
+ * @param spec - 分组规则,详见 {@link GroupBySpec}
59
+ * @param options - 可选项(自定义 groupId、分组/组内排序)
60
+ * @returns 分组结果数组,默认保持各分组首次出现的顺序、组内保持原始相对顺序
61
+ * @description
62
+ * 该函数为纯函数,不修改入参(排序时对内部拷贝操作)。相同分组键的数据会被归入同一分组。
63
+ * @example
64
+ * // 单字段
65
+ * groupBy(list, 'productId');
66
+ * @example
67
+ * // 商品 + 时间组合
68
+ * groupBy(list, ['productId', 'time']);
69
+ * @example
70
+ * // 自定义键
71
+ * groupBy(list, (item) => `${item.productId}_${item.time}`);
72
+ */
73
+ export declare const groupBy: <T>(dataSource: T[], spec: GroupBySpec<T>, options?: GroupByOptions<T>) => GroupResult<T>[];