@pisell/pisellos 2.3.136 → 2.3.138
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/model/strategy/adapter/promotion/index.js +9 -0
- package/dist/modules/ResourcePlanner/planner.js +10 -8
- package/dist/solution/BookingByStep/index.d.ts +1 -1
- package/dist/solution/RegisterAndLogin/config.js +50 -26
- package/dist/solution/RegisterAndLogin/index.d.ts +2 -0
- package/dist/solution/RegisterAndLogin/index.js +1 -1
- package/dist/solution/RegisterAndLogin/utils.d.ts +12 -4
- package/dist/solution/RegisterAndLogin/utils.js +36 -16
- package/dist/solution/Sales/index.d.ts +2 -0
- package/dist/solution/Sales/index.js +15 -1
- package/dist/solution/UnifiedBookingSales/index.js +3 -4
- package/lib/model/strategy/adapter/promotion/index.js +46 -1
- package/lib/modules/ResourcePlanner/planner.js +10 -8
- package/lib/solution/BookingByStep/index.d.ts +1 -1
- package/lib/solution/RegisterAndLogin/config.js +52 -26
- package/lib/solution/RegisterAndLogin/index.d.ts +2 -0
- package/lib/solution/RegisterAndLogin/index.js +9 -1
- package/lib/solution/RegisterAndLogin/utils.d.ts +12 -4
- package/lib/solution/RegisterAndLogin/utils.js +24 -17
- package/lib/solution/Sales/index.d.ts +2 -0
- package/lib/solution/Sales/index.js +10 -1
- package/lib/solution/UnifiedBookingSales/index.js +3 -4
- package/package.json +1 -1
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// 导出评估器
|
|
2
|
+
export { PromotionEvaluator } from "./evaluator";
|
|
3
|
+
|
|
4
|
+
// 导出适配器
|
|
5
|
+
export { PromotionAdapter } from "./adapter";
|
|
6
|
+
export { default } from "./adapter";
|
|
7
|
+
|
|
8
|
+
// 导出策略配置示例常量
|
|
9
|
+
export { X_ITEMS_FOR_Y_PRICE_STRATEGY, BUY_X_GET_Y_FREE_STRATEGY, ITEM_REWARD_STRATEGY } from "./examples";
|
|
@@ -436,7 +436,7 @@ function normalizeOccupancy(occupancy, coverage, timezoneName, fixedOffsetMinute
|
|
|
436
436
|
}
|
|
437
437
|
function normalizeRequirementGroup(product, requirementGroup) {
|
|
438
438
|
var _requirementGroup$for;
|
|
439
|
-
var resourceType = 'single';
|
|
439
|
+
var resourceType = requirementGroup.resourceType === 'multiple' || requirementGroup.resourceType === 'capacity' ? requirementGroup.resourceType : 'single';
|
|
440
440
|
var required = requirementGroup.required !== false;
|
|
441
441
|
var min = required ? 1 : 0;
|
|
442
442
|
return _objectSpread(_objectSpread({}, requirementGroup), {}, {
|
|
@@ -482,7 +482,7 @@ function normalizeProducts(products) {
|
|
|
482
482
|
function normalizeResources(resources) {
|
|
483
483
|
return resources.map(function (resource) {
|
|
484
484
|
return _objectSpread(_objectSpread({}, resource), {}, {
|
|
485
|
-
resourceType: 'single',
|
|
485
|
+
resourceType: resource.resourceType === 'multiple' || resource.resourceType === 'capacity' ? resource.resourceType : 'single',
|
|
486
486
|
capacity: Math.max(1, Number(resource.capacity || 1) || 1),
|
|
487
487
|
windows: (resource.windows || []).map(function (window) {
|
|
488
488
|
return _objectSpread({}, window);
|
|
@@ -637,7 +637,8 @@ function buildCells(projection) {
|
|
|
637
637
|
var usedCapacity = segmentOccupancies.reduce(function (sum, occupancy) {
|
|
638
638
|
return sum + Math.max(1, Number(occupancy.pax || 1) || 1);
|
|
639
639
|
}, 0);
|
|
640
|
-
|
|
640
|
+
// 预约模式来自商品资源组;v2 资源记录可能不包含 type。
|
|
641
|
+
var remainingCapacity = requirementGroup.resourceType === 'single' && segmentOccupancies.length > 0 ? 0 : Math.max(0, resource.capacity - usedCapacity);
|
|
641
642
|
var requiredCapacity = resolveRequiredCapacity(requirementGroup, product);
|
|
642
643
|
var reasonCodes = Array.from(new Set(segmentOccupancies.flatMap(function (occupancy) {
|
|
643
644
|
var source = occupancy.source || 'remote';
|
|
@@ -671,7 +672,7 @@ function buildCells(projection) {
|
|
|
671
672
|
requirementFormId: requirementGroup.formId,
|
|
672
673
|
resourceId: resource.id,
|
|
673
674
|
resourceName: resource.name,
|
|
674
|
-
resourceType:
|
|
675
|
+
resourceType: requirementGroup.resourceType,
|
|
675
676
|
startAt: formatDateTime(start, timezoneName, fixedOffsetMinutes),
|
|
676
677
|
endAt: formatDateTime(end, timezoneName, fixedOffsetMinutes),
|
|
677
678
|
date: formatDate(start, timezoneName),
|
|
@@ -679,7 +680,7 @@ function buildCells(projection) {
|
|
|
679
680
|
endTime: formatTime(end, timezoneName),
|
|
680
681
|
timezone: projection.meta.timezone,
|
|
681
682
|
status: status,
|
|
682
|
-
//
|
|
683
|
+
// capacity 表示人数;single 被占用时不再贡献可用容量。
|
|
683
684
|
remainingCapacity: remainingCapacity,
|
|
684
685
|
maxCapacity: resource.capacity,
|
|
685
686
|
reasonCodes: Array.from(new Set(reasonCodes)),
|
|
@@ -1739,7 +1740,7 @@ function evaluateCandidate(projection, candidate, request) {
|
|
|
1739
1740
|
resourceName: resource.name,
|
|
1740
1741
|
resourceFormId: resource.formId,
|
|
1741
1742
|
policyId: (_resource$raw = resource.raw) === null || _resource$raw === void 0 ? void 0 : _resource$raw.policy_id,
|
|
1742
|
-
resourceType:
|
|
1743
|
+
resourceType: requirementGroup.resourceType,
|
|
1743
1744
|
status: _isPast ? 'past' : 'unavailable',
|
|
1744
1745
|
maxPartySize: 0,
|
|
1745
1746
|
reasonCodes: _isPast ? ['past'] : [].concat(_toConsumableArray(isExcludedScheduleDate ? ['schedule_excluded_date'] : []), ['outside_resource_window'])
|
|
@@ -1763,7 +1764,7 @@ function evaluateCandidate(projection, candidate, request) {
|
|
|
1763
1764
|
resourceName: resource.name,
|
|
1764
1765
|
resourceFormId: resource.formId,
|
|
1765
1766
|
policyId: (_resource$raw2 = resource.raw) === null || _resource$raw2 === void 0 ? void 0 : _resource$raw2.policy_id,
|
|
1766
|
-
resourceType:
|
|
1767
|
+
resourceType: requirementGroup.resourceType,
|
|
1767
1768
|
status: status,
|
|
1768
1769
|
remainingCapacity: remainingCapacity,
|
|
1769
1770
|
maxCapacity: maxCapacity,
|
|
@@ -2330,7 +2331,8 @@ export function validateAvailabilitySelection(projection, params) {
|
|
|
2330
2331
|
var remainingCapacity = matchedCell === null || matchedCell === void 0 ? void 0 : matchedCell.remainingCapacity;
|
|
2331
2332
|
if (option && (option.status === 'available' || option.status === 'limited') && remainingCapacity !== undefined) {
|
|
2332
2333
|
var capacityPerBooking = normalizedPartySize !== null && normalizedPartySize !== void 0 ? normalizedPartySize : 1;
|
|
2333
|
-
var
|
|
2334
|
+
var capacityQuantity = Math.max(0, Math.floor(remainingCapacity / capacityPerBooking));
|
|
2335
|
+
var availableQuantity = matchedGroup.resourceType === 'single' ? Math.min(1, capacityQuantity) : capacityQuantity;
|
|
2334
2336
|
if (normalizedBookingCount > availableQuantity) {
|
|
2335
2337
|
conflicts.push({
|
|
2336
2338
|
code: 'resource_capacity_exceeded',
|
|
@@ -326,7 +326,7 @@ export declare class BookingByStepImpl extends BaseModule implements Module {
|
|
|
326
326
|
date: string;
|
|
327
327
|
status: string;
|
|
328
328
|
week: string;
|
|
329
|
-
weekNum: 0 | 2 | 1 |
|
|
329
|
+
weekNum: 0 | 2 | 1 | 3 | 4 | 5 | 6;
|
|
330
330
|
}[]>;
|
|
331
331
|
submitTimeSlot(timeSlots: TimeSliceItem): void;
|
|
332
332
|
private getScheduleDataByIds;
|
|
@@ -386,23 +386,25 @@ function withH5AuthPrefix(config) {
|
|
|
386
386
|
return nextConfig;
|
|
387
387
|
}
|
|
388
388
|
var h5OnlineStoreConfig = withH5AuthPrefix(onlineStoreConfig);
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
return _objectSpread(_objectSpread({},
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
}),
|
|
399
|
-
phoneCodeRegister: _objectSpread(_objectSpread({}, h5OnlineStoreConfig.phoneCodeRegister), {}, {
|
|
400
|
-
transformParams: function transformParams(params) {
|
|
401
|
-
return _objectSpread(_objectSpread({}, h5OnlineStoreConfig.phoneCodeRegister.transformParams(params)), {}, {
|
|
402
|
-
login_channel: 'kiosk'
|
|
389
|
+
function withKioskSalesChannel(config) {
|
|
390
|
+
return _objectSpread(_objectSpread({}, config), {}, {
|
|
391
|
+
options: function options(params) {
|
|
392
|
+
var options = (typeof config.options === 'function' ? config.options(params) : config.options) || {};
|
|
393
|
+
var headers = typeof options.headers === 'function' ? options.headers(params) : options.headers;
|
|
394
|
+
return _objectSpread(_objectSpread({}, options), {}, {
|
|
395
|
+
headers: _objectSpread(_objectSpread({}, headers), {}, {
|
|
396
|
+
'Sales-Channel': 'kiosk'
|
|
397
|
+
})
|
|
403
398
|
});
|
|
404
399
|
}
|
|
405
|
-
})
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
// Kiosk 复用网店顾客认证接口,通过注册(含验证码登录自动注册)请求头标识会员归属渠道。
|
|
404
|
+
var kioskConfig = _objectSpread(_objectSpread({}, h5OnlineStoreConfig), {}, {
|
|
405
|
+
emailCodeRegister: withKioskSalesChannel(h5OnlineStoreConfig.emailCodeRegister),
|
|
406
|
+
phoneCodeRegister: withKioskSalesChannel(h5OnlineStoreConfig.phoneCodeRegister),
|
|
407
|
+
phoneCodeLogin: withKioskSalesChannel(h5OnlineStoreConfig.phoneCodeLogin)
|
|
406
408
|
});
|
|
407
409
|
|
|
408
410
|
/**
|
|
@@ -731,6 +733,16 @@ var defaultConfig = {
|
|
|
731
733
|
/**
|
|
732
734
|
* webpos 渠道配置
|
|
733
735
|
*/
|
|
736
|
+
var webposPasswordResetOptions = {
|
|
737
|
+
prefix: false,
|
|
738
|
+
isDefaultSaasDomain: true,
|
|
739
|
+
cache: false,
|
|
740
|
+
noToast: true,
|
|
741
|
+
headers: {
|
|
742
|
+
Accept: 'application/json',
|
|
743
|
+
'Content-Type': 'application/json'
|
|
744
|
+
}
|
|
745
|
+
};
|
|
734
746
|
var webposConfig = {
|
|
735
747
|
sendEmailVerificationCode: {
|
|
736
748
|
url: '/auth/email/register-code',
|
|
@@ -883,15 +895,23 @@ var webposConfig = {
|
|
|
883
895
|
}
|
|
884
896
|
},
|
|
885
897
|
checkEmailCode: {
|
|
886
|
-
url: '/
|
|
887
|
-
method: '
|
|
898
|
+
url: '/tenant/account/reset-password/email-code/verify',
|
|
899
|
+
method: 'POST',
|
|
888
900
|
transformParams: function transformParams(params) {
|
|
889
901
|
return {
|
|
890
902
|
email: params.email,
|
|
891
|
-
code: params.code
|
|
892
|
-
action: params.action
|
|
903
|
+
code: params.code
|
|
893
904
|
};
|
|
894
|
-
}
|
|
905
|
+
},
|
|
906
|
+
// SaaS 成功时 data 为 null,统一为既有验证码校验返回结构。
|
|
907
|
+
transformResponse: function transformResponse(response) {
|
|
908
|
+
return (response === null || response === void 0 ? void 0 : response.status) === true ? _objectSpread(_objectSpread({}, response), {}, {
|
|
909
|
+
data: {
|
|
910
|
+
valid: true
|
|
911
|
+
}
|
|
912
|
+
}) : response;
|
|
913
|
+
},
|
|
914
|
+
options: webposPasswordResetOptions
|
|
895
915
|
},
|
|
896
916
|
checkMobileCode: {
|
|
897
917
|
url: '/auth/mobile/check-mobile-code',
|
|
@@ -917,13 +937,14 @@ var webposConfig = {
|
|
|
917
937
|
}
|
|
918
938
|
},
|
|
919
939
|
sendPasswordResetEmail: {
|
|
920
|
-
url: '/
|
|
921
|
-
method: '
|
|
940
|
+
url: '/tenant/account/reset-password/email-code/send',
|
|
941
|
+
method: 'POST',
|
|
922
942
|
transformParams: function transformParams(params) {
|
|
923
943
|
return {
|
|
924
944
|
email: params.email
|
|
925
945
|
};
|
|
926
|
-
}
|
|
946
|
+
},
|
|
947
|
+
options: webposPasswordResetOptions
|
|
927
948
|
},
|
|
928
949
|
sendPasswordResetSms: {
|
|
929
950
|
url: '/auth/mobile/sms-forget',
|
|
@@ -964,15 +985,18 @@ var webposConfig = {
|
|
|
964
985
|
}
|
|
965
986
|
},
|
|
966
987
|
resetPasswordByEmail: {
|
|
967
|
-
url: '/
|
|
988
|
+
url: '/tenant/account/reset-password/email-code',
|
|
968
989
|
method: 'POST',
|
|
969
990
|
transformParams: function transformParams(params) {
|
|
970
991
|
return {
|
|
971
992
|
email: params.email,
|
|
972
993
|
password: params.password,
|
|
973
|
-
code: params.code
|
|
994
|
+
code: params.code,
|
|
995
|
+
// 页面先校验两次输入完全一致,SDK 保持原有重置参数。
|
|
996
|
+
password_confirmation: params.password
|
|
974
997
|
};
|
|
975
|
-
}
|
|
998
|
+
},
|
|
999
|
+
options: webposPasswordResetOptions
|
|
976
1000
|
},
|
|
977
1001
|
resetPasswordByPhone: {
|
|
978
1002
|
url: '/auth/mobile/reset-password',
|
|
@@ -4,6 +4,8 @@ import { RegisterParams, LoginParams, OAuthLoginParams, SendVerificationCodePara
|
|
|
4
4
|
import { ApiCaller } from './config';
|
|
5
5
|
export * from './types';
|
|
6
6
|
export * from './config';
|
|
7
|
+
export { validatePassword } from './utils';
|
|
8
|
+
export type { PasswordValidationOptions } from './utils';
|
|
7
9
|
/**
|
|
8
10
|
* 注册登录解决方案实现
|
|
9
11
|
*/
|
|
@@ -22,7 +22,7 @@ import { RegisterAndLoginHooks, VerificationCodeType, VerificationPurpose } from
|
|
|
22
22
|
import { createApiCaller } from "./config";
|
|
23
23
|
export * from "./types";
|
|
24
24
|
export * from "./config";
|
|
25
|
-
|
|
25
|
+
export { validatePassword } from "./utils";
|
|
26
26
|
/**
|
|
27
27
|
* 注册登录解决方案实现
|
|
28
28
|
*/
|
|
@@ -10,10 +10,18 @@ export declare function validateEmail(email: string): boolean;
|
|
|
10
10
|
* 验证手机号格式(支持多种国际格式)
|
|
11
11
|
*/
|
|
12
12
|
export declare function validatePhone(phone: string): boolean;
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
export interface PasswordValidationOptions {
|
|
14
|
+
minLength?: number;
|
|
15
|
+
maxLength?: number;
|
|
16
|
+
requireLowercase?: boolean;
|
|
17
|
+
requireUppercase?: boolean;
|
|
18
|
+
requireNumber?: boolean;
|
|
19
|
+
requireSpecialCharacters?: boolean;
|
|
20
|
+
specialCharactersPattern?: RegExp;
|
|
21
|
+
messages?: Partial<Record<'minLength' | 'maxLength' | 'requireLowercase' | 'requireUppercase' | 'requireNumber' | 'requireSpecialCharacters', string>>;
|
|
22
|
+
}
|
|
23
|
+
/** 验证密码强度;不传 options 时保持原有规则与返回格式。 */
|
|
24
|
+
export declare function validatePassword(password: string, options?: PasswordValidationOptions): {
|
|
17
25
|
isValid: boolean;
|
|
18
26
|
errors: string[];
|
|
19
27
|
};
|
|
@@ -33,29 +33,49 @@ export function validatePhone(phone) {
|
|
|
33
33
|
var phoneRegex = /^(\+\d{1,3}[- ]?)?\d{10,14}$/;
|
|
34
34
|
return phoneRegex.test(cleanPhone);
|
|
35
35
|
}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* 验证密码强度
|
|
39
|
-
*/
|
|
36
|
+
/** 验证密码强度;不传 options 时保持原有规则与返回格式。 */
|
|
40
37
|
export function validatePassword(password) {
|
|
38
|
+
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
41
39
|
var errors = [];
|
|
42
|
-
|
|
43
|
-
|
|
40
|
+
var _options$minLength = options.minLength,
|
|
41
|
+
minLength = _options$minLength === void 0 ? 8 : _options$minLength,
|
|
42
|
+
_options$maxLength = options.maxLength,
|
|
43
|
+
maxLength = _options$maxLength === void 0 ? 128 : _options$maxLength,
|
|
44
|
+
_options$requireLower = options.requireLowercase,
|
|
45
|
+
requireLowercase = _options$requireLower === void 0 ? true : _options$requireLower,
|
|
46
|
+
_options$requireUpper = options.requireUppercase,
|
|
47
|
+
requireUppercase = _options$requireUpper === void 0 ? true : _options$requireUpper,
|
|
48
|
+
_options$requireNumbe = options.requireNumber,
|
|
49
|
+
requireNumber = _options$requireNumbe === void 0 ? true : _options$requireNumbe,
|
|
50
|
+
_options$requireSpeci = options.requireSpecialCharacters,
|
|
51
|
+
requireSpecialCharacters = _options$requireSpeci === void 0 ? true : _options$requireSpeci,
|
|
52
|
+
_options$specialChara = options.specialCharactersPattern,
|
|
53
|
+
specialCharactersPattern = _options$specialChara === void 0 ? /[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]/ : _options$specialChara,
|
|
54
|
+
_options$messages = options.messages,
|
|
55
|
+
messages = _options$messages === void 0 ? {} : _options$messages;
|
|
56
|
+
if (password.length < minLength) {
|
|
57
|
+
var _messages$minLength;
|
|
58
|
+
errors.push((_messages$minLength = messages.minLength) !== null && _messages$minLength !== void 0 ? _messages$minLength : "\u5BC6\u7801\u957F\u5EA6\u81F3\u5C11\u4E3A".concat(minLength, "\u4F4D"));
|
|
44
59
|
}
|
|
45
|
-
if (password.length >
|
|
46
|
-
|
|
60
|
+
if (password.length > maxLength) {
|
|
61
|
+
var _messages$maxLength;
|
|
62
|
+
errors.push((_messages$maxLength = messages.maxLength) !== null && _messages$maxLength !== void 0 ? _messages$maxLength : "\u5BC6\u7801\u957F\u5EA6\u4E0D\u80FD\u8D85\u8FC7".concat(maxLength, "\u4F4D"));
|
|
47
63
|
}
|
|
48
|
-
if (!/[a-z]/.test(password)) {
|
|
49
|
-
|
|
64
|
+
if (requireLowercase && !/[a-z]/.test(password)) {
|
|
65
|
+
var _messages$requireLowe;
|
|
66
|
+
errors.push((_messages$requireLowe = messages.requireLowercase) !== null && _messages$requireLowe !== void 0 ? _messages$requireLowe : '密码必须包含至少一个小写字母');
|
|
50
67
|
}
|
|
51
|
-
if (!/[A-Z]/.test(password)) {
|
|
52
|
-
|
|
68
|
+
if (requireUppercase && !/[A-Z]/.test(password)) {
|
|
69
|
+
var _messages$requireUppe;
|
|
70
|
+
errors.push((_messages$requireUppe = messages.requireUppercase) !== null && _messages$requireUppe !== void 0 ? _messages$requireUppe : '密码必须包含至少一个大写字母');
|
|
53
71
|
}
|
|
54
|
-
if (!/\d/.test(password)) {
|
|
55
|
-
|
|
72
|
+
if (requireNumber && !/\d/.test(password)) {
|
|
73
|
+
var _messages$requireNumb;
|
|
74
|
+
errors.push((_messages$requireNumb = messages.requireNumber) !== null && _messages$requireNumb !== void 0 ? _messages$requireNumb : '密码必须包含至少一个数字');
|
|
56
75
|
}
|
|
57
|
-
if (
|
|
58
|
-
|
|
76
|
+
if (requireSpecialCharacters && !specialCharactersPattern.test(password)) {
|
|
77
|
+
var _messages$requireSpec;
|
|
78
|
+
errors.push((_messages$requireSpec = messages.requireSpecialCharacters) !== null && _messages$requireSpec !== void 0 ? _messages$requireSpec : '密码必须包含至少一个特殊字符');
|
|
59
79
|
}
|
|
60
80
|
return {
|
|
61
81
|
isValid: errors.length === 0,
|
|
@@ -105,6 +105,8 @@ export declare class SalesImpl extends BaseModule implements Module, SalesModule
|
|
|
105
105
|
private groupBookingsByResource;
|
|
106
106
|
private isBookingMatchedResource;
|
|
107
107
|
private sortMatchedBookings;
|
|
108
|
+
/** 已完成:按订单创建时间降序,避免实时追加和重新加载导致显示顺序不同。 */
|
|
109
|
+
private sortCompletedBookings;
|
|
108
110
|
/** Unpaid:与 Current 重合的订单沿用 Current 顺序,其余欠款订单按开始时间降序追加。 */
|
|
109
111
|
private sortUnpaidBookings;
|
|
110
112
|
/**
|
|
@@ -760,6 +760,20 @@ export var SalesImpl = /*#__PURE__*/function (_BaseModule) {
|
|
|
760
760
|
});
|
|
761
761
|
}
|
|
762
762
|
|
|
763
|
+
/** 已完成:按订单创建时间降序,避免实时追加和重新加载导致显示顺序不同。 */
|
|
764
|
+
}, {
|
|
765
|
+
key: "sortCompletedBookings",
|
|
766
|
+
value: function sortCompletedBookings(bookings) {
|
|
767
|
+
var createdAt = function createdAt(booking) {
|
|
768
|
+
var _booking$order3;
|
|
769
|
+
var timestamp = dayjs(((_booking$order3 = booking.order) === null || _booking$order3 === void 0 ? void 0 : _booking$order3.created_at) || '').valueOf();
|
|
770
|
+
return Number.isFinite(timestamp) ? timestamp : 0;
|
|
771
|
+
};
|
|
772
|
+
return _toConsumableArray(bookings).sort(function (left, right) {
|
|
773
|
+
return createdAt(right) - createdAt(left);
|
|
774
|
+
});
|
|
775
|
+
}
|
|
776
|
+
|
|
763
777
|
/** Unpaid:与 Current 重合的订单沿用 Current 顺序,其余欠款订单按开始时间降序追加。 */
|
|
764
778
|
}, {
|
|
765
779
|
key: "sortUnpaidBookings",
|
|
@@ -1260,7 +1274,7 @@ export var SalesImpl = /*#__PURE__*/function (_BaseModule) {
|
|
|
1260
1274
|
bookings = this.pickBookingsForCurrentPoint(current, this.normalizeResourceBookings(current, deviceCurrent, matchedBookingList), {
|
|
1261
1275
|
includeAllFuture: true
|
|
1262
1276
|
});
|
|
1263
|
-
completedBookings = completedBookingList.map(function (booking) {
|
|
1277
|
+
completedBookings = this.sortCompletedBookings(completedBookingList).map(function (booking) {
|
|
1264
1278
|
return _objectSpread(_objectSpread({}, booking), {}, {
|
|
1265
1279
|
status: 'compelete',
|
|
1266
1280
|
isTimeout: false,
|
|
@@ -304,10 +304,9 @@ function assertAvailabilityCatalogProductIds(productIds, context) {
|
|
|
304
304
|
});
|
|
305
305
|
}
|
|
306
306
|
}
|
|
307
|
-
function normalizeResourceType(
|
|
308
|
-
//
|
|
309
|
-
|
|
310
|
-
return 'single';
|
|
307
|
+
function normalizeResourceType(value) {
|
|
308
|
+
// 预约是否独占与每组选择几个资源是两个独立配置。
|
|
309
|
+
return value === 'multiple' || value === 'capacity' ? value : 'single';
|
|
311
310
|
}
|
|
312
311
|
function resolveProductKind(product) {
|
|
313
312
|
var _product$cartDetailVa, _product$_extend;
|
|
@@ -1 +1,46 @@
|
|
|
1
|
-
"use strict";
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
Object.defineProperty(exports, "BUY_X_GET_Y_FREE_STRATEGY", {
|
|
7
|
+
enumerable: true,
|
|
8
|
+
get: function () {
|
|
9
|
+
return _examples.BUY_X_GET_Y_FREE_STRATEGY;
|
|
10
|
+
}
|
|
11
|
+
});
|
|
12
|
+
Object.defineProperty(exports, "ITEM_REWARD_STRATEGY", {
|
|
13
|
+
enumerable: true,
|
|
14
|
+
get: function () {
|
|
15
|
+
return _examples.ITEM_REWARD_STRATEGY;
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
Object.defineProperty(exports, "PromotionAdapter", {
|
|
19
|
+
enumerable: true,
|
|
20
|
+
get: function () {
|
|
21
|
+
return _adapter.PromotionAdapter;
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
Object.defineProperty(exports, "PromotionEvaluator", {
|
|
25
|
+
enumerable: true,
|
|
26
|
+
get: function () {
|
|
27
|
+
return _evaluator.PromotionEvaluator;
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
Object.defineProperty(exports, "X_ITEMS_FOR_Y_PRICE_STRATEGY", {
|
|
31
|
+
enumerable: true,
|
|
32
|
+
get: function () {
|
|
33
|
+
return _examples.X_ITEMS_FOR_Y_PRICE_STRATEGY;
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
Object.defineProperty(exports, "default", {
|
|
37
|
+
enumerable: true,
|
|
38
|
+
get: function () {
|
|
39
|
+
return _adapter.default;
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
var _evaluator = require("./evaluator");
|
|
43
|
+
var _adapter = _interopRequireWildcard(require("./adapter"));
|
|
44
|
+
var _examples = require("./examples");
|
|
45
|
+
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
|
46
|
+
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
|
@@ -412,7 +412,7 @@ function normalizeOccupancy(occupancy, coverage, timezoneName, fixedOffsetMinute
|
|
|
412
412
|
};
|
|
413
413
|
}
|
|
414
414
|
function normalizeRequirementGroup(product, requirementGroup) {
|
|
415
|
-
const resourceType = 'single';
|
|
415
|
+
const resourceType = requirementGroup.resourceType === 'multiple' || requirementGroup.resourceType === 'capacity' ? requirementGroup.resourceType : 'single';
|
|
416
416
|
const required = requirementGroup.required !== false;
|
|
417
417
|
const min = required ? 1 : 0;
|
|
418
418
|
return {
|
|
@@ -457,7 +457,7 @@ function normalizeProducts(products) {
|
|
|
457
457
|
function normalizeResources(resources) {
|
|
458
458
|
return resources.map(resource => ({
|
|
459
459
|
...resource,
|
|
460
|
-
resourceType: 'single',
|
|
460
|
+
resourceType: resource.resourceType === 'multiple' || resource.resourceType === 'capacity' ? resource.resourceType : 'single',
|
|
461
461
|
capacity: Math.max(1, Number(resource.capacity || 1) || 1),
|
|
462
462
|
windows: (resource.windows || []).map(window => ({
|
|
463
463
|
...window
|
|
@@ -573,7 +573,8 @@ function buildCells(projection) {
|
|
|
573
573
|
occupancy
|
|
574
574
|
}) => occupancy) : [];
|
|
575
575
|
const usedCapacity = segmentOccupancies.reduce((sum, occupancy) => sum + Math.max(1, Number(occupancy.pax || 1) || 1), 0);
|
|
576
|
-
|
|
576
|
+
// 预约模式来自商品资源组;v2 资源记录可能不包含 type。
|
|
577
|
+
const remainingCapacity = requirementGroup.resourceType === 'single' && segmentOccupancies.length > 0 ? 0 : Math.max(0, resource.capacity - usedCapacity);
|
|
577
578
|
const requiredCapacity = resolveRequiredCapacity(requirementGroup, product);
|
|
578
579
|
const reasonCodes = Array.from(new Set(segmentOccupancies.flatMap(occupancy => {
|
|
579
580
|
const source = occupancy.source || 'remote';
|
|
@@ -607,7 +608,7 @@ function buildCells(projection) {
|
|
|
607
608
|
requirementFormId: requirementGroup.formId,
|
|
608
609
|
resourceId: resource.id,
|
|
609
610
|
resourceName: resource.name,
|
|
610
|
-
resourceType:
|
|
611
|
+
resourceType: requirementGroup.resourceType,
|
|
611
612
|
startAt: formatDateTime(start, timezoneName, fixedOffsetMinutes),
|
|
612
613
|
endAt: formatDateTime(end, timezoneName, fixedOffsetMinutes),
|
|
613
614
|
date: formatDate(start, timezoneName),
|
|
@@ -615,7 +616,7 @@ function buildCells(projection) {
|
|
|
615
616
|
endTime: formatTime(end, timezoneName),
|
|
616
617
|
timezone: projection.meta.timezone,
|
|
617
618
|
status,
|
|
618
|
-
//
|
|
619
|
+
// capacity 表示人数;single 被占用时不再贡献可用容量。
|
|
619
620
|
remainingCapacity,
|
|
620
621
|
maxCapacity: resource.capacity,
|
|
621
622
|
reasonCodes: Array.from(new Set(reasonCodes)),
|
|
@@ -1500,7 +1501,7 @@ function evaluateCandidate(projection, candidate, request) {
|
|
|
1500
1501
|
resourceName: resource.name,
|
|
1501
1502
|
resourceFormId: resource.formId,
|
|
1502
1503
|
policyId: resource.raw?.policy_id,
|
|
1503
|
-
resourceType:
|
|
1504
|
+
resourceType: requirementGroup.resourceType,
|
|
1504
1505
|
status: isPast ? 'past' : 'unavailable',
|
|
1505
1506
|
maxPartySize: 0,
|
|
1506
1507
|
reasonCodes: isPast ? ['past'] : [...(isExcludedScheduleDate ? ['schedule_excluded_date'] : []), 'outside_resource_window']
|
|
@@ -1524,7 +1525,7 @@ function evaluateCandidate(projection, candidate, request) {
|
|
|
1524
1525
|
resourceName: resource.name,
|
|
1525
1526
|
resourceFormId: resource.formId,
|
|
1526
1527
|
policyId: resource.raw?.policy_id,
|
|
1527
|
-
resourceType:
|
|
1528
|
+
resourceType: requirementGroup.resourceType,
|
|
1528
1529
|
status,
|
|
1529
1530
|
remainingCapacity,
|
|
1530
1531
|
maxCapacity,
|
|
@@ -2008,7 +2009,8 @@ function validateAvailabilitySelection(projection, params, runtimeOptions = {})
|
|
|
2008
2009
|
const remainingCapacity = matchedCell?.remainingCapacity;
|
|
2009
2010
|
if (option && (option.status === 'available' || option.status === 'limited') && remainingCapacity !== undefined) {
|
|
2010
2011
|
const capacityPerBooking = normalizedPartySize ?? 1;
|
|
2011
|
-
const
|
|
2012
|
+
const capacityQuantity = Math.max(0, Math.floor(remainingCapacity / capacityPerBooking));
|
|
2013
|
+
const availableQuantity = matchedGroup.resourceType === 'single' ? Math.min(1, capacityQuantity) : capacityQuantity;
|
|
2012
2014
|
if (normalizedBookingCount > availableQuantity) {
|
|
2013
2015
|
conflicts.push({
|
|
2014
2016
|
code: 'resource_capacity_exceeded',
|
|
@@ -326,7 +326,7 @@ export declare class BookingByStepImpl extends BaseModule implements Module {
|
|
|
326
326
|
date: string;
|
|
327
327
|
status: string;
|
|
328
328
|
week: string;
|
|
329
|
-
weekNum: 0 | 2 | 1 |
|
|
329
|
+
weekNum: 0 | 2 | 1 | 3 | 4 | 5 | 6;
|
|
330
330
|
}[]>;
|
|
331
331
|
submitTimeSlot(timeSlots: TimeSliceItem): void;
|
|
332
332
|
private getScheduleDataByIds;
|
|
@@ -305,24 +305,29 @@ function withH5AuthPrefix(config) {
|
|
|
305
305
|
return nextConfig;
|
|
306
306
|
}
|
|
307
307
|
const h5OnlineStoreConfig = withH5AuthPrefix(onlineStoreConfig);
|
|
308
|
+
function withKioskSalesChannel(config) {
|
|
309
|
+
return {
|
|
310
|
+
...config,
|
|
311
|
+
options: params => {
|
|
312
|
+
const options = (typeof config.options === 'function' ? config.options(params) : config.options) || {};
|
|
313
|
+
const headers = typeof options.headers === 'function' ? options.headers(params) : options.headers;
|
|
314
|
+
return {
|
|
315
|
+
...options,
|
|
316
|
+
headers: {
|
|
317
|
+
...headers,
|
|
318
|
+
'Sales-Channel': 'kiosk'
|
|
319
|
+
}
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
};
|
|
323
|
+
}
|
|
308
324
|
|
|
309
|
-
// Kiosk
|
|
325
|
+
// Kiosk 复用网店顾客认证接口,通过注册(含验证码登录自动注册)请求头标识会员归属渠道。
|
|
310
326
|
const kioskConfig = {
|
|
311
327
|
...h5OnlineStoreConfig,
|
|
312
|
-
emailCodeRegister:
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
...h5OnlineStoreConfig.emailCodeRegister.transformParams(params),
|
|
316
|
-
login_channel: 'kiosk'
|
|
317
|
-
})
|
|
318
|
-
},
|
|
319
|
-
phoneCodeRegister: {
|
|
320
|
-
...h5OnlineStoreConfig.phoneCodeRegister,
|
|
321
|
-
transformParams: params => ({
|
|
322
|
-
...h5OnlineStoreConfig.phoneCodeRegister.transformParams(params),
|
|
323
|
-
login_channel: 'kiosk'
|
|
324
|
-
})
|
|
325
|
-
}
|
|
328
|
+
emailCodeRegister: withKioskSalesChannel(h5OnlineStoreConfig.emailCodeRegister),
|
|
329
|
+
phoneCodeRegister: withKioskSalesChannel(h5OnlineStoreConfig.phoneCodeRegister),
|
|
330
|
+
phoneCodeLogin: withKioskSalesChannel(h5OnlineStoreConfig.phoneCodeLogin)
|
|
326
331
|
};
|
|
327
332
|
|
|
328
333
|
/**
|
|
@@ -583,6 +588,16 @@ const defaultConfig = {
|
|
|
583
588
|
/**
|
|
584
589
|
* webpos 渠道配置
|
|
585
590
|
*/
|
|
591
|
+
const webposPasswordResetOptions = {
|
|
592
|
+
prefix: false,
|
|
593
|
+
isDefaultSaasDomain: true,
|
|
594
|
+
cache: false,
|
|
595
|
+
noToast: true,
|
|
596
|
+
headers: {
|
|
597
|
+
Accept: 'application/json',
|
|
598
|
+
'Content-Type': 'application/json'
|
|
599
|
+
}
|
|
600
|
+
};
|
|
586
601
|
const webposConfig = {
|
|
587
602
|
sendEmailVerificationCode: {
|
|
588
603
|
url: '/auth/email/register-code',
|
|
@@ -705,13 +720,20 @@ const webposConfig = {
|
|
|
705
720
|
})
|
|
706
721
|
},
|
|
707
722
|
checkEmailCode: {
|
|
708
|
-
url: '/
|
|
709
|
-
method: '
|
|
723
|
+
url: '/tenant/account/reset-password/email-code/verify',
|
|
724
|
+
method: 'POST',
|
|
710
725
|
transformParams: params => ({
|
|
711
726
|
email: params.email,
|
|
712
|
-
code: params.code
|
|
713
|
-
|
|
714
|
-
|
|
727
|
+
code: params.code
|
|
728
|
+
}),
|
|
729
|
+
// SaaS 成功时 data 为 null,统一为既有验证码校验返回结构。
|
|
730
|
+
transformResponse: response => response?.status === true ? {
|
|
731
|
+
...response,
|
|
732
|
+
data: {
|
|
733
|
+
valid: true
|
|
734
|
+
}
|
|
735
|
+
} : response,
|
|
736
|
+
options: webposPasswordResetOptions
|
|
715
737
|
},
|
|
716
738
|
checkMobileCode: {
|
|
717
739
|
url: '/auth/mobile/check-mobile-code',
|
|
@@ -733,11 +755,12 @@ const webposConfig = {
|
|
|
733
755
|
})
|
|
734
756
|
},
|
|
735
757
|
sendPasswordResetEmail: {
|
|
736
|
-
url: '/
|
|
737
|
-
method: '
|
|
758
|
+
url: '/tenant/account/reset-password/email-code/send',
|
|
759
|
+
method: 'POST',
|
|
738
760
|
transformParams: params => ({
|
|
739
761
|
email: params.email
|
|
740
|
-
})
|
|
762
|
+
}),
|
|
763
|
+
options: webposPasswordResetOptions
|
|
741
764
|
},
|
|
742
765
|
sendPasswordResetSms: {
|
|
743
766
|
url: '/auth/mobile/sms-forget',
|
|
@@ -770,13 +793,16 @@ const webposConfig = {
|
|
|
770
793
|
})
|
|
771
794
|
},
|
|
772
795
|
resetPasswordByEmail: {
|
|
773
|
-
url: '/
|
|
796
|
+
url: '/tenant/account/reset-password/email-code',
|
|
774
797
|
method: 'POST',
|
|
775
798
|
transformParams: params => ({
|
|
776
799
|
email: params.email,
|
|
777
800
|
password: params.password,
|
|
778
|
-
code: params.code
|
|
779
|
-
|
|
801
|
+
code: params.code,
|
|
802
|
+
// 页面先校验两次输入完全一致,SDK 保持原有重置参数。
|
|
803
|
+
password_confirmation: params.password
|
|
804
|
+
}),
|
|
805
|
+
options: webposPasswordResetOptions
|
|
780
806
|
},
|
|
781
807
|
resetPasswordByPhone: {
|
|
782
808
|
url: '/auth/mobile/reset-password',
|
|
@@ -4,6 +4,8 @@ import { RegisterParams, LoginParams, OAuthLoginParams, SendVerificationCodePara
|
|
|
4
4
|
import { ApiCaller } from './config';
|
|
5
5
|
export * from './types';
|
|
6
6
|
export * from './config';
|
|
7
|
+
export { validatePassword } from './utils';
|
|
8
|
+
export type { PasswordValidationOptions } from './utils';
|
|
7
9
|
/**
|
|
8
10
|
* 注册登录解决方案实现
|
|
9
11
|
*/
|
|
@@ -4,9 +4,16 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
var _exportNames = {
|
|
7
|
-
RegisterAndLoginImpl: true
|
|
7
|
+
RegisterAndLoginImpl: true,
|
|
8
|
+
validatePassword: true
|
|
8
9
|
};
|
|
9
10
|
exports.RegisterAndLoginImpl = void 0;
|
|
11
|
+
Object.defineProperty(exports, "validatePassword", {
|
|
12
|
+
enumerable: true,
|
|
13
|
+
get: function () {
|
|
14
|
+
return _utils.validatePassword;
|
|
15
|
+
}
|
|
16
|
+
});
|
|
10
17
|
var _BaseModule = require("../../modules/BaseModule");
|
|
11
18
|
var _types = require("./types");
|
|
12
19
|
Object.keys(_types).forEach(function (key) {
|
|
@@ -32,6 +39,7 @@ Object.keys(_config).forEach(function (key) {
|
|
|
32
39
|
}
|
|
33
40
|
});
|
|
34
41
|
});
|
|
42
|
+
var _utils = require("./utils");
|
|
35
43
|
/**
|
|
36
44
|
* 注册登录解决方案实现
|
|
37
45
|
*/
|
|
@@ -10,10 +10,18 @@ export declare function validateEmail(email: string): boolean;
|
|
|
10
10
|
* 验证手机号格式(支持多种国际格式)
|
|
11
11
|
*/
|
|
12
12
|
export declare function validatePhone(phone: string): boolean;
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
export interface PasswordValidationOptions {
|
|
14
|
+
minLength?: number;
|
|
15
|
+
maxLength?: number;
|
|
16
|
+
requireLowercase?: boolean;
|
|
17
|
+
requireUppercase?: boolean;
|
|
18
|
+
requireNumber?: boolean;
|
|
19
|
+
requireSpecialCharacters?: boolean;
|
|
20
|
+
specialCharactersPattern?: RegExp;
|
|
21
|
+
messages?: Partial<Record<'minLength' | 'maxLength' | 'requireLowercase' | 'requireUppercase' | 'requireNumber' | 'requireSpecialCharacters', string>>;
|
|
22
|
+
}
|
|
23
|
+
/** 验证密码强度;不传 options 时保持原有规则与返回格式。 */
|
|
24
|
+
export declare function validatePassword(password: string, options?: PasswordValidationOptions): {
|
|
17
25
|
isValid: boolean;
|
|
18
26
|
errors: string[];
|
|
19
27
|
};
|
|
@@ -49,29 +49,36 @@ function validatePhone(phone) {
|
|
|
49
49
|
const phoneRegex = /^(\+\d{1,3}[- ]?)?\d{10,14}$/;
|
|
50
50
|
return phoneRegex.test(cleanPhone);
|
|
51
51
|
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
* 验证密码强度
|
|
55
|
-
*/
|
|
56
|
-
function validatePassword(password) {
|
|
52
|
+
/** 验证密码强度;不传 options 时保持原有规则与返回格式。 */
|
|
53
|
+
function validatePassword(password, options = {}) {
|
|
57
54
|
const errors = [];
|
|
58
|
-
|
|
59
|
-
|
|
55
|
+
const {
|
|
56
|
+
minLength = 8,
|
|
57
|
+
maxLength = 128,
|
|
58
|
+
requireLowercase = true,
|
|
59
|
+
requireUppercase = true,
|
|
60
|
+
requireNumber = true,
|
|
61
|
+
requireSpecialCharacters = true,
|
|
62
|
+
specialCharactersPattern = /[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]/,
|
|
63
|
+
messages = {}
|
|
64
|
+
} = options;
|
|
65
|
+
if (password.length < minLength) {
|
|
66
|
+
errors.push(messages.minLength ?? `密码长度至少为${minLength}位`);
|
|
60
67
|
}
|
|
61
|
-
if (password.length >
|
|
62
|
-
errors.push(
|
|
68
|
+
if (password.length > maxLength) {
|
|
69
|
+
errors.push(messages.maxLength ?? `密码长度不能超过${maxLength}位`);
|
|
63
70
|
}
|
|
64
|
-
if (!/[a-z]/.test(password)) {
|
|
65
|
-
errors.push('密码必须包含至少一个小写字母');
|
|
71
|
+
if (requireLowercase && !/[a-z]/.test(password)) {
|
|
72
|
+
errors.push(messages.requireLowercase ?? '密码必须包含至少一个小写字母');
|
|
66
73
|
}
|
|
67
|
-
if (!/[A-Z]/.test(password)) {
|
|
68
|
-
errors.push('密码必须包含至少一个大写字母');
|
|
74
|
+
if (requireUppercase && !/[A-Z]/.test(password)) {
|
|
75
|
+
errors.push(messages.requireUppercase ?? '密码必须包含至少一个大写字母');
|
|
69
76
|
}
|
|
70
|
-
if (!/\d/.test(password)) {
|
|
71
|
-
errors.push('密码必须包含至少一个数字');
|
|
77
|
+
if (requireNumber && !/\d/.test(password)) {
|
|
78
|
+
errors.push(messages.requireNumber ?? '密码必须包含至少一个数字');
|
|
72
79
|
}
|
|
73
|
-
if (
|
|
74
|
-
errors.push('密码必须包含至少一个特殊字符');
|
|
80
|
+
if (requireSpecialCharacters && !specialCharactersPattern.test(password)) {
|
|
81
|
+
errors.push(messages.requireSpecialCharacters ?? '密码必须包含至少一个特殊字符');
|
|
75
82
|
}
|
|
76
83
|
return {
|
|
77
84
|
isValid: errors.length === 0,
|
|
@@ -105,6 +105,8 @@ export declare class SalesImpl extends BaseModule implements Module, SalesModule
|
|
|
105
105
|
private groupBookingsByResource;
|
|
106
106
|
private isBookingMatchedResource;
|
|
107
107
|
private sortMatchedBookings;
|
|
108
|
+
/** 已完成:按订单创建时间降序,避免实时追加和重新加载导致显示顺序不同。 */
|
|
109
|
+
private sortCompletedBookings;
|
|
108
110
|
/** Unpaid:与 Current 重合的订单沿用 Current 顺序,其余欠款订单按开始时间降序追加。 */
|
|
109
111
|
private sortUnpaidBookings;
|
|
110
112
|
/**
|
|
@@ -507,6 +507,15 @@ class SalesImpl extends _BaseModule.BaseModule {
|
|
|
507
507
|
});
|
|
508
508
|
}
|
|
509
509
|
|
|
510
|
+
/** 已完成:按订单创建时间降序,避免实时追加和重新加载导致显示顺序不同。 */
|
|
511
|
+
sortCompletedBookings(bookings) {
|
|
512
|
+
const createdAt = booking => {
|
|
513
|
+
const timestamp = (0, _dayjs.default)(booking.order?.created_at || '').valueOf();
|
|
514
|
+
return Number.isFinite(timestamp) ? timestamp : 0;
|
|
515
|
+
};
|
|
516
|
+
return [...bookings].sort((left, right) => createdAt(right) - createdAt(left));
|
|
517
|
+
}
|
|
518
|
+
|
|
510
519
|
/** Unpaid:与 Current 重合的订单沿用 Current 顺序,其余欠款订单按开始时间降序追加。 */
|
|
511
520
|
sortUnpaidBookings(bookings, currentBookings) {
|
|
512
521
|
const currentOrderIndexes = new Map(currentBookings.map((booking, index) => [String(booking.order.order_id), index]));
|
|
@@ -881,7 +890,7 @@ class SalesImpl extends _BaseModule.BaseModule {
|
|
|
881
890
|
const bookings = this.pickBookingsForCurrentPoint(current, this.normalizeResourceBookings(current, deviceCurrent, matchedBookingList), {
|
|
882
891
|
includeAllFuture: true
|
|
883
892
|
});
|
|
884
|
-
const completedBookings = completedBookingList.map(booking => {
|
|
893
|
+
const completedBookings = this.sortCompletedBookings(completedBookingList).map(booking => {
|
|
885
894
|
return {
|
|
886
895
|
...booking,
|
|
887
896
|
status: 'compelete',
|
|
@@ -283,10 +283,9 @@ function assertAvailabilityCatalogProductIds(productIds, context) {
|
|
|
283
283
|
});
|
|
284
284
|
}
|
|
285
285
|
}
|
|
286
|
-
function normalizeResourceType(
|
|
287
|
-
//
|
|
288
|
-
|
|
289
|
-
return 'single';
|
|
286
|
+
function normalizeResourceType(value) {
|
|
287
|
+
// 预约是否独占与每组选择几个资源是两个独立配置。
|
|
288
|
+
return value === 'multiple' || value === 'capacity' ? value : 'single';
|
|
290
289
|
}
|
|
291
290
|
function resolveProductKind(product) {
|
|
292
291
|
if (product.duration || product.service_duration) return 'duration';
|