@pisell/pisellos 2.3.95 → 2.3.96

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.
@@ -194,9 +194,139 @@ const I18N_FIELD_MAP = {
194
194
  description_i18n: 'description',
195
195
  cover_i18n: 'cover'
196
196
  };
197
+ /**
198
+ * 按当前语言读取 i18n 文案;当前语言为空时回退到商家原始文案。
199
+ */
200
+ function resolveI18nValue(i18nDict, locale) {
201
+ if (!i18nDict) return undefined;
202
+ return i18nDict[locale] || i18nDict.original || undefined;
203
+ }
204
+
205
+ /**
206
+ * 递归格式化商品分类名称,同时保留未变化节点的引用。
207
+ */
208
+ function applyI18nToCategory(category, locale) {
209
+ if (!category || typeof category !== 'object') return category;
210
+ const localizedName = resolveI18nValue(category.name_i18n, locale);
211
+ const localizedParent = applyI18nToCategory(category.parent_category, locale);
212
+ const nameChanged = localizedName !== undefined && localizedName !== category.name;
213
+ const parentChanged = localizedParent !== category.parent_category;
214
+ if (!nameChanged && !parentChanged) return category;
215
+ return {
216
+ ...category,
217
+ ...(nameChanged ? {
218
+ name: localizedName
219
+ } : {}),
220
+ ...(parentChanged ? {
221
+ parent_category: localizedParent
222
+ } : {})
223
+ };
224
+ }
225
+ function applyI18nToCategories(categories, locale) {
226
+ if (!Array.isArray(categories)) return categories;
227
+ let changed = false;
228
+ const localizedCategories = categories.map(category => {
229
+ const localizedCategory = applyI18nToCategory(category, locale);
230
+ if (localizedCategory !== category) changed = true;
231
+ return localizedCategory;
232
+ });
233
+ return changed ? localizedCategories : categories;
234
+ }
235
+ function applyI18nToNamedItems(items, locale) {
236
+ if (!Array.isArray(items)) return items;
237
+ let changed = false;
238
+ const localizedItems = items.map(item => {
239
+ if (!item || typeof item !== 'object') return item;
240
+ const localizedName = resolveI18nValue(item.name_i18n, locale);
241
+ if (localizedName === undefined || localizedName === item.name) return item;
242
+ changed = true;
243
+ return {
244
+ ...item,
245
+ name: localizedName
246
+ };
247
+ });
248
+ return changed ? localizedItems : items;
249
+ }
250
+
251
+ /**
252
+ * 格式化选项组、规格组及其子项名称,同时保留未变化节点的引用。
253
+ */
254
+ function applyI18nToNamedGroups(groups, itemKey, locale) {
255
+ if (!Array.isArray(groups)) return groups;
256
+ let changed = false;
257
+ const localizedGroups = groups.map(group => {
258
+ if (!group || typeof group !== 'object') return group;
259
+ const localizedName = resolveI18nValue(group.name_i18n, locale);
260
+ const localizedItems = applyI18nToNamedItems(group[itemKey], locale);
261
+ const nameChanged = localizedName !== undefined && localizedName !== group.name;
262
+ const itemsChanged = localizedItems !== group[itemKey];
263
+ if (!nameChanged && !itemsChanged) return group;
264
+ changed = true;
265
+ return {
266
+ ...group,
267
+ ...(nameChanged ? {
268
+ name: localizedName
269
+ } : {}),
270
+ ...(itemsChanged ? {
271
+ [itemKey]: localizedItems
272
+ } : {})
273
+ };
274
+ });
275
+ return changed ? localizedGroups : groups;
276
+ }
277
+ function applyI18nToBundleItems(items, locale) {
278
+ if (!Array.isArray(items)) return items;
279
+ let changed = false;
280
+ const localizedItems = items.map(item => {
281
+ if (!item || typeof item !== 'object') return item;
282
+ const localizedTitle = resolveI18nValue(item.trans_title, locale);
283
+ const localizedOptionGroups = applyI18nToNamedGroups(item.option_group, 'option_item', locale);
284
+ const titleChanged = localizedTitle !== undefined && localizedTitle !== item.title;
285
+ const optionGroupsChanged = localizedOptionGroups !== item.option_group;
286
+ if (!titleChanged && !optionGroupsChanged) return item;
287
+ changed = true;
288
+ return {
289
+ ...item,
290
+ ...(titleChanged ? {
291
+ title: localizedTitle
292
+ } : {}),
293
+ ...(optionGroupsChanged ? {
294
+ option_group: localizedOptionGroups
295
+ } : {})
296
+ };
297
+ });
298
+ return changed ? localizedItems : items;
299
+ }
197
300
 
