ts-glitter 22.4.6 → 22.4.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/glitterBundle/Glitter.css +74 -62
- package/lowcode/Entry.js +1 -1
- package/lowcode/Entry.ts +1 -1
- package/lowcode/backend-manager/bg-product.js +49 -32
- package/lowcode/backend-manager/bg-product.ts +57 -39
- package/lowcode/backend-manager/bg-widget.js +17 -0
- package/lowcode/backend-manager/bg-widget.ts +18 -0
- package/lowcode/cms-plugin/information/information-module.js +5 -5
- package/lowcode/cms-plugin/information/information-module.ts +9 -5
- package/lowcode/cms-plugin/menus-setting.js +69 -55
- package/lowcode/cms-plugin/menus-setting.ts +77 -61
- package/lowcode/cms-plugin/module/form-module.js +109 -89
- package/lowcode/cms-plugin/module/form-module.ts +680 -650
- package/lowcode/cms-plugin/module/product-excel.js +1 -0
- package/lowcode/cms-plugin/module/product-excel.ts +2 -0
- package/lowcode/cms-plugin/pos-pages/payment-page.js +28 -10
- package/lowcode/cms-plugin/pos-pages/payment-page.ts +29 -10
- package/lowcode/cms-plugin/shopping-allowance-manager.js +0 -1
- package/lowcode/cms-plugin/shopping-allowance-manager.ts +0 -1
- package/lowcode/cms-plugin/shopping-collections.js +367 -193
- package/lowcode/cms-plugin/shopping-collections.ts +664 -243
- package/lowcode/cms-plugin/shopping-information.js +392 -38
- package/lowcode/cms-plugin/shopping-information.ts +479 -87
- package/lowcode/cms-plugin/shopping-product-setting.js +2 -2
- package/lowcode/cms-plugin/shopping-product-setting.ts +2 -2
- package/lowcode/cms-plugin/shopping-setting-advance.js +906 -766
- package/lowcode/cms-plugin/shopping-setting-advance.ts +977 -841
- package/lowcode/cms-plugin/shopping-setting-basic.js +1547 -1285
- package/lowcode/cms-plugin/shopping-setting-basic.ts +1742 -1466
- package/lowcode/cms-plugin/stock-stores.js +1 -0
- package/lowcode/cms-plugin/stock-stores.ts +1 -0
- package/lowcode/cms-plugin/user-list.js +47 -12
- package/lowcode/cms-plugin/user-list.ts +52 -14
- package/lowcode/css/editor.css +6 -0
- package/lowcode/glitterBundle/Glitter.css +74 -62
- package/lowcode/jslib/nestable/index.html +317 -0
- package/lowcode/jslib/nestable/jquery.nestable.js +484 -0
- package/lowcode/official_view_component/form-widget/input-custom.js +98 -6
- package/lowcode/official_view_component/form-widget/input-custom.ts +121 -16
- package/lowcode/public-components/headers/header-class.js +63 -0
- package/lowcode/public-components/headers/header-class.ts +65 -0
- package/lowcode/public-components/headers/sy-02.js +386 -400
- package/lowcode/public-components/headers/sy-02.ts +482 -492
- package/lowcode/public-components/headers/sy-03.js +42 -43
- package/lowcode/public-components/headers/sy-03.ts +46 -43
- package/lowcode/public-components/headers/sy-04.js +43 -41
- package/lowcode/public-components/headers/sy-04.ts +48 -41
- package/lowcode/public-components/headers/sy-05.js +30 -27
- package/lowcode/public-components/headers/sy-05.ts +33 -27
- package/lowcode/public-components/product/product-list.js +160 -148
- package/lowcode/public-components/product/product-list.ts +186 -165
- package/lowcode/public-models/product.ts +26 -1
- package/lowcode/src/glitterBundle/Glitter.css +74 -62
- package/package.json +1 -1
- package/rxmnt81tnk.json +1 -0
- package/src/api-public/controllers/shop.js +10 -4
- package/src/api-public/controllers/shop.js.map +1 -1
- package/src/api-public/controllers/shop.ts +14 -9
- package/src/api-public/services/ezpay/tool.d.ts +1 -0
- package/src/api-public/services/mail.js +1 -1
- package/src/api-public/services/mail.js.map +1 -1
- package/src/api-public/services/mail.ts +1 -1
- package/src/api-public/services/monitor.js.map +1 -1
- package/src/api-public/services/monitor.ts +2 -0
- package/src/api-public/services/schedule.d.ts +0 -1
- package/src/api-public/services/schedule.js +12 -35
- package/src/api-public/services/schedule.js.map +1 -1
- package/src/api-public/services/schedule.ts +15 -39
- package/src/api-public/services/shopee.js +7 -17
- package/src/api-public/services/shopping.d.ts +27 -6
- package/src/api-public/services/shopping.js +364 -85
- package/src/api-public/services/shopping.js.map +1 -1
- package/src/api-public/services/shopping.ts +510 -101
- package/src/api-public/services/updated-table-checked.js +58 -1
- package/src/api-public/services/updated-table-checked.js.map +1 -1
- package/src/api-public/services/updated-table-checked.ts +62 -1
- package/src/api-public/services/user-update.js +14 -0
- package/src/api-public/services/user-update.js.map +1 -1
- package/src/api-public/services/user-update.ts +15 -0
- package/src/api-public/services/user.js +1 -1
- package/src/api-public/services/user.js.map +1 -1
- package/src/api-public/services/user.ts +1 -1
- package/src/app-project/serverless/src/modules/database.d.ts +1 -1
- package/src/app-project/serverless/src/modules/redis.d.ts +1 -1
- package/src/helper/glitter-util.d.ts +1 -0
- package/src/index.js +7 -4
- package/src/index.js.map +1 -1
- package/src/index.ts +45 -36
- package/src/modules/firebase.js +1 -0
- package/src/modules/firebase.js.map +1 -1
- package/src/modules/firebase.ts +1 -0
- package/src/seo-config.d.ts +2 -1
- package/src/seo-config.js +28 -22
- package/src/seo-config.js.map +1 -1
- package/src/seo-config.ts +33 -26
- package/src/services/saas-table-check.js.map +1 -1
- package/src/services/ses.js +4 -3
- package/src/services/ses.js.map +1 -1
- package/src/services/system-schedule.js.map +1 -1
- package/src/services/system-schedule.ts +1 -0
|
@@ -119,7 +119,7 @@ export interface VoucherData {
|
|
|
119
119
|
rebate_total: number;
|
|
120
120
|
}
|
|
121
121
|
|
|
122
|
-
interface
|
|
122
|
+
interface Seo {
|
|
123
123
|
title: string;
|
|
124
124
|
seo: {
|
|
125
125
|
domain: string;
|
|
@@ -300,7 +300,7 @@ class OrderDetail {
|
|
|
300
300
|
type Collection = {
|
|
301
301
|
title: string;
|
|
302
302
|
array: Collection[];
|
|
303
|
-
checked
|
|
303
|
+
checked?: boolean;
|
|
304
304
|
product_id?: number[];
|
|
305
305
|
parentTitles: string[];
|
|
306
306
|
subCollections: string[];
|
|
@@ -310,9 +310,24 @@ type Collection = {
|
|
|
310
310
|
seo_image: string;
|
|
311
311
|
code: string;
|
|
312
312
|
language_data: {
|
|
313
|
-
'en-US':
|
|
314
|
-
'zh-CN':
|
|
315
|
-
'zh-TW':
|
|
313
|
+
'en-US': Seo;
|
|
314
|
+
'zh-CN': Seo;
|
|
315
|
+
'zh-TW': Seo;
|
|
316
|
+
};
|
|
317
|
+
hidden?: boolean;
|
|
318
|
+
};
|
|
319
|
+
|
|
320
|
+
type FormatCollection = {
|
|
321
|
+
title: string;
|
|
322
|
+
array: FormatCollection[];
|
|
323
|
+
seo_title: string;
|
|
324
|
+
seo_content: string;
|
|
325
|
+
seo_image: string;
|
|
326
|
+
code: string;
|
|
327
|
+
language_data: {
|
|
328
|
+
'en-US': Seo;
|
|
329
|
+
'zh-CN': Seo;
|
|
330
|
+
'zh-TW': Seo;
|
|
316
331
|
};
|
|
317
332
|
hidden?: boolean;
|
|
318
333
|
};
|
|
@@ -768,15 +783,19 @@ export class Shopping {
|
|
|
768
783
|
if (product.content.designated_logistics.group === '' && !product.content.designated_logistics.type) {
|
|
769
784
|
product.content.designated_logistics = { list: [], type: 'all' };
|
|
770
785
|
}
|
|
786
|
+
|
|
771
787
|
product.content.collection = Array.from(
|
|
772
788
|
new Set(
|
|
773
|
-
(
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
789
|
+
(product.content.collection ?? []).map(
|
|
790
|
+
(path: string) =>
|
|
791
|
+
path
|
|
792
|
+
.split('/') // 拆分所有層級
|
|
793
|
+
.map(segment => segment.trim()) // 去除空白
|
|
794
|
+
.join(' / ') // 統一格式為「空白/空白」
|
|
795
|
+
)
|
|
778
796
|
)
|
|
779
797
|
);
|
|
798
|
+
|
|
780
799
|
return new Promise(async resolve => {
|
|
781
800
|
if (product) {
|
|
782
801
|
let totalSale = 0;
|
|
@@ -1709,18 +1728,18 @@ export class Shopping {
|
|
|
1709
1728
|
axios
|
|
1710
1729
|
.request(config)
|
|
1711
1730
|
.then((response: any) => {
|
|
1712
|
-
if(response.data.returnCode === '0000'){
|
|
1731
|
+
if (response.data.returnCode === '0000') {
|
|
1713
1732
|
resolve(true);
|
|
1714
|
-
}else{
|
|
1715
|
-
console.error(`line_pay_error:`,response)
|
|
1716
|
-
console.error(`line_pay_error_config:`,config)
|
|
1733
|
+
} else {
|
|
1734
|
+
console.error(`line_pay_error:`, response);
|
|
1735
|
+
console.error(`line_pay_error_config:`, config);
|
|
1717
1736
|
resolve(false);
|
|
1718
1737
|
}
|
|
1719
1738
|
})
|
|
1720
1739
|
.catch((error: any) => {
|
|
1721
1740
|
resolve(false);
|
|
1722
|
-
console.error(`line_pay_error:`,error)
|
|
1723
|
-
console.error(`line_pay_error_config:`,config)
|
|
1741
|
+
console.error(`line_pay_error:`, error);
|
|
1742
|
+
console.error(`line_pay_error_config:`, config);
|
|
1724
1743
|
});
|
|
1725
1744
|
});
|
|
1726
1745
|
}
|
|
@@ -3836,6 +3855,7 @@ export class Shopping {
|
|
|
3836
3855
|
sql = `${baseSelect} \`${this.app}\`.t_checkout o ${joinClause} ${whereClause} ${orderString}`;
|
|
3837
3856
|
}
|
|
3838
3857
|
|
|
3858
|
+
console.log(`query-sql====>${sql}`)
|
|
3839
3859
|
if (query.returnSearch == 'true') {
|
|
3840
3860
|
const data = await db.query(
|
|
3841
3861
|
`SELECT *
|
|
@@ -3912,62 +3932,64 @@ export class Shopping {
|
|
|
3912
3932
|
await Promise.all(
|
|
3913
3933
|
obMap
|
|
3914
3934
|
.map(async (order: any) => {
|
|
3915
|
-
|
|
3916
|
-
|
|
3917
|
-
order.orderData.
|
|
3918
|
-
|
|
3919
|
-
|
|
3920
|
-
|
|
3921
|
-
|
|
3922
|
-
|
|
3923
|
-
|
|
3924
|
-
|
|
3925
|
-
|
|
3926
|
-
|
|
3927
|
-
|
|
3928
|
-
|
|
3929
|
-
|
|
3930
|
-
|
|
3931
|
-
|
|
3932
|
-
|
|
3933
|
-
|
|
3934
|
-
|
|
3935
|
-
|
|
3936
|
-
|
|
3937
|
-
|
|
3938
|
-
|
|
3939
|
-
|
|
3940
|
-
|
|
3941
|
-
|
|
3942
|
-
|
|
3943
|
-
|
|
3944
|
-
|
|
3945
|
-
|
|
3946
|
-
|
|
3947
|
-
|
|
3948
|
-
|
|
3949
|
-
|
|
3950
|
-
|
|
3951
|
-
|
|
3952
|
-
|
|
3953
|
-
|
|
3954
|
-
|
|
3955
|
-
|
|
3956
|
-
|
|
3957
|
-
|
|
3935
|
+
if(obMap.length === 1){
|
|
3936
|
+
try {
|
|
3937
|
+
if (order.orderData.customer_info.payment_select === 'ecPay') {
|
|
3938
|
+
order.orderData.cash_flow = await new EcPay(this.app).checkPaymentStatus(order.cart_token);
|
|
3939
|
+
}
|
|
3940
|
+
if (order.orderData.customer_info.payment_select === 'paynow') {
|
|
3941
|
+
try {
|
|
3942
|
+
order.orderData.cash_flow = (
|
|
3943
|
+
await new PayNow(this.app, keyData['paynow']).confirmAndCaptureOrder(order.orderData.paynow_id)
|
|
3944
|
+
).result;
|
|
3945
|
+
} catch (e) {}
|
|
3946
|
+
}
|
|
3947
|
+
if (order.orderData.user_info.shipment_refer === 'paynow') {
|
|
3948
|
+
const pay_now = new PayNowLogistics(this.app);
|
|
3949
|
+
order.orderData.user_info.shipment_detail = await pay_now.getOrderInfo(order.cart_token);
|
|
3950
|
+
const status = (() => {
|
|
3951
|
+
switch (order.orderData.user_info.shipment_detail.PayNowLogisticCode) {
|
|
3952
|
+
case '0000':
|
|
3953
|
+
case '7101':
|
|
3954
|
+
case '7201':
|
|
3955
|
+
return 'wait';
|
|
3956
|
+
case '0101':
|
|
3957
|
+
case '4000':
|
|
3958
|
+
case '4019':
|
|
3959
|
+
case '0102':
|
|
3960
|
+
case '9411':
|
|
3961
|
+
return 'shipping';
|
|
3962
|
+
case '0103':
|
|
3963
|
+
case '4033':
|
|
3964
|
+
case '4031':
|
|
3965
|
+
case '4032':
|
|
3966
|
+
case '4036':
|
|
3967
|
+
case '4040':
|
|
3968
|
+
case '5001':
|
|
3969
|
+
case '8100':
|
|
3970
|
+
case '8110':
|
|
3971
|
+
case '8120':
|
|
3972
|
+
return 'returns';
|
|
3973
|
+
case '5000':
|
|
3974
|
+
return 'arrived';
|
|
3975
|
+
case '8000':
|
|
3976
|
+
case '8010':
|
|
3977
|
+
case '8020':
|
|
3978
|
+
return 'finish';
|
|
3979
|
+
}
|
|
3980
|
+
})();
|
|
3981
|
+
//貨態更新
|
|
3982
|
+
if (status && order.orderData.progress !== status) {
|
|
3983
|
+
order.orderData.progress = status;
|
|
3984
|
+
await this.putOrder({
|
|
3985
|
+
status: undefined,
|
|
3986
|
+
orderData: order.orderData,
|
|
3987
|
+
id: order.id,
|
|
3988
|
+
});
|
|
3958
3989
|
}
|
|
3959
|
-
})();
|
|
3960
|
-
//貨態更新
|
|
3961
|
-
if (status && order.orderData.progress !== status) {
|
|
3962
|
-
order.orderData.progress = status;
|
|
3963
|
-
await this.putOrder({
|
|
3964
|
-
status: undefined,
|
|
3965
|
-
orderData: order.orderData,
|
|
3966
|
-
id: order.id,
|
|
3967
|
-
});
|
|
3968
3990
|
}
|
|
3969
|
-
}
|
|
3970
|
-
}
|
|
3991
|
+
} catch (e) {}
|
|
3992
|
+
}
|
|
3971
3993
|
})
|
|
3972
3994
|
//補上發票號碼資訊
|
|
3973
3995
|
.concat(
|
|
@@ -3982,13 +4004,27 @@ export class Shopping {
|
|
|
3982
4004
|
).data[0];
|
|
3983
4005
|
order.invoice_number = invoice && invoice.invoice_no;
|
|
3984
4006
|
})
|
|
3985
|
-
)
|
|
3986
|
-
|
|
3987
|
-
|
|
3988
|
-
|
|
3989
|
-
|
|
4007
|
+
).concat(
|
|
4008
|
+
[
|
|
4009
|
+
new Promise(async resolve => {
|
|
4010
|
+
const setEmail=await db.query(`select * from \`${this.app}\`.t_user where (email or phone) in (
|
|
4011
|
+
${
|
|
4012
|
+
[db.escape('dmle3'),
|
|
4013
|
+
...Array.from((new Set(obMap.map((order:any)=>{
|
|
4014
|
+
return db.escape(`${order.email}`)
|
|
4015
|
+
}))))].join(',')
|
|
4016
|
+
}
|
|
4017
|
+
)`,[])
|
|
4018
|
+
obMap.map((order: any) => {
|
|
4019
|
+
const find=setEmail.find((d1:any)=>{
|
|
4020
|
+
return (d1.email==order.email) || (d1.phone==order.email)
|
|
4021
|
+
})
|
|
4022
|
+
order.user_data = find && find.userData;
|
|
4023
|
+
})
|
|
4024
|
+
resolve(true)
|
|
3990
4025
|
})
|
|
3991
|
-
|
|
4026
|
+
]
|
|
4027
|
+
)
|
|
3992
4028
|
);
|
|
3993
4029
|
|
|
3994
4030
|
timer.checkPoint('finish-query-all');
|
|
@@ -4282,17 +4318,16 @@ export class Shopping {
|
|
|
4282
4318
|
}
|
|
4283
4319
|
}
|
|
4284
4320
|
|
|
4285
|
-
async resetVoucherHistory() {
|
|
4321
|
+
async resetVoucherHistory(order_id:string) {
|
|
4286
4322
|
try {
|
|
4287
|
-
|
|
4288
|
-
const now = moment().tz('Asia/Taipei').format('YYYY-MM-DD HH:mm:ss');
|
|
4323
|
+
|
|
4289
4324
|
await db.query(
|
|
4290
4325
|
`
|
|
4291
4326
|
UPDATE \`${this.app}\`.t_voucher_history
|
|
4292
4327
|
SET status = 0
|
|
4293
4328
|
WHERE status = 2
|
|
4294
|
-
AND
|
|
4295
|
-
[]
|
|
4329
|
+
AND order_id = ?;`,
|
|
4330
|
+
[order_id]
|
|
4296
4331
|
);
|
|
4297
4332
|
} catch (error) {
|
|
4298
4333
|
throw exception.BadRequestError('BAD_REQUEST', 'insertVoucherHistory Error:' + e, null);
|
|
@@ -4673,7 +4708,7 @@ export class Shopping {
|
|
|
4673
4708
|
)[0] ?? {};
|
|
4674
4709
|
config.value = config.value || [];
|
|
4675
4710
|
|
|
4676
|
-
if (replace.parentTitles[0] === '
|
|
4711
|
+
if (replace.parentTitles[0] === '請選擇項目') {
|
|
4677
4712
|
replace.parentTitles = [];
|
|
4678
4713
|
}
|
|
4679
4714
|
|
|
@@ -4779,11 +4814,14 @@ export class Shopping {
|
|
|
4779
4814
|
const delete_id_list = (original.product_id ?? []).filter(oid => {
|
|
4780
4815
|
return (replace.product_id ?? []).findIndex(rid => rid === oid) === -1;
|
|
4781
4816
|
});
|
|
4817
|
+
|
|
4782
4818
|
if (delete_id_list.length > 0) {
|
|
4783
|
-
const
|
|
4784
|
-
|
|
4785
|
-
|
|
4786
|
-
|
|
4819
|
+
const delete_product_list = await db.query(
|
|
4820
|
+
` SELECT * FROM \`${this.app}\`.t_manager_post WHERE id IN (${delete_id_list.join(',')});
|
|
4821
|
+
`,
|
|
4822
|
+
[]
|
|
4823
|
+
);
|
|
4824
|
+
|
|
4787
4825
|
for (const product of delete_product_list) {
|
|
4788
4826
|
product.content.collection = product.content.collection.filter((str: string) => {
|
|
4789
4827
|
if (original.parentTitles[0]) {
|
|
@@ -4872,6 +4910,251 @@ export class Shopping {
|
|
|
4872
4910
|
}
|
|
4873
4911
|
}
|
|
4874
4912
|
|
|
4913
|
+
async putCollectionV2(replace: Collection, original: Collection) {
|
|
4914
|
+
try {
|
|
4915
|
+
// 遞迴移除 original
|
|
4916
|
+
const removeCollection = (config: FormatCollection[], target: Collection): number => {
|
|
4917
|
+
const path = target.parentTitles;
|
|
4918
|
+
let currentLevel = config;
|
|
4919
|
+
|
|
4920
|
+
// 先遞迴找到要刪除的分類
|
|
4921
|
+
for (let i = 0; i < path.length; i++) {
|
|
4922
|
+
const found = currentLevel.find(c => c.title === path[i]);
|
|
4923
|
+
if (!found) {
|
|
4924
|
+
console.warn(`putCollectionV2 無法找到原本的父類別:${path[i]},略過刪除`);
|
|
4925
|
+
return -1;
|
|
4926
|
+
}
|
|
4927
|
+
currentLevel = found.array;
|
|
4928
|
+
}
|
|
4929
|
+
|
|
4930
|
+
const index = currentLevel.findIndex(c => c.title === target.title);
|
|
4931
|
+
if (index !== -1) {
|
|
4932
|
+
if (Array.isArray(currentLevel[index].array)) {
|
|
4933
|
+
const currentLevelArray = currentLevel[index].array.filter(item => {
|
|
4934
|
+
return replace.subCollections.includes(item.title);
|
|
4935
|
+
});
|
|
4936
|
+
|
|
4937
|
+
formatData.array = currentLevelArray;
|
|
4938
|
+
} else {
|
|
4939
|
+
formatData.array = [];
|
|
4940
|
+
}
|
|
4941
|
+
|
|
4942
|
+
currentLevel.splice(index, 1); // 刪除原始項目
|
|
4943
|
+
return index;
|
|
4944
|
+
} else {
|
|
4945
|
+
console.warn(`putCollectionV2 找不到原始項目:${target.title},略過刪除`);
|
|
4946
|
+
return -1;
|
|
4947
|
+
}
|
|
4948
|
+
};
|
|
4949
|
+
|
|
4950
|
+
function setHiddenStatus(parentCollection: FormatCollection) {
|
|
4951
|
+
for (const collection of parentCollection.array) {
|
|
4952
|
+
collection.hidden = Boolean(parentCollection.hidden || collection.hidden);
|
|
4953
|
+
if (collection.array.length > 0) {
|
|
4954
|
+
setHiddenStatus(collection);
|
|
4955
|
+
}
|
|
4956
|
+
}
|
|
4957
|
+
}
|
|
4958
|
+
|
|
4959
|
+
// 插入 replace 到指定位置(和前面邏輯相同)
|
|
4960
|
+
function insertIntoConfig(config: FormatCollection[], replace: Collection, originIndex: number) {
|
|
4961
|
+
if (replace.parentTitles.length === 0 || replace.parentTitles[0] === '無') {
|
|
4962
|
+
const start = originIndex > -1 ? originIndex : config.length;
|
|
4963
|
+
config.splice(start, 0, formatData);
|
|
4964
|
+
return;
|
|
4965
|
+
}
|
|
4966
|
+
|
|
4967
|
+
const copyParentTitles = replace.parentTitles.slice();
|
|
4968
|
+
for (let i = 0; i < copyParentTitles.length; i++) {
|
|
4969
|
+
if (copyParentTitles[i] === '請選擇項目') {
|
|
4970
|
+
replace.parentTitles = copyParentTitles.slice(0, i);
|
|
4971
|
+
}
|
|
4972
|
+
}
|
|
4973
|
+
|
|
4974
|
+
let currentLevel = config;
|
|
4975
|
+
for (let i = 0; i < replace.parentTitles.length; i++) {
|
|
4976
|
+
const title = replace.parentTitles[i];
|
|
4977
|
+
const found = currentLevel.find(c => c.title === title);
|
|
4978
|
+
if (!found) {
|
|
4979
|
+
throw new Error(`找不到父類別:${title}`);
|
|
4980
|
+
}
|
|
4981
|
+
if (i === replace.parentTitles.length - 1) {
|
|
4982
|
+
const start = originIndex > -1 ? originIndex : found.array.length;
|
|
4983
|
+
found.array.splice(start, 0, formatData);
|
|
4984
|
+
} else {
|
|
4985
|
+
currentLevel = found.array;
|
|
4986
|
+
}
|
|
4987
|
+
}
|
|
4988
|
+
}
|
|
4989
|
+
|
|
4990
|
+
// product id 交集物件
|
|
4991
|
+
function analyzeIdLists(leftIds: number[], rightIds: number[]) {
|
|
4992
|
+
const leftSet = new Set(leftIds);
|
|
4993
|
+
const rightSet = new Set(rightIds);
|
|
4994
|
+
|
|
4995
|
+
return {
|
|
4996
|
+
left_only: leftIds.filter(id => !rightSet.has(id)),
|
|
4997
|
+
intersection: leftIds.filter(id => rightSet.has(id)),
|
|
4998
|
+
right_only: rightIds.filter(id => !leftSet.has(id)),
|
|
4999
|
+
all_set: [...new Set([...leftSet, ...rightSet])],
|
|
5000
|
+
};
|
|
5001
|
+
}
|
|
5002
|
+
|
|
5003
|
+
// 更新前與更新後的類別字串
|
|
5004
|
+
function buildStringMapFromConfig(
|
|
5005
|
+
config: FormatCollection[],
|
|
5006
|
+
original: Collection,
|
|
5007
|
+
replace: Collection
|
|
5008
|
+
): { old: string; new: string }[] {
|
|
5009
|
+
const targetDepth = (original.parentTitles ?? []).length;
|
|
5010
|
+
const stringMap: { old: string; new: string }[] = [];
|
|
5011
|
+
|
|
5012
|
+
const originalTitle = original.title;
|
|
5013
|
+
const replaceTitle = replace.title;
|
|
5014
|
+
const replaceParentPath = [...replace.parentTitles, replace.title];
|
|
5015
|
+
|
|
5016
|
+
const findAllPaths = (item: FormatCollection, parentPath: string[] = []) => {
|
|
5017
|
+
const currentPath = [...parentPath, item.title];
|
|
5018
|
+
const fullPath = currentPath.join(' / ');
|
|
5019
|
+
|
|
5020
|
+
if (currentPath[targetDepth] === originalTitle) {
|
|
5021
|
+
// 只替換指定深度層級的 title
|
|
5022
|
+
const newPathParts = [...currentPath];
|
|
5023
|
+
newPathParts[targetDepth] = replaceTitle;
|
|
5024
|
+
let newFullPath = newPathParts.join(' / ');
|
|
5025
|
+
|
|
5026
|
+
if (fullPath.startsWith(replaceParentPath.join(' / '))) {
|
|
5027
|
+
// 找出在舊的商品類別中,被移除的子類別字串
|
|
5028
|
+
const isRemoveCol = replace.subCollections.some(sub => {
|
|
5029
|
+
return !fullPath.startsWith([...replaceParentPath, sub].join(' / '));
|
|
5030
|
+
});
|
|
5031
|
+
if (isRemoveCol) {
|
|
5032
|
+
newFullPath = '';
|
|
5033
|
+
}
|
|
5034
|
+
}
|
|
5035
|
+
|
|
5036
|
+
stringMap.push({
|
|
5037
|
+
old: fullPath,
|
|
5038
|
+
new: newFullPath,
|
|
5039
|
+
});
|
|
5040
|
+
}
|
|
5041
|
+
|
|
5042
|
+
// 遞迴處理子項
|
|
5043
|
+
(item.array ?? []).forEach(child => {
|
|
5044
|
+
findAllPaths(child, currentPath);
|
|
5045
|
+
});
|
|
5046
|
+
};
|
|
5047
|
+
|
|
5048
|
+
for (const item of config) {
|
|
5049
|
+
findAllPaths(item);
|
|
5050
|
+
}
|
|
5051
|
+
|
|
5052
|
+
return stringMap;
|
|
5053
|
+
}
|
|
5054
|
+
|
|
5055
|
+
// 主程式
|
|
5056
|
+
function main(config: FormatCollection[], original: Collection, replace: Collection): FormatCollection[] {
|
|
5057
|
+
const originIndex = removeCollection(config, original); // 從 config 中刪除 original
|
|
5058
|
+
insertIntoConfig(config, replace, originIndex); // 插入 replace 到正確位置
|
|
5059
|
+
for (const col of config) {
|
|
5060
|
+
setHiddenStatus(col);
|
|
5061
|
+
}
|
|
5062
|
+
return config;
|
|
5063
|
+
}
|
|
5064
|
+
|
|
5065
|
+
// 取得類別設定資料
|
|
5066
|
+
const configData = (
|
|
5067
|
+
await db.query(
|
|
5068
|
+
`SELECT * FROM \`${this.app}\`.public_config WHERE \`key\` = 'collection';
|
|
5069
|
+
`,
|
|
5070
|
+
[]
|
|
5071
|
+
)
|
|
5072
|
+
)[0];
|
|
5073
|
+
const config: FormatCollection[] = configData.value || [];
|
|
5074
|
+
|
|
5075
|
+
// 排除類別標題前後空白
|
|
5076
|
+
replace.title = replace.title.replace(/[\s,\/\\]+/g, '');
|
|
5077
|
+
|
|
5078
|
+
// 格式化類別資料
|
|
5079
|
+
const formatData: FormatCollection = {
|
|
5080
|
+
array: [],
|
|
5081
|
+
code: replace.code,
|
|
5082
|
+
title: replace.title,
|
|
5083
|
+
seo_title: replace.seo_title,
|
|
5084
|
+
seo_content: replace.seo_content,
|
|
5085
|
+
seo_image: replace.seo_image,
|
|
5086
|
+
language_data: replace.language_data,
|
|
5087
|
+
hidden: Boolean(replace.hidden),
|
|
5088
|
+
};
|
|
5089
|
+
|
|
5090
|
+
const id_container = analyzeIdLists(original.product_id ?? [], replace.product_id ?? []);
|
|
5091
|
+
const update_path_array = buildStringMapFromConfig(config, original, replace);
|
|
5092
|
+
|
|
5093
|
+
// 更新類別設定資料
|
|
5094
|
+
main(config, original, replace);
|
|
5095
|
+
|
|
5096
|
+
// 更新商品內的類別陣列
|
|
5097
|
+
if (id_container.all_set.length > 0) {
|
|
5098
|
+
const product_list = await db.query(
|
|
5099
|
+
`SELECT * FROM \`${this.app}\`.t_manager_post WHERE id IN (${id_container.all_set.join(',')});`,
|
|
5100
|
+
[]
|
|
5101
|
+
);
|
|
5102
|
+
|
|
5103
|
+
for (const product of product_list) {
|
|
5104
|
+
const currentPaths = product.content.collection ?? [];
|
|
5105
|
+
|
|
5106
|
+
const isLeftOnly = id_container.left_only.includes(product.id);
|
|
5107
|
+
const isRightOnly = id_container.right_only.includes(product.id);
|
|
5108
|
+
const isIntersection = id_container.intersection.includes(product.id);
|
|
5109
|
+
|
|
5110
|
+
// 1. 移除:left_only
|
|
5111
|
+
let pathsAfterRemoval = currentPaths;
|
|
5112
|
+
if (isLeftOnly) {
|
|
5113
|
+
for (const { old } of update_path_array) {
|
|
5114
|
+
pathsAfterRemoval = pathsAfterRemoval.filter((path: string) => path !== old);
|
|
5115
|
+
}
|
|
5116
|
+
}
|
|
5117
|
+
|
|
5118
|
+
// 2. 更新:intersection
|
|
5119
|
+
let pathsAfterUpdate = pathsAfterRemoval;
|
|
5120
|
+
if (isIntersection) {
|
|
5121
|
+
for (const { old, new: newVal } of update_path_array) {
|
|
5122
|
+
const index = pathsAfterUpdate.findIndex((p: string) => p === old);
|
|
5123
|
+
if (index !== -1) {
|
|
5124
|
+
pathsAfterUpdate[index] = newVal;
|
|
5125
|
+
}
|
|
5126
|
+
}
|
|
5127
|
+
}
|
|
5128
|
+
|
|
5129
|
+
// 3. 新增:right_only
|
|
5130
|
+
const newPath = replace.title ? [...(replace.parentTitles ?? []), replace.title] : [];
|
|
5131
|
+
const newMap = newPath.map((path, i) => [...newPath.slice(0, i), path].join(' / '));
|
|
5132
|
+
|
|
5133
|
+
let finalPaths = pathsAfterUpdate;
|
|
5134
|
+
if (isRightOnly || isIntersection) {
|
|
5135
|
+
finalPaths = [...new Set([...pathsAfterUpdate, ...newMap])];
|
|
5136
|
+
}
|
|
5137
|
+
|
|
5138
|
+
product.content.collection = finalPaths.filter((path: string) => path.length > 0);
|
|
5139
|
+
|
|
5140
|
+
await this.updateProductCollection(product.content, product.id);
|
|
5141
|
+
}
|
|
5142
|
+
}
|
|
5143
|
+
|
|
5144
|
+
// 更新商品類別 config
|
|
5145
|
+
await db.execute(
|
|
5146
|
+
`UPDATE \`${this.app}\`.public_config SET value = ? WHERE \`key\` = 'collection';
|
|
5147
|
+
`,
|
|
5148
|
+
[config]
|
|
5149
|
+
);
|
|
5150
|
+
|
|
5151
|
+
return { result: true };
|
|
5152
|
+
} catch (e) {
|
|
5153
|
+
console.error(e);
|
|
5154
|
+
throw exception.BadRequestError('BAD_REQUEST', 'putCollection Error:' + e, null);
|
|
5155
|
+
}
|
|
5156
|
+
}
|
|
5157
|
+
|
|
4875
5158
|
async sortCollection(data: Collection[]) {
|
|
4876
5159
|
try {
|
|
4877
5160
|
if (data && data[0]) {
|
|
@@ -4879,9 +5162,8 @@ export class Shopping {
|
|
|
4879
5162
|
const config =
|
|
4880
5163
|
(
|
|
4881
5164
|
await db.query(
|
|
4882
|
-
`SELECT *
|
|
4883
|
-
|
|
4884
|
-
WHERE \`key\` = 'collection';`,
|
|
5165
|
+
`SELECT * FROM \`${this.app}\`.public_config WHERE \`key\` = 'collection';
|
|
5166
|
+
`,
|
|
4885
5167
|
[]
|
|
4886
5168
|
)
|
|
4887
5169
|
)[0] ?? {};
|
|
@@ -4905,9 +5187,7 @@ export class Shopping {
|
|
|
4905
5187
|
}
|
|
4906
5188
|
|
|
4907
5189
|
await db.execute(
|
|
4908
|
-
`UPDATE \`${this.app}\`.public_config
|
|
4909
|
-
SET value = ?
|
|
4910
|
-
WHERE \`key\` = 'collection';
|
|
5190
|
+
`UPDATE \`${this.app}\`.public_config SET value = ? WHERE \`key\` = 'collection';
|
|
4911
5191
|
`,
|
|
4912
5192
|
[config.value]
|
|
4913
5193
|
);
|
|
@@ -4920,6 +5200,48 @@ export class Shopping {
|
|
|
4920
5200
|
}
|
|
4921
5201
|
}
|
|
4922
5202
|
|
|
5203
|
+
async sortCollectionV2(dataArray: FormatCollection[]) {
|
|
5204
|
+
try {
|
|
5205
|
+
if (!Array.isArray(dataArray) || !dataArray.length) return false;
|
|
5206
|
+
|
|
5207
|
+
const configRow = await db.query(`SELECT * FROM \`${this.app}\`.public_config WHERE \`key\` = 'collection';`, []);
|
|
5208
|
+
const originConfig = configRow[0]?.value ?? [];
|
|
5209
|
+
|
|
5210
|
+
function isSameStructure(origin: FormatCollection[], target: FormatCollection[]): boolean {
|
|
5211
|
+
const getMapByTitle = (list: FormatCollection[]) => new Map(list.map(item => [item.title, item]));
|
|
5212
|
+
|
|
5213
|
+
const originMap = getMapByTitle(origin);
|
|
5214
|
+
const targetMap = getMapByTitle(target);
|
|
5215
|
+
|
|
5216
|
+
if (originMap.size !== targetMap.size) return false;
|
|
5217
|
+
|
|
5218
|
+
for (const [title, a] of originMap) {
|
|
5219
|
+
const b = targetMap.get(title);
|
|
5220
|
+
if (!b) return false;
|
|
5221
|
+
|
|
5222
|
+
const aChildren = Array.isArray(a.array) ? a.array : [];
|
|
5223
|
+
const bChildren = Array.isArray(b.array) ? b.array : [];
|
|
5224
|
+
|
|
5225
|
+
if (!isSameStructure(aChildren, bChildren)) return false;
|
|
5226
|
+
}
|
|
5227
|
+
|
|
5228
|
+
return true;
|
|
5229
|
+
}
|
|
5230
|
+
|
|
5231
|
+
// 檢查結構相同,不管順序
|
|
5232
|
+
if (!isSameStructure(originConfig, dataArray)) {
|
|
5233
|
+
throw exception.BadRequestError('BAD_REQUEST', '僅允許更動排序,不可新增、刪除或修改分類結構。', null);
|
|
5234
|
+
}
|
|
5235
|
+
|
|
5236
|
+
await db.execute(`UPDATE \`${this.app}\`.public_config SET value = ? WHERE \`key\` = 'collection';`, [dataArray]);
|
|
5237
|
+
|
|
5238
|
+
return true;
|
|
5239
|
+
} catch (e) {
|
|
5240
|
+
console.error(e);
|
|
5241
|
+
throw exception.BadRequestError('BAD_REQUEST', 'sortCollection Error: ' + e, null);
|
|
5242
|
+
}
|
|
5243
|
+
}
|
|
5244
|
+
|
|
4923
5245
|
checkVariantDataType(variants: any[]) {
|
|
4924
5246
|
const propertiesToParse = ['stock', 'product_id', 'sale_price', 'compare_price', 'shipment_weight'];
|
|
4925
5247
|
|
|
@@ -5398,14 +5720,100 @@ export class Shopping {
|
|
|
5398
5720
|
});
|
|
5399
5721
|
|
|
5400
5722
|
// 更新商品類別
|
|
5401
|
-
|
|
5402
|
-
|
|
5403
|
-
|
|
5404
|
-
|
|
5723
|
+
await db.execute(
|
|
5724
|
+
`UPDATE \`${this.app}\`.public_config SET value = ? WHERE \`key\` = 'collection';
|
|
5725
|
+
`,
|
|
5726
|
+
[config.value]
|
|
5727
|
+
);
|
|
5405
5728
|
|
|
5406
5729
|
return { result: true };
|
|
5407
5730
|
} catch (e) {
|
|
5408
|
-
throw exception.BadRequestError('BAD_REQUEST', '
|
|
5731
|
+
throw exception.BadRequestError('BAD_REQUEST', 'deleteCollection Error:' + e, null);
|
|
5732
|
+
}
|
|
5733
|
+
}
|
|
5734
|
+
|
|
5735
|
+
async deleteCollectionV2(dataArray: Collection[]) {
|
|
5736
|
+
try {
|
|
5737
|
+
// 取得類別設定資料
|
|
5738
|
+
const configData = (
|
|
5739
|
+
await db.query(
|
|
5740
|
+
`SELECT * FROM \`${this.app}\`.public_config WHERE \`key\` = 'collection';
|
|
5741
|
+
`,
|
|
5742
|
+
[]
|
|
5743
|
+
)
|
|
5744
|
+
)[0];
|
|
5745
|
+
const config: Collection[] = configData.value || [];
|
|
5746
|
+
|
|
5747
|
+
// 搜尋指定商品分類 SQL
|
|
5748
|
+
const containsAnyTagSQL = (tags: string[]): string => {
|
|
5749
|
+
const conditions = tags.map(tag => `JSON_CONTAINS(content, '["${tag}"]', '$.collection')`).join(' OR ');
|
|
5750
|
+
return `SELECT * FROM \`${this.app}\`.t_manager_post WHERE ${conditions}`;
|
|
5751
|
+
};
|
|
5752
|
+
|
|
5753
|
+
const removeCollection = async (config: FormatCollection[], target: Collection) => {
|
|
5754
|
+
const path = target.parentTitles ?? [];
|
|
5755
|
+
let currentLevel = config;
|
|
5756
|
+
|
|
5757
|
+
// 先遞迴找到要刪除的分類
|
|
5758
|
+
for (let i = 0; i < path.length; i++) {
|
|
5759
|
+
const found = currentLevel.find(c => c.title === path[i]);
|
|
5760
|
+
if (!found) {
|
|
5761
|
+
console.warn(`deleteCollectionV2 無法找到原本的父類別:${path[i]},略過刪除`);
|
|
5762
|
+
return;
|
|
5763
|
+
}
|
|
5764
|
+
currentLevel = found.array;
|
|
5765
|
+
}
|
|
5766
|
+
|
|
5767
|
+
const index = currentLevel.findIndex(c => c.title === target.title);
|
|
5768
|
+
if (index === -1) {
|
|
5769
|
+
console.warn(`deleteCollectionV2 找不到原始項目:${target.title},略過刪除`);
|
|
5770
|
+
return;
|
|
5771
|
+
}
|
|
5772
|
+
|
|
5773
|
+
const removed = currentLevel.splice(index, 1)[0]; // 移除並取得項目
|
|
5774
|
+
if (!removed) return;
|
|
5775
|
+
|
|
5776
|
+
// 同時刪除商品中使用此分類路徑的 collection
|
|
5777
|
+
const tagToDelete: string[] = [];
|
|
5778
|
+
|
|
5779
|
+
function gatherAllPaths(node: FormatCollection, currentPath: string[]) {
|
|
5780
|
+
const full = [...currentPath, node.title].join(' / ');
|
|
5781
|
+
tagToDelete.push(full);
|
|
5782
|
+
|
|
5783
|
+
if (node.array?.length > 0) {
|
|
5784
|
+
for (const child of node.array) {
|
|
5785
|
+
gatherAllPaths(child, [...currentPath, node.title]);
|
|
5786
|
+
}
|
|
5787
|
+
}
|
|
5788
|
+
}
|
|
5789
|
+
|
|
5790
|
+
gatherAllPaths(removed, path); // path = 父層,removed 是當前被刪的節點
|
|
5791
|
+
|
|
5792
|
+
// 更新每個使用該分類的商品
|
|
5793
|
+
const productList = await db.query(containsAnyTagSQL(tagToDelete), []);
|
|
5794
|
+
|
|
5795
|
+
for (const product of productList) {
|
|
5796
|
+
const before = product.content.collection ?? [];
|
|
5797
|
+
product.content.collection = before.filter((c: string) => !tagToDelete.includes(c));
|
|
5798
|
+
|
|
5799
|
+
await this.updateProductCollection(product.content, product.id);
|
|
5800
|
+
}
|
|
5801
|
+
};
|
|
5802
|
+
|
|
5803
|
+
for (const data of dataArray) {
|
|
5804
|
+
removeCollection(config, data);
|
|
5805
|
+
}
|
|
5806
|
+
|
|
5807
|
+
// 更新商品類別
|
|
5808
|
+
await db.execute(
|
|
5809
|
+
`UPDATE \`${this.app}\`.public_config SET value = ? WHERE \`key\` = 'collection';
|
|
5810
|
+
`,
|
|
5811
|
+
[config]
|
|
5812
|
+
);
|
|
5813
|
+
|
|
5814
|
+
return { result: true };
|
|
5815
|
+
} catch (e) {
|
|
5816
|
+
throw exception.BadRequestError('BAD_REQUEST', 'deleteCollection Error:' + e, null);
|
|
5409
5817
|
}
|
|
5410
5818
|
}
|
|
5411
5819
|
|
|
@@ -5471,10 +5879,11 @@ export class Shopping {
|
|
|
5471
5879
|
|
|
5472
5880
|
async updateProductCollection(content: string[], id: number) {
|
|
5473
5881
|
try {
|
|
5474
|
-
|
|
5475
|
-
|
|
5476
|
-
|
|
5477
|
-
|
|
5882
|
+
await db.execute(
|
|
5883
|
+
`UPDATE \`${this.app}\`.t_manager_post SET content = ? WHERE \`id\` = ?;
|
|
5884
|
+
`,
|
|
5885
|
+
[content, id]
|
|
5886
|
+
);
|
|
5478
5887
|
} catch (error) {
|
|
5479
5888
|
throw exception.BadRequestError('BAD_REQUEST', 'updateProductCollection Error:' + e, null);
|
|
5480
5889
|
}
|