ts-glitter 14.9.5 → 14.9.6

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/lowcode/Entry.js CHANGED
@@ -80,7 +80,7 @@ export class Entry {
80
80
  }
81
81
  window.renderClock = (_a = window.renderClock) !== null && _a !== void 0 ? _a : clockF();
82
82
  console.log(`Entry-time:`, window.renderClock.stop());
83
- glitter.share.editerVersion = 'V_14.9.5';
83
+ glitter.share.editerVersion = 'V_14.9.6';
84
84
  glitter.share.start = new Date();
85
85
  const vm = {
86
86
  appConfig: [],
package/lowcode/Entry.ts CHANGED
@@ -82,7 +82,7 @@ export class Entry {
82
82
 
83
83
  (window as any).renderClock = (window as any).renderClock ?? clockF();
84
84
  console.log(`Entry-time:`, (window as any).renderClock.stop());
85
- glitter.share.editerVersion = 'V_14.9.5';
85
+ glitter.share.editerVersion = 'V_14.9.6';
86
86
  glitter.share.start = new Date();
87
87
  const vm: {
88
88
  appConfig: any;
@@ -166,216 +166,223 @@ export class ProductExcel {
166
166
  });
167
167
  });
168
168
  reader.onload = (e) => __awaiter(this, void 0, void 0, function* () {
169
- const arrayBuffer = e.target.result;
170
- const workbook = new this.ExcelJS.Workbook();
171
- yield workbook.xlsx.load(arrayBuffer);
172
- const worksheet = workbook.getWorksheet(1);
173
- const data = [];
174
- worksheet.eachRow({ includeEmpty: true }, (row, rowNumber) => {
175
- const rowData = [];
176
- row.eachCell({ includeEmpty: true }, (cell, colNumber) => {
177
- rowData.push(cell.value);
169
+ try {
170
+ const arrayBuffer = e.target.result;
171
+ const workbook = new this.ExcelJS.Workbook();
172
+ yield workbook.xlsx.load(arrayBuffer);
173
+ const worksheet = workbook.getWorksheet(1);
174
+ const data = [];
175
+ worksheet.eachRow({ includeEmpty: true }, (row, rowNumber) => {
176
+ const rowData = [];
177
+ row.eachCell({ includeEmpty: true }, (cell, colNumber) => {
178
+ rowData.push(cell.value);
179
+ });
180
+ const isEmptyRow = rowData.every((cellValue) => cellValue === null || cellValue === '');
181
+ if (!isEmptyRow) {
182
+ data.push(rowData);
183
+ }
178
184
  });
179
- const isEmptyRow = rowData.every((cellValue) => cellValue === null || cellValue === '');
180
- if (!isEmptyRow) {
181
- data.push(rowData);
182
- }
183
- });
184
- let error = false;
185
- let addCollection = [];
186
- let postMD = [];
187
- let productData = {};
188
- const getVariantData = () => {
189
- return {
190
- barcode: '',
191
- compare_price: 0,
192
- cost: 0,
193
- preview_image: '',
194
- profit: 0,
195
- sale_price: 0,
196
- shipment_type: 'weight',
197
- show_understocking: '',
198
- sku: '',
199
- spec: [],
200
- stock: 0,
201
- type: '',
202
- v_height: 0,
203
- v_length: 0,
204
- v_width: 0,
205
- weight: 0,
185
+ let error = false;
186
+ let addCollection = [];
187
+ let postMD = [];
188
+ let productData = {};
189
+ const getVariantData = () => {
190
+ return {
191
+ barcode: '',
192
+ compare_price: 0,
193
+ cost: 0,
194
+ preview_image: '',
195
+ profit: 0,
196
+ sale_price: 0,
197
+ shipment_type: 'weight',
198
+ show_understocking: '',
199
+ sku: '',
200
+ spec: [],
201
+ stock: 0,
202
+ type: '',
203
+ v_height: 0,
204
+ v_length: 0,
205
+ v_width: 0,
206
+ weight: 0,
207
+ };
206
208
  };
207
- };
208
- function errorCallback(text, obj) {
209
- error = true;
210
- dialog.dataLoading({ visible: false });
211
- if (obj && obj.warningMessageView) {
212
- dialog.warningMessage({ text, callback: () => { } });
209
+ function errorCallback(text, obj) {
210
+ error = true;
211
+ dialog.dataLoading({ visible: false });
212
+ if (obj && obj.warningMessageView) {
213
+ dialog.warningMessage({ text, callback: () => { } });
214
+ }
215
+ else {
216
+ dialog.infoMessage({ text });
217
+ }
213
218
  }
214
- else {
215
- dialog.infoMessage({ text });
219
+ const domainList = data.map((item) => {
220
+ if (CheckInput.isEmpty(item[5])) {
221
+ item[5] = item[0];
222
+ }
223
+ return `${item[5]}`;
224
+ });
225
+ const filteredArr = domainList.filter((item) => {
226
+ return item && item.length > 0 && item.trim().length > 0;
227
+ });
228
+ const hasDuplicates = new Set(filteredArr).size !== filteredArr.length;
229
+ if (hasDuplicates) {
230
+ errorCallback('「商品連結」的值不可重複<br/>如果「商品連結」為空,預設值為該商品的「商品名稱」<br/>則該「商品名稱」不可與其它「商品連結」重複', {
231
+ warningMessageView: true,
232
+ });
216
233
  }
217
- }
218
- const domainList = data.map((item) => {
219
- if (CheckInput.isEmpty(item[5])) {
220
- item[5] = item[0];
234
+ const productDomainSet = new Set(allProductDomain);
235
+ const duplicateDomain = domainList.find((domain) => domain.length > 0 && productDomainSet.has(domain));
236
+ if (duplicateDomain) {
237
+ errorCallback(`商品連結「${duplicateDomain}」已有產品使用,請更換該欄位的值`);
221
238
  }
222
- return `${item[5]}`;
223
- });
224
- const filteredArr = domainList.filter((item) => {
225
- return item && item.length > 0 && item.trim().length > 0;
226
- });
227
- const hasDuplicates = new Set(filteredArr).size !== filteredArr.length;
228
- if (hasDuplicates) {
229
- errorCallback('「商品連結」的值不可重複<br/>如果「商品連結」為空,預設值為該商品的「商品名稱」<br/>則該「商品名稱」不可與其它「商品連結」重複', {
230
- warningMessageView: true,
231
- });
232
- }
233
- const productDomainSet = new Set(allProductDomain);
234
- const duplicateDomain = domainList.find((domain) => domain.length > 0 && productDomainSet.has(domain));
235
- if (duplicateDomain) {
236
- errorCallback(`商品連結「${duplicateDomain}」已有產品使用,請更換該欄位的值`);
237
- }
238
- data.forEach((row, index) => {
239
- var _a;
240
- const variantData = getVariantData();
241
- if (index != 0) {
242
- if (row[1]) {
243
- if (Object.keys(productData).length != 0) {
244
- postMD.push(productData);
245
- }
246
- addCollection = [];
247
- productData = {
248
- title: '',
249
- productType: {
250
- product: false,
251
- addProduct: false,
252
- giveaway: false,
253
- },
254
- visible: 'true',
255
- content: '',
256
- status: 'active',
257
- collection: [],
258
- hideIndex: 'false',
259
- preview_image: '',
260
- specs: [],
261
- variants: [],
262
- seo: {
263
- domain: '',
239
+ data.forEach((row, index) => {
240
+ var _a;
241
+ const variantData = getVariantData();
242
+ if (index != 0) {
243
+ if (row[1]) {
244
+ if (Object.keys(productData).length != 0) {
245
+ postMD.push(productData);
246
+ }
247
+ addCollection = [];
248
+ productData = {
264
249
  title: '',
250
+ productType: {
251
+ product: false,
252
+ addProduct: false,
253
+ giveaway: false,
254
+ },
255
+ visible: 'true',
265
256
  content: '',
266
- keywords: '',
267
- },
268
- template: '',
269
- };
270
- productData.title = this.checkString(row[0]);
271
- productData.status = row[1] == '啟用' ? 'active' : 'draft';
272
- productData.collection = (_a = row[2].split(',')) !== null && _a !== void 0 ? _a : [];
273
- const regex = /[\s\/\\]+/g;
274
- productData.collection = productData.collection.map((item) => item.replace(/\s+/g, ''));
275
- productData.collection.forEach((row) => {
276
- let collection = row.replace(/\s+/g, '');
277
- if (regex.test(collection)) {
278
- errorCallback(`第${index + 1}行的類別名稱不可包含空白格與以下符號:「 / 」「 \\ 」,並以「 , 」區分不同類別`);
279
- return;
280
- }
281
- function splitStringIncrementally(input) {
282
- const parts = input.split('/');
283
- const result = [];
284
- parts.reduce((acc, part) => {
285
- const newAcc = acc ? `${acc}/${part}` : part;
286
- result.push(newAcc);
287
- return newAcc;
288
- }, '');
289
- return result;
257
+ status: 'active',
258
+ collection: [],
259
+ hideIndex: 'false',
260
+ preview_image: '',
261
+ specs: [],
262
+ variants: [],
263
+ seo: {
264
+ domain: '',
265
+ title: '',
266
+ content: '',
267
+ keywords: '',
268
+ },
269
+ template: '',
270
+ };
271
+ productData.title = this.checkString(row[0]);
272
+ productData.status = row[1] == '啟用' ? 'active' : 'draft';
273
+ productData.collection = (_a = row[2].split(',')) !== null && _a !== void 0 ? _a : [];
274
+ const regex = /[\s\/\\]+/g;
275
+ productData.collection = productData.collection.map((item) => item.replace(/\s+/g, ''));
276
+ productData.collection.forEach((row) => {
277
+ let collection = row.replace(/\s+/g, '');
278
+ if (regex.test(collection)) {
279
+ errorCallback(`第${index + 1}行的類別名稱不可包含空白格與以下符號:「 / 」「 \\ 」,並以「 , 」區分不同類別`);
280
+ return;
281
+ }
282
+ function splitStringIncrementally(input) {
283
+ const parts = input.split('/');
284
+ const result = [];
285
+ parts.reduce((acc, part) => {
286
+ const newAcc = acc ? `${acc}/${part}` : part;
287
+ result.push(newAcc);
288
+ return newAcc;
289
+ }, '');
290
+ return result;
291
+ }
292
+ if (collection.split('/').length > 1) {
293
+ let check = splitStringIncrementally(collection);
294
+ const newItems = check.filter((item) => !productData.collection.includes(item));
295
+ addCollection.push(...newItems);
296
+ }
297
+ addCollection.push(collection);
298
+ });
299
+ productData.collection = addCollection;
300
+ switch (row[3]) {
301
+ case '贈品':
302
+ productData.productType.giveaway = true;
303
+ break;
304
+ case '加購品':
305
+ productData.productType.addProduct = true;
306
+ break;
307
+ case '隱形賣場':
308
+ productData.productType.product = true;
309
+ productData.visible = 'false';
310
+ break;
311
+ default:
312
+ productData.productType.product = true;
313
+ break;
290
314
  }
291
- if (collection.split('/').length > 1) {
292
- let check = splitStringIncrementally(collection);
293
- const newItems = check.filter((item) => !productData.collection.includes(item));
294
- addCollection.push(...newItems);
295
- }
296
- addCollection.push(collection);
297
- });
298
- productData.collection = addCollection;
299
- switch (row[3]) {
300
- case '贈品':
301
- productData.productType.giveaway = true;
302
- break;
303
- case '加購品':
304
- productData.productType.addProduct = true;
305
- break;
306
- case '隱形賣場':
307
- productData.productType.product = true;
308
- productData.visible = 'false';
309
- break;
310
- default:
311
- productData.productType.product = true;
312
- break;
315
+ productData.preview_image = row[4] ? [row[4]] : ['商品圖片'];
316
+ productData.seo.domain = this.checkString(row[5]);
317
+ productData.seo.title = this.checkString(row[6]);
318
+ productData.seo.content = this.checkString(row[7]);
319
+ let indices = [8, 10, 12];
320
+ indices.forEach((index) => {
321
+ if (row[index]) {
322
+ productData.specs.push({
323
+ title: row[index],
324
+ option: [],
325
+ });
326
+ }
327
+ });
313
328
  }
314
- productData.preview_image = row[4] ? [row[4]] : ['商品圖片'];
315
- productData.seo.domain = this.checkString(row[5]);
316
- productData.seo.title = this.checkString(row[6]);
317
- productData.seo.content = this.checkString(row[7]);
318
- let indices = [8, 10, 12];
319
- indices.forEach((index) => {
320
- if (row[index]) {
321
- productData.specs.push({
322
- title: row[index],
323
- option: [],
324
- });
329
+ let indices = [9, 11, 13];
330
+ indices.forEach((rowindex, key) => {
331
+ var _a;
332
+ if (row[rowindex] && productData.specs.length > key) {
333
+ productData.specs[key].option = (_a = productData.specs[key].option) !== null && _a !== void 0 ? _a : [];
334
+ const exists = productData.specs[key].option.some((item) => item.title === row[rowindex]);
335
+ if (!exists) {
336
+ productData.specs[key].option.push({ title: row[rowindex], expand: true });
337
+ }
338
+ variantData.spec.push(row[rowindex]);
325
339
  }
326
340
  });
341
+ variantData.sku = this.checkString(row[14]);
342
+ variantData.cost = this.checkString(row[15]);
343
+ variantData.sale_price = this.checkNumber(row[16]);
344
+ variantData.compare_price = this.checkNumber(row[17]);
345
+ variantData.profit = this.checkNumber(row[18]);
346
+ const shipmentTypeMap = {
347
+ 依重量計算: 'weight',
348
+ 依材積計算: 'volume',
349
+ };
350
+ variantData.shipment_type = shipmentTypeMap[row[19]] || 'none';
351
+ variantData.v_length = this.checkNumber(row[20]);
352
+ variantData.v_width = this.checkNumber(row[21]);
353
+ variantData.v_height = this.checkNumber(row[22]);
354
+ variantData.weight = this.checkNumber(row[23]);
355
+ variantData.show_understocking = row[25] == '追蹤' ? 'true' : 'false';
356
+ variantData.stock = this.checkNumber(row[26]);
357
+ variantData.save_stock = this.checkNumber(row[27]);
358
+ variantData.barcode = this.checkString(row[28]);
359
+ productData.variants.push(JSON.parse(JSON.stringify(variantData)));
327
360
  }
328
- let indices = [9, 11, 13];
329
- indices.forEach((rowindex, key) => {
330
- var _a;
331
- if (row[rowindex] && productData.specs.length > key) {
332
- productData.specs[key].option = (_a = productData.specs[key].option) !== null && _a !== void 0 ? _a : [];
333
- const exists = productData.specs[key].option.some((item) => item.title === row[rowindex]);
334
- if (!exists) {
335
- productData.specs[key].option.push({ title: row[rowindex], expand: true });
336
- }
337
- variantData.spec.push(row[rowindex]);
338
- }
361
+ });
362
+ postMD.push(productData);
363
+ productData.reverse;
364
+ let passData = {
365
+ data: postMD,
366
+ collection: addCollection,
367
+ };
368
+ dialog.dataLoading({ visible: false });
369
+ if (!error) {
370
+ dialog.dataLoading({ visible: true, text: '上傳資料中' });
371
+ yield ApiShop.postMultiProduct({
372
+ data: passData,
373
+ token: window.parent.config.token,
374
+ }).then(() => {
375
+ dialog.dataLoading({ visible: false });
376
+ dialog.successMessage({ text: '上傳成功' });
377
+ this.gvc.glitter.closeDiaLog();
378
+ this.gvc.notifyDataChange(notifyId);
339
379
  });
340
- variantData.sku = this.checkString(row[14]);
341
- variantData.cost = this.checkString(row[15]);
342
- variantData.sale_price = this.checkNumber(row[16]);
343
- variantData.compare_price = this.checkNumber(row[17]);
344
- variantData.profit = this.checkNumber(row[18]);
345
- const shipmentTypeMap = {
346
- 依重量計算: 'weight',
347
- 依材積計算: 'volume',
348
- };
349
- variantData.shipment_type = shipmentTypeMap[row[19]] || 'none';
350
- variantData.v_length = this.checkNumber(row[20]);
351
- variantData.v_width = this.checkNumber(row[21]);
352
- variantData.v_height = this.checkNumber(row[22]);
353
- variantData.weight = this.checkNumber(row[23]);
354
- variantData.show_understocking = row[25] == '追蹤' ? 'true' : 'false';
355
- variantData.stock = this.checkNumber(row[26]);
356
- variantData.save_stock = this.checkNumber(row[27]);
357
- variantData.barcode = this.checkString(row[28]);
358
- productData.variants.push(JSON.parse(JSON.stringify(variantData)));
359
380
  }
360
- });
361
- postMD.push(productData);
362
- productData.reverse;
363
- let passData = {
364
- data: postMD,
365
- collection: addCollection,
366
- };
367
- dialog.dataLoading({ visible: false });
368
- if (!error) {
369
- dialog.dataLoading({ visible: true, text: '上傳資料中' });
370
- yield ApiShop.postMultiProduct({
371
- data: passData,
372
- token: window.parent.config.token,
373
- }).then(() => {
374
- dialog.dataLoading({ visible: false });
375
- dialog.successMessage({ text: '上傳成功' });
376
- this.gvc.glitter.closeDiaLog();
377
- this.gvc.notifyDataChange(notifyId);
378
- });
381
+ }
382
+ catch (e) {
383
+ console.log(e);
384
+ dialog.dataLoading({ visible: false });
385
+ dialog.errorMessage({ text: '資料錯誤' });
379
386
  }
380
387
  });
381
388
  reader.readAsArrayBuffer(file);
@@ -241,267 +241,272 @@ export class ProductExcel {
241
241
  });
242
242
 
243
243
  reader.onload = async (e) => {
244
- const arrayBuffer = e.target!.result;
245
- const workbook = new this.ExcelJS.Workbook();
246
- await workbook.xlsx.load(arrayBuffer);
247
- const worksheet = workbook.getWorksheet(1);
248
-
249
- const data: any = [];
250
- worksheet.eachRow({ includeEmpty: true }, (row: any, rowNumber: any) => {
251
- const rowData: any = [];
252
- row.eachCell({ includeEmpty: true }, (cell: any, colNumber: any) => {
253
- rowData.push(cell.value);
254
- });
255
- const isEmptyRow = rowData.every((cellValue: any) => cellValue === null || cellValue === '');
256
- if (!isEmptyRow) {
257
- data.push(rowData);
258
- }
259
- });
260
- let error = false;
261
- let addCollection: any = [];
262
- let postMD: {
263
- title: string;
264
- productType: {
265
- product: boolean;
266
- addProduct: boolean;
267
- giveaway: boolean;
268
- };
269
- content: string;
270
- preview_image: string;
271
- hideIndex: string;
272
- collection: string[];
273
- status: 'active' | 'draft';
274
- specs: { title: string; option: any }[];
275
- variants: Variant[];
276
- seo: {
277
- title: string;
278
- content: string;
279
- keywords: string;
280
- };
281
- template: string;
282
- }[] = [];
283
- let productData: any = {};
284
- const getVariantData: () => Variant = () => {
285
- return {
286
- barcode: '',
287
- compare_price: 0,
288
- cost: 0,
289
- preview_image: '',
290
- profit: 0,
291
- sale_price: 0,
292
- shipment_type: 'weight',
293
- show_understocking: '',
294
- sku: '',
295
- spec: [],
296
- stock: 0,
297
- type: '',
298
- v_height: 0,
299
- v_length: 0,
300
- v_width: 0,
301
- weight: 0,
302
- };
303
- };
304
- function errorCallback(
305
- text: string,
306
- obj?: {
307
- warningMessageView?: boolean;
308
- }
309
- ) {
310
- error = true;
311
- dialog.dataLoading({ visible: false });
312
- if (obj && obj.warningMessageView) {
313
- dialog.warningMessage({ text, callback: () => {} });
314
- } else {
315
- dialog.infoMessage({ text });
316
- }
317
- }
318
-
319
- // 商品連結若為空,則預設值為商品名稱
320
- const domainList = data.map((item: string[]) => {
321
- if (CheckInput.isEmpty(item[5])) {
322
- item[5] = item[0];
323
- }
324
- return `${item[5]}`;
325
- });
326
-
327
- // 判斷excel中是否有重複的domain
328
- const filteredArr = domainList.filter((item: string) => {
329
- return item && item.length > 0 && item.trim().length > 0;
330
- });
331
-
332
- // 過濾掉空白字串
333
- const hasDuplicates = new Set(filteredArr).size !== filteredArr.length;
334
- if (hasDuplicates) {
335
- errorCallback('「商品連結」的值不可重複<br/>如果「商品連結」為空,預設值為該商品的「商品名稱」<br/>則該「商品名稱」不可與其它「商品連結」重複', {
336
- warningMessageView: true,
337
- });
338
- }
339
-
340
- // 判斷已建立產品中是否有重複存在的domain
341
- const productDomainSet = new Set(allProductDomain);
342
- const duplicateDomain = domainList.find((domain: string) => domain.length > 0 && productDomainSet.has(domain));
343
- if (duplicateDomain) {
344
- errorCallback(`商品連結「${duplicateDomain}」已有產品使用,請更換該欄位的值`);
345
- }
346
-
347
- data.forEach((row: any, index: number) => {
348
- const variantData = getVariantData();
349
- if (index != 0) {
350
- if (row[1]) {
351
- if (Object.keys(productData).length != 0) {
352
- postMD.push(productData);
353
- }
354
- addCollection = [];
355
- productData = {
356
- title: '',
357
- productType: {
358
- product: false,
359
- addProduct: false,
360
- giveaway: false,
361
- },
362
- visible: 'true',
363
- content: '',
364
- status: 'active',
365
- collection: [],
366
- hideIndex: 'false',
367
- preview_image: '',
368
- specs: [],
369
- variants: [],
370
- seo: {
371
- domain: '',
372
- title: '',
373
- content: '',
374
- keywords: '',
375
- },
376
- template: '',
377
- };
378
-
379
- productData.title = this.checkString(row[0]);
380
- productData.status = row[1] == '啟用' ? 'active' : 'draft';
381
- productData.collection = row[2].split(',') ?? [];
382
- const regex = /[\s\/\\]+/g;
383
- // 去除多餘空白
384
- productData.collection = productData.collection.map((item: string) => item.replace(/\s+/g, ''));
385
- productData.collection.forEach((row: any) => {
386
- let collection = row.replace(/\s+/g, '');
387
- if (regex.test(collection)) {
388
- errorCallback(`第${index + 1}行的類別名稱不可包含空白格與以下符號:「 / 」「 \\ 」,並以「 , 」區分不同類別`);
389
- return;
390
- }
391
-
392
- // 若帶有/,要自動加上父類
393
- function splitStringIncrementally(input: string): string[] {
394
- const parts = input.split('/');
395
- const result: string[] = [];
396
-
397
- // 使用 reduce 来构建每一部分的拼接字符串
398
- parts.reduce((acc, part) => {
399
- const newAcc = acc ? `${acc}/${part}` : part;
400
- result.push(newAcc);
401
- return newAcc;
402
- }, '');
403
-
404
- return result;
405
- }
406
-
407
- if (collection.split('/').length > 1) {
408
- // 會進來代表有/的內容 需要檢查放進去的collection有沒有父類
409
- // 先取得目前分層 例如 貓/貓用品/貓砂/A品牌 會拆分成 貓/貓用品/貓砂 , 貓/貓用品, 貓
410
- // 然後把父層自動推進去
411
- let check = splitStringIncrementally(collection);
412
- const newItems = check.filter((item: string) => !productData.collection.includes(item));
413
- addCollection.push(...newItems);
414
- }
415
- addCollection.push(collection);
416
- });
417
- productData.collection = addCollection;
418
- switch (row[3]) {
419
- case '贈品':
420
- productData.productType.giveaway = true;
421
- break;
422
- case '加購品':
423
- productData.productType.addProduct = true;
424
- break;
425
- case '隱形賣場':
426
- productData.productType.product = true;
427
- productData.visible = 'false';
428
- break;
429
- default:
430
- productData.productType.product = true;
431
- break;
432
- }
433
- productData.preview_image = row[4] ? [row[4]] : ['商品圖片'];
434
- productData.seo.domain = this.checkString(row[5]);
435
- productData.seo.title = this.checkString(row[6]);
436
- productData.seo.content = this.checkString(row[7]);
437
- // spec值 merge
438
- let indices = [8, 10, 12];
439
- indices.forEach((index) => {
440
- if (row[index]) {
441
- productData.specs.push({
442
- title: row[index],
443
- option: [],
444
- });
445
- }
446
- });
447
- }
448
-
449
- let indices = [9, 11, 13];
450
- indices.forEach((rowindex, key) => {
451
- if (row[rowindex] && productData.specs.length > key) {
452
- productData.specs[key].option = productData.specs[key].option ?? [];
453
- const exists = productData.specs[key].option.some((item: any) => item.title === row[rowindex]);
454
- if (!exists) {
455
- productData.specs[key].option.push({ title: row[rowindex], expand: true });
456
- }
457
- variantData.spec.push(row[rowindex]);
458
- }
459
- });
460
-
461
- variantData.sku = this.checkString(row[14]);
462
- variantData.cost = this.checkString(row[15]);
463
- variantData.sale_price = this.checkNumber(row[16]);
464
- variantData.compare_price = this.checkNumber(row[17]);
465
- variantData.profit = this.checkNumber(row[18]);
466
-
467
- const shipmentTypeMap: { [key: string]: 'weight' | 'volume' } = {
468
- 依重量計算: 'weight',
469
- 依材積計算: 'volume',
470
- };
471
- variantData.shipment_type = shipmentTypeMap[row[19]] || 'none';
472
-
473
- variantData.v_length = this.checkNumber(row[20]);
474
- variantData.v_width = this.checkNumber(row[21]);
475
- variantData.v_height = this.checkNumber(row[22]);
476
- variantData.weight = this.checkNumber(row[23]);
477
- variantData.show_understocking = row[25] == '追蹤' ? 'true' : 'false';
478
- variantData.stock = this.checkNumber(row[26]);
479
- variantData.save_stock = this.checkNumber(row[27]);
480
- variantData.barcode = this.checkString(row[28]);
481
-
482
- productData.variants.push(JSON.parse(JSON.stringify(variantData)));
483
- }
484
- });
485
- postMD.push(productData);
486
- productData.reverse;
487
- let passData = {
488
- data: postMD,
489
- collection: addCollection,
490
- };
491
-
492
- dialog.dataLoading({ visible: false });
493
- if (!error) {
494
- dialog.dataLoading({ visible: true, text: '上傳資料中' });
495
- await ApiShop.postMultiProduct({
496
- data: passData,
497
- token: (window.parent as any).config.token,
498
- }).then(() => {
499
- dialog.dataLoading({ visible: false });
500
- dialog.successMessage({ text: '上傳成功' });
501
- this.gvc.glitter.closeDiaLog();
502
- this.gvc.notifyDataChange(notifyId);
503
- });
504
- }
244
+ try {
245
+ const arrayBuffer = e.target!.result;
246
+ const workbook = new this.ExcelJS.Workbook();
247
+ await workbook.xlsx.load(arrayBuffer);
248
+ const worksheet = workbook.getWorksheet(1);
249
+
250
+ const data: any = [];
251
+ worksheet.eachRow({ includeEmpty: true }, (row: any, rowNumber: any) => {
252
+ const rowData: any = [];
253
+ row.eachCell({ includeEmpty: true }, (cell: any, colNumber: any) => {
254
+ rowData.push(cell.value);
255
+ });
256
+ const isEmptyRow = rowData.every((cellValue: any) => cellValue === null || cellValue === '');
257
+ if (!isEmptyRow) {
258
+ data.push(rowData);
259
+ }
260
+ });
261
+ let error = false;
262
+ let addCollection: any = [];
263
+ let postMD: {
264
+ title: string;
265
+ productType: {
266
+ product: boolean;
267
+ addProduct: boolean;
268
+ giveaway: boolean;
269
+ };
270
+ content: string;
271
+ preview_image: string;
272
+ hideIndex: string;
273
+ collection: string[];
274
+ status: 'active' | 'draft';
275
+ specs: { title: string; option: any }[];
276
+ variants: Variant[];
277
+ seo: {
278
+ title: string;
279
+ content: string;
280
+ keywords: string;
281
+ };
282
+ template: string;
283
+ }[] = [];
284
+ let productData: any = {};
285
+ const getVariantData: () => Variant = () => {
286
+ return {
287
+ barcode: '',
288
+ compare_price: 0,
289
+ cost: 0,
290
+ preview_image: '',
291
+ profit: 0,
292
+ sale_price: 0,
293
+ shipment_type: 'weight',
294
+ show_understocking: '',
295
+ sku: '',
296
+ spec: [],
297
+ stock: 0,
298
+ type: '',
299
+ v_height: 0,
300
+ v_length: 0,
301
+ v_width: 0,
302
+ weight: 0,
303
+ };
304
+ };
305
+ function errorCallback(
306
+ text: string,
307
+ obj?: {
308
+ warningMessageView?: boolean;
309
+ }
310
+ ) {
311
+ error = true;
312
+ dialog.dataLoading({ visible: false });
313
+ if (obj && obj.warningMessageView) {
314
+ dialog.warningMessage({ text, callback: () => {} });
315
+ } else {
316
+ dialog.infoMessage({ text });
317
+ }
318
+ }
319
+
320
+ // 商品連結若為空,則預設值為商品名稱
321
+ const domainList = data.map((item: string[]) => {
322
+ if (CheckInput.isEmpty(item[5])) {
323
+ item[5] = item[0];
324
+ }
325
+ return `${item[5]}`;
326
+ });
327
+
328
+ // 判斷excel中是否有重複的domain
329
+ const filteredArr = domainList.filter((item: string) => {
330
+ return item && item.length > 0 && item.trim().length > 0;
331
+ });
332
+
333
+ // 過濾掉空白字串
334
+ const hasDuplicates = new Set(filteredArr).size !== filteredArr.length;
335
+ if (hasDuplicates) {
336
+ errorCallback('「商品連結」的值不可重複<br/>如果「商品連結」為空,預設值為該商品的「商品名稱」<br/>則該「商品名稱」不可與其它「商品連結」重複', {
337
+ warningMessageView: true,
338
+ });
339
+ }
340
+
341
+ // 判斷已建立產品中是否有重複存在的domain
342
+ const productDomainSet = new Set(allProductDomain);
343
+ const duplicateDomain = domainList.find((domain: string) => domain.length > 0 && productDomainSet.has(domain));
344
+ if (duplicateDomain) {
345
+ errorCallback(`商品連結「${duplicateDomain}」已有產品使用,請更換該欄位的值`);
346
+ }
347
+
348
+ data.forEach((row: any, index: number) => {
349
+ const variantData = getVariantData();
350
+ if (index != 0) {
351
+ if (row[1]) {
352
+ if (Object.keys(productData).length != 0) {
353
+ postMD.push(productData);
354
+ }
355
+ addCollection = [];
356
+ productData = {
357
+ title: '',
358
+ productType: {
359
+ product: false,
360
+ addProduct: false,
361
+ giveaway: false,
362
+ },
363
+ visible: 'true',
364
+ content: '',
365
+ status: 'active',
366
+ collection: [],
367
+ hideIndex: 'false',
368
+ preview_image: '',
369
+ specs: [],
370
+ variants: [],
371
+ seo: {
372
+ domain: '',
373
+ title: '',
374
+ content: '',
375
+ keywords: '',
376
+ },
377
+ template: '',
378
+ };
379
+
380
+ productData.title = this.checkString(row[0]);
381
+ productData.status = row[1] == '啟用' ? 'active' : 'draft';
382
+ productData.collection = row[2].split(',') ?? [];
383
+ const regex = /[\s\/\\]+/g;
384
+ // 去除多餘空白
385
+ productData.collection = productData.collection.map((item: string) => item.replace(/\s+/g, ''));
386
+ productData.collection.forEach((row: any) => {
387
+ let collection = row.replace(/\s+/g, '');
388
+ if (regex.test(collection)) {
389
+ errorCallback(`第${index + 1}行的類別名稱不可包含空白格與以下符號:「 / 」「 \\ 」,並以「 , 」區分不同類別`);
390
+ return;
391
+ }
392
+
393
+ // 若帶有/,要自動加上父類
394
+ function splitStringIncrementally(input: string): string[] {
395
+ const parts = input.split('/');
396
+ const result: string[] = [];
397
+
398
+ // 使用 reduce 来构建每一部分的拼接字符串
399
+ parts.reduce((acc, part) => {
400
+ const newAcc = acc ? `${acc}/${part}` : part;
401
+ result.push(newAcc);
402
+ return newAcc;
403
+ }, '');
404
+
405
+ return result;
406
+ }
407
+
408
+ if (collection.split('/').length > 1) {
409
+ // 會進來代表有/的內容 需要檢查放進去的collection有沒有父類
410
+ // 先取得目前分層 例如 貓/貓用品/貓砂/A品牌 會拆分成 貓/貓用品/貓砂 , 貓/貓用品, 貓
411
+ // 然後把父層自動推進去
412
+ let check = splitStringIncrementally(collection);
413
+ const newItems = check.filter((item: string) => !productData.collection.includes(item));
414
+ addCollection.push(...newItems);
415
+ }
416
+ addCollection.push(collection);
417
+ });
418
+ productData.collection = addCollection;
419
+ switch (row[3]) {
420
+ case '贈品':
421
+ productData.productType.giveaway = true;
422
+ break;
423
+ case '加購品':
424
+ productData.productType.addProduct = true;
425
+ break;
426
+ case '隱形賣場':
427
+ productData.productType.product = true;
428
+ productData.visible = 'false';
429
+ break;
430
+ default:
431
+ productData.productType.product = true;
432
+ break;
433
+ }
434
+ productData.preview_image = row[4] ? [row[4]] : ['商品圖片'];
435
+ productData.seo.domain = this.checkString(row[5]);
436
+ productData.seo.title = this.checkString(row[6]);
437
+ productData.seo.content = this.checkString(row[7]);
438
+ // spec值 merge
439
+ let indices = [8, 10, 12];
440
+ indices.forEach((index) => {
441
+ if (row[index]) {
442
+ productData.specs.push({
443
+ title: row[index],
444
+ option: [],
445
+ });
446
+ }
447
+ });
448
+ }
449
+
450
+ let indices = [9, 11, 13];
451
+ indices.forEach((rowindex, key) => {
452
+ if (row[rowindex] && productData.specs.length > key) {
453
+ productData.specs[key].option = productData.specs[key].option ?? [];
454
+ const exists = productData.specs[key].option.some((item: any) => item.title === row[rowindex]);
455
+ if (!exists) {
456
+ productData.specs[key].option.push({ title: row[rowindex], expand: true });
457
+ }
458
+ variantData.spec.push(row[rowindex]);
459
+ }
460
+ });
461
+
462
+ variantData.sku = this.checkString(row[14]);
463
+ variantData.cost = this.checkString(row[15]);
464
+ variantData.sale_price = this.checkNumber(row[16]);
465
+ variantData.compare_price = this.checkNumber(row[17]);
466
+ variantData.profit = this.checkNumber(row[18]);
467
+
468
+ const shipmentTypeMap: { [key: string]: 'weight' | 'volume' } = {
469
+ 依重量計算: 'weight',
470
+ 依材積計算: 'volume',
471
+ };
472
+ variantData.shipment_type = shipmentTypeMap[row[19]] || 'none';
473
+
474
+ variantData.v_length = this.checkNumber(row[20]);
475
+ variantData.v_width = this.checkNumber(row[21]);
476
+ variantData.v_height = this.checkNumber(row[22]);
477
+ variantData.weight = this.checkNumber(row[23]);
478
+ variantData.show_understocking = row[25] == '追蹤' ? 'true' : 'false';
479
+ variantData.stock = this.checkNumber(row[26]);
480
+ variantData.save_stock = this.checkNumber(row[27]);
481
+ variantData.barcode = this.checkString(row[28]);
482
+
483
+ productData.variants.push(JSON.parse(JSON.stringify(variantData)));
484
+ }
485
+ });
486
+ postMD.push(productData);
487
+ productData.reverse;
488
+ let passData = {
489
+ data: postMD,
490
+ collection: addCollection,
491
+ };
492
+ dialog.dataLoading({ visible: false });
493
+ if (!error) {
494
+ dialog.dataLoading({ visible: true, text: '上傳資料中' });
495
+ await ApiShop.postMultiProduct({
496
+ data: passData,
497
+ token: (window.parent as any).config.token,
498
+ }).then(() => {
499
+ dialog.dataLoading({ visible: false });
500
+ dialog.successMessage({ text: '上傳成功' });
501
+ this.gvc.glitter.closeDiaLog();
502
+ this.gvc.notifyDataChange(notifyId);
503
+ });
504
+ }
505
+ }catch (e) {
506
+ console.log(e)
507
+ dialog.dataLoading({ visible: false });
508
+ dialog.errorMessage({ text: '資料錯誤' });
509
+ }
505
510
  };
506
511
  reader.readAsArrayBuffer(file);
507
512
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ts-glitter",
3
- "version": "14.9.5",
3
+ "version": "14.9.6",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "scripts": {