198
301
  /**
199
- * 根据 locale 将商品的 i18n 字段覆盖到对应原始字段
302
+ * 格式化套餐组名称、套餐子商品标题及子商品选项,同时保留未变化节点的引用。
303
+ */
304
+ function applyI18nToBundleGroups(groups, locale) {
305
+ if (!Array.isArray(groups)) return groups;
306
+ let changed = false;
307
+ const localizedGroups = groups.map(group => {
308
+ if (!group || typeof group !== 'object') return group;
309
+ const localizedName = resolveI18nValue(group.name_i18n, locale);
310
+ const localizedItems = applyI18nToBundleItems(group.bundle_item, locale);
311
+ const nameChanged = localizedName !== undefined && localizedName !== group.name;
312
+ const itemsChanged = localizedItems !== group.bundle_item;
313
+ if (!nameChanged && !itemsChanged) return group;
314
+ changed = true;
315
+ return {
316
+ ...group,
317
+ ...(nameChanged ? {
318
+ name: localizedName
319
+ } : {}),
320
+ ...(itemsChanged ? {
321
+ bundle_item: localizedItems
322
+ } : {})
323
+ };
324
+ });
325
+ return changed ? localizedGroups : groups;
326
+ }
327
+
328
+ /**
329
+ * 根据 locale 格式化商品、分类、规格及套餐文案,缺少对应语言时回退 original。
200
330
  */
201
331
  function applyI18nToProducts(products, locale) {
202
332
  if (!locale) return products;
@@ -205,14 +335,44 @@ function applyI18nToProducts(products, locale) {
205
335
  for (const [i18nKey, originalKey] of Object.entries(I18N_FIELD_MAP)) {
206
336
  const i18nDict = product[i18nKey];
207
337
  if (!i18nDict) continue;
208
- const localizedValue = i18nDict[locale];
209
- if (localizedValue) {
338
+ const localizedValue = resolveI18nValue(i18nDict, locale);
339
+ if (localizedValue !== undefined && localizedValue !== product[originalKey]) {
210
340
  if (!patched) patched = {
211
341
  ...product
212
342
  };
213
343
  patched[originalKey] = localizedValue;
214
344
  }
215
345
  }
346
+ for (const categoryKey of ['category', 'categories']) {
347
+ const categories = product[categoryKey];
348
+ const localizedCategories = applyI18nToCategories(categories, locale);
349
+ if (localizedCategories !== categories) {
350
+ if (!patched) patched = {
351
+ ...product
352
+ };
353
+ patched[categoryKey] = localizedCategories;
354
+ }
355
+ }
356
+ for (const [groupKey, itemKey] of [['option_group', 'option_item'], ['variant_group', 'variant_item']]) {
357
+ const groups = product[groupKey];
358
+ const localizedGroups = applyI18nToNamedGroups(groups, itemKey, locale);
359
+ if (localizedGroups !== groups) {
360
+ if (!patched) patched = {
361
+ ...product
362
+ };
363
+ patched[groupKey] = localizedGroups;
364
+ }
365
+ }
366
+ for (const bundleGroupKey of ['bundle_group', 'bundle_groups']) {
367
+ const groups = product[bundleGroupKey];
368
+ const localizedGroups = applyI18nToBundleGroups(groups, locale);
369
+ if (localizedGroups !== groups) {
370
+ if (!patched) patched = {
371
+ ...product
372
+ };
373
+ patched[bundleGroupKey] = localizedGroups;
374
+ }
375
+ }
216
376
  return patched ?? product;
217
377
  });
218
378
  }
@@ -32,6 +32,7 @@ const LABELS = {
32
32
  orderDiscount: 'Order discount',
33
33
  manualDiscount: 'Manual discount',
34
34
  payProcessingFee: 'Pay processing fee',
35
+ surchargeIncluded: 'Surcharge(Included)',
35
36
  rounding: 'Rounding',
36
37
  gross: 'Gross',
37
38
  tare: 'Tare'
@@ -58,6 +59,7 @@ const LABELS = {
58
59
  orderDiscount: '整单折扣',
59
60
  manualDiscount: '手动折扣',
60
61
  payProcessingFee: '支付手续费',
62
+ surchargeIncluded: '手续费(已含)',
61
63
  rounding: '抹零',
62
64
  gross: '重量',
63
65
  tare: '去皮'
@@ -84,6 +86,7 @@ const LABELS = {
84
86
  orderDiscount: '整單折扣',
85
87
  manualDiscount: '手動折扣',
86
88
  payProcessingFee: '支付手續費',
89
+ surchargeIncluded: '手續費(已含)',
87
90
  rounding: '抹零',
88
91
  gross: '重量',
89
92
  tare: '去皮'
@@ -730,10 +733,17 @@ function buildResources(order, locale) {
730
733
  function buildPaymentData(params) {
731
734
  const payments = normalizeArray(params.order.payments);
732
735
  return payments.filter(payment => isPrintablePayment(payment)).map(payment => {
736
+ const serviceFee = getPaymentServiceFee(payment);
733
737
  const items = [{
734
738
  item: paymentMethodName(payment, params.locale),
735
- value: formatMoney(payment.amount ?? payment.origin_amount, params.currencySymbol)
739
+ value: formatMoney(resolveReceiptPaymentAmount(payment, serviceFee), params.currencySymbol)
736
740
  }];
741
+ if (serviceFee.gt(0)) {
742
+ items.push({
743
+ item: label(params.locale, 'surchargeIncluded'),
744
+ value: formatMoney(serviceFee, params.currencySymbol)
745
+ });
746
+ }
737
747
  const fundRecord = getFundRecord(payment);
738
748
  if (fundRecord) {
739
749
  items.push(...buildFundRecordPaymentItems({
@@ -758,6 +768,17 @@ function buildPaymentData(params) {
758
768
  return items;
759
769
  });
760
770
  }
771
+ function getPaymentServiceFee(payment) {
772
+ return new _decimal.default(toMoneyNumber(payment.service_fee));
773
+ }
774
+ function resolveReceiptPaymentAmount(payment, serviceFee) {
775
+ const amount = new _decimal.default(toMoneyNumber(payment.amount ?? payment.origin_amount));
776
+ const hasAmount = payment.amount !== undefined && payment.amount !== null && payment.amount !== '';
777
+ const hasOriginAmount = payment.origin_amount !== undefined && payment.origin_amount !== null && payment.origin_amount !== '';
778
+ if (!hasAmount || !hasOriginAmount || serviceFee.lte(0)) return amount;
779
+ const originAmount = new _decimal.default(toMoneyNumber(payment.origin_amount));
780
+ return amount.eq(originAmount) ? amount.plus(serviceFee) : amount;
781
+ }
761
782
  function buildFundRecordPaymentItems(params) {
762
783
  const {
763
784
  payment,
@@ -191,9 +191,10 @@ function transformBaseProductToOrderProduct(params) {
191
191
  metadata.unique_identification_number = String(uniqueIdentificationNumber);
192
192
  }
193
193
  // 从源头补充商品多语言标题
194
+ const productTitleI18n = sourceProduct?.title_i18n || {};
194
195
  const productTitle = {
195
- ...(sourceProduct?.title_i18n || {}),
196
- original: sourceProduct?.original_title || sourceProduct?.title || ''
196
+ ...productTitleI18n,
197
+ original: productTitleI18n.original || sourceProduct?.original_title || sourceProduct?.title || ''
197
198
  };
198
199
  return {
199
200
  product_id: productId,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "private": false,
3
3
  "name": "@pisell/pisellos",
4
- "version": "2.3.95",
4
+ "version": "2.3.96",
5
5
  "description": "一个可扩展的前端模块化SDK框架,支持插件系统",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",