diff --git a/apps/admin/src/components.d.ts b/apps/admin/src/components.d.ts index b79c8fe..e39958f 100644 --- a/apps/admin/src/components.d.ts +++ b/apps/admin/src/components.d.ts @@ -18,6 +18,7 @@ declare module 'vue' { ElButton: typeof import('element-plus/es')['ElButton'] ElCascader: typeof import('element-plus/es')['ElCascader'] ElCheckbox: typeof import('element-plus/es')['ElCheckbox'] + ElCheckboxGroup: typeof import('element-plus/es')['ElCheckboxGroup'] ElColorPicker: typeof import('element-plus/es')['ElColorPicker'] ElContainer: typeof import('element-plus/es')['ElContainer'] ElDescriptions: typeof import('element-plus/es')['ElDescriptions'] @@ -41,6 +42,7 @@ declare module 'vue' { ElOption: typeof import('element-plus/es')['ElOption'] ElOptionGroup: typeof import('element-plus/es')['ElOptionGroup'] ElPopover: typeof import('element-plus/es')['ElPopover'] + ElRadio: typeof import('element-plus/es')['ElRadio'] ElRadioButton: typeof import('element-plus/es')['ElRadioButton'] ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup'] ElSelect: typeof import('element-plus/es')['ElSelect'] diff --git a/apps/admin/src/utils/origin-name.spec.ts b/apps/admin/src/utils/origin-name.spec.ts new file mode 100644 index 0000000..362ed98 --- /dev/null +++ b/apps/admin/src/utils/origin-name.spec.ts @@ -0,0 +1,46 @@ +import { describe, expect, it } from 'vitest'; +import { sameOriginGroup, truncateToProcess } from './origin-name'; + +describe('truncateToProcess', () => { + it('keeps first 3 dash segments (drops warehouse)', () => { + expect(truncateToProcess('美国(包邮)180g纯棉T恤成人款-DG001-单面印花-美西洛杉矶一仓')) + .toBe('美国(包邮)180g纯棉T恤成人款-DG001-单面印花'); + }); + it('is identity for 3-segment names', () => { + expect(truncateToProcess('美国(不包邮)230g水洗炒雪花T恤-KRHM003-直喷双面')) + .toBe('美国(不包邮)230g水洗炒雪花T恤-KRHM003-直喷双面'); + }); + it('handles malformed names without country prefix', () => { + expect(truncateToProcess('(不包邮)230g水洗炒雪花T恤-FRTM001-双面印花')) + .toBe('(不包邮)230g水洗炒雪花T恤-FRTM001-双面印花'); + }); + it('returns empty for null/empty', () => { + expect(truncateToProcess(null)).toBe(''); + expect(truncateToProcess('')).toBe(''); + }); +}); + +describe('sameOriginGroup', () => { + it('matches same product across warehouses', () => { + expect(sameOriginGroup( + '美国(包邮)180g纯棉T恤成人款-DG001-单面印花-美西洛杉矶一仓', + '美国(包邮)180g纯棉T恤成人款-DG001-单面印花-美中亚特兰大仓', + )).toBe(true); + }); + it('does not match different SKU', () => { + expect(sameOriginGroup( + '美国(包邮)180g纯棉T恤成人款-DG001-单面印花', + '美国(包邮)180g纯棉T恤成人款-DG002-单面印花', + )).toBe(false); + }); + it('does not match different country', () => { + expect(sameOriginGroup( + '美国(包邮)T恤-DG001-单面印花', + '德国(不包邮)T恤-DG001-单面印花', + )).toBe(false); + }); + it('does not match empty names', () => { + expect(sameOriginGroup(null, 'x-y-z')).toBe(false); + expect(sameOriginGroup('', '')).toBe(false); + }); +}); diff --git a/apps/admin/src/utils/origin-name.ts b/apps/admin/src/utils/origin-name.ts new file mode 100644 index 0000000..7da4a09 --- /dev/null +++ b/apps/admin/src/utils/origin-name.ts @@ -0,0 +1,20 @@ +/** + * 原产品名匹配规则(见 data-cleaning/README.md 命名调查): + * `国家(物流备注)品名-SKU-工艺位置[-仓库名]` + * 截断到「工艺位置」= 保留按 `-` 分段的前 3 段,丢弃可选的仓库名段。 + * 国家前缀在第 1 段内,天然参与比较(不同国家不合并)。 + */ +const KEEP_SEGMENTS = 3; + +export function truncateToProcess(name: string | null | undefined): string { + if (!name) return ''; + return name.split('-').slice(0, KEEP_SEGMENTS).join('-'); +} + +export function sameOriginGroup( + a: string | null | undefined, + b: string | null | undefined, +): boolean { + const ka = truncateToProcess(a); + return ka !== '' && ka === truncateToProcess(b); +}