ts-glitter 21.7.7 → 21.7.8

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 (49) hide show
  1. package/lowcode/Entry.js +1 -1
  2. package/lowcode/Entry.ts +1 -1
  3. package/lowcode/backend-manager/bg-blog.js +1 -6
  4. package/lowcode/backend-manager/bg-blog.ts +1 -11
  5. package/lowcode/backend-manager/bg-dialog.js +176 -0
  6. package/lowcode/backend-manager/bg-dialog.ts +230 -0
  7. package/lowcode/backend-manager/bg-product.js +1 -3
  8. package/lowcode/backend-manager/bg-product.ts +1 -5
  9. package/lowcode/cms-plugin/module/delivery-html.js +56 -5
  10. package/lowcode/cms-plugin/module/delivery-html.ts +69 -14
  11. package/lowcode/cms-plugin/shopping-discount-setting.js +234 -1468
  12. package/lowcode/cms-plugin/shopping-discount-setting.ts +302 -1732
  13. package/lowcode/jslib/lottie-player.js +143 -39
  14. package/lowcode/public-components/product/pd-card-01.js +1 -1
  15. package/lowcode/public-components/product/pd-card-01.ts +5 -8
  16. package/lowcode/public-components/product/pd-card-02.js +3 -2
  17. package/lowcode/public-components/product/pd-card-02.ts +7 -10
  18. package/lowcode/public-components/product/pd-card-03.js +3 -2
  19. package/lowcode/public-components/product/pd-card-03.ts +7 -9
  20. package/package.json +1 -1
  21. package/src/api-public/services/checkout-event.js +17 -7
  22. package/src/api-public/services/checkout-event.js.map +5 -1
  23. package/src/api-public/services/financial-serviceV2.js +17 -7
  24. package/src/api-public/services/financial-serviceV2.js.map +1 -1
  25. package/src/api-public/services/monitor.d.ts +0 -1
  26. package/src/api-public/services/shopee.js +17 -7
  27. package/src/api-public/services/shopee.js.map +1 -1
  28. package/src/api-public/services/shopping.js +3 -3
  29. package/src/api-public/services/shopping.js.map +1 -1
  30. package/src/api-public/services/shopping.ts +3 -5
  31. package/src/api-public/services/user.js +17 -7
  32. package/src/api-public/services/user.js.map +5 -1
  33. package/src/app-project/serverless/src/index.js +17 -7
  34. package/src/app-project/serverless/src/index.js.map +1 -1
  35. package/src/app-project/serverless/src/modules/CryptoJS.js +17 -7
  36. package/src/app-project/serverless/src/modules/CryptoJS.js.map +1 -1
  37. package/src/app-project/serverless/src/modules/ssh.js +17 -7
  38. package/src/app-project/serverless/src/modules/ssh.js.map +1 -1
  39. package/src/index.js +3 -3
  40. package/src/modules/CryptoJS.js +17 -7
  41. package/src/modules/CryptoJS.js.map +1 -1
  42. package/src/modules/database.d.ts +1 -1
  43. package/src/modules/ssh.js +17 -7
  44. package/src/modules/ssh.js.map +1 -1
  45. package/src/modules/tool.d.ts +4 -4
  46. package/src/modules/tool.js +1 -2
  47. package/src/modules/tool.js.map +1 -1
  48. package/src/services/app.js +17 -7
  49. package/src/services/app.js.map +1 -1
@@ -8,6 +8,7 @@ import { ShareDialog } from '../glitterBundle/dialog/ShareDialog.js';
8
8
  import { FilterOptions } from './filter-options.js';
9
9
  import { Tool } from '../modules/tool.js';
10
10
  import { ShipmentConfig } from '../glitter-base/global/shipment-config.js';
11
+ import { BgDialog } from '../backend-manager/bg-dialog.js';
11
12
 
12
13
  type VoucherForType = 'all' | 'collection' | 'product' | 'manager_tag';
13
14
  type RebackType = 'rebate' | 'discount' | 'shipment_free' | 'add_on_items' | 'giveaway';
@@ -62,18 +63,7 @@ interface VoucherData {
62
63
  const html = String.raw;
63
64
 
64
65
  export class ShoppingDiscountSetting {
65
- public static getLabel(voucher_type: string): string {
66
- const labels: Record<string, string> = {
67
- giveaway: '贈品活動',
68
- add_on_items: '加價購活動',
69
- discount: '折扣活動',
70
- rebate: '回饋金活動',
71
- shipment_free: '免運費活動',
72
- };
73
- return labels[voucher_type] ?? '未知活動';
74
- }
75
-
76
- public static main(gvc: GVC, voucher_type: RebackType) {
66
+ static main(gvc: GVC, voucher_type: RebackType) {
77
67
  const glitter = gvc.glitter;
78
68
  const dialog = new ShareDialog(glitter);
79
69
  const vm: {
@@ -246,7 +236,25 @@ export class ShoppingDiscountSetting {
246
236
  });
247
237
  }
248
238
 
249
- public static getDateTime = (n = 0) => {
239
+ static productForList = [
240
+ { title: '所有商品', value: 'all' },
241
+ { title: '商品分類', value: 'collection' },
242
+ { title: '管理員標籤', value: 'manager_tag' },
243
+ { title: '特定商品', value: 'product' },
244
+ ];
245
+
246
+ static getLabel(voucher_type: string): string {
247
+ const labels: Record<string, string> = {
248
+ giveaway: '贈品活動',
249
+ add_on_items: '加價購活動',
250
+ discount: '折扣活動',
251
+ rebate: '回饋金活動',
252
+ shipment_free: '免運費活動',
253
+ };
254
+ return labels[voucher_type] ?? '未知活動';
255
+ }
256
+
257
+ static getDateTime = (n = 0) => {
250
258
  const now = new Date();
251
259
  now.setDate(now.getDate() + n);
252
260
  const year = now.getFullYear();
@@ -258,13 +266,6 @@ export class ShoppingDiscountSetting {
258
266
  return { date: dateStr, time: timeStr };
259
267
  };
260
268
 
261
- static productForList = [
262
- { title: '所有商品', value: 'all' },
263
- { title: '商品分類', value: 'collection' },
264
- { title: '管理員標籤', value: 'manager_tag' },
265
- { title: '特定商品', value: 'product' },
266
- ];
267
-
268
269
  static emptyVoucher = (reBackType: RebackType) => {
269
270
  return {
270
271
  title: '',
@@ -364,1678 +365,7 @@ export class ShoppingDiscountSetting {
364
365
  ];
365
366
  }
366
367
 
367
- public static voucherEditor(obj: { vm: any; gvc: GVC; type?: 'add' | 'replace'; defData?: any; reBackType: string }) {
368
- const gvc = obj.gvc;
369
- const glitter = gvc.glitter;
370
- const vm = obj.vm;
371
- const dialog = new ShareDialog(glitter);
372
-
373
- const pageVM = {
374
- viewID: gvc.glitter.getUUID(),
375
- conditionID: gvc.glitter.getUUID(),
376
- countingID: gvc.glitter.getUUID(),
377
- productOffID: gvc.glitter.getUUID(),
378
- };
379
-
380
- const voucherData: VoucherData = {
381
- title: '',
382
- code: '',
383
- trigger: 'auto',
384
- method: 'fixed',
385
- value: '0',
386
- for: 'all',
387
- forKey: [],
388
- device: ['normal'],
389
- rule: 'min_price',
390
- ruleValue: 1000,
391
- startDate: this.getDateTime().date,
392
- startTime: this.getDateTime().time,
393
- endDate: undefined,
394
- endTime: undefined,
395
- status: 1,
396
- type: 'voucher',
397
- overlay: false,
398
- start_ISO_Date: '',
399
- end_ISO_Date: '',
400
- reBackType: obj.reBackType,
401
- rebateEndDay: '30',
402
- target: 'all',
403
- targetList: [],
404
- macroLimited: 0,
405
- microLimited: 0,
406
- counting: 'single',
407
- conditionType: 'order',
408
- includeDiscount: 'before',
409
- productOffStart: 'price_desc',
410
- selectShipment: { type: 'all', list: [] },
411
- ...vm.data,
412
- };
413
-
414
- function summaryTextList() {
415
- return [
416
- `活動標題:${voucherData.title && voucherData.title.length > 0 ? voucherData.title : '尚無標題'}`,
417
- `適用商品:${(() => {
418
- const forData = ShoppingDiscountSetting.productForList.find(item => item.value === voucherData.for);
419
- return forData ? forData.title : '';
420
- })()}`,
421
- `活動方式:${(() => {
422
- if (voucherData.trigger === 'auto') return '自動折扣';
423
- if (voucherData.trigger === 'distribution') return '分銷連結';
424
- if (voucherData.trigger === 'code') return `優惠代碼「${voucherData.code ?? ''}」`;
425
- return '';
426
- })()}`,
427
- `活動對象:${(() => {
428
- const targetMapping: Record<string, string> = {
429
- customer: '特定顧客',
430
- levels: '會員等級',
431
- group: '顧客分群',
432
- all: '所有顧客',
433
- };
434
-
435
- return targetMapping[voucherData.target] || targetMapping.all;
436
- })()}`,
437
- '',
438
- `消費條件:${(() => {
439
- if (voucherData.rule === 'min_price') return '最少消費金額';
440
- if (voucherData.rule === 'min_count') return '最少購買數量';
441
- return '';
442
- })()}`,
443
- `條件值:${(() => {
444
- if (voucherData.rule === 'min_price') return voucherData.ruleValue + ' 元';
445
- if (voucherData.rule === 'min_count') return voucherData.ruleValue + ' 個';
446
- return '';
447
- })()}`,
448
- `折扣優惠:${(() => {
449
- const voucherMessages: { [key: string]: (method: string, value: string) => string } = {
450
- rebate: (method, value) =>
451
- method === 'fixed' ? `${value} 點購物金` : `符合條件商品總額的 ${value} %作為購物金`,
452
- discount: (method, value) => (method === 'fixed' ? `折扣 ${value} 元` : `符合條件商品折扣 ${value} %`),
453
- shipment_free: () => '免運費',
454
- };
455
-
456
- const messageFunction = voucherMessages[voucherData.reBackType];
457
- return messageFunction ? messageFunction(voucherData.method, voucherData.value) : '';
458
- })()}`,
459
- `將此優惠套用至:${(() => {
460
- const length = voucherData.forKey?.length ?? 0;
461
- const forMaps: Record<string, string> = {
462
- collection: `指定 ${length} 種商品分類`,
463
- product: `指定 ${length} 個商品`,
464
- all: '所有商品',
465
- };
466
- return forMaps[voucherData.for] || forMaps.all;
467
- })()}`,
468
- '',
469
- voucherData.overlay ? '可以疊加,套用最大優惠' : '不可疊加',
470
- `啟用時間:${voucherData.startDate ?? '未設定日期'} ${voucherData.startTime ?? '尚未設定時間'}`,
471
- `結束時間:${(() => {
472
- if (!voucherData.endDate) return '無期限';
473
- return `${voucherData.endDate ?? '未設定日期'} ${voucherData.endTime ?? '尚未設定時間'}`;
474
- })()}`,
475
- ];
476
- }
477
-
478
- function title() {
479
- return html` <div class="tx_700">活動標題</div>
480
- ${BgWidget.mbContainer(18)}
481
- ${BgWidget.editeInput({
482
- gvc: gvc,
483
- title: '',
484
- default: voucherData.title,
485
- placeHolder: '請輸入活動標題',
486
- callback: text => {
487
- voucherData.title = text;
488
- },
489
- })}
490
- ${BgWidget.grayNote('顧客將會在「購物車」與「結帳」看見此標題', 'margin-left: 4px;')}`;
491
- }
492
-
493
- function status() {
494
- return html` <div class="tx_700">活動狀態</div>
495
- ${BgWidget.mbContainer(12)}
496
- ${BgWidget.switchTextButton(
497
- gvc,
498
- voucherData.status === 1,
499
- {
500
- left: '關閉',
501
- right: '啟用',
502
- },
503
- bool => {
504
- voucherData.status = bool ? 1 : 0;
505
- }
506
- )}`;
507
- }
508
-
509
- function trigger() {
510
- return html` <div class="tx_700">活動方式</div>
511
- ${BgWidget.mbContainer(18)}
512
- ${BgWidget.multiCheckboxContainer(
513
- gvc,
514
- [
515
- {
516
- key: 'auto',
517
- name: '自動折扣',
518
- innerHtml: BgWidget.grayNote('顧客將在結帳時,自動獲得折扣'),
519
- },
520
- {
521
- key: 'code',
522
- name: '優惠代碼',
523
- innerHtml: (() => {
524
- const id = glitter.getUUID();
525
- return gvc.bindView({
526
- bind: id,
527
- view: () =>
528
- gvc.map([
529
- BgWidget.grayNote('顧客可在結帳時輸入優惠代碼,來獲得折扣'),
530
- BgWidget.editeInput({
531
- gvc: gvc,
532
- title: '',
533
- default: voucherData.code ?? '',
534
- placeHolder: '請輸入優惠券代碼',
535
- callback: text => {
536
- voucherData.code = text.toUpperCase();
537
- },
538
- endText: html` <div class="d-flex justify-content-end">
539
- ${BgWidget.mbContainer(8)}
540
- ${BgWidget.blueNote(
541
- document.body.clientWidth > 768 ? '隨機產生優惠代碼' : '隨機產生',
542
- gvc.event(() => {
543
- voucherData.code = Tool.randomString(6).toUpperCase();
544
- gvc.notifyDataChange(id);
545
- })
546
- )}
547
- </div>`,
548
- }),
549
- ]),
550
- });
551
- })(),
552
- },
553
- {
554
- key: 'distribution',
555
- name: '供分銷連結或一頁式網頁使用',
556
- },
557
- ],
558
- [voucherData.trigger],
559
- text => {
560
- if (text[0] === 'auto') {
561
- voucherData.code = undefined;
562
- }
563
- if (text[0] === 'distribution') {
564
- voucherData.for = 'all';
565
- }
566
- voucherData.trigger = text[0] as Trigger;
567
- gvc.notifyDataChange(pageVM.viewID);
568
- },
569
- { single: true }
570
- )}`;
571
- }
572
-
573
- function target() {
574
- return html` <div class="tx_700">活動對象</div>
575
- ${BgWidget.mbContainer(18)}
576
- ${gvc.bindView(() => {
577
- const id = gvc.glitter.getUUID();
578
- return {
579
- bind: id,
580
- view: () => {
581
- return html`
582
- <div style="display: flex; flex-direction: column; gap: 8px;">
583
- ${BgWidget.selectFilter({
584
- gvc: gvc,
585
- callback: text => {
586
- voucherData.target = text;
587
- gvc.notifyDataChange(id);
588
- },
589
- default: voucherData.target ?? 'all',
590
- options: [
591
- {
592
- key: 'all',
593
- value: '所有顧客',
594
- },
595
- {
596
- key: 'customer',
597
- value: '特定顧客',
598
- },
599
- {
600
- key: 'levels',
601
- value: '會員等級',
602
- },
603
- // {
604
- // key: 'group',
605
- // value: '顧客分群',
606
- // },
607
- ],
608
- style: 'width: 100%;',
609
- })}
610
- <div>
611
- ${(() => {
612
- switch (voucherData.target) {
613
- case 'all':
614
- return '';
615
- case 'customer':
616
- return gvc.bindView(() => {
617
- const customVM = {
618
- id: gvc.glitter.getUUID(),
619
- loading: true,
620
- dataList: [] as OptionsItem[],
621
- };
622
- return {
623
- bind: customVM.id,
624
- view: () => {
625
- if (customVM.loading) {
626
- return BgWidget.spinner();
627
- }
628
- return html`
629
- <div class="d-flex flex-column p-2" style="gap: 18px;">
630
- <div
631
- class="d-flex align-items-center gray-bottom-line-18"
632
- style="justify-content: space-between;"
633
- >
634
- <div class="form-check-label c_updown_label">
635
- <div class="tx_normal">顧客名稱</div>
636
- </div>
637
- ${BgWidget.grayButton(
638
- '查看全部',
639
- gvc.event(() => {
640
- BgWidget.selectDropDialog({
641
- gvc: gvc,
642
- title: '搜尋特定顧客',
643
- tag: 'select_users',
644
- updownOptions: FilterOptions.userOrderBy,
645
- callback: value => {
646
- voucherData.targetList = value;
647
- customVM.loading = true;
648
- gvc.notifyDataChange(customVM.id);
649
- },
650
- default: (voucherData.targetList ?? []).map(id => id.toString()),
651
- api: (data: { query: string; orderString: string }) => {
652
- return new Promise(resolve => {
653
- ApiUser.getUserListOrders({
654
- page: 0,
655
- limit: 99999,
656
- search: data.query,
657
- orderString: data.orderString,
658
- only_id: true,
659
- }).then(dd => {
660
- if (dd.response.data) {
661
- resolve(
662
- dd.response.data.map(
663
- (item: {
664
- userID: number;
665
- userData: {
666
- name: string;
667
- email: string;
668
- };
669
- }) => {
670
- return {
671
- key: item.userID,
672
- value: item.userData.name ?? '(尚無姓名)',
673
- note: item.userData.email,
674
- };
675
- }
676
- )
677
- );
678
- }
679
- });
680
- });
681
- },
682
- style: 'width: 100%;',
683
- });
684
- }),
685
- { textStyle: 'font-weight: 400;' }
686
- )}
687
- </div>
688
- ${obj.gvc.map(
689
- customVM.dataList.map((opt: OptionsItem, index) => {
690
- return html` <div class="form-check-label c_updown_label">
691
- <span class="tx_normal">${index + 1}. ${opt.value}</span>
692
- ${opt.note ? html` <span class="tx_gray_12 ms-2">${opt.note}</span> ` : ''}
693
- </div>`;
694
- })
695
- )}
696
- </div>
697
- `;
698
- },
699
- onCreate: () => {
700
- if (customVM.loading) {
701
- if (voucherData.targetList.length === 0) {
702
- setTimeout(() => {
703
- customVM.dataList = [];
704
- customVM.loading = false;
705
- gvc.notifyDataChange(customVM.id);
706
- }, 200);
707
- } else {
708
- ApiUser.getUserList({
709
- page: 0,
710
- limit: 99999,
711
- id: voucherData.targetList.join(','),
712
- only_id: true,
713
- }).then(dd => {
714
- if (dd.response.data) {
715
- customVM.dataList = dd.response.data.map(
716
- (item: {
717
- userID: string;
718
- userData: {
719
- name: string;
720
- email: string;
721
- };
722
- }) => {
723
- return {
724
- key: item.userID,
725
- value: item.userData.name,
726
- note: item.userData.email,
727
- };
728
- }
729
- );
730
- }
731
- customVM.loading = false;
732
- gvc.notifyDataChange(customVM.id);
733
- });
734
- }
735
- }
736
- },
737
- };
738
- });
739
- case 'levels':
740
- return (() => {
741
- const levelVM = {
742
- id: gvc.glitter.getUUID(),
743
- loading: true,
744
- dataList: [],
745
- };
746
- return gvc.bindView({
747
- bind: levelVM.id,
748
- view: () => {
749
- if (levelVM.loading) {
750
- return BgWidget.spinner({ text: { visible: false } });
751
- } else {
752
- return BgWidget.selectDropList({
753
- gvc: gvc,
754
- callback: value => {
755
- voucherData.targetList = value;
756
- gvc.notifyDataChange(id);
757
- },
758
- default: (voucherData.targetList ?? []).map(id => id.toString()),
759
- options: levelVM.dataList,
760
- style: 'width: 100%;',
761
- });
762
- }
763
- },
764
- divCreate: {
765
- style: 'width: 100%;',
766
- },
767
- onCreate: () => {
768
- if (levelVM.loading) {
769
- ApiUser.getPublicConfig('member_level_config', 'manager').then((dd: any) => {
770
- if (dd.result && dd.response.value) {
771
- levelVM.dataList = dd.response.value.levels.map(
772
- (item: { id: string; tag_name: string }) => {
773
- return {
774
- key: item.id,
775
- value: item.tag_name,
776
- // note.txt: '人數'
777
- };
778
- }
779
- );
780
- levelVM.loading = false;
781
- gvc.notifyDataChange(levelVM.id);
782
- }
783
- });
784
- }
785
- },
786
- });
787
- })();
788
- case 'group':
789
- return (() => {
790
- const levelVM = {
791
- id: gvc.glitter.getUUID(),
792
- loading: true,
793
- dataList: [],
794
- };
795
- return gvc.bindView({
796
- bind: levelVM.id,
797
- view: () => {
798
- if (levelVM.loading) {
799
- return BgWidget.spinner({ text: { visible: false } });
800
- } else {
801
- return BgWidget.selectDropList({
802
- gvc: gvc,
803
- callback: (value: []) => {
804
- voucherData.targetList = value;
805
- gvc.notifyDataChange(id);
806
- },
807
- default: (voucherData.targetList ?? []).map(id => id.toString()),
808
- options: levelVM.dataList,
809
- style: 'width: 100%;',
810
- });
811
- }
812
- },
813
- divCreate: {
814
- style: 'width: 100%;',
815
- },
816
- onCreate: () => {
817
- if (levelVM.loading) {
818
- ApiUser.getUserGroupList().then((dd: any) => {
819
- if (dd.result && dd.response.data) {
820
- levelVM.dataList = dd.response.data
821
- .filter((item: any) => {
822
- return item.type !== 'level';
823
- })
824
- .map((item: any) => {
825
- return {
826
- key: item.type,
827
- value: item.title,
828
- };
829
- });
830
- levelVM.loading = false;
831
- gvc.notifyDataChange(levelVM.id);
832
- }
833
- });
834
- }
835
- },
836
- });
837
- })();
838
- default:
839
- return '';
840
- }
841
- })()}
842
- </div>
843
- </div>
844
- `;
845
- },
846
- };
847
- })}`;
848
- }
849
-
850
- function device() {
851
- return html` <div class="tx_700">可使用訂單來源</div>
852
- ${BgWidget.mbContainer(18)}
853
- ${BgWidget.multiCheckboxContainer(
854
- gvc,
855
- [
856
- { key: 'normal', name: 'APP & 官網' },
857
- { key: 'pos', name: 'POS' },
858
- ],
859
- voucherData.device ?? ['normal'],
860
- text => {
861
- voucherData.device = text as Device[];
862
- gvc.notifyDataChange(pageVM.viewID);
863
- },
864
- { single: false }
865
- )}`;
866
- }
867
-
868
- function selectShipment() {
869
- const id = glitter.getUUID();
870
- return gvc.bindView({
871
- bind: id,
872
- view: () => {
873
- return html` <div class="tx_700">可使用物流</div>
874
- ${BgWidget.mbContainer(18)}
875
- ${[
876
- BgWidget.select({
877
- gvc,
878
- callback: value => {
879
- voucherData.selectShipment.type = value as SelectShipmentType;
880
- gvc.notifyDataChange(id);
881
- },
882
- default: voucherData.selectShipment.type,
883
- options: [
884
- { key: 'all', value: '所有物流' },
885
- { key: 'select', value: '指定物流' },
886
- ],
887
- }),
888
- voucherData.selectShipment.type === 'all'
889
- ? ''
890
- : BgWidget.selectDropList({
891
- gvc: gvc,
892
- callback: value => {
893
- voucherData.selectShipment.list = value;
894
- },
895
- default: voucherData.selectShipment.list ?? [],
896
- options: ShipmentConfig.list.map(item => {
897
- return {
898
- key: item.value,
899
- value: item.title,
900
- };
901
- }),
902
- style: 'width: 100%;',
903
- }),
904
- ]
905
- .filter(Boolean)
906
- .join(BgWidget.mbContainer(8))}`;
907
- },
908
- });
909
- }
910
-
911
- function rebackProduct() {
912
- return gvc.bindView(() => {
913
- const vm = {
914
- id: gvc.glitter.getUUID(),
915
- };
916
- if (!Array.isArray(voucherData.add_on_products)) {
917
- voucherData.add_on_products = [];
918
- }
919
- return {
920
- bind: vm.id,
921
- view: () => {
922
- return html`
923
- <div class="tx_700">${voucherData.reBackType === 'add_on_items' ? `加購品項` : `贈品品項`}</div>
924
- ${BgWidget.mbContainer(18)}
925
- ${obj.gvc.bindView(() => {
926
- const id = gvc.glitter.getUUID();
927
- return {
928
- bind: id,
929
- view: () => {
930
- try {
931
- return html`
932
- <div
933
- class="d-flex align-items-center gray-bottom-line-18"
934
- style="gap: 24px; justify-content: space-between;"
935
- >
936
- <div class="form-check-label c_updown_label">
937
- <div class="tx_normal">商品列表</div>
938
- </div>
939
- ${BgWidget.grayButton(
940
- '選擇商品',
941
- gvc.event(() => {
942
- BgProduct.productsDialog({
943
- gvc: gvc,
944
- default: voucherData.add_on_products ?? [],
945
- callback: async value => {
946
- voucherData.add_on_products = value;
947
- gvc.notifyDataChange(id);
948
- },
949
- filter: dd => {
950
- return true;
951
- },
952
- productType: voucherData.reBackType === 'add_on_items' ? 'addProduct' : 'giveaway',
953
- });
954
- }),
955
- { textStyle: 'font-weight: 400;' }
956
- )}
957
- </div>
958
- ${gvc.bindView(() => {
959
- const vm: {
960
- id: string;
961
- loading: boolean;
962
- data: OptionsItem[];
963
- } = {
964
- id: gvc.glitter.getUUID(),
965
- loading: true,
966
- data: [],
967
- };
968
- BgProduct.getProductOpts(
969
- voucherData.add_on_products!,
970
- voucherData.reBackType === 'add_on_items' ? 'addProduct' : 'giveaway'
971
- ).then(res => {
972
- vm.data = res;
973
- vm.loading = false;
974
- gvc.notifyDataChange(vm.id);
975
- });
976
- return {
977
- bind: vm.id,
978
- view: async () => {
979
- if (vm.loading) {
980
- return BgWidget.spinner();
981
- }
982
- return vm.data
983
- .map((opt: OptionsItem, index) => {
984
- return html` <div
985
- class="d-flex align-items-center form-check-label c_updown_label gap-3"
986
- >
987
- <span class="tx_normal">${index + 1}.</span>
988
- ${BgWidget.validImageBox({
989
- gvc: gvc,
990
- image: opt.image,
991
- width: 40,
992
- })}
993
- <div class="tx_normal ${opt.note ? 'mb-1' : ''}">${opt.value}</div>
994
- ${opt.note ? html` <div class="tx_gray_12">${opt.note}</div> ` : ''}
995
- </div>`;
996
- })
997
- .join('');
998
- },
999
- divCreate: {
1000
- class: `d-flex py-2 flex-column`,
1001
- style: `gap:10px;`,
1002
- },
1003
- };
1004
- })}
1005
- `;
1006
- } catch (e) {
1007
- console.error(e);
1008
- return '';
1009
- }
1010
- },
1011
- divCreate: {
1012
- class: `w-100`,
1013
- },
1014
- };
1015
- })}
1016
- `;
1017
- },
1018
- };
1019
- });
1020
- }
1021
-
1022
- return gvc.bindView(() => {
1023
- return {
1024
- bind: pageVM.viewID,
1025
- view: () => {
1026
- return BgWidget.container(
1027
- [
1028
- // 上層導覽
1029
- html` <div class="title-container">
1030
- ${BgWidget.goBack(
1031
- gvc.event(() => {
1032
- vm.type = 'list';
1033
- })
1034
- )}
1035
- ${BgWidget.title(
1036
- `${obj.type === 'add' ? '新增' : '編輯'}${ShoppingDiscountSetting.getLabel(obj.reBackType)}`
1037
- )}
1038
- </div>`,
1039
- // 左右容器
1040
- BgWidget.container1x2(
1041
- {
1042
- // 優惠券設定
1043
- html: [
1044
- BgWidget.mainCard([title(), status()].join(BgWidget.horizontalLine())),
1045
- BgWidget.mainCard(
1046
- [
1047
- trigger(),
1048
- target(),
1049
- device(),
1050
- voucherData.reBackType === 'shipment_free' ? selectShipment() : '',
1051
- ]
1052
- .filter(Boolean)
1053
- .map(str => html` <div>${str}</div>`)
1054
- .join(BgWidget.horizontalLine())
1055
- ),
1056
- ...(['shipment_free', 'add_on_items', 'giveaway'].includes(voucherData.reBackType)
1057
- ? []
1058
- : [
1059
- BgWidget.mainCard(
1060
- gvc.bindView(() => {
1061
- const id = glitter.getUUID();
1062
- const originForType = String(voucherData.for);
1063
- voucherData.forKey = voucherData.forKey ?? [];
1064
- let defKeys: any = {
1065
- collection: JSON.parse(JSON.stringify(voucherData.forKey)),
1066
- product: JSON.parse(JSON.stringify(voucherData.forKey)),
1067
- manager_tag: JSON.parse(JSON.stringify(voucherData.forKey)),
1068
- };
1069
-
1070
- return {
1071
- bind: id,
1072
- dataList: [
1073
- { obj: voucherData, key: 'method' },
1074
- { obj: voucherData, key: 'reBackType' },
1075
- ],
1076
- view: () => {
1077
- return [
1078
- ...(() => {
1079
- if (
1080
- ['shipment_free', 'add_on_items', 'giveaway'].includes(voucherData.reBackType)
1081
- ) {
1082
- return [];
1083
- }
1084
-
1085
- const valueInput = (obj: { startText?: string; endText?: string }) => {
1086
- return BgWidget.editeInput({
1087
- gvc: gvc,
1088
- type: 'number',
1089
- divStyle: 'width:150px;',
1090
- title: '',
1091
- default: voucherData.value,
1092
- placeHolder: '',
1093
- callback: text => {
1094
- const texInt = parseInt(text, 10);
1095
- if (voucherData.method === 'percent' && (texInt > 100 || texInt < 0)) {
1096
- dialog.infoMessage({ text: '數值需介於0~100' });
1097
- gvc.notifyDataChange(id);
1098
- gvc.notifyDataChange(pageVM.countingID);
1099
- } else {
1100
- voucherData.value = text;
1101
- }
1102
- },
1103
- startText: obj.startText,
1104
- endText: obj.endText,
1105
- });
1106
- };
1107
-
1108
- return [
1109
- html` <div>
1110
- <div class="tx_700">折扣金額</div>
1111
- ${BgWidget.mbContainer(18)}
1112
- ${BgWidget.multiCheckboxContainer(
1113
- gvc,
1114
- [
1115
- {
1116
- key: 'fixed',
1117
- name: '固定金額',
1118
- innerHtml: valueInput({ startText: '$' }),
1119
- },
1120
- {
1121
- key: 'percent',
1122
- name: '百分比',
1123
- innerHtml: valueInput({ endText: '%' }),
1124
- },
1125
- ],
1126
- [voucherData.method],
1127
- text => {
1128
- voucherData.value = '0';
1129
- voucherData.method = text[0] as Method;
1130
- gvc.notifyDataChange(pageVM.conditionID);
1131
- },
1132
- { single: true }
1133
- )}
1134
- </div>`,
1135
- ];
1136
- })(),
1137
- ...(() => {
1138
- if (voucherData.trigger === 'distribution') {
1139
- return [];
1140
- }
1141
-
1142
- return [
1143
- html`
1144
- <div class="tx_700">套用至</div>
1145
- ${BgWidget.mbContainer(18)}
1146
- ${EditorElem.radio({
1147
- gvc: gvc,
1148
- title: '',
1149
- def: voucherData.for ?? 'all',
1150
- array: ShoppingDiscountSetting.productForList,
1151
- callback: text => {
1152
- voucherData.forKey = defKeys[text];
1153
- voucherData.for = text as VoucherForType;
1154
- gvc.notifyDataChange(id);
1155
- },
1156
- oneLine: true,
1157
- })}
1158
- ${BgWidget.mbContainer(8)}
1159
- ${(() => {
1160
- switch (voucherData.for) {
1161
- case 'manager_tag':
1162
- return gvc.bindView(() => {
1163
- const subVM = {
1164
- id: gvc.glitter.getUUID(),
1165
- dataList:
1166
- originForType === 'manager_tag' ? [...defKeys.manager_tag] : [],
1167
- };
1168
-
1169
- return {
1170
- bind: subVM.id,
1171
- view: () => {
1172
- return html`
1173
- <div class="d-flex flex-column p-2" style="gap: 18px;">
1174
- <div
1175
- class="d-flex align-items-center gray-bottom-line-18"
1176
- style="gap: 24px; justify-content: space-between;"
1177
- >
1178
- <div class="form-check-label c_updown_label">
1179
- <div class="tx_normal">標籤列表</div>
1180
- </div>
1181
- ${BgWidget.grayButton(
1182
- '選擇標籤',
1183
- gvc.event(() => {
1184
- BgProduct.useProductTags({
1185
- gvc,
1186
- config_key: 'product_manager_tags',
1187
- def:
1188
- originForType === 'manager_tag' &&
1189
- voucherData.forKey
1190
- ? voucherData.forKey.map(item => `${item}`)
1191
- : [],
1192
- callback: async value => {
1193
- voucherData.forKey = value;
1194
- defKeys.manager_tag = value;
1195
- subVM.dataList = value;
1196
- gvc.notifyDataChange(subVM.id);
1197
- },
1198
- });
1199
- }),
1200
- { textStyle: 'font-weight: 400;' }
1201
- )}
1202
- </div>
1203
- ${obj.gvc.map(
1204
- subVM.dataList.map((opt, index) => {
1205
- return html` <div
1206
- class="d-flex align-items-center form-check-label c_updown_label gap-3"
1207
- >
1208
- <span class="tx_normal">${index + 1}. #${opt}</span>
1209
- </div>`;
1210
- })
1211
- )}
1212
- </div>
1213
- `;
1214
- },
1215
- };
1216
- });
1217
- case 'collection':
1218
- return gvc.bindView(() => {
1219
- const subVM = {
1220
- id: gvc.glitter.getUUID(),
1221
- loading: true,
1222
- dataList: [] as OptionsItem[],
1223
- };
1224
- return {
1225
- bind: subVM.id,
1226
- view: () => {
1227
- if (subVM.loading) {
1228
- return BgWidget.spinner();
1229
- }
1230
- return html`
1231
- <div class="d-flex flex-column p-2" style="gap: 18px;">
1232
- <div
1233
- class="d-flex align-items-center gray-bottom-line-18"
1234
- style="gap: 24px; justify-content: space-between;"
1235
- >
1236
- <div class="form-check-label c_updown_label">
1237
- <div class="tx_normal">分類列表</div>
1238
- </div>
1239
- ${BgWidget.grayButton(
1240
- '選擇分類',
1241
- gvc.event(() => {
1242
- BgProduct.collectionsDialog({
1243
- gvc: gvc,
1244
- default: voucherData.forKey ?? [],
1245
- callback: async value => {
1246
- voucherData.forKey = value;
1247
- defKeys.collection = value;
1248
- subVM.dataList =
1249
- await BgProduct.getCollectiosOpts(value);
1250
- subVM.loading = true;
1251
- gvc.notifyDataChange(subVM.id);
1252
- },
1253
- });
1254
- }),
1255
- { textStyle: 'font-weight: 400;' }
1256
- )}
1257
- </div>
1258
- ${obj.gvc.map(
1259
- subVM.dataList.map((opt: OptionsItem, index) => {
1260
- return html` <div
1261
- class="d-flex align-items-center form-check-label c_updown_label gap-3"
1262
- >
1263
- <span class="tx_normal"
1264
- >${index + 1}. ${opt.value}</span
1265
- >
1266
- ${opt.note
1267
- ? html`
1268
- <span class="tx_gray_12 ms-2">${opt.note}</span>
1269
- `
1270
- : ''}
1271
- </div>`;
1272
- })
1273
- )}
1274
- </div>
1275
- `;
1276
- },
1277
- onCreate: () => {
1278
- if (subVM.loading) {
1279
- if (voucherData.forKey.length === 0) {
1280
- setTimeout(() => {
1281
- subVM.dataList = [];
1282
- subVM.loading = false;
1283
- gvc.notifyDataChange(subVM.id);
1284
- }, 200);
1285
- } else {
1286
- new Promise<OptionsItem[]>(resolve => {
1287
- resolve(BgProduct.getCollectiosOpts(voucherData.forKey));
1288
- }).then(data => {
1289
- subVM.dataList = data;
1290
- subVM.loading = false;
1291
- gvc.notifyDataChange(subVM.id);
1292
- });
1293
- }
1294
- }
1295
- },
1296
- };
1297
- });
1298
- case 'product':
1299
- return gvc.bindView(() => {
1300
- const subVM = {
1301
- id: gvc.glitter.getUUID(),
1302
- loading: true,
1303
- dataList: [] as OptionsItem[],
1304
- };
1305
- return {
1306
- bind: subVM.id,
1307
- view: () => {
1308
- if (subVM.loading) {
1309
- return BgWidget.spinner();
1310
- }
1311
- return html`
1312
- <div class="d-flex flex-column p-2" style="gap: 18px;">
1313
- <div
1314
- class="d-flex align-items-center gray-bottom-line-18"
1315
- style="gap: 24px; justify-content: space-between;"
1316
- >
1317
- <div class="form-check-label c_updown_label">
1318
- <div class="tx_normal">商品列表</div>
1319
- </div>
1320
- ${BgWidget.grayButton(
1321
- '選擇商品',
1322
- gvc.event(() => {
1323
- BgProduct.productsDialog({
1324
- gvc: gvc,
1325
- default: voucherData.forKey ?? [],
1326
- callback: async value => {
1327
- voucherData.forKey = value;
1328
- defKeys.product = value;
1329
- subVM.dataList = await BgProduct.getProductOpts(
1330
- voucherData.forKey
1331
- );
1332
- subVM.loading = true;
1333
- gvc.notifyDataChange(subVM.id);
1334
- },
1335
- });
1336
- }),
1337
- { textStyle: 'font-weight: 400;' }
1338
- )}
1339
- </div>
1340
- ${subVM.dataList
1341
- .map((opt: OptionsItem, index) => {
1342
- return html` <div
1343
- class="d-flex align-items-center form-check-label c_updown_label gap-3"
1344
- >
1345
- <span class="tx_normal">${index + 1}.</span>
1346
- ${BgWidget.validImageBox({
1347
- gvc: gvc,
1348
- image: opt.image,
1349
- width: 40,
1350
- })}
1351
- <div class="tx_normal ${opt.note ? 'mb-1' : ''}">
1352
- ${opt.value}
1353
- </div>
1354
- ${opt.note
1355
- ? html` <div class="tx_gray_12">${opt.note}</div> `
1356
- : ''}
1357
- </div>`;
1358
- })
1359
- .join(``)}
1360
- </div>
1361
- `;
1362
- },
1363
- onCreate: () => {
1364
- if (subVM.loading) {
1365
- if (voucherData.forKey.length === 0) {
1366
- setTimeout(() => {
1367
- subVM.dataList = [];
1368
- subVM.loading = false;
1369
- gvc.notifyDataChange(subVM.id);
1370
- }, 200);
1371
- } else {
1372
- new Promise<OptionsItem[]>(resolve => {
1373
- resolve(BgProduct.getProductOpts(voucherData.forKey));
1374
- }).then(data => {
1375
- subVM.dataList = data;
1376
- subVM.loading = false;
1377
- gvc.notifyDataChange(subVM.id);
1378
- });
1379
- }
1380
- }
1381
- },
1382
- };
1383
- });
1384
- case 'all':
1385
- default:
1386
- return '';
1387
- }
1388
- })()}
1389
- `,
1390
- ];
1391
- })(),
1392
- ].join(BgWidget.horizontalLine());
1393
- },
1394
- };
1395
- })
1396
- ),
1397
- ]),
1398
- ['giveaway', 'add_on_items'].includes(voucherData.reBackType)
1399
- ? BgWidget.mainCard(rebackProduct())
1400
- : '',
1401
- BgWidget.mainCard(
1402
- gvc.bindView(() => {
1403
- return {
1404
- bind: pageVM.conditionID,
1405
- view: () => {
1406
- const conditionInput = (text: string) => {
1407
- return BgWidget.editeInput({
1408
- gvc: gvc,
1409
- title: '',
1410
- divStyle: 'width:150px;',
1411
- default: `${voucherData.ruleValue ?? 0}`,
1412
- placeHolder: '',
1413
- callback: value => {
1414
- voucherData.ruleValue = parseInt(value, 10);
1415
- gvc.notifyDataChange(pageVM.conditionID);
1416
- },
1417
- endText: text,
1418
- });
1419
- };
1420
- const n = voucherData.ruleValue;
1421
- const floor = Math.floor(n / 2);
1422
- const ruleText = (sum: number) => {
1423
- return `${sum}${voucherData.rule === 'min_count' ? '個' : '元'}`;
1424
- };
1425
- voucherData.counting =
1426
- voucherData.method === 'percent' || voucherData.reBackType === 'shipment_free'
1427
- ? 'single'
1428
- : voucherData.counting;
1429
- voucherData.conditionType =
1430
- voucherData.reBackType === 'shipment_free' ? 'order' : voucherData.conditionType;
1431
- return [
1432
- html` <div class="tx_700">消費條件</div>
1433
- ${BgWidget.mbContainer(18)}
1434
- ${BgWidget.multiCheckboxContainer(
1435
- gvc,
1436
- [
1437
- {
1438
- key: 'min_price',
1439
- name: '最低消費金額',
1440
- innerHtml: conditionInput('元'),
1441
- },
1442
- {
1443
- key: 'min_count',
1444
- name: '最少購買數量',
1445
- innerHtml: conditionInput('個'),
1446
- },
1447
- ],
1448
- [voucherData.rule],
1449
- text => {
1450
- voucherData.ruleValue = 0;
1451
- voucherData.rule = text[0] as Rule;
1452
- gvc.notifyDataChange(pageVM.conditionID);
1453
- },
1454
- { single: true }
1455
- )}`,
1456
- html` ${BgWidget.horizontalLine()}
1457
- <div class="tx_700">計算單位</div>
1458
- ${BgWidget.mbContainer(18)}
1459
- ${BgWidget.multiCheckboxContainer(
1460
- gvc,
1461
- [
1462
- {
1463
- key: 'order',
1464
- name: '以整份訂單計算',
1465
- innerHtml: BgWidget.grayNote(
1466
- (() => {
1467
- if (voucherData.reBackType === 'shipment_free') {
1468
- return '優惠條件為整份訂單免運費';
1469
- }
1470
- return `若商品A購買${ruleText(floor)},加上商品B購買${ruleText(n - floor)},可觸發優惠`;
1471
- })()
1472
- ),
1473
- },
1474
- {
1475
- key: 'item',
1476
- name: '以商品計算',
1477
- innerHtml: BgWidget.grayNote(
1478
- `需要商品A購買滿${ruleText(n)},或商品B購買滿${ruleText(n)},即可觸發優惠<br/>若商品A購買${ruleText(
1479
- floor
1480
- )},加上商品B購買${ruleText(n - floor)},無法觸發優惠`
1481
- ),
1482
- },
1483
- ],
1484
- [voucherData.conditionType],
1485
- text => {
1486
- voucherData.conditionType = text[0] as ConditionType;
1487
- gvc.notifyDataChange(pageVM.conditionID);
1488
- },
1489
- {
1490
- single: true,
1491
- readonly: voucherData.reBackType === 'shipment_free',
1492
- }
1493
- )}`,
1494
- gvc.bindView({
1495
- bind: pageVM.countingID,
1496
- view: () => {
1497
- if (voucherData.method === 'percent' || voucherData.reBackType === 'shipment_free') {
1498
- return '';
1499
- }
1500
- return html` ${BgWidget.horizontalLine()}
1501
- <div class="tx_700">重複觸發</div>
1502
- ${BgWidget.mbContainer(18)}
1503
- ${BgWidget.multiCheckboxContainer(
1504
- gvc,
1505
- [
1506
- {
1507
- key: 'single',
1508
- name: '不重複',
1509
- innerHtml: BgWidget.grayNote(
1510
- `購買${ruleText(n)}折Y元,額外購買至${ruleText(n * 2)}或${ruleText(n * 3)}依然是折Y元`
1511
- ),
1512
- },
1513
- {
1514
- key: 'each',
1515
- name: '重複',
1516
- innerHtml: BgWidget.grayNote(
1517
- `購買${ruleText(n)}折Y元,購買${ruleText(n * 2)}則折Y * 2元,購買${ruleText(n * 3)}則折Y * 3元,以此類推`
1518
- ),
1519
- },
1520
- ],
1521
- [voucherData.counting],
1522
- text => {
1523
- voucherData.counting = text[0] as Counting;
1524
- gvc.notifyDataChange(pageVM.conditionID);
1525
- },
1526
- {
1527
- single: true,
1528
- }
1529
- )}`;
1530
- },
1531
- }),
1532
- gvc.bindView({
1533
- bind: pageVM.productOffID,
1534
- view: () => {
1535
- if (
1536
- !(
1537
- voucherData.method === 'percent' &&
1538
- voucherData.conditionType === 'order' &&
1539
- voucherData.rule === 'min_count' &&
1540
- voucherData.reBackType === 'discount'
1541
- )
1542
- ) {
1543
- return '';
1544
- }
1545
- return html` ${BgWidget.horizontalLine()}
1546
- <div class="tx_700">打折範圍</div>
1547
- ${BgWidget.mbContainer(18)}
1548
- ${BgWidget.multiCheckboxContainer(
1549
- gvc,
1550
- [
1551
- {
1552
- key: 'price_desc',
1553
- name: '從最高價的商品打折',
1554
- innerHtml: BgWidget.grayNote(
1555
- `購物車訂單將會從最高價且符合優惠的${ruleText(n)}商品進行打折`
1556
- ),
1557
- },
1558
- {
1559
- key: 'price_asc',
1560
- name: '從最低價的商品打折',
1561
- innerHtml: BgWidget.grayNote(
1562
- `購物車訂單將會從最低價且符合優惠的${ruleText(n)}商品進行打折`
1563
- ),
1564
- },
1565
- {
1566
- key: 'price_all',
1567
- name: '符合優惠的商品全部打折',
1568
- innerHtml: BgWidget.grayNote(`購物車訂單符合優惠的商品進行打折`),
1569
- },
1570
- ],
1571
- [voucherData.productOffStart],
1572
- text => {
1573
- voucherData.productOffStart = text[0] as
1574
- | 'price_asc'
1575
- | 'price_desc'
1576
- | 'price_all';
1577
- gvc.notifyDataChange(pageVM.productOffID);
1578
- },
1579
- {
1580
- single: true,
1581
- }
1582
- )}`;
1583
- },
1584
- }),
1585
- ].join('');
1586
- },
1587
- };
1588
- })
1589
- ),
1590
- BgWidget.mainCard(
1591
- gvc.bindView(() => {
1592
- const id = glitter.getUUID();
1593
- return {
1594
- bind: id,
1595
- view: () => {
1596
- function overlay() {
1597
- return html` <div class="tx_700">是否與其他優惠券疊加使用</div>
1598
- ${BgWidget.mbContainer(18)}
1599
- ${BgWidget.multiCheckboxContainer(
1600
- gvc,
1601
- [
1602
- {
1603
- key: 'false',
1604
- name: '不可疊加',
1605
- innerHtml: BgWidget.grayNote('系統將以最大優惠排序進行判定'),
1606
- },
1607
- {
1608
- key: 'true',
1609
- name: '可以疊加',
1610
- },
1611
- ],
1612
- [voucherData.overlay ? 'true' : 'false'],
1613
- text => {
1614
- voucherData.overlay = text[0] === 'true';
1615
- gvc.notifyDataChange(id);
1616
- },
1617
- { single: true }
1618
- )}`;
1619
- }
1620
-
1621
- function isIncludeDiscount() {
1622
- return html` <div class="tx_700">消費金額於其他折扣觸發時機</div>
1623
- ${BgWidget.mbContainer(18)}
1624
- ${BgWidget.multiCheckboxContainer(
1625
- gvc,
1626
- [
1627
- {
1628
- key: 'before',
1629
- name: '觸發前',
1630
- innerHtml: BgWidget.grayNote(
1631
- '在其他折扣觸發前,訂單的消費金額將做為達成消費條件的金額,來判斷是否可使用此優惠券'
1632
- ),
1633
- },
1634
- {
1635
- key: 'after',
1636
- name: '觸發後',
1637
- innerHtml: BgWidget.grayNote(
1638
- '將訂單的消費金額包含其他折扣後,做為達成消費條件的金額,來判斷是否可使用此優惠券'
1639
- ),
1640
- },
1641
- ],
1642
- [voucherData.includeDiscount],
1643
- text => {
1644
- voucherData.includeDiscount = text[0] as IncludeDiscount;
1645
- gvc.notifyDataChange(pageVM.conditionID);
1646
- },
1647
- {
1648
- single: true,
1649
- }
1650
- )}`;
1651
- }
1652
-
1653
- return [overlay(), voucherData.overlay ? isIncludeDiscount() : '']
1654
- .filter(Boolean)
1655
- .join(BgWidget.horizontalLine());
1656
- },
1657
- };
1658
- })
1659
- ),
1660
- BgWidget.mainCard(
1661
- gvc.bindView(() => {
1662
- const id = glitter.getUUID();
1663
- const inputStyle = 'display: block; width: 200px;';
1664
-
1665
- return {
1666
- bind: id,
1667
- view: () => {
1668
- function storeUseTimeLimit() {
1669
- return html` <div class="tx_700">全館總使用次數</div>
1670
- ${BgWidget.mbContainer(18)}
1671
- ${BgWidget.multiCheckboxContainer(
1672
- gvc,
1673
- [
1674
- {
1675
- key: 'noLimited',
1676
- name: '無限制',
1677
- },
1678
- {
1679
- key: 'hasLimited',
1680
- name: '限制次數',
1681
- innerHtml: html` <div class="d-flex align-items-center">
1682
- <span class="tx_normal me-2">可使用次數</span>
1683
- ${BgWidget.editeInput({
1684
- gvc: gvc,
1685
- title: '',
1686
- type: 'number',
1687
- divStyle: 'width: 150px;',
1688
- default: `${voucherData.macroLimited ?? 0}`,
1689
- placeHolder: '',
1690
- callback: text => {
1691
- voucherData.macroLimited = parseInt(text, 10);
1692
- },
1693
- endText: '次',
1694
- })}
1695
- </div>`,
1696
- },
1697
- ],
1698
- [voucherData.macroLimited === 0 ? 'noLimited' : 'hasLimited'],
1699
- text => {
1700
- if (text[0] === 'noLimited') {
1701
- voucherData.macroLimited = 0;
1702
- }
1703
- },
1704
- { single: true }
1705
- )}`;
1706
- }
1707
-
1708
- function memberUseTimeLimit() {
1709
- return html` <div class="tx_700">個人總使用次數</div>
1710
- ${BgWidget.mbContainer(18)}
1711
- ${BgWidget.multiCheckboxContainer(
1712
- gvc,
1713
- [
1714
- {
1715
- key: 'noLimited',
1716
- name: '無限制',
1717
- },
1718
- {
1719
- key: 'hasLimited',
1720
- name: '限制次數',
1721
- innerHtml: html` <div class="d-flex align-items-center">
1722
- <span class="tx_normal me-2">可使用次數</span>
1723
- ${BgWidget.editeInput({
1724
- gvc: gvc,
1725
- title: '',
1726
- type: 'number',
1727
- divStyle: 'width: 150px;',
1728
- default: `${voucherData.microLimited ?? 0}`,
1729
- placeHolder: '',
1730
- callback: text => {
1731
- voucherData.microLimited = parseInt(text, 10);
1732
- },
1733
- endText: '次',
1734
- })}
1735
- </div>`,
1736
- },
1737
- ],
1738
- [voucherData.microLimited === 0 ? 'noLimited' : 'hasLimited'],
1739
- text => {
1740
- if (text[0] === 'noLimited') {
1741
- voucherData.microLimited = 0;
1742
- }
1743
- },
1744
- { single: true }
1745
- )}`;
1746
- }
1747
-
1748
- function startDateTime() {
1749
- return html` <div class="tx_700">優惠券有效日期</div>
1750
- <div
1751
- class="d-flex mb-3 ${document.body.clientWidth < 768 ? 'flex-column' : ''}"
1752
- style="gap: 12px"
1753
- >
1754
- <div class="d-flex align-items-center">
1755
- <span class="tx_normal me-2">開始日期</span>
1756
- ${BgWidget.editeInput({
1757
- gvc: gvc,
1758
- title: '',
1759
- type: 'date',
1760
- style: inputStyle,
1761
- default: `${voucherData.startDate}`,
1762
- placeHolder: '',
1763
- callback: text => {
1764
- voucherData.startDate = text;
1765
- },
1766
- })}
1767
- </div>
1768
- <div class="d-flex align-items-center">
1769
- <span class="tx_normal me-2">開始時間</span>
1770
- ${BgWidget.editeInput({
1771
- gvc: gvc,
1772
- title: '',
1773
- type: 'time',
1774
- style: inputStyle,
1775
- default: `${voucherData.startTime}`,
1776
- placeHolder: '',
1777
- callback: text => {
1778
- voucherData.startTime = text;
1779
- },
1780
- })}
1781
- </div>
1782
- </div>
1783
- ${BgWidget.multiCheckboxContainer(
1784
- gvc,
1785
- [
1786
- {
1787
- key: 'noEnd',
1788
- name: '無期限',
1789
- },
1790
- {
1791
- key: 'withEnd',
1792
- name: '有效期限',
1793
- innerHtml: html` <div
1794
- class="d-flex mt-0 mt-md-3 ${document.body.clientWidth < 768
1795
- ? 'flex-column'
1796
- : ''}"
1797
- style="gap: 12px"
1798
- >
1799
- <div class="d-flex align-items-center">
1800
- <span class="tx_normal me-2">結束日期</span>
1801
- ${BgWidget.editeInput({
1802
- gvc: gvc,
1803
- title: '',
1804
- type: 'date',
1805
- style: inputStyle,
1806
- default: `${voucherData.endDate}`,
1807
- placeHolder: '',
1808
- callback: text => {
1809
- voucherData.endDate = text;
1810
- },
1811
- })}
1812
- </div>
1813
- <div class="d-flex align-items-center">
1814
- <span class="tx_normal me-2">結束時間</span>
1815
- ${BgWidget.editeInput({
1816
- gvc: gvc,
1817
- title: '',
1818
- type: 'time',
1819
- style: inputStyle,
1820
- default: `${voucherData.endTime}`,
1821
- placeHolder: '',
1822
- callback: text => {
1823
- voucherData.endTime = text;
1824
- },
1825
- })}
1826
- </div>
1827
- </div>`,
1828
- },
1829
- ],
1830
- [voucherData.endDate ? `withEnd` : `noEnd`],
1831
- text => {
1832
- if (text[0] === 'noEnd') {
1833
- voucherData.endDate = undefined;
1834
- voucherData.endTime = undefined;
1835
- }
1836
- },
1837
- { single: true }
1838
- )}`;
1839
- }
1840
-
1841
- function rebateEndDay() {
1842
- return [
1843
- html` <div class="tx_700">購物金有效天數</div>`,
1844
- BgWidget.multiCheckboxContainer(
1845
- gvc,
1846
- [
1847
- {
1848
- key: 'noEnd',
1849
- name: '無期限',
1850
- },
1851
- {
1852
- key: 'withEnd',
1853
- name: '有效期限',
1854
- innerHtml: html` <div
1855
- class="d-flex mt-0 mt-md-3 ${document.body.clientWidth < 768
1856
- ? 'flex-column'
1857
- : ''}"
1858
- style="gap: 12px"
1859
- >
1860
- <div class="d-flex align-items-center" style="gap:10px;">
1861
- ${BgWidget.editeInput({
1862
- gvc: gvc,
1863
- title: '',
1864
- type: 'number',
1865
- style: inputStyle,
1866
- default: `${voucherData.rebateEndDay ?? ''}`,
1867
- placeHolder: '0則為無期限',
1868
- callback: text => {
1869
- voucherData.rebateEndDay = text;
1870
- gvc.notifyDataChange(id);
1871
- },
1872
- })}
1873
- <span class="tx_normal me-2">天</span>
1874
- </div>
1875
- </div>`,
1876
- },
1877
- ],
1878
- [parseInt(voucherData.rebateEndDay ?? '0', 10) ? `withEnd` : `noEnd`],
1879
- text => {
1880
- if (text[0] === 'noEnd') {
1881
- voucherData.rebateEndDay = '0';
1882
- }
1883
- },
1884
- { single: true }
1885
- ),
1886
- ].join(html` <div class="my-2"></div>`);
1887
- }
1888
-
1889
- return [
1890
- storeUseTimeLimit(),
1891
- memberUseTimeLimit(),
1892
- voucherData.reBackType === 'rebate' ? rebateEndDay() : '',
1893
- startDateTime(),
1894
- ]
1895
- .filter(Boolean)
1896
- .join(BgWidget.horizontalLine());
1897
- },
1898
- };
1899
- })
1900
- ),
1901
- ]
1902
- .filter(Boolean)
1903
- .join(BgWidget.mbContainer(24)),
1904
- ratio: 68,
1905
- },
1906
- {
1907
- // 摘要預覽
1908
- html: gvc.bindView(() => {
1909
- const id = gvc.glitter.getUUID();
1910
- return {
1911
- bind: id,
1912
- dataList: Object.keys(voucherData).map(key => {
1913
- return { obj: voucherData, key };
1914
- }),
1915
- view: () => {
1916
- return BgWidget.mainCard(html`
1917
- <div class="tx_700">摘要</div>
1918
- ${BgWidget.mbContainer(18)}
1919
- <div style="display: flex; gap: 12px; flex-direction: column;">
1920
- ${summaryTextList()
1921
- .map(text => {
1922
- return html` <div class="${text.length > 0 ? 'tx_normal' : 'gray-top-bottom-line-6'}">
1923
- ${text}
1924
- </div>`;
1925
- })
1926
- .join('')}
1927
- </div>
1928
- `);
1929
- },
1930
- divCreate: {
1931
- class: 'summary-card p-0',
1932
- },
1933
- };
1934
- }),
1935
- ratio: 32,
1936
- }
1937
- ),
1938
- // 空白容器
1939
- BgWidget.mbContainer(240),
1940
- // 儲存資料
1941
- html` <div class="update-bar-container">
1942
- ${obj.type === 'replace'
1943
- ? BgWidget.cancel(
1944
- gvc.event(() => {
1945
- const dialog = new ShareDialog(obj.gvc.glitter);
1946
- dialog.checkYesOrNot({
1947
- text: '是否確認刪除優惠券?',
1948
- callback: response => {
1949
- if (response) {
1950
- dialog.dataLoading({ visible: true });
1951
- ApiShop.deleteVoucher({
1952
- id: voucherData.id,
1953
- }).then(res => {
1954
- dialog.dataLoading({ visible: false });
1955
- if (res.result) {
1956
- vm.type = 'list';
1957
- } else {
1958
- dialog.errorMessage({ text: '刪除失敗' });
1959
- }
1960
- });
1961
- }
1962
- },
1963
- });
1964
- }),
1965
- '刪除優惠券'
1966
- )
1967
- : ''}
1968
- ${BgWidget.cancel(
1969
- gvc.event(() => {
1970
- vm.type = 'list';
1971
- })
1972
- )}
1973
- ${BgWidget.save(
1974
- gvc.event(() => {
1975
- voucherData.start_ISO_Date = '';
1976
- voucherData.end_ISO_Date = '';
1977
-
1978
- glitter.ut.tryMethod([
1979
- () => {
1980
- voucherData.start_ISO_Date = new Date(
1981
- `${voucherData.startDate} ${voucherData.startTime}`
1982
- ).toISOString();
1983
- },
1984
- () => {
1985
- voucherData.end_ISO_Date = new Date(
1986
- `${voucherData.endDate} ${voucherData.endTime}`
1987
- ).toISOString();
1988
- },
1989
- ]);
1990
-
1991
- if (obj.type === 'replace') {
1992
- dialog.dataLoading({ text: '正在更新優惠券', visible: true });
1993
- ApiShop.putVoucher({
1994
- postData: voucherData,
1995
- token: (window.parent as any).saasConfig.config.token,
1996
- type: 'manager',
1997
- }).then(re => {
1998
- dialog.dataLoading({ visible: false });
1999
- if (re.result) {
2000
- vm.type = 'list';
2001
- dialog.successMessage({ text: '上傳成功' });
2002
- } else {
2003
- dialog.errorMessage({ text: '上傳失敗' });
2004
- }
2005
- });
2006
- } else {
2007
- dialog.dataLoading({ text: '正在新增優惠券', visible: true });
2008
- ApiShop.postVoucher({
2009
- postData: voucherData,
2010
- token: (window.parent as any).saasConfig.config.token,
2011
- type: 'manager',
2012
- }).then(re => {
2013
- dialog.dataLoading({ visible: false });
2014
- if (re.result) {
2015
- vm.type = 'list';
2016
- dialog.successMessage({ text: `上傳成功` });
2017
- } else {
2018
- dialog.errorMessage({ text: `上傳失敗` });
2019
- }
2020
- });
2021
- }
2022
- })
2023
- )}
2024
- </div>`,
2025
- ].join(BgWidget.mbContainer(24))
2026
- );
2027
- },
2028
- };
2029
- });
2030
- }
2031
-
2032
- public static voucherEditorV2(obj: {
2033
- vm: any;
2034
- gvc: GVC;
2035
- type?: 'add' | 'replace';
2036
- defData?: any;
2037
- reBackType: RebackType;
2038
- }) {
368
+ static voucherEditorV2(obj: { vm: any; gvc: GVC; type: 'add' | 'replace'; reBackType: RebackType }) {
2039
369
  const gvc = obj.gvc;
2040
370
  const vm = obj.vm;
2041
371
  const glitter = gvc.glitter;
@@ -2046,15 +376,25 @@ export class ShoppingDiscountSetting {
2046
376
  viewID: getUUID(),
2047
377
  };
2048
378
 
379
+ // 優惠券初始化
2049
380
  const voucherData: VoucherData = {
2050
381
  ...this.emptyVoucher(obj.reBackType),
2051
382
  ...vm.data,
2052
383
  };
2053
384
 
385
+ // 克隆物件方法
2054
386
  const cloneForKey = <T>(key: T): T => JSON.parse(JSON.stringify(key));
2055
387
 
2056
- const setTitle = (title: string) => html` <div class="tx_700">${title}</div>`;
388
+ // 標題模板
389
+ const setTitle = (title: string, button?: string) => {
390
+ return html`<div class="title-container">
391
+ <div class="tx_700">${title}</div>
392
+ <div class="flex-fill"></div>
393
+ ${button ?? ''}
394
+ </div>`;
395
+ };
2057
396
 
397
+ // 卡片模板
2058
398
  const voucherSettingCard = (array: string[][]) =>
2059
399
  BgWidget.mainCard(
2060
400
  array
@@ -2067,6 +407,10 @@ export class ShoppingDiscountSetting {
2067
407
  return gvc.bindView({
2068
408
  bind: pageVM.viewID,
2069
409
  view: () => {
410
+ /*
411
+ ** 優惠券的判定值與資料宣告
412
+ */
413
+
2070
414
  const ruleValue = voucherData.ruleValue;
2071
415
  const isPercentMethod = voucherData.method === 'percent';
2072
416
  const isShipmentFree = voucherData.reBackType === 'shipment_free';
@@ -2100,6 +444,10 @@ export class ShoppingDiscountSetting {
2100
444
  manager_tag: cloneForKey(voucherData.forKey),
2101
445
  };
2102
446
 
447
+ /*
448
+ ** 優惠券的方法與選項
449
+ */
450
+
2103
451
  // 活動啟用
2104
452
  function status() {
2105
453
  return html` <div class="d-flex gap-1">
@@ -2112,7 +460,7 @@ export class ShoppingDiscountSetting {
2112
460
 
2113
461
  // 活動標題
2114
462
  function title() {
2115
- return html` <div class="tx_normal">活動標題 ${BgWidget.requiredStar()}</div>
463
+ return html` <div class="tx_normal">活動標題${BgWidget.requiredStar()}</div>
2116
464
  ${BgWidget.mbContainer(8)}
2117
465
  ${BgWidget.editeInput({
2118
466
  gvc: gvc,
@@ -2467,6 +815,11 @@ export class ShoppingDiscountSetting {
2467
815
 
2468
816
  // 適用訂單類型
2469
817
  function device() {
818
+ if (voucherData.trigger === 'distribution') {
819
+ voucherData.device = ['normal'];
820
+ return '';
821
+ }
822
+
2470
823
  return BgWidget.multiCheckboxContainer(
2471
824
  gvc,
2472
825
  [
@@ -2530,21 +883,23 @@ export class ShoppingDiscountSetting {
2530
883
 
2531
884
  // 優惠套用至
2532
885
  function setVoucherFor() {
2533
- let view=[EditorElem.radio({
2534
- gvc: gvc,
2535
- title: '',
2536
- def: voucherData.for ?? 'all',
2537
- array: ShoppingDiscountSetting.productForList,
2538
- callback: text => {
2539
- voucherData.forKey = defKeys[text as VoucherForType];
2540
- voucherData.for = text as VoucherForType;
2541
- gvc.notifyDataChange(pageVM.viewID);
2542
- },
2543
- oneLine: true,
2544
- })]
2545
- if( ['add_on_items','giveaway','shipment_free'].includes(voucherData.reBackType)){
2546
- view.push(`<div class="mx-n2">${selectProduct()}</div>`)
2547
- }
886
+ let view = [
887
+ EditorElem.radio({
888
+ gvc: gvc,
889
+ title: '',
890
+ def: voucherData.for ?? 'all',
891
+ array: ShoppingDiscountSetting.productForList,
892
+ callback: text => {
893
+ voucherData.forKey = defKeys[text as VoucherForType];
894
+ voucherData.for = text as VoucherForType;
895
+ gvc.notifyDataChange(pageVM.viewID);
896
+ },
897
+ oneLine: true,
898
+ }),
899
+ ];
900
+ if (['add_on_items', 'giveaway', 'shipment_free'].includes(voucherData.reBackType)) {
901
+ view.push(`<div class="mx-n2">${selectProduct()}</div>`);
902
+ }
2548
903
  // [
2549
904
  // selectProduct()
2550
905
  // ]
@@ -3150,7 +1505,8 @@ export class ShoppingDiscountSetting {
3150
1505
 
3151
1506
  // 消費金額於其他折扣觸發時機
3152
1507
  function isIncludeDiscount() {
3153
- if (!voucherData.overlay) {
1508
+ if (!voucherData.overlay || voucherData.rule === 'min_count') {
1509
+ voucherData.includeDiscount = 'before';
3154
1510
  return '';
3155
1511
  }
3156
1512
 
@@ -3159,17 +1515,13 @@ export class ShoppingDiscountSetting {
3159
1515
  [
3160
1516
  {
3161
1517
  key: 'before',
3162
- name: '觸發前',
3163
- innerHtml: BgWidget.grayNote(
3164
- '在其他折扣觸發前,訂單的消費金額將做為達成消費條件的金額,來判斷是否可使用此優惠券'
3165
- ),
1518
+ name: '折扣前金額',
1519
+ innerHtml: BgWidget.grayNote('以未套用其他優惠前的商品原始金額,來判斷是否達成此優惠券的使用門檻'),
3166
1520
  },
3167
1521
  {
3168
1522
  key: 'after',
3169
- name: '觸發後',
3170
- innerHtml: BgWidget.grayNote(
3171
- '將訂單的消費金額包含其他折扣後,做為達成消費條件的金額,來判斷是否可使用此優惠券'
3172
- ),
1523
+ name: '折扣後金額',
1524
+ innerHtml: BgWidget.grayNote('使用已套用其他優惠後的實際結帳,來判斷是否達成此優惠券的使用門檻'),
3173
1525
  },
3174
1526
  ],
3175
1527
  [voucherData.includeDiscount],
@@ -3196,14 +1548,14 @@ export class ShoppingDiscountSetting {
3196
1548
  key: 'single',
3197
1549
  name: '不重複',
3198
1550
  innerHtml: BgWidget.grayNote(
3199
- `購買${ruleText(ruleValue)}折Y元,額外購買至${ruleText(ruleValue * 2)}或${ruleText(ruleValue * 3)}依然是折Y元`
1551
+ `購買 ${ruleText(ruleValue)}折 Y 元,額外購買至 ${ruleText(ruleValue * 2)}或 ${ruleText(ruleValue * 3)}依然是折 Y 元`
3200
1552
  ),
3201
1553
  },
3202
1554
  {
3203
1555
  key: 'each',
3204
1556
  name: '重複',
3205
1557
  innerHtml: BgWidget.grayNote(
3206
- `購買${ruleText(ruleValue)}折Y元,購買${ruleText(ruleValue * 2)}則折Y * 2元,購買${ruleText(ruleValue * 3)}則折Y * 3元,以此類推`
1558
+ `購買 ${ruleText(ruleValue)}折 Y 元,購買 ${ruleText(ruleValue * 2)}則折 Y * 2 元,購買 ${ruleText(ruleValue * 3)}則折 Y * 3 元,以此類推`
3207
1559
  ),
3208
1560
  },
3209
1561
  ],
@@ -3231,18 +1583,14 @@ export class ShoppingDiscountSetting {
3231
1583
  if (voucherData.reBackType === 'shipment_free') {
3232
1584
  return '優惠條件為整份訂單免運費';
3233
1585
  }
3234
- return `若商品A購買${ruleText(floor)},加上商品B購買${ruleText(ruleValue - floor)},可觸發優惠`;
1586
+ return `若商品 A 購買 ${ruleText(floor)} + 商品 B 購買 ${ruleText(ruleValue - floor)},可觸發優惠`;
3235
1587
  })()
3236
1588
  ),
3237
1589
  },
3238
1590
  {
3239
1591
  key: 'item',
3240
1592
  name: '以商品計算',
3241
- innerHtml: BgWidget.grayNote(
3242
- `需要商品A購買滿${ruleText(ruleValue)},或商品B購買滿${ruleText(ruleValue)},即可觸發優惠<br/>若商品A購買${ruleText(
3243
- floor
3244
- )},加上商品B購買${ruleText(ruleValue - floor)},無法觸發優惠`
3245
- ),
1593
+ innerHtml: BgWidget.grayNote(`商品 A 及 商品 B 需各滿 ${ruleText(ruleValue)},才可觸發優惠`),
3246
1594
  },
3247
1595
  ],
3248
1596
  [voucherData.conditionType],
@@ -3330,7 +1678,7 @@ export class ShoppingDiscountSetting {
3330
1678
 
3331
1679
  // 單位文字
3332
1680
  function ruleText(sum: number) {
3333
- return `${sum}${voucherData.rule === 'min_count' ? '個' : '元'}`;
1681
+ return `${sum} ${voucherData.rule === 'min_count' ? '個' : '元'}`;
3334
1682
  }
3335
1683
 
3336
1684
  // 數字 Input
@@ -3356,21 +1704,239 @@ export class ShoppingDiscountSetting {
3356
1704
  });
3357
1705
  }
3358
1706
 
1707
+ // 套用賣場
1708
+ function applyShop() {
1709
+ if (voucherData.trigger !== 'distribution') {
1710
+ return '';
1711
+ }
1712
+
1713
+ const prefix = Tool.randomString(6);
1714
+
1715
+ type ShopType = 'group' | 'recommend' | 'hidden' | 'onepage';
1716
+
1717
+ const shopTypeRecord: Record<ShopType, { icon: string; title: string }> = {
1718
+ group: {
1719
+ icon: html`<i class="fa-regular fa-puzzle-piece ${prefix}_icon"></i>`,
1720
+ title: '拼團賣場',
1721
+ },
1722
+ recommend: {
1723
+ icon: html`<i class="fa-regular fa-share-nodes ${prefix}_icon"></i>`,
1724
+ title: '分銷連結',
1725
+ },
1726
+ hidden: {
1727
+ icon: html`<i class="fa-solid fa-face-dotted ${prefix}_icon"></i>`,
1728
+ title: '隱形賣場',
1729
+ },
1730
+ onepage: {
1731
+ icon: html`<i class="fa-regular fa-file ${prefix}_icon"></i>`,
1732
+ title: '一頁商店',
1733
+ },
1734
+ };
1735
+
1736
+ gvc.addStyle(`
1737
+ .${prefix}_container {
1738
+ max-width: 800px;
1739
+ margin: 0 auto;
1740
+ background-color: #fff;
1741
+ border-radius: 8px;
1742
+ overflow: hidden;
1743
+ }
1744
+
1745
+ .${prefix}_table {
1746
+ width: 100%;
1747
+ border-collapse: collapse;
1748
+ }
1749
+
1750
+ .${prefix}_thead {
1751
+ }
1752
+
1753
+ .${prefix}_th {
1754
+ padding: 8px 16px;
1755
+ text-align: left;
1756
+ font-size: 16px;
1757
+ font-weight: normal;
1758
+ text-transform: uppercase;
1759
+ letter-spacing: 0.05em;
1760
+ border-bottom: 1px solid #e5e7eb;
1761
+ }
1762
+
1763
+ .${prefix}_td {
1764
+ padding: 8px 16px;
1765
+ font-size: 16px;
1766
+ }
1767
+
1768
+ .${prefix}_tr:hover {
1769
+ background-color: #f9fafb;
1770
+ }
1771
+
1772
+ .${prefix}_type_cell {
1773
+ display: flex;
1774
+ align-items: center;
1775
+ }
1776
+
1777
+ .${prefix}_icon {
1778
+ width: 16px;
1779
+ height: 16px;
1780
+ margin-right: 8px;
1781
+ text-align: center;
1782
+ }
1783
+
1784
+ .${prefix}_empty_referrer {
1785
+ color: #9ca3af;
1786
+ }
1787
+
1788
+ .${prefix}_expend {
1789
+ display: flex;
1790
+ height: 34px;
1791
+ padding: 6px 0px;
1792
+ justify-content: center;
1793
+ align-items: center;
1794
+ gap: 8px;
1795
+ border-radius: 10px;
1796
+ border: 1px solid #ddd;
1797
+ background: #fff;
1798
+ margin-top: 18px;
1799
+ }
1800
+
1801
+ .${prefix}_expend:hover {
1802
+ background-color: #f9fafb;
1803
+ }
1804
+ `);
1805
+
1806
+ return gvc.bindView({
1807
+ bind: 'idnnn',
1808
+ view: () => {
1809
+ return html`<div class="${prefix}_container">
1810
+ <table id="beauty-salon-table" class="${prefix}_table">
1811
+ <thead class="${prefix}_thead">
1812
+ <tr class="${prefix}_tr">
1813
+ <th class="${prefix}_th">賣場類型</th>
1814
+ <th class="${prefix}_th">賣場名稱</th>
1815
+ <th class="${prefix}_th">推薦人</th>
1816
+ </tr>
1817
+ </thead>
1818
+ <tbody>
1819
+ <!-- 資料將由 JavaScript 動態填入 -->
1820
+ </tbody>
1821
+ </table>
1822
+ <div id="beauty-salon-div">
1823
+ <!-- 資料將由 JavaScript 動態填入 -->
1824
+ </div>
1825
+ </div>`;
1826
+ },
1827
+ divCreate: {},
1828
+ onCreate: () => {
1829
+ const salons: { type: ShopType; name: string; referrer: string }[] = [
1830
+ {
1831
+ type: 'group',
1832
+ name: 'SPA 梳毛刷現實揪團中!人越多越便宜!',
1833
+ referrer: '-',
1834
+ },
1835
+ {
1836
+ type: 'recommend',
1837
+ name: '與Sandy的貓跳台合作,給貓咪最好的跳台!',
1838
+ referrer: 'YT貓皇',
1839
+ },
1840
+ {
1841
+ type: 'hidden',
1842
+ name: '毛孩防寒任三件8折,一起溫暖過冬!',
1843
+ referrer: 'Sandy',
1844
+ },
1845
+ {
1846
+ type: 'onepage',
1847
+ name: '毛孩玩具聯名,買三件折扣8折!',
1848
+ referrer: '-',
1849
+ },
1850
+ ];
1851
+
1852
+ const tableBody = document.querySelector('#beauty-salon-table tbody');
1853
+ const tableDiv = document.querySelector('#beauty-salon-div');
1854
+
1855
+ if (!tableBody || !tableDiv) {
1856
+ return;
1857
+ }
1858
+
1859
+ // 將資料填入表格
1860
+ salons.forEach(salon => {
1861
+ const data = shopTypeRecord[salon.type];
1862
+ const row = document.createElement('tr');
1863
+ row.className = `${prefix}_tr`;
1864
+
1865
+ // 賣場類型欄位
1866
+ const typeCell = document.createElement('td');
1867
+ typeCell.className = `${prefix}_td ${prefix}_type_cell`;
1868
+ typeCell.innerHTML = `${data.icon} ${data.title}`;
1869
+ row.appendChild(typeCell);
1870
+
1871
+ // 賣場名稱欄位
1872
+ const nameCell = document.createElement('td');
1873
+ nameCell.className = `${prefix}_td`;
1874
+ nameCell.textContent = salon.name;
1875
+ row.appendChild(nameCell);
1876
+
1877
+ // 推薦人欄位
1878
+ const referrerCell = document.createElement('td');
1879
+ referrerCell.className = `${prefix}_td`;
1880
+ if (salon.referrer) {
1881
+ referrerCell.textContent = salon.referrer;
1882
+ } else {
1883
+ referrerCell.textContent = '-';
1884
+ referrerCell.classList.add(`${prefix}_empty_referrer`);
1885
+ }
1886
+ row.appendChild(referrerCell);
1887
+
1888
+ // 將該行添加到表格中
1889
+ tableBody.appendChild(row);
1890
+ });
1891
+
1892
+ // 展開更多
1893
+ const expendMore = document.createElement('div');
1894
+ expendMore.className = `${prefix}_expend`;
1895
+ const moreText = html`<span>展開更多</span>`;
1896
+ const chevronDown = html`<i class="fa-solid fa-chevron-dow ms-1"></i>`;
1897
+ expendMore.innerHTML = `${moreText} ${chevronDown}`;
1898
+ tableDiv.appendChild(expendMore);
1899
+ },
1900
+ });
1901
+ }
1902
+
1903
+ /*
1904
+ ** 優惠券的排版
1905
+ */
1906
+
3359
1907
  // 優惠促銷模式的選取商品畫面
3360
1908
  const reBackProductView: Record<RebackType, { title: string; html: string | string[] }> = {
3361
1909
  rebate: { title: '活動商品', html: voucherData.for === 'all' ? '' : selectProduct() },
3362
1910
  discount: { title: '活動商品', html: voucherData.for === 'all' ? '' : selectProduct() },
3363
1911
  shipment_free: { title: '活動商品', html: '' },
3364
1912
  add_on_items: { title: '加購品項', html: [addProductView()].join('') },
3365
- giveaway: { title: '贈品品項', html: [addProductView()].join('') },
1913
+ giveaway: { title: '贈品品項', html: [addProductView()].join('') },
3366
1914
  };
1915
+
1916
+ // 優惠套用賣場按鈕
1917
+ const applyShopButton = BgWidget.customButton({
1918
+ button: { color: 'gray', size: 'md' },
1919
+ text: { name: '選擇賣場' },
1920
+ event: gvc.event(() => {
1921
+ const bgDialog = new BgDialog(gvc);
1922
+ bgDialog.marketShop();
1923
+ }),
1924
+ });
1925
+
3367
1926
  // 優惠券設定 Layout Data
3368
- const viewList: { title: string; html: string | string[] }[][] = [
1927
+ const viewList: { title: string; html: string | string[]; button?: string }[][] = [
3369
1928
  [
3370
1929
  { title: '活動設定', html: [status(), title()] },
3371
1930
  { title: '折扣方式', html: trigger() },
3372
1931
  { title: '適用訂單類型', html: device() },
3373
1932
  ],
1933
+ // [
1934
+ // {
1935
+ // title: '套用賣場',
1936
+ // button: applyShopButton,
1937
+ // html: applyShop(),
1938
+ // },
1939
+ // ],
3374
1940
  [
3375
1941
  { title: '折扣設定', html: method() },
3376
1942
  { title: '使用條件', html: rule() },
@@ -3386,7 +1952,7 @@ export class ShoppingDiscountSetting {
3386
1952
  { title: '打折範圍', html: offStart() },
3387
1953
  { title: '重複觸發', html: countingBoolean() },
3388
1954
  { title: '是否與其他優惠券疊加使用', html: overlay() },
3389
- { title: '消費金額於其他折扣觸發時機', html: isIncludeDiscount() },
1955
+ { title: '優惠門檻判斷依據', html: isIncludeDiscount() },
3390
1956
  ],
3391
1957
  [
3392
1958
  { title: '全館總使用次數', html: storeUseTimeLimit() },
@@ -3416,14 +1982,18 @@ export class ShoppingDiscountSetting {
3416
1982
  {
3417
1983
  html: viewList
3418
1984
  .map(viewData => {
1985
+ if (viewData.length === 0 || viewData.every(view => !view.html)) {
1986
+ return '';
1987
+ }
1988
+
3419
1989
  return voucherSettingCard(
3420
1990
  viewData.map(view => {
3421
1991
  if (!view.html) {
3422
1992
  return [];
3423
1993
  } else if (Array.isArray(view.html)) {
3424
- return [setTitle(view.title), ...view.html];
1994
+ return [setTitle(view.title, view.button), ...view.html];
3425
1995
  } else {
3426
- return [setTitle(view.title), view.html];
1996
+ return [setTitle(view.title, view.button), view.html];
3427
1997
  }
3428
1998
  })
3429
1999
  );