diff --git a/src/assets/jingdong/jingdong.png b/src/assets/jingdong/jingdong.png index 356639e..1500685 100644 Binary files a/src/assets/jingdong/jingdong.png and b/src/assets/jingdong/jingdong.png differ diff --git a/src/utils/jingdong-product-title.ts b/src/utils/jingdong-product-title.ts index ba43464..63c3a12 100644 --- a/src/utils/jingdong-product-title.ts +++ b/src/utils/jingdong-product-title.ts @@ -1,9 +1,23 @@ /** 对齐 agent catalog.productTitleSpec:哪些产品需要标题输入,以及提交 scene_prop */ +export type ProductTitleMode = 'chars' | 'props' + export interface ProductTitleSpec { label: string placeholder: string + /** chars:按字符数;props:按分号分隔的道具个数 */ + mode: ProductTitleMode max: number + /** 输入框下方辅助说明 */ + hint?: string +} + +const SCENE_PROP_SPEC: ProductTitleSpec = { + label: '场景道具', + placeholder: '例如:沙发;台灯;花瓶;地毯', + mode: 'props', + max: 4, + hint: '最多填写4个场景道具,用英文或中文分号隔开', } export function isRibbonVariant(name: string) { @@ -13,25 +27,58 @@ export function isRibbonVariant(name: string) { export function productTitleSpec(label: string, variantName = ''): ProductTitleSpec | null { const name = String(label || '').trim() if (name.includes('场景手办')) { - return { label: '场景道具', placeholder: '可输入1-4个场景道具', max: 4 } + return { ...SCENE_PROP_SPEC } } if (name.includes('相框手办挂饰') || (name.includes('相框') && name.includes('挂饰'))) { - return { label: '相框标题', placeholder: '输入相框名称,如「我们的小家」', max: 18 } + return { + label: '相框标题', + placeholder: '输入相框名称,如「我们的小家」', + mode: 'chars', + max: 18, + } } if (name.includes('相框手办') && isRibbonVariant(variantName)) { - return { label: '飘带标题', placeholder: '输入飘带名称,如「生日快乐」', max: 18 } + return { + label: '飘带标题', + placeholder: '输入飘带名称,如「生日快乐」', + mode: 'chars', + max: 18, + } } return null } +/** 按分号拆成道具列表(忽略空段) */ +export function splitSceneProps(text: string): string[] { + return String(text || '') + .split(/[;;]/) + .map((item) => item.trim()) + .filter(Boolean) +} + +/** 限制最多 max 个道具,统一用英文分号拼接 */ +export function clampSceneProps(text: string, max = 4): string { + const parts = splitSceneProps(text).slice(0, Math.max(0, max)) + const endsWithSep = /[;;]\s*$/.test(String(text || '')) + // 输入未满且以分号结尾时,保留末尾分号,方便继续填下一项 + if (endsWithSep && parts.length < max) { + return parts.length ? `${parts.join(';')};` : '' + } + return parts.join(';') +} + /** 生成接口 scene_prop:仅当产品需要标题字段且已填写时传原文 */ export function resolveSceneProp( productName: string, title: string, variantName = '', ): string { - if (!productTitleSpec(productName, variantName)) return '' - return String(title || '').trim() + const spec = productTitleSpec(productName, variantName) + if (!spec) return '' + const raw = String(title || '').trim() + if (!raw) return '' + if (spec.mode === 'props') return clampSceneProps(raw, spec.max) + return raw } function textOf(...values: unknown[]) { @@ -81,10 +128,15 @@ export function productTitleSpecFromOrder( const typeId = Number(orderStat.type_id || 0) if (typeId === 9) { - return { label: '场景道具', placeholder: '可输入1-4个场景道具', max: 4 } + return { ...SCENE_PROP_SPEC } } if (typeId === 11) { - return { label: '相框标题', placeholder: '输入相框名称,如「我们的小家」', max: 18 } + return { + label: '相框标题', + placeholder: '输入相框名称,如「我们的小家」', + mode: 'chars', + max: 18, + } } return null } @@ -93,6 +145,10 @@ export function resolveScenePropFromOrder( orderStat: Record | null | undefined, title: string, ): string { - if (!productTitleSpecFromOrder(orderStat)) return '' - return String(title || '').trim() + const spec = productTitleSpecFromOrder(orderStat) + if (!spec) return '' + const raw = String(title || '').trim() + if (!raw) return '' + if (spec.mode === 'props') return clampSceneProps(raw, spec.max) + return raw } diff --git a/src/views/jingdong/components/ChatComposer.vue b/src/views/jingdong/components/ChatComposer.vue index af9f468..568bd56 100644 --- a/src/views/jingdong/components/ChatComposer.vue +++ b/src/views/jingdong/components/ChatComposer.vue @@ -36,14 +36,15 @@ class="composer__title-input" type="text" :value="productTitle" - :maxlength="titleSpec.max" + :maxlength="titleInputMaxlength" :placeholder="titleSpec.placeholder" :disabled="disabled" :aria-invalid="titleInvalid" @input="onTitleInput" /> - {{ productTitleLength }} / {{ titleSpec.max }} + {{ titleCount }} / {{ titleSpec.max }} +

{{ titleSpec.hint }}

@@ -96,7 +97,11 @@