ts-glitter 22.4.7 → 22.5.0

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 (98) hide show
  1. package/lib/glitterBundle/Glitter.css +74 -62
  2. package/lowcode/Entry.js +1 -1
  3. package/lowcode/Entry.ts +1 -1
  4. package/lowcode/backend-manager/bg-product.js +49 -32
  5. package/lowcode/backend-manager/bg-product.ts +57 -39
  6. package/lowcode/backend-manager/bg-widget.js +17 -0
  7. package/lowcode/backend-manager/bg-widget.ts +18 -0
  8. package/lowcode/cms-plugin/information/information-module.js +5 -5
  9. package/lowcode/cms-plugin/information/information-module.ts +9 -5
  10. package/lowcode/cms-plugin/menus-setting.js +69 -55
  11. package/lowcode/cms-plugin/menus-setting.ts +77 -61
  12. package/lowcode/cms-plugin/module/form-module.js +109 -89
  13. package/lowcode/cms-plugin/module/form-module.ts +680 -650
  14. package/lowcode/cms-plugin/module/product-excel.js +1 -0
  15. package/lowcode/cms-plugin/module/product-excel.ts +2 -0
  16. package/lowcode/cms-plugin/pos-pages/payment-page.js +28 -10
  17. package/lowcode/cms-plugin/pos-pages/payment-page.ts +29 -10
  18. package/lowcode/cms-plugin/shopping-allowance-manager.js +0 -1
  19. package/lowcode/cms-plugin/shopping-allowance-manager.ts +0 -1
  20. package/lowcode/cms-plugin/shopping-collections.js +367 -193
  21. package/lowcode/cms-plugin/shopping-collections.ts +664 -243
  22. package/lowcode/cms-plugin/shopping-information.js +392 -38
  23. package/lowcode/cms-plugin/shopping-information.ts +479 -87
  24. package/lowcode/cms-plugin/shopping-product-setting.js +2 -2
  25. package/lowcode/cms-plugin/shopping-product-setting.ts +2 -2
  26. package/lowcode/cms-plugin/shopping-setting-advance.js +906 -766
  27. package/lowcode/cms-plugin/shopping-setting-advance.ts +977 -841
  28. package/lowcode/cms-plugin/shopping-setting-basic.js +1547 -1285
  29. package/lowcode/cms-plugin/shopping-setting-basic.ts +1742 -1466
  30. package/lowcode/cms-plugin/stock-stores.js +1 -0
  31. package/lowcode/cms-plugin/stock-stores.ts +1 -0
  32. package/lowcode/cms-plugin/user-list.js +47 -12
  33. package/lowcode/cms-plugin/user-list.ts +52 -14
  34. package/lowcode/css/editor.css +6 -0
  35. package/lowcode/glitterBundle/Glitter.css +74 -62
  36. package/lowcode/jslib/nestable/index.html +317 -0
  37. package/lowcode/jslib/nestable/jquery.nestable.js +484 -0
  38. package/lowcode/official_view_component/form-widget/input-custom.js +98 -6
  39. package/lowcode/official_view_component/form-widget/input-custom.ts +121 -16
  40. package/lowcode/public-components/headers/header-class.js +63 -0
  41. package/lowcode/public-components/headers/header-class.ts +65 -0
  42. package/lowcode/public-components/headers/sy-02.js +386 -400
  43. package/lowcode/public-components/headers/sy-02.ts +482 -492
  44. package/lowcode/public-components/headers/sy-03.js +42 -43
  45. package/lowcode/public-components/headers/sy-03.ts +46 -43
  46. package/lowcode/public-components/headers/sy-04.js +43 -41
  47. package/lowcode/public-components/headers/sy-04.ts +48 -41
  48. package/lowcode/public-components/headers/sy-05.js +30 -27
  49. package/lowcode/public-components/headers/sy-05.ts +33 -27
  50. package/lowcode/public-components/product/product-list.js +160 -148
  51. package/lowcode/public-components/product/product-list.ts +186 -165
  52. package/lowcode/public-models/product.ts +26 -1
  53. package/lowcode/src/glitterBundle/Glitter.css +74 -62
  54. package/package.json +1 -1
  55. package/rxmnt81tnk.json +1 -0
  56. package/src/api-public/controllers/shop.js +10 -4
  57. package/src/api-public/controllers/shop.js.map +1 -1
  58. package/src/api-public/controllers/shop.ts +14 -9
  59. package/src/api-public/services/ezpay/tool.d.ts +1 -0
  60. package/src/api-public/services/mail.js +1 -1
  61. package/src/api-public/services/mail.js.map +1 -1
  62. package/src/api-public/services/mail.ts +1 -1
  63. package/src/api-public/services/schedule.d.ts +0 -1
  64. package/src/api-public/services/schedule.js +12 -35
  65. package/src/api-public/services/schedule.js.map +1 -1
  66. package/src/api-public/services/schedule.ts +15 -39
  67. package/src/api-public/services/shopee.js +7 -17
  68. package/src/api-public/services/shopping.d.ts +27 -6
  69. package/src/api-public/services/shopping.js +364 -85
  70. package/src/api-public/services/shopping.js.map +1 -1
  71. package/src/api-public/services/shopping.ts +510 -101
  72. package/src/api-public/services/updated-table-checked.js +58 -1
  73. package/src/api-public/services/updated-table-checked.js.map +1 -1
  74. package/src/api-public/services/updated-table-checked.ts +62 -1
  75. package/src/api-public/services/user-update.js +14 -0
  76. package/src/api-public/services/user-update.js.map +1 -1
  77. package/src/api-public/services/user-update.ts +15 -0
  78. package/src/api-public/services/user.js +1 -1
  79. package/src/api-public/services/user.js.map +1 -1
  80. package/src/api-public/services/user.ts +1 -1
  81. package/src/app-project/serverless/src/modules/database.d.ts +1 -1
  82. package/src/app-project/serverless/src/modules/redis.d.ts +1 -1
  83. package/src/helper/glitter-util.d.ts +1 -0
  84. package/src/index.js +7 -5
  85. package/src/index.js.map +1 -1
  86. package/src/index.ts +45 -38
  87. package/src/modules/firebase.js +1 -0
  88. package/src/modules/firebase.js.map +1 -1
  89. package/src/modules/firebase.ts +1 -0
  90. package/src/seo-config.d.ts +1 -1
  91. package/src/seo-config.js +1 -2
  92. package/src/seo-config.js.map +1 -1
  93. package/src/seo-config.ts +1 -2
  94. package/src/services/saas-table-check.js.map +1 -1
  95. package/src/services/ses.js +4 -3
  96. package/src/services/ses.js.map +1 -1
  97. package/src/services/system-schedule.js.map +1 -1
  98. package/src/services/system-schedule.ts +1 -0
@@ -14,9 +14,11 @@ type ViewModel = {
14
14
  filterID: string;
15
15
  type: 'list' | 'add' | 'replace';
16
16
  data: Collection;
17
- dataList: any;
17
+ dataList: Collection[] | undefined;
18
+ collectionList: Collection[];
18
19
  query: string;
19
20
  allParents: string[];
21
+ cloneTarget: Collection | null;
20
22
  };
21
23
 
22
24
  type Product = {
@@ -90,8 +92,10 @@ export class ShoppingCollections {
90
92
  },
91
93
  },
92
94
  dataList: undefined,
95
+ collectionList: [],
93
96
  query: '',
94
97
  allParents: [],
98
+ cloneTarget: null,
95
99
  };
96
100
  const dialog = new ShareDialog(gvc.glitter);
97
101
 
@@ -179,6 +183,8 @@ export class ShoppingCollections {
179
183
  dataList: [{ obj: vm, key: 'type' }],
180
184
  view: () => {
181
185
  if (vm.type === 'list') {
186
+ vm.cloneTarget = null;
187
+
182
188
  return BgWidget.container(html`
183
189
  <div class="title-container">
184
190
  ${BgWidget.title('商品分類')}
@@ -187,127 +193,171 @@ export class ShoppingCollections {
187
193
  ${BgWidget.grayButton(
188
194
  '編輯順序',
189
195
  gvc.event(() => {
190
- return BgWidget.infoDialog({
196
+ const cloneCollectionList = vm.collectionList.slice();
197
+
198
+ return BgWidget.settingDialog({
191
199
  gvc,
192
200
  title: '編輯順序',
193
- innerHTML: gvc.bindView(
194
- (() => {
195
- const id = glitter.getUUID();
196
- let loading = true;
197
- return {
198
- bind: id,
199
- view: () => {
200
- if (loading) {
201
- this.addStyle(gvc);
202
- return '';
203
- } else {
204
- return html` <div class="d-flex">
205
- <div class="parent-container">
206
- <div class="tx_title text-center mb-2">父層類別</div>
207
- <ul class="ul-style" id="parent-list">
208
- <!-- TS 生成的父層類別列表 -->
209
- </ul>
210
- </div>
211
- <div class="child-container">
212
- <div class="tx_title text-center mb-2">子層類別</div>
213
- <ul class="ul-style" id="child-list">
214
- <!-- TS 生成的子層類別列表 -->
215
- </ul>
216
- </div>
217
- </div>`;
218
- }
219
- },
220
- divCreate: {},
221
- onCreate: () => {
222
- if (loading) {
223
- gvc.addMtScript(
224
- [
225
- {
226
- src: 'https://cdn.jsdelivr.net/npm/sortablejs@1.15.0/Sortable.min.js',
227
- },
228
- ],
229
- () => {
230
- const si = setInterval(() => {
231
- if ((window as any).Sortable !== undefined) {
232
- loading = false;
233
- clearInterval(si);
234
- gvc.notifyDataChange(id);
235
- }
236
- }, 300);
237
- },
238
- () => {}
239
- );
240
- } else {
241
- // 建立列表項目
242
- function createListItem(item: any, index: number): HTMLLIElement {
243
- const li = document.createElement('li');
244
- li.className = 'li-style';
245
- li.setAttribute('data-index', index.toString());
246
- li.setAttribute('data-parent', item.parentTitles[0] || 'root');
247
- li.innerHTML = `<span class="drag-icon"></span><span class="tx_normal">${item.title}</span>`;
248
- return li;
249
- }
201
+ width: 700,
202
+ innerHTML: (iGVC: GVC) => {
203
+ const id = glitter.getUUID();
204
+ let loading = true;
205
+ this.addStyle(iGVC);
250
206
 
251
- // 初始化 Sortable 功能
252
- function initSortable(containerId: string) {
253
- const el = document.querySelector(`#${containerId}`) as HTMLElement;
254
- (window as any).Sortable.create(el, {
255
- animation: 150,
256
- onEnd: function () {
257
- const items = [...el.children].map(
258
- child => vm.dataList[parseInt(child.getAttribute('data-index') as string)]
259
- );
260
- ApiShop.sortCollections({
261
- data: { list: items },
262
- token: (window.parent as any).config.token,
263
- }).then(res => {
264
- if (res.result && !res.response) {
265
- dialog.errorMessage({ text: '更改順序失敗' });
266
- }
267
- });
268
- },
269
- });
270
- }
271
-
272
- function loadChildItems(parentTitle: string) {
273
- const childContainer = document.getElementById('child-list') as HTMLElement;
274
- childContainer.innerHTML = '';
207
+ return iGVC.bindView({
208
+ bind: id,
209
+ view: () => {
210
+ if (loading) {
211
+ return BgWidget.spinner();
212
+ }
275
213
 
276
- vm.dataList.forEach((item: any, index: number) => {
277
- if (item.parentTitles[0] === parentTitle) {
278
- childContainer.appendChild(createListItem(item, index));
214
+ return html`
215
+ <div class="p-2">
216
+ ${BgWidget.grayNote('提示:左鍵拖曳可排列順序,點擊可顯示向下一層分類')}
217
+ <div class="d-flex justify-content-start mt-3">
218
+ <div class="layer-container">
219
+ ${[0, 1, 2]
220
+ .map(depth => {
221
+ const title = ['第一層分類', '第二層分類', '第三層分類'][depth];
222
+ return html` <div class="flex-1 layer-block">
223
+ <div class="tx_700 fs-4 text-center mb-2">${title}</div>
224
+ <ul class="ul-style" id="layer-list-${depth}"></ul>
225
+ </div>`;
226
+ })
227
+ .join('')}
228
+ </div>
229
+ </div>
230
+ </div>
231
+ `;
232
+ },
233
+ onCreate: () => {
234
+ if (loading) {
235
+ iGVC.addMtScript(
236
+ [{ src: 'https://cdn.jsdelivr.net/npm/sortablejs@1.15.0/Sortable.min.js' }],
237
+ () => {
238
+ const si = setInterval(() => {
239
+ if ((window.parent as any).Sortable !== undefined) {
240
+ loading = false;
241
+ clearInterval(si);
242
+ iGVC.notifyDataChange(id);
279
243
  }
280
- });
244
+ }, 300);
245
+ },
246
+ () => {}
247
+ );
248
+ } else {
249
+ const document = window.parent.document;
281
250
 
282
- initSortable('child-list');
283
- }
251
+ function createListItem(item: Collection, index: number): HTMLLIElement {
252
+ const li = document.createElement('li');
253
+ li.className = 'li-style';
254
+ li.setAttribute('data-index', index.toString());
255
+ li.setAttribute('data-path', [...(item.parentTitles ?? []), item.title].join(' / '));
256
+ li.innerHTML = `<span class="drag-icon"></span><span class="tx_normal">${item.title}</span>`;
257
+
258
+ li.addEventListener('click', (e: MouseEvent) => {
259
+ const target = e.currentTarget as HTMLElement;
284
260
 
285
- const parentContainer = document.getElementById('parent-list') as HTMLElement;
286
-
287
- // 初始化父層類別列表
288
- vm.dataList.forEach((item: any, index: number) => {
289
- if (item.parentTitles.length === 0) {
290
- // 如果是父層類別
291
- const li = createListItem(item, index);
292
- li.addEventListener('click', () => {
293
- loadChildItems(item.title);
294
- document
295
- .querySelectorAll('#parent-list li')
296
- .forEach(li => li.classList.remove('selectCol'));
297
- li.classList.add('selectCol');
261
+ // 取得最近的 ul 並取得其 id
262
+ const ul = target.closest('ul');
263
+
264
+ // 取代 root(靜態 ID)為動態找到的 ul
265
+ if (ul) {
266
+ Array.from(ul.children).forEach((element: Element) => {
267
+ (element as HTMLElement).style.backgroundColor = '#dddddd';
298
268
  });
299
- parentContainer.appendChild(li);
300
269
  }
270
+
271
+ // 設定被點擊的項目背景色
272
+ target.style.backgroundColor = '#d8ecda';
273
+
274
+ // 呼叫對應層級的載入方法
275
+ loadChildLayer([...(item.parentTitles ?? []), item.title]);
301
276
  });
302
277
 
303
- initSortable('parent-list');
278
+ return li;
304
279
  }
305
- },
306
- };
307
- })()
308
- ),
309
- closeCallback: () => {
310
- gvc.notifyDataChange(vm.id);
280
+
281
+ function initSortable(containerId: string) {
282
+ const el = document.getElementById(containerId) as HTMLElement;
283
+ (window.parent as any).Sortable.create(el, {
284
+ animation: 200,
285
+ scroll: true,
286
+ onEnd: () => {
287
+ const items = [...el.children].map(
288
+ child => vm.dataList?.[parseInt(child.getAttribute('data-index') as string)]
289
+ ) as Collection[];
290
+
291
+ vm.collectionList = ShoppingCollections.sortedCollectionConfig(
292
+ vm.collectionList,
293
+ items
294
+ );
295
+ },
296
+ });
297
+ }
298
+
299
+ function loadChildLayer(parentPath: string[]) {
300
+ const nextDepth = parentPath.length;
301
+ const nextId = `layer-list-${nextDepth}`;
302
+ for (let i = nextDepth; i < 3; i++) {
303
+ const el = document.getElementById(`layer-list-${i}`);
304
+ if (el) el.innerHTML = '';
305
+ }
306
+ const container = document.getElementById(nextId) as HTMLElement;
307
+ container.innerHTML = '';
308
+ vm.dataList?.forEach((item, index) => {
309
+ const path = item.parentTitles ?? [];
310
+ if (
311
+ path.length === parentPath.length &&
312
+ path.every((p, i) => p === parentPath[i])
313
+ ) {
314
+ container.appendChild(createListItem(item, index));
315
+ }
316
+ });
317
+ initSortable(nextId);
318
+ }
319
+
320
+ const root = document.getElementById('layer-list-0') as HTMLElement;
321
+ root.innerHTML = '';
322
+
323
+ vm.dataList?.forEach((item, index) => {
324
+ if ((item.parentTitles ?? []).length === 0) {
325
+ root.appendChild(createListItem(item, index));
326
+ }
327
+ });
328
+ initSortable('layer-list-0');
329
+ }
330
+ },
331
+ });
332
+ },
333
+ footer_html: (fGVC: GVC) => {
334
+ return [
335
+ BgWidget.cancel(
336
+ fGVC.event(() => {
337
+ vm.collectionList = cloneCollectionList;
338
+ fGVC.closeDialog();
339
+ })
340
+ ),
341
+ BgWidget.save(
342
+ fGVC.event(() => {
343
+ dialog.dataLoading({ visible: true });
344
+
345
+ ApiShop.sortCollections({
346
+ data: { list: vm.collectionList },
347
+ token: (window.parent as any).config.token,
348
+ }).then(res => {
349
+ dialog.dataLoading({ visible: false });
350
+ if (res.result && !res.response) {
351
+ dialog.errorMessage({ text: '更改順序失敗' });
352
+ } else {
353
+ dialog.successMessage({ text: '更改順序成功' });
354
+ fGVC.closeDialog();
355
+ gvc.notifyDataChange(vm.id);
356
+ }
357
+ });
358
+ })
359
+ ),
360
+ ].join('');
311
361
  },
312
362
  });
313
363
  })
@@ -347,8 +397,202 @@ export class ShoppingCollections {
347
397
  gvc.notifyDataChange(vm.id);
348
398
  }),
349
399
  vm.query || '',
350
- '搜尋類別'
400
+ '搜尋分類'
351
401
  ),
402
+ // gvc.bindView(() => {
403
+ // const vm = {
404
+ // id: gvc.glitter.getUUID(),
405
+ // loading: true,
406
+ // data: [],
407
+ // };
408
+ //
409
+ // async function loading() {
410
+ // const collection = await ApiShop.getCollection();
411
+ // vm.data = collection.response.value ?? [];
412
+ // vm.loading = false;
413
+ // gvc.notifyDataChange(vm.id);
414
+ // }
415
+ //
416
+ // loading();
417
+ // return {
418
+ // bind: vm.id,
419
+ // view: () => {
420
+ // if (vm.loading) {
421
+ // return BgWidget.spinner();
422
+ // }
423
+ //
424
+ // function loop(array: any[], index: string): string {
425
+ // return html`
426
+ // <ol class="dd-list">
427
+ // ${array.map((dd, index) => {
428
+ // return html` <li class="dd-item" data-id="${index}-${index}">
429
+ // <div class="dd-handle">${dd.title}</div>
430
+ // ${dd.array && dd.array.length ? loop(dd.array, `${index}-${index}`) : []}
431
+ // </li>`;
432
+ // }).join('')}
433
+ // </ol>
434
+ // `;
435
+ // }
436
+ //
437
+ // return html` <div class="dd" id="nestable">${loop(vm.data, '0')}</div>`;
438
+ // },
439
+ // divCreate: {
440
+ // class: `px-lg-3 px-1`,
441
+ // },
442
+ // onCreate: () => {
443
+ // ApiShop.putCollections
444
+ // gvc.glitter.addStyle(`
445
+ // .cf:after { visibility: hidden; display: block; font-size: 0; content: " "; clear: both; height: 0; }
446
+ // *:first-child+html .cf { zoom: 1; }
447
+ //
448
+ // h1 { font-size: 1.75em; margin: 0 0 0.6em 0; }
449
+ //
450
+ // a { color: #2996cc; }
451
+ // a:hover { text-decoration: none; }
452
+ //
453
+ // p { line-height: 1.5em; }
454
+ // .small { color: #666; font-size: 0.875em; }
455
+ // .large { font-size: 1.25em; }
456
+ //
457
+ // /**
458
+ // * Nestable
459
+ // */
460
+ //
461
+ // .dd { position: relative; display: block; margin: 0; padding: 0; list-style: none; font-size: 13px; line-height: 20px; }
462
+ //
463
+ // .dd-list { display: block; position: relative; margin: 0; padding: 0; list-style: none; }
464
+ // .dd-list .dd-list { padding-left: 30px; }
465
+ // .dd-collapsed .dd-list { display: none; }
466
+ //
467
+ // .dd-item,
468
+ // .dd-empty,
469
+ // .dd-placeholder { display: block; position: relative; margin: 0; padding: 0; min-height: 20px; font-size: 13px; line-height: 20px; }
470
+ //
471
+ // .dd-handle { display: block; height: 30px; margin: 5px 0; padding: 5px 10px; color: #333; text-decoration: none; font-weight: bold; border: 1px solid #ccc;
472
+ // background: #fafafa;
473
+ // background: -webkit-linear-gradient(top, #fafafa 0%, #eee 100%);
474
+ // background: -moz-linear-gradient(top, #fafafa 0%, #eee 100%);
475
+ // background: linear-gradient(top, #fafafa 0%, #eee 100%);
476
+ // -webkit-border-radius: 3px;
477
+ // border-radius: 3px;
478
+ // box-sizing: border-box; -moz-box-sizing: border-box;
479
+ // }
480
+ // .dd-handle:hover { color: #2ea8e5; background: #fff; }
481
+ //
482
+ // .dd-item > button { display: block; position: relative; cursor: pointer; float: left; width: 25px; height: 20px; margin: 5px 0; padding: 0; text-indent: 100%; white-space: nowrap; overflow: hidden; border: 0; background: transparent; font-size: 12px; line-height: 1; text-align: center; font-weight: bold; }
483
+ // .dd-item > button:before { content: '+'; display: block; position: absolute; width: 100%; text-align: center; text-indent: 0; }
484
+ // .dd-item > button[data-action="collapse"]:before { content: '-'; }
485
+ //
486
+ // .dd-placeholder,
487
+ // .dd-empty { margin: 5px 0; padding: 0; min-height: 30px; background: #f2fbff; border: 1px dashed #b6bcbf; box-sizing: border-box; -moz-box-sizing: border-box; }
488
+ // .dd-empty { border: 1px dashed #bbb; min-height: 100px; background-color: #e5e5e5;
489
+ // background-image: -webkit-linear-gradient(45deg, #fff 25%, transparent 25%, transparent 75%, #fff 75%, #fff),
490
+ // -webkit-linear-gradient(45deg, #fff 25%, transparent 25%, transparent 75%, #fff 75%, #fff);
491
+ // background-image: -moz-linear-gradient(45deg, #fff 25%, transparent 25%, transparent 75%, #fff 75%, #fff),
492
+ // -moz-linear-gradient(45deg, #fff 25%, transparent 25%, transparent 75%, #fff 75%, #fff);
493
+ // background-image: linear-gradient(45deg, #fff 25%, transparent 25%, transparent 75%, #fff 75%, #fff),
494
+ // linear-gradient(45deg, #fff 25%, transparent 25%, transparent 75%, #fff 75%, #fff);
495
+ // background-size: 60px 60px;
496
+ // background-position: 0 0, 30px 30px;
497
+ // }
498
+ //
499
+ // .dd-dragel { position: absolute; pointer-events: none; z-index: 9999; }
500
+ // .dd-dragel > .dd-item .dd-handle { margin-top: 0; }
501
+ // .dd-dragel .dd-handle {
502
+ // -webkit-box-shadow: 2px 4px 6px 0 rgba(0,0,0,.1);
503
+ // box-shadow: 2px 4px 6px 0 rgba(0,0,0,.1);
504
+ // }
505
+ //
506
+ // /**
507
+ // * Nestable Extras
508
+ // */
509
+ //
510
+ // .nestable-lists { display: block; clear: both; padding: 30px 0; width: 100%; border: 0; border-top: 2px solid #ddd; border-bottom: 2px solid #ddd; }
511
+ //
512
+ // #nestable-menu { padding: 0; margin: 20px 0; }
513
+ //
514
+ // #nestable-output,
515
+ // #nestable2-output { width: 100%; height: 7em; font-size: 0.75em; line-height: 1.333333em; font-family: Consolas, monospace; padding: 5px; box-sizing: border-box; -moz-box-sizing: border-box; }
516
+ //
517
+ // #nestable2 .dd-handle {
518
+ // color: #fff;
519
+ // border: 1px solid #999;
520
+ // background: #bbb;
521
+ // background: -webkit-linear-gradient(top, #bbb 0%, #999 100%);
522
+ // background: -moz-linear-gradient(top, #bbb 0%, #999 100%);
523
+ // background: linear-gradient(top, #bbb 0%, #999 100%);
524
+ // }
525
+ // #nestable2 .dd-handle:hover { background: #bbb; }
526
+ // #nestable2 .dd-item > button:before { color: #fff; }
527
+ //
528
+ // @media only screen and (min-width: 700px) {
529
+ //
530
+ // .dd { width: 100%; }
531
+ // .dd + .dd { margin-left: 2%; }
532
+ //
533
+ // }
534
+ //
535
+ // .dd-hover > .dd-handle { background: #2ea8e5 !important; }
536
+ //
537
+ // /**
538
+ // * Nestable Draggable Handles
539
+ // */
540
+ //
541
+ // .dd3-content { display: block; height: 30px; margin: 5px 0; padding: 5px 10px 5px 40px; color: #333; text-decoration: none; font-weight: bold; border: 1px solid #ccc;
542
+ // background: #fafafa;
543
+ // background: -webkit-linear-gradient(top, #fafafa 0%, #eee 100%);
544
+ // background: -moz-linear-gradient(top, #fafafa 0%, #eee 100%);
545
+ // background: linear-gradient(top, #fafafa 0%, #eee 100%);
546
+ // -webkit-border-radius: 3px;
547
+ // border-radius: 3px;
548
+ // box-sizing: border-box; -moz-box-sizing: border-box;
549
+ // }
550
+ // .dd3-content:hover { color: #2ea8e5; background: #fff; }
551
+ //
552
+ // .dd-dragel > .dd3-item > .dd3-content { margin: 0; }
553
+ //
554
+ // .dd3-item > button { margin-left: 30px; }
555
+ //
556
+ // .dd3-handle { position: absolute; margin: 0; left: 0; top: 0; cursor: pointer; width: 30px; text-indent: 100%; white-space: nowrap; overflow: hidden;
557
+ // border: 1px solid #aaa;
558
+ // background: #ddd;
559
+ // background: -webkit-linear-gradient(top, #ddd 0%, #bbb 100%);
560
+ // background: -moz-linear-gradient(top, #ddd 0%, #bbb 100%);
561
+ // background: linear-gradient(top, #ddd 0%, #bbb 100%);
562
+ // border-top-right-radius: 0;
563
+ // border-bottom-right-radius: 0;
564
+ // }
565
+ // .dd3-handle:before { content: '≡'; display: block; position: absolute; left: 0; top: 3px; width: 100%; text-align: center; text-indent: 0; color: #fff; font-size: 20px; font-weight: normal; }
566
+ // .dd3-handle:hover { background: #ddd; }
567
+ //
568
+ // /**
569
+ // * Socialite
570
+ // */
571
+ //
572
+ // .socialite { display: block; float: left; height: 35px; }
573
+ // `);
574
+ // gvc.glitter.addMtScript(
575
+ // [`${gvc.glitter.root_path}/jslib/nestable/jquery.nestable.js`],
576
+ // () => {
577
+ // const updateOutput = function (e: any) {
578
+ // const list = e.length ? e : $(e.target),
579
+ // output = list.data('output');
580
+ // console.log(`output-data`, window.JSON.stringify(list.nestable('serialize')));
581
+ // };
582
+ //
583
+ // //@ts-ignore
584
+ // $('#nestable')
585
+ // //@ts-ignore
586
+ // .nestable({
587
+ // group: 1,
588
+ // })
589
+ // .on('change', updateOutput);
590
+ // },
591
+ // () => {}
592
+ // );
593
+ // },
594
+ // };
595
+ // }),
352
596
  BgWidget.tableV3({
353
597
  gvc: gvc,
354
598
  getData: vmi => {
@@ -361,7 +605,9 @@ export class ShoppingCollections {
361
605
 
362
606
  ApiShop.getCollection().then(data => {
363
607
  if (data.result && data.response.value.length > 0) {
364
- vm.allParents = ['(無)'].concat(
608
+ vm.collectionList = data.response.value;
609
+
610
+ vm.allParents = [this.undefinedOption].concat(
365
611
  data.response.value.map((item: { title: string }) => item.title)
366
612
  );
367
613
 
@@ -377,7 +623,7 @@ export class ShoppingCollections {
377
623
  const original = structuredClone(dd);
378
624
  const isChildren = dd.parentTitles && dd.parentTitles.length > 0;
379
625
 
380
- // 更新類別顯示資料
626
+ // 更新分類顯示資料
381
627
  function triggerHidden() {
382
628
  dd.hidden = !Boolean(dd.hidden);
383
629
 
@@ -396,7 +642,7 @@ export class ShoppingCollections {
396
642
  if (isChildren) {
397
643
  const parent = collectionsMap.get(dd.parentTitles[0]);
398
644
  if (parent?.hidden && dd.hidden) {
399
- dialog.infoMessage({ text: '請先開啟顯示父層類別' });
645
+ dialog.infoMessage({ text: '請先開啟顯示第一層分類' });
400
646
  } else {
401
647
  triggerHidden();
402
648
  }
@@ -405,7 +651,7 @@ export class ShoppingCollections {
405
651
  triggerHidden();
406
652
  } else {
407
653
  dialog.checkYesOrNot({
408
- text: '若關閉顯示該父層類別,所有子層也將會關閉顯示,是否確定要執行?',
654
+ text: '若關閉顯示該層分類,子層分類也將會關閉顯示,是否確定要執行?',
409
655
  callback: bool => bool && triggerHidden(),
410
656
  });
411
657
  }
@@ -417,12 +663,14 @@ export class ShoppingCollections {
417
663
  key: '標題',
418
664
  value: html`<div
419
665
  class="fs-7"
420
- style="min-width: ${document.body.clientWidth > 768 ? 400 : 225}px;"
666
+ style="min-width: ${document.body.clientWidth > 768
667
+ ? 400
668
+ : 225}px; padding-left: ${4 + dd.parentTitles.length * 12}px"
421
669
  >
422
- ${isChildren
423
- ? html` <i class="fa-solid fa-arrow-turn-down-right me-2"></i
424
- >${dd.parentTitles.join(' / ')} / ${dd.title}`
425
- : dd.title}
670
+ ${dd.parentTitles.length > 0
671
+ ? html`<i class="fa-solid fa-arrow-turn-down-right me-1"></i>`
672
+ : ''}
673
+ ${dd.title}
426
674
  </div>`,
427
675
  },
428
676
  {
@@ -459,7 +707,9 @@ export class ShoppingCollections {
459
707
  });
460
708
  },
461
709
  rowClick: (_, index) => {
462
- vm.data = vm.dataList[index];
710
+ if (vm.dataList) {
711
+ vm.data = vm.dataList[index];
712
+ }
463
713
  vm.type = 'replace';
464
714
  },
465
715
  filter: [
@@ -467,12 +717,12 @@ export class ShoppingCollections {
467
717
  name: '批量移除',
468
718
  event: () => {
469
719
  dialog.checkYesOrNot({
470
- text: '確定要刪除商品類別嗎?<br/>(若此類別包含子類別,也將一併刪除)',
720
+ text: '確定要刪除商品分類嗎?<br/>(若包含子分類,也將一併刪除)',
471
721
  callback: response => {
472
722
  if (response) {
473
723
  dialog.dataLoading({ visible: true });
474
724
  ApiShop.deleteCollections({
475
- data: { data: vm.dataList.filter((dd: any) => dd.checked) },
725
+ data: { data: vm.dataList?.filter(dd => dd.checked) },
476
726
  token: (window.parent as any).config.token,
477
727
  }).then(res => {
478
728
  dialog.dataLoading({ visible: false });
@@ -498,6 +748,8 @@ export class ShoppingCollections {
498
748
  ${BgWidget.mbContainer(120)}
499
749
  `);
500
750
  } else if (vm.type == 'replace') {
751
+ vm.cloneTarget = null;
752
+
501
753
  return this.editorDetail({
502
754
  vm: vm,
503
755
  gvc: gvc,
@@ -517,33 +769,33 @@ export class ShoppingCollections {
517
769
 
518
770
  static addStyle(gvc: GVC) {
519
771
  gvc.addStyle(`
520
- .parent-container,
521
- .child-container {
522
- flex: 1;
772
+ .layer-container {
773
+ display: flex;
774
+ gap: 18px;
523
775
  margin-right: 20px;
524
776
  }
525
777
 
526
- .parent-container:last-child,
527
- .child-container:last-child {
528
- margin-right: 0;
529
- }
530
-
531
778
  .ul-style {
532
779
  list-style-type: none;
533
- padding: 0;
780
+ padding: 8px;
534
781
  margin: 0;
535
782
  min-height: 200px;
536
783
  border: 1px solid #ccc;
784
+ border-radius: 10px;
785
+ width: 190px;
786
+ height: 380px;
787
+ overflow-y: auto;
537
788
  }
538
789
 
539
790
  .li-style {
791
+ width: 100%;
540
792
  padding: 6px 10px;
541
- margin-bottom: 5px;
542
- background-color: #eee;
793
+ margin-bottom: 6px;
794
+ background-color: #dddddd;
543
795
  cursor: move;
544
- border: 1px solid #ccc;
545
796
  display: flex;
546
797
  align-items: center;
798
+ border-radius: 10px;
547
799
  }
548
800
 
549
801
  .drag-icon {
@@ -552,13 +804,9 @@ export class ShoppingCollections {
552
804
  }
553
805
 
554
806
  .drag-icon::before {
555
- content: '';
556
- font-size: 18px;
557
- margin-right: 10px;
558
- }
559
-
560
- .selectCol {
561
- background-color: #dcdcdc;
807
+ content: '';
808
+ font-size: 20px;
809
+ cursor: grab;
562
810
  }
563
811
  `);
564
812
  }
@@ -567,22 +815,26 @@ export class ShoppingCollections {
567
815
  const gvc = obj.gvc;
568
816
  const glitter = gvc.glitter;
569
817
  const vm = obj.vm;
570
- const original = JSON.parse(JSON.stringify(vm.data));
571
- const dialog = new ShareDialog(gvc.glitter);
818
+ const dialog = new ShareDialog(glitter);
819
+
820
+ const original = JSON.parse(JSON.stringify(vm.data)) as Collection;
821
+ const originDataList = JSON.parse(JSON.stringify(vm.dataList ?? [])) as Collection[];
572
822
  const language_setting = (window.parent as any).store_info.language_setting;
573
823
  let select_lan = language_setting.def;
574
824
 
575
825
  function getValidLangDomain(): { result: boolean; text: string } {
576
826
  const supports = language_setting.support as LanguageLocation[];
577
- const dataList = vm.dataList.filter((data: { title: string }) => data.title !== vm.data.title);
827
+
828
+ const targetData = vm.cloneTarget && vm.cloneTarget.title === vm.data.title ? vm.cloneTarget : original;
829
+
830
+ const dataList = originDataList.filter(item => {
831
+ return [...item.parentTitles, item.title].join('') !== [...targetData.parentTitles, targetData.title].join('');
832
+ });
578
833
 
579
834
  const lagDomain = supports.map(lang => {
580
- const domainMap = dataList.map((item: any) => {
581
- if (!item.language_data) {
582
- return '';
583
- }
835
+ const domainMap = dataList?.map(item => {
584
836
  try {
585
- return item.language_data[lang].seo.domain;
837
+ return item.language_data ? item.language_data[lang].seo.domain : '';
586
838
  } catch (error) {
587
839
  return '';
588
840
  }
@@ -599,7 +851,7 @@ export class ShoppingCollections {
599
851
  });
600
852
  return {
601
853
  result: false,
602
- text: `語系「${text}」的連結網址「${vm.data.language_data[data.lang].seo.domain}」<br />已存在於其他類別,請更換連結網址`,
854
+ text: `語系「${text}」的連結網址「${vm.data.language_data[data.lang].seo.domain}」<br />已存在於其他分類,請更換連結網址`,
603
855
  };
604
856
  }
605
857
  }
@@ -611,9 +863,7 @@ export class ShoppingCollections {
611
863
  const viewID = gvc.glitter.getUUID();
612
864
  const domainID = gvc.glitter.getUUID();
613
865
 
614
- function refresh() {
615
- gvc.notifyDataChange(viewID);
616
- }
866
+ const refresh = () => gvc.notifyDataChange(viewID);
617
867
 
618
868
  return {
619
869
  bind: viewID,
@@ -632,6 +882,7 @@ export class ShoppingCollections {
632
882
  'en-US': getEmptyLanguageData(),
633
883
  };
634
884
  }
885
+
635
886
  const language_data: LanguageData = (vm.data.language_data as any)[select_lan];
636
887
  const prefixURL = `https://${(window.parent as any).glitter.share.editorViewModel.domain}/${Language.getLanguageLinkPrefix(true, select_lan)}collections/`;
637
888
 
@@ -644,11 +895,11 @@ export class ShoppingCollections {
644
895
  vm.type = 'list';
645
896
  })
646
897
  )}
647
- ${BgWidget.title(obj.type === 'add' ? '新增類別' : '編輯類別')}
898
+ ${BgWidget.title(obj.type === 'add' ? '新增分類' : '編輯分類')}
648
899
  <div class="flex-fill"></div>
649
- <div class="me-2 ">
900
+ <div class="d-flex align-items-center gap-2">
650
901
  ${BgWidget.grayButton(
651
- html`<div class="d-flex align-items-center" style="gap:5px;">
902
+ html` <div class="d-flex align-items-center gap-2">
652
903
  <i class="fa-duotone fa-solid fa-earth-americas"></i>${Language.getLanguageText({
653
904
  local: true,
654
905
  compare: select_lan,
@@ -665,7 +916,7 @@ export class ShoppingCollections {
665
916
  bind: id,
666
917
  view: () => {
667
918
  return html` <div
668
- style="position: relative;word-break: break-all;white-space: normal;"
919
+ style="position: relative; word-break: break-all; white-space: normal;"
669
920
  >
670
921
  ${BgWidget.grayNote('前往商店設定->商店訊息中,設定支援的語言。')}
671
922
  ${gvc.bindView(() => {
@@ -691,7 +942,7 @@ export class ShoppingCollections {
691
942
  window.parent as any
692
943
  ).store_info.language_setting.support.includes(dd.key);
693
944
  })
694
- .sort((dd: any) => {
945
+ .sort(dd => {
695
946
  return dd.key === select_lan ? -1 : 1;
696
947
  });
697
948
 
@@ -700,7 +951,7 @@ export class ShoppingCollections {
700
951
  style="gap:15px;"
701
952
  >
702
953
  ${sup
703
- .map((dd: any) => {
954
+ .map(dd => {
704
955
  return html`
705
956
  <div
706
957
  class="px-3 py-1 text-white position-relative d-flex align-items-center justify-content-center"
@@ -715,8 +966,8 @@ export class ShoppingCollections {
715
966
  <div
716
967
  class="position-absolute text-white rounded-2 px-2 d-flex align-items-center rounded-3 ${dd.key !==
717
968
  select_lan
718
- ? `d-none`
719
- : ``}"
969
+ ? 'd-none'
970
+ : ''}"
720
971
  style="top: -12px;right: -10px; height:20px;font-size: 11px;background: #ff6c02;"
721
972
  >
722
973
  已選擇
@@ -731,8 +982,6 @@ export class ShoppingCollections {
731
982
  })}
732
983
  </div>`;
733
984
  },
734
- divCreate: {},
735
- onCreate: () => {},
736
985
  };
737
986
  })()
738
987
  );
@@ -745,6 +994,28 @@ export class ShoppingCollections {
745
994
  });
746
995
  })
747
996
  )}
997
+ ${vm.type === 'add'
998
+ ? BgWidget.grayButton(
999
+ '代入現有分類',
1000
+ gvc.event(() => {
1001
+ BgProduct.collectionsDialog({
1002
+ gvc: gvc,
1003
+ default: [],
1004
+ callback: value => {
1005
+ const data = vm.dataList?.find(
1006
+ item => [...item.parentTitles, item.title].join(' / ') === value[0]
1007
+ );
1008
+ if (data) {
1009
+ vm.data = data;
1010
+ vm.cloneTarget = structuredClone(data);
1011
+ }
1012
+ gvc.notifyDataChange(viewID);
1013
+ },
1014
+ single: true,
1015
+ });
1016
+ })
1017
+ )
1018
+ : ''}
748
1019
  </div>
749
1020
  </div>`,
750
1021
  // 左右容器
@@ -767,7 +1038,7 @@ export class ShoppingCollections {
767
1038
  BgWidget.mainCard(
768
1039
  html` <div class="d-flex flex-column" style="margin-bottom: 12px; gap:5px;">
769
1040
  <div class="tx_700">前台分類顯示名稱 ${BgWidget.languageInsignia(select_lan)}</div>
770
- ${BgWidget.grayNote(`未設定則參照分類標籤顯示`)}
1041
+ ${BgWidget.grayNote('未設定則參照分類標籤顯示')}
771
1042
  </div>
772
1043
  ${EditorElem.editeInput({
773
1044
  gvc: gvc,
@@ -796,7 +1067,7 @@ export class ShoppingCollections {
796
1067
  gvc.bindView({
797
1068
  bind: domainID,
798
1069
  view: () => {
799
- return html`<div
1070
+ return html` <div
800
1071
  class="${document.body.clientWidth < 800
801
1072
  ? 'w-100'
802
1073
  : ''} justify-content-start justify-content-lg-center"
@@ -978,63 +1249,132 @@ export class ShoppingCollections {
978
1249
  ,
979
1250
  ].join(BgWidget.mbContainer(10))
980
1251
  ),
981
- ].join(html` <div style="margin-top: 24px;"></div>`),
1252
+ ].join(BgWidget.mbContainer(24)),
982
1253
  ratio: 75,
983
1254
  },
984
1255
  {
985
1256
  // 右容器
986
- html: [
987
- BgWidget.summaryCard(
988
- (() => {
989
- if (
990
- (vm.data.allCollections &&
991
- vm.data.allCollections.length > 0 &&
992
- vm.data.parentTitles &&
993
- vm.data.parentTitles.length > 0) ||
994
- vm.type === 'add'
995
- ) {
996
- return html` <div class="tx_700" style="margin-bottom: 12px">父層</div>
997
- ${BgWidget.select({
998
- gvc: gvc,
999
- callback: text => {
1000
- vm.data.parentTitles[0] = text;
1001
- },
1002
- default: vm.data.parentTitles[0] ?? '',
1003
- options: vm.data.allCollections.map((item: string) => {
1004
- return { key: item, value: item };
1257
+ html: gvc.bindView(
1258
+ (() => {
1259
+ const summaryId = glitter.getUUID();
1260
+ return {
1261
+ bind: summaryId,
1262
+ view: () => {
1263
+ function isUndefinedOption(key: string) {
1264
+ return !key || ShoppingCollections.undefinedOption === key;
1265
+ }
1266
+
1267
+ function firstParentView() {
1268
+ return [
1269
+ html` <div class="tx_700">第一層</div>`,
1270
+ vm.type === 'add' && isUndefinedOption(vm.data.parentTitles[0])
1271
+ ? BgWidget.grayNote('若未選取項目,則為第一層分類')
1272
+ : '',
1273
+ BgWidget.select({
1274
+ gvc: gvc,
1275
+ callback: text => {
1276
+ vm.data.parentTitles[0] = text;
1277
+ gvc.notifyDataChange(summaryId);
1278
+ },
1279
+ default: vm.data.parentTitles[0] ?? '',
1280
+ options: vm.data.allCollections.map((item: string) => {
1281
+ return { key: item, value: item };
1282
+ }),
1283
+ style: 'margin: 8px 0;',
1284
+ readonly: vm.type === 'replace',
1005
1285
  }),
1006
- style: 'margin: 8px 0;',
1007
- })}`;
1008
- }
1009
- const id = gvc.glitter.getUUID();
1010
- return html`
1011
- <div class="tx_700" style="margin-bottom: 12px">子分類</div>
1012
- ${gvc.bindView({
1013
- bind: id,
1014
- view: () => {
1015
- return gvc.map(
1016
- vm.data.subCollections.map((item: string) => {
1017
- return html` <div
1018
- style="display: flex; align-items: center; justify-content: space-between; margin-top: 8px;"
1019
- >
1020
- ${item}<i
1021
- class="fa-regular fa-trash cursor_pointer"
1022
- onclick="${gvc.event(() => {
1023
- vm.data.subCollections = vm.data.subCollections.filter(
1024
- (sub: string) => item !== sub
1025
- );
1026
- gvc.notifyDataChange(id);
1027
- })}"
1028
- ></i>
1029
- </div>`;
1030
- })
1031
- );
1032
- },
1033
- })}
1034
- `;
1035
- })()
1036
- ),
1037
- ].join(html` <div style="margin-top: 24px;"></div>`),
1286
+ BgWidget.mbContainer(12),
1287
+ ].join('');
1288
+ }
1289
+
1290
+ function secondParentView(subs: string[]) {
1291
+ return [
1292
+ html` <div class="tx_700">第二層</div>`,
1293
+ vm.type === 'add' && isUndefinedOption(vm.data.parentTitles[1])
1294
+ ? BgWidget.grayNote('若未選取項目,則為第二層分類')
1295
+ : '',
1296
+ BgWidget.select({
1297
+ gvc: gvc,
1298
+ callback: text => {
1299
+ vm.data.parentTitles[1] = text;
1300
+ gvc.notifyDataChange(summaryId);
1301
+ },
1302
+ default: vm.data.parentTitles[1] ?? '',
1303
+ options: [ShoppingCollections.undefinedOption, ...subs].map((item: string) => {
1304
+ return { key: item, value: item };
1305
+ }),
1306
+ style: 'margin: 8px 0;',
1307
+ readonly: vm.type === 'replace',
1308
+ }),
1309
+ ].join('');
1310
+ }
1311
+
1312
+ function editSubCollection() {
1313
+ const id = gvc.glitter.getUUID();
1314
+
1315
+ if (vm.data.parentTitles.length === 2) {
1316
+ return '';
1317
+ }
1318
+
1319
+ return [
1320
+ html` <div class="tx_700" style="margin-bottom: 12px">子分類</div>`,
1321
+ !vm.data.subCollections || vm.data.subCollections.length === 0
1322
+ ? '尚未建立子分類'
1323
+ : gvc.bindView({
1324
+ bind: id,
1325
+ view: () =>
1326
+ vm.data.subCollections
1327
+ .map((item: string) => {
1328
+ return html` <div
1329
+ class="d-flex align-items-center justify-content-between mt-2"
1330
+ >
1331
+ ${item}<i
1332
+ class="fa-regular fa-trash cursor_pointer"
1333
+ onclick="${gvc.event(() => {
1334
+ vm.data.subCollections = vm.data.subCollections.filter(
1335
+ (sub: string) => item !== sub
1336
+ );
1337
+ gvc.notifyDataChange(id);
1338
+ })}"
1339
+ ></i>
1340
+ </div>`;
1341
+ })
1342
+ .join(''),
1343
+ }),
1344
+ ].join('');
1345
+ }
1346
+
1347
+ function levelSetting() {
1348
+ const parentTitles = Array.isArray(vm.data.parentTitles) ? vm.data.parentTitles : [];
1349
+ const parentTab = vm.dataList?.find(item => item.title === parentTitles[0]);
1350
+ const parentSubs =
1351
+ parentTab && Array.isArray(parentTab.subCollections) ? parentTab.subCollections : [];
1352
+
1353
+ if (vm.type === 'add') {
1354
+ return [
1355
+ firstParentView(),
1356
+ parentSubs.length > 0
1357
+ ? secondParentView(parentSubs.filter((item: string) => item !== vm.data.title))
1358
+ : '',
1359
+ ].join('');
1360
+ }
1361
+
1362
+ if (vm.type === 'replace' && parentTitles.length > 0) {
1363
+ return [
1364
+ firstParentView(),
1365
+ parentTitles[1] && parentSubs.length > 0 ? secondParentView(parentSubs) : '',
1366
+ editSubCollection(),
1367
+ ].join('');
1368
+ }
1369
+
1370
+ return editSubCollection();
1371
+ }
1372
+
1373
+ return [BgWidget.summaryCard(levelSetting())].join(BgWidget.mbContainer(24));
1374
+ },
1375
+ };
1376
+ })()
1377
+ ),
1038
1378
  ratio: 25,
1039
1379
  }
1040
1380
  ),
@@ -1044,10 +1384,10 @@ export class ShoppingCollections {
1044
1384
  html` <div class="update-bar-container">
1045
1385
  ${obj.type === 'replace'
1046
1386
  ? BgWidget.redButton(
1047
- '刪除類別',
1387
+ '刪除分類',
1048
1388
  gvc.event(() => {
1049
1389
  dialog.checkYesOrNot({
1050
- text: '確定要刪除商品類別嗎?<br/>(若此類別包含子類別,也將一併刪除)',
1390
+ text: '確定要刪除商品分類嗎?<br/>(若包含子分類,也將一併刪除)',
1051
1391
  callback: bool => {
1052
1392
  if (bool) {
1053
1393
  dialog.dataLoading({ visible: true });
@@ -1076,20 +1416,36 @@ export class ShoppingCollections {
1076
1416
  )}
1077
1417
  ${BgWidget.save(
1078
1418
  gvc.event(() => {
1419
+ // 驗證分類標籤是否填寫
1079
1420
  if (CheckInput.isEmpty(vm.data.title)) {
1080
1421
  dialog.infoMessage({ text: '請填寫「分類標籤」' });
1081
1422
  return;
1082
1423
  }
1083
1424
 
1425
+ // 驗證分類路徑是否已存在
1426
+ const updateDataPath = [...vm.data.parentTitles, vm.data.title].join('');
1427
+
1428
+ if ([...original.parentTitles, original.title].join('') !== updateDataPath) {
1429
+ const somePath = originDataList.some(item => {
1430
+ return [...item.parentTitles, item.title].join('') === updateDataPath;
1431
+ });
1432
+
1433
+ if (somePath) {
1434
+ dialog.infoMessage({ text: '此「分類標籤」已存在' });
1435
+ return;
1436
+ }
1437
+ }
1438
+
1439
+ // 標題格式化
1084
1440
  const forbiddenRegex = /[,/\\]/;
1085
1441
  if (forbiddenRegex.test(vm.data.title)) {
1086
1442
  dialog.infoMessage({ text: '標題不可包含空白格與以下符號:<br />「 , 」「 / 」「 \\ 」' });
1087
1443
  return;
1088
1444
  }
1445
+
1446
+ // 驗證連結網址是否填寫
1089
1447
  const no_fill_language = (window.parent as any).store_info.language_setting.support.find(
1090
- (dd: any) => {
1091
- return !(vm.data.language_data as any)[dd].seo.domain;
1092
- }
1448
+ (dd: any) => !(vm.data.language_data as any)[dd].seo.domain
1093
1449
  );
1094
1450
  if (no_fill_language) {
1095
1451
  select_lan = no_fill_language;
@@ -1097,14 +1453,15 @@ export class ShoppingCollections {
1097
1453
  dialog.infoMessage({ text: '請重新填寫「連結網址」' });
1098
1454
  return;
1099
1455
  }
1456
+
1457
+ // 連結格式化
1100
1458
  if (
1101
1459
  (window.parent as any).store_info.language_setting.support.find((dd: any) => {
1102
- if (!CheckInput.isChineseEnglishNumberHyphen((vm.data.language_data as any)[dd].seo.domain)) {
1103
- select_lan = dd;
1104
- return true;
1105
- } else {
1460
+ if (CheckInput.isChineseEnglishNumberHyphen((vm.data.language_data as any)[dd].seo.domain)) {
1106
1461
  return false;
1107
1462
  }
1463
+ select_lan = dd;
1464
+ return true;
1108
1465
  })
1109
1466
  ) {
1110
1467
  refresh();
@@ -1112,30 +1469,55 @@ export class ShoppingCollections {
1112
1469
  return;
1113
1470
  }
1114
1471
 
1472
+ // 驗證是否有重複使用的 domain
1115
1473
  const validLangDomain = getValidLangDomain();
1116
1474
  if (!validLangDomain.result) {
1117
1475
  refresh();
1118
- dialog.warningMessage({ text: validLangDomain.text, callback: () => {} });
1476
+ dialog.infoMessage({ text: validLangDomain.text });
1119
1477
  return;
1120
1478
  }
1121
1479
 
1122
- dialog.dataLoading({ visible: true });
1123
- ApiShop.putCollections({
1124
- data: { replace: vm.data, original },
1125
- token: (window.parent as any).config.token,
1126
- }).then(res => {
1127
- dialog.dataLoading({ visible: false });
1128
- if (res.result) {
1129
- if (res.response.result) {
1130
- vm.type = 'list';
1131
- dialog.successMessage({ text: '更新成功' });
1480
+ // --- 更新分類方法 ---
1481
+ function putEvent() {
1482
+ dialog.dataLoading({ visible: true });
1483
+ ApiShop.putCollections({
1484
+ data: { replace: vm.data, original },
1485
+ token: (window.parent as any).config.token,
1486
+ }).then(res => {
1487
+ dialog.dataLoading({ visible: false });
1488
+ if (res.result) {
1489
+ if (res.response.result) {
1490
+ vm.type = 'list';
1491
+ dialog.successMessage({ text: '更新成功' });
1492
+ } else {
1493
+ dialog.errorMessage({ text: res.response.message });
1494
+ }
1132
1495
  } else {
1133
- dialog.errorMessage({ text: res.response.message });
1496
+ dialog.errorMessage({ text: '更新失敗' });
1134
1497
  }
1135
- } else {
1136
- dialog.errorMessage({ text: '更新失敗' });
1137
- }
1138
- });
1498
+ });
1499
+ }
1500
+
1501
+ // --- 取代原有的標籤程序 ---
1502
+ if (vm.cloneTarget && vm.cloneTarget.title === vm.data.title) {
1503
+ dialog.checkYesOrNot({
1504
+ text: ['若移動當前商品分類,<b>附帶的子分類將不會保留</b>,', '確定要執行嗎?'].join('<br/>'),
1505
+ callback: bool => {
1506
+ if (bool && vm.cloneTarget) {
1507
+ ApiShop.deleteCollections({
1508
+ data: { data: [vm.cloneTarget] },
1509
+ token: (window.parent as any).config.token,
1510
+ }).then(() => {
1511
+ putEvent();
1512
+ });
1513
+ }
1514
+ },
1515
+ });
1516
+ return;
1517
+ }
1518
+
1519
+ // --- 確認更新 ---
1520
+ putEvent();
1139
1521
  })
1140
1522
  )}
1141
1523
  </div>`,
@@ -1145,6 +1527,45 @@ export class ShoppingCollections {
1145
1527
  };
1146
1528
  });
1147
1529
  }
1530
+
1531
+ static sortedCollectionConfig(config: Collection[], dataArray: Collection[]) {
1532
+ if (!dataArray?.length) return config;
1533
+
1534
+ const parentTitles = dataArray[0].parentTitles ?? [];
1535
+
1536
+ // 遞迴查找並替換目標層級的 children
1537
+ function updateTree(currentLevel: any[], depth: number): boolean {
1538
+ if (depth === parentTitles.length) {
1539
+ // 已到排序目標層級
1540
+ const sorted = dataArray
1541
+ .map(item => {
1542
+ return currentLevel.find(c => c.title === item.title);
1543
+ })
1544
+ .filter(Boolean); // 避免 null
1545
+
1546
+ // 取代原本順序
1547
+ for (let i = 0; i < sorted.length; i++) {
1548
+ currentLevel[i] = sorted[i];
1549
+ }
1550
+
1551
+ // 裁剪多餘元素(若原始長度 > 排序長度)
1552
+ currentLevel.length = sorted.length;
1553
+ return true;
1554
+ }
1555
+
1556
+ const next = currentLevel.find(c => c.title === parentTitles[depth]);
1557
+ if (!next) return false;
1558
+ return updateTree(next.array, depth + 1);
1559
+ }
1560
+
1561
+ const updated = updateTree(config, 0);
1562
+
1563
+ if (!updated) throw new Error('找不到對應的父節點進行排序');
1564
+
1565
+ return config;
1566
+ }
1567
+
1568
+ static undefinedOption = '請選擇項目';
1148
1569
  }
1149
1570
 
1150
1571
  (window as any).glitter.setModule(import.meta.url, ShoppingCollections);