@etsoo/appscript 1.6.55 → 1.6.57

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.
@@ -1,747 +0,0 @@
1
- import {
2
- DataTypes,
3
- IStorage,
4
- IdType,
5
- NumberUtils,
6
- WindowStorage
7
- } from "@etsoo/shared";
8
- import { Currency } from "./Currency";
9
-
10
- /**
11
- * Shopping cart owner
12
- * 购物篮所有人
13
- */
14
- export type ShoppingCartOwner = DataTypes.IdNameItem & {
15
- culture?: string;
16
- currency?: Currency;
17
- };
18
-
19
- /**
20
- * Shopping cart data
21
- * 购物篮数据
22
- */
23
- export type ShoppingCartData<T extends ShoppingCartItem> = {
24
- culture: string;
25
- currency: Currency;
26
- owner: ShoppingCartOwner;
27
- items: T[];
28
- promotions: ShoppingPromotion[];
29
- formData?: any;
30
- cache?: Record<string, unknown>;
31
- };
32
-
33
- /**
34
- * Shopping promotion
35
- * 购物促销
36
- */
37
- export type ShoppingPromotion = {
38
- /**
39
- * Promotion id
40
- * 促销编号
41
- */
42
- id: number;
43
-
44
- /**
45
- * Promotion title
46
- * 促销标题
47
- */
48
- title: string;
49
-
50
- /**
51
- * Discount amount
52
- * 折扣金额
53
- */
54
- amount: number;
55
- };
56
-
57
- /**
58
- * Shopping cart base item
59
- * 购物篮基础项目
60
- */
61
- export type ShoppingCartItemBase = {
62
- /**
63
- * Product id
64
- * 产品编号
65
- */
66
- id: IdType;
67
-
68
- /**
69
- * Product title, default is name
70
- * 产品标题,默认为name
71
- */
72
- title?: string;
73
-
74
- /**
75
- * Sale price
76
- * 销售价格
77
- */
78
- price: number;
79
-
80
- /**
81
- * Qty
82
- * 数量
83
- */
84
- qty: number;
85
-
86
- /**
87
- * Asset qty
88
- */
89
- assetQty?: number;
90
-
91
- /**
92
- * Product level promotions
93
- * 产品层次促销
94
- */
95
- promotions: ShoppingPromotion[];
96
- };
97
-
98
- /**
99
- * Shopping cart item
100
- * 购物篮项目
101
- */
102
- export type ShoppingCartItem = ShoppingCartItemBase & {
103
- /**
104
- * Product name
105
- * 产品名称
106
- */
107
- name: string;
108
-
109
- /**
110
- * Current price for cache
111
- * 当前缓存价格
112
- */
113
- currentPrice?: number;
114
-
115
- /**
116
- * Subtotal
117
- * 小计
118
- */
119
- subtotal: number;
120
-
121
- /**
122
- * Total discount amount
123
- * 总折扣金额
124
- */
125
- discount: number;
126
- };
127
-
128
- /**
129
- * Shopping cart change reason
130
- * 购物篮改变原因
131
- */
132
- export type ShoppingCartChangeReason =
133
- | "add"
134
- | "clear"
135
- | "remove"
136
- | "title"
137
- | "update";
138
-
139
- const ShoppingCartKeyField = "ETSOO-CART-KEYS";
140
-
141
- /**
142
- * Shopping cart
143
- * 购物篮
144
- */
145
- export class ShoppingCart<T extends ShoppingCartItem> {
146
- /**
147
- * Create identifier key
148
- * 创建识别键
149
- * @param currency Currency
150
- * @param culture Culture
151
- * @param key Additional key
152
- * @returns Result
153
- */
154
- static createKey(currency: Currency, culture: string, key: string) {
155
- return `ETSOO-CART-${culture}-${key}-${currency}`;
156
- }
157
-
158
- /**
159
- * Clear shopping cart
160
- * 清除购物篮
161
- * @param identifier Identifier
162
- * @param storage Storage
163
- */
164
- static clear(identifier: string, storage: IStorage) {
165
- try {
166
- storage.setData(identifier, null);
167
- storage.setPersistedData(identifier, null);
168
- } catch (error) {
169
- console.warn(`ShoppingCart clear ${identifier} error`, error);
170
- }
171
- }
172
-
173
- /**
174
- * Get cart data
175
- * 获取购物篮数据
176
- * @param storage Storage
177
- * @param id Cart id
178
- * @returns Result
179
- */
180
- static getCartData<D extends ShoppingCartItem>(
181
- storage: IStorage,
182
- id: string
183
- ) {
184
- try {
185
- return (
186
- storage.getPersistedObject<ShoppingCartData<D>>(id) ??
187
- storage.getObject<ShoppingCartData<D>>(id)
188
- );
189
- } catch (error) {
190
- console.warn(`ShoppingCart getCartData ${id} error`, error);
191
- }
192
- }
193
-
194
- /**
195
- * Owner data
196
- * 所有者信息
197
- */
198
- owner?: ShoppingCartOwner;
199
-
200
- _currency!: Currency;
201
-
202
- /**
203
- * ISO currency id
204
- * 标准货币编号
205
- */
206
- get currency() {
207
- return this._currency;
208
- }
209
- private set currency(value: Currency) {
210
- this._currency = value;
211
- }
212
-
213
- _culture!: string;
214
- /**
215
- * ISO culture id, like zh-Hans
216
- * 标准语言文化编号
217
- */
218
- get culture() {
219
- return this._culture;
220
- }
221
- private set culture(value: string) {
222
- this._culture = value;
223
- }
224
-
225
- _items: T[] = [];
226
-
227
- /**
228
- * Items
229
- * 项目
230
- */
231
- get items() {
232
- return this._items;
233
- }
234
- private set items(value) {
235
- this._items = value;
236
- }
237
-
238
- _promotions: ShoppingPromotion[] = [];
239
- /**
240
- * Order level promotions
241
- * 订单层面促销
242
- */
243
- get promotions() {
244
- return this._promotions;
245
- }
246
- private set promotions(value) {
247
- this._promotions = value;
248
- }
249
-
250
- /**
251
- * Related form data
252
- * 关联的表单数据
253
- */
254
- formData: any;
255
-
256
- /**
257
- * Cache
258
- * 缓存对象
259
- */
260
- cache?: Record<string, unknown>;
261
-
262
- _symbol: string | undefined;
263
- /**
264
- * Currency symbol
265
- * 币种符号
266
- */
267
- get symbol() {
268
- return this._symbol;
269
- }
270
- private set symbol(value: string | undefined) {
271
- this._symbol = value;
272
- }
273
-
274
- /**
275
- * Key for identifier
276
- */
277
- readonly key: string;
278
-
279
- /**
280
- * Cart identifier
281
- * 购物篮标识
282
- */
283
- get identifier() {
284
- const o = this.owner;
285
- return ShoppingCart.createKey(this.currency, this.culture, this.key);
286
- }
287
-
288
- /**
289
- * All data keys
290
- * 所有的数据键
291
- */
292
- get keys() {
293
- return this.storage.getPersistedData<string[]>(ShoppingCartKeyField, []);
294
- }
295
- set keys(items: string[]) {
296
- this.storage.setPersistedData(ShoppingCartKeyField, items);
297
- }
298
-
299
- /**
300
- * Lines count
301
- * 项目数量
302
- */
303
- get lines() {
304
- return this.items.length;
305
- }
306
-
307
- /**
308
- * Total qty
309
- * 总数量
310
- */
311
- get totalQty() {
312
- return this.items.map((item) => item.qty).sum();
313
- }
314
-
315
- /**
316
- * Total amount
317
- * 总金额
318
- */
319
- get totalAmount() {
320
- const subtotal = this.items
321
- .map((item) => item.subtotal - item.discount)
322
- .sum();
323
-
324
- const discount = this.promotions.sum("amount");
325
-
326
- return subtotal - discount;
327
- }
328
-
329
- /**
330
- * Total amount string
331
- * 总金额字符串
332
- */
333
- get totalAmountStr() {
334
- return this.formatAmount(this.totalAmount);
335
- }
336
-
337
- /**
338
- * Cached prices
339
- * 缓存的价格
340
- */
341
- private prices = <Record<T["id"], number>>{};
342
-
343
- /**
344
- * Onchange callback
345
- * 改变时回调
346
- */
347
- onChange?: (reason: ShoppingCartChangeReason, changedItems: T[]) => void;
348
-
349
- /**
350
- * Constructor
351
- * 构造函数
352
- * @param key Key for identifier
353
- * @param init Currency & culture ISO code array
354
- * @param storage Data storage
355
- */
356
- constructor(key: string, init: [Currency, string], storage?: IStorage);
357
-
358
- /**
359
- * Constructor
360
- * 构造函数
361
- * @param key Key for identifier
362
- * @param state Initialization state
363
- * @param storage Data storage
364
- */
365
- constructor(key: string, state: ShoppingCartData<T>, storage?: IStorage);
366
-
367
- /**
368
- * Constructor
369
- * 构造函数
370
- * @param key Key for identifier
371
- * @param currency Currency ISO code
372
- * @param storage Data storage
373
- */
374
- constructor(
375
- key: string,
376
- currencyOrState: [Currency, string] | ShoppingCartData<T>,
377
- private readonly storage: IStorage = new WindowStorage()
378
- ) {
379
- this.key = key;
380
-
381
- if (Array.isArray(currencyOrState)) {
382
- this.reset(currencyOrState[0], currencyOrState[1]);
383
- } else {
384
- this.setCartData(currencyOrState);
385
- this.changeCurrency(currencyOrState.currency);
386
- this.changeCulture(currencyOrState.culture);
387
- }
388
- }
389
- private getCartData(): ShoppingCartData<T> | undefined {
390
- return (
391
- this.storage.getPersistedObject(this.identifier) ??
392
- this.storage.getObject(this.identifier)
393
- );
394
- }
395
-
396
- private setCartData(state: ShoppingCartData<T> | undefined) {
397
- const { owner, items = [], promotions = [], formData, cache } = state ?? {};
398
- this.owner = owner;
399
- this.items = items;
400
- this.promotions = promotions;
401
- this.formData = formData;
402
- this.cache = cache;
403
- }
404
-
405
- private doChange(reason: ShoppingCartChangeReason, changedItems: T[]) {
406
- if (this.onChange) this.onChange(reason, changedItems);
407
- }
408
-
409
- /**
410
- * Add item
411
- * 添加项目
412
- * @param item New item
413
- */
414
- addItem(item: T) {
415
- this.addItems([item]);
416
- }
417
-
418
- /**
419
- * Add items
420
- * @param items New items
421
- */
422
- addItems(items: T[]) {
423
- this.items.push(...items);
424
- this.doChange("add", items);
425
- }
426
-
427
- /**
428
- * Cache price
429
- * @param id Item id
430
- * @param price Price
431
- * @param overrideExisting Override existing price
432
- */
433
- cachePrice(id: T["id"], price: number, overrideExisting: boolean = false) {
434
- if (overrideExisting || this.prices[id] == null) this.prices[id] = price;
435
- }
436
-
437
- /**
438
- * Change currency
439
- * @param currency Currency
440
- */
441
- changeCurrency(currency: Currency) {
442
- this.currency = currency;
443
- this.symbol = NumberUtils.getCurrencySymbol(this.currency);
444
- }
445
-
446
- /**
447
- * Change culture
448
- * @param culture Culture
449
- */
450
- changeCulture(culture: string) {
451
- this.culture = culture;
452
- }
453
-
454
- /**
455
- * Clear storage
456
- * @param keepOwner Keep owner data
457
- */
458
- clear(keepOwner?: boolean) {
459
- this.items.length = 0;
460
- this.promotions.length = 0;
461
- this.prices = <Record<T["id"], number>>{};
462
- this.cache = undefined;
463
-
464
- if (keepOwner) {
465
- this.save();
466
- } else {
467
- ShoppingCart.clear(this.identifier, this.storage);
468
- this.keys.remove(this.identifier);
469
- }
470
-
471
- this.doChange("clear", []);
472
- }
473
-
474
- /**
475
- * Format amount
476
- * @param amount Amount
477
- * @returns Result
478
- */
479
- formatAmount(amount: number) {
480
- return NumberUtils.formatMoney(amount, this.currency);
481
- }
482
-
483
- /**
484
- * Get item
485
- * @param id Item id
486
- * @returns Result
487
- */
488
- getItem(id: T["id"]) {
489
- return this.items.find((item) => item.id === id);
490
- }
491
-
492
- /**
493
- * Push item
494
- * 推送项目
495
- * @param data Item data
496
- * @returns Added or not
497
- */
498
- pushItem(data: T) {
499
- if (this.items.some((item) => item.id === data.id)) {
500
- return false;
501
- } else {
502
- this.addItem(data);
503
- return true;
504
- }
505
- }
506
-
507
- /**
508
- * Reset currency and culture
509
- * @param currency New currency
510
- * @param culture New culture
511
- */
512
- reset(currency: Currency, culture: string) {
513
- this.changeCurrency(currency);
514
- this.changeCulture(culture);
515
- this.setCartData(this.getCartData());
516
- }
517
-
518
- /**
519
- * Remove item from the index
520
- * @param index Item index
521
- */
522
- removeItem(index: number) {
523
- const removedItems = this.items.splice(index, 1);
524
- this.doChange("remove", removedItems);
525
- }
526
-
527
- /**
528
- * Reset item
529
- * @param item Shopping cart item
530
- */
531
- resetItem(item: ShoppingCartItem) {
532
- item.discount = 0;
533
- item.currentPrice = undefined;
534
- item.promotions = [];
535
- }
536
-
537
- /**
538
- * Save cart data
539
- * @param persisted For persisted storage
540
- */
541
- save(persisted: boolean = true) {
542
- if (this.owner == null) return;
543
-
544
- const { currency, culture, owner, items, promotions, formData, cache } =
545
- this;
546
- const data: ShoppingCartData<T> = {
547
- currency,
548
- culture,
549
- owner,
550
- items,
551
- promotions,
552
- formData,
553
- cache
554
- };
555
-
556
- try {
557
- if (persisted) {
558
- this.storage.setPersistedData(this.identifier, data);
559
-
560
- const keys = this.keys;
561
- if (!keys.includes(this.identifier)) {
562
- keys.push(this.identifier);
563
- this.keys = keys;
564
- }
565
- } else {
566
- this.storage.setData(this.identifier, data);
567
- }
568
- } catch (error) {
569
- console.warn(`ShoppingCart save ${this.identifier} error`, error);
570
- }
571
-
572
- return data;
573
- }
574
-
575
- /**
576
- * Trigger update
577
- * 触发更新
578
- */
579
- update() {
580
- this.doChange("update", []);
581
- }
582
-
583
- /**
584
- * Update discount
585
- * @param item Shopping cart item
586
- */
587
- updateDiscount(item: ShoppingCartItem) {
588
- item.discount = item.promotions.sum("amount");
589
- }
590
-
591
- /**
592
- * Update asset item
593
- * 更新资产项目
594
- * @param id Product id
595
- * @param qty Asset qty
596
- * @param itemCreator New item creator
597
- * @returns Updated or not
598
- */
599
- updateAssetItem(
600
- id: T["id"],
601
- assetQty: number | undefined,
602
- itemCreator?: () => Omit<
603
- T,
604
- "id" | "price" | "assetQty" | "subtotal" | "discount" | "promotions"
605
- >
606
- ) {
607
- if (assetQty == null || assetQty <= 0) assetQty = 1;
608
-
609
- const index = this.items.findIndex((item) => item.id === id);
610
- if (index === -1) {
611
- // New
612
- if (itemCreator) {
613
- const price = this.prices[id];
614
- const data = itemCreator();
615
- const qty = data.qty;
616
- const newItem = {
617
- ...data,
618
- id,
619
- price,
620
- assetQty,
621
- subtotal: price * qty * assetQty,
622
- discount: 0,
623
- promotions: Array<ShoppingPromotion>()
624
- } as T;
625
- this.addItem(newItem);
626
- }
627
- return false;
628
- } else {
629
- // Update
630
- const item = this.items[index];
631
-
632
- // Price may be cached first
633
- const price = this.prices[id] ?? item.price;
634
- const qty = item.qty;
635
-
636
- const newItem = {
637
- ...item,
638
- price,
639
- assetQty,
640
- subtotal: price * qty * assetQty,
641
- discount: 0
642
- };
643
- this.items.splice(index, 1, newItem);
644
- this.doChange("update", [item, newItem]);
645
- }
646
-
647
- return true;
648
- }
649
-
650
- /**
651
- * Update item
652
- * 更新项目
653
- * @param id Product id
654
- * @param qty Qty
655
- * @param itemCreator New item creator
656
- * @returns Updated or not
657
- */
658
- updateItem(
659
- id: T["id"],
660
- qty: number | undefined,
661
- itemCreator?: () => Omit<
662
- T,
663
- "id" | "price" | "qty" | "subtotal" | "discount" | "promotions"
664
- >
665
- ) {
666
- const index = this.items.findIndex((item) => item.id === id);
667
- if (qty == null) {
668
- // Remove the item
669
- if (index !== -1) {
670
- this.removeItem(index);
671
- }
672
- } else if (index === -1) {
673
- // New
674
- if (itemCreator) {
675
- const price = this.prices[id];
676
- const data = itemCreator();
677
- const newItem = {
678
- ...data,
679
- id,
680
- price,
681
- qty,
682
- subtotal: price * qty * (data.assetQty || 1),
683
- discount: 0,
684
- promotions: Array<ShoppingPromotion>()
685
- } as T;
686
- this.addItem(newItem);
687
- }
688
- return false;
689
- } else {
690
- // Update
691
- const item = this.items[index];
692
-
693
- // Price may be cached first
694
- const price = this.prices[id] ?? item.price;
695
- const newItem = {
696
- ...item,
697
- qty,
698
- price,
699
- subtotal: price * qty * (item.assetQty || 1),
700
- discount: 0
701
- };
702
- this.items.splice(index, 1, newItem);
703
- this.doChange("update", [item, newItem]);
704
- }
705
-
706
- return true;
707
- }
708
-
709
- /**
710
- * Update price
711
- * @param id Item id
712
- * @param price New price
713
- */
714
- updatePrice(id: T["id"], price: number) {
715
- this.cachePrice(id, price, true);
716
-
717
- const index = this.items.findIndex((item) => item.id === id);
718
- if (index !== -1) {
719
- const item = this.items[index];
720
- const qty = item.qty;
721
- const assetQty = item.assetQty || 1;
722
- const newItem = {
723
- ...item,
724
- price,
725
- subtotal: price * qty * assetQty
726
- };
727
- this.items.splice(index, 1, newItem);
728
- this.doChange("update", [item, newItem]);
729
- }
730
- }
731
-
732
- /**
733
- * Update title
734
- * @param id Item id
735
- * @param title New title
736
- */
737
- updateTitle(id: T["id"], title: string) {
738
- const index = this.items.findIndex((item) => item.id === id);
739
- if (index !== -1) {
740
- const item = this.items[index];
741
- const newItem: T = { ...item, title };
742
- if (newItem.name === newItem.title) newItem.title = undefined;
743
- this.items.splice(index, 1, newItem);
744
- this.doChange("title", [item, newItem]);
745
- }
746
- }
747
- }