diff --git a/src/assets/jingdong/jingdong.png b/src/assets/jingdong/jingdong.png new file mode 100644 index 0000000..356639e Binary files /dev/null and b/src/assets/jingdong/jingdong.png differ diff --git a/src/config/jingdong.ts b/src/config/jingdong.ts index 0812fe2..6c6cc96 100644 --- a/src/config/jingdong.ts +++ b/src/config/jingdong.ts @@ -1,5 +1,6 @@ import logoAvatar from '@/assets/jingdong/logo-mark.png' import logoIcon from '@/assets/jingdong/logo_1.png' +import userAvatar from '@/assets/jingdong/jingdong.png' import caseOriginal from '@/assets/jingdong/原图.png' import caseEffect from '@/assets/jingdong/效果图.png' import caseScene from '@/assets/jingdong/场景图.png' @@ -19,9 +20,11 @@ export const jingdongConfig = { background: '#F5F5F5', images: { - /** 顶栏 / 对话头像 */ + /** 顶栏 / Agent 对话头像 */ avatar: logoAvatar, logo: logoAvatar, + /** 用户侧对话头像 */ + userAvatar, /** 浏览器标签 favicon */ logoIcon, caseOriginal, @@ -74,5 +77,8 @@ export const jingdongConfig = { effectPreparing: '效果图准备中,请稍后再试', generateFail: '这次没生成成功,请换个描述或照片再试', editFail: '这次调整没成功,请换个描述再试', + /** 内容安全策略拒绝(未成年 / 公众人物等) */ + contentPolicyFail: + '当前照片不符合内容规范,可能涉及未成年或公众人物,请换一张照片再试', }, } diff --git a/src/utils/jingdong-error.ts b/src/utils/jingdong-error.ts index ff6db7b..0dcc83c 100644 --- a/src/utils/jingdong-error.ts +++ b/src/utils/jingdong-error.ts @@ -1,5 +1,7 @@ /** 将后端 / 网络 / 前端技术错误转成用户可读提示 */ +import { jingdongConfig } from '@/config/jingdong' + const KNOWN: Record = { 未登录: '登录已失效,请关闭后重新进入再试', 请求失败: '服务繁忙,请稍后重试', @@ -17,7 +19,7 @@ const KNOWN: Record = { '签发上传地址失败:缺少 upload_url': '照片上传失败,请稍后重试', '获取 upload_id 失败': '照片上传失败,请稍后重试', 'upload/url 响应无法解析': '照片上传失败,请稍后重试', - 生成失败: '这次没生成成功,请换个描述或照片再试', + 生成失败: jingdongConfig.messages.generateFail, 提交生成失败: '提交失败了,请稍后重试', 没有生成结果: '这次没有生成出效果图,请再试一次', 生成超时: '生成时间有点久,请稍后重试', @@ -32,6 +34,32 @@ const KNOWN: Record = { 'product_id不在Agent开放分类': '当前产品暂不支持在线生图,请联系客服', } +/** 生图 / 编辑:内容策略单独翻译;Processing/ApiError 走场景兜底文案 */ +const GENERATE_EDIT_RULES: Array<{ test: RegExp; tipKey: 'contentPolicyFail' | 'processingFail' }> = [ + { + test: /content\s*policy|minor|public\s*figure|recognizable\s*public/i, + tipKey: 'contentPolicyFail', + }, + { + test: /processing\s*failed|ApiError/i, + tipKey: 'processingFail', + }, +] + +/** 前端自抛、可原样映射的短文案(生图场景仍保留) */ +const GENERATE_EDIT_KEEP = new Set([ + '已取消生成', + '生成超时', + '生成超时,请稍后重试', + '提交生成失败', + '没有生成结果', + '请先上传照片', + '缺少可编辑的效果图', + '缺少会话信息,请刷新后重试', + '缺少产品信息,请刷新后重试', + 'product_id不在Agent开放分类', +]) + const PATTERN_RULES: Array<{ test: RegExp; tip: string }> = [ { test: /timeout|超时|ETIMEDOUT|ECONNABORTED/i, tip: '等待超时,请稍后重试' }, { test: /network|fetch failed|ERR_NETWORK|断网|网络/i, tip: '网络异常,请检查网络后重试' }, @@ -49,8 +77,8 @@ const PATTERN_RULES: Array<{ test: RegExp; tip: string }> = [ export type FriendlyErrorScene = 'generate' | 'edit' | 'upload' | 'inspect' | 'download' | 'common' const SCENE_FALLBACK: Record = { - generate: '这次没生成成功,请换个描述或照片再试', - edit: '这次调整没成功,请换个描述再试', + generate: jingdongConfig.messages.generateFail, + edit: jingdongConfig.messages.editFail, upload: '照片上传失败,请换一张或稍后重试', inspect: '照片识别失败,请换一张或稍后重试', download: '下载失败,请换用其他浏览器,或长按效果图保存', @@ -86,11 +114,27 @@ function looksTechnical(text: string) { return false } +function mapGenerateEditError(text: string, tip: string): string { + for (const rule of GENERATE_EDIT_RULES) { + if (!rule.test.test(text)) continue + // 内容策略用专用文案;Processing/ApiError 与其它失败一样走场景兜底 + if (rule.tipKey === 'contentPolicyFail') { + return jingdongConfig.messages.contentPolicyFail + } + return tip + } + if (GENERATE_EDIT_KEEP.has(text) && KNOWN[text]) return KNOWN[text] + for (const key of GENERATE_EDIT_KEEP) { + if (key.length >= 4 && text.includes(key) && KNOWN[key]) return KNOWN[key] + } + // 生图/编辑:未识别的后端英文或其它错误,统一兜底 + return tip +} + /** * 任意错误 → 友好中文提示。 - * - 优先命中已知文案 / 规则 - * - 短中文业务提示可原样展示 - * - 技术性内容回落场景默认文案 + * - 生图/编辑:仅内容策略、Processing/ApiError 单独翻译,其余走 generateFail / editFail + * - 其它场景:已知文案 / 规则优先,技术性内容回落默认文案 */ export function friendlyError( error: unknown, @@ -101,6 +145,10 @@ export function friendlyError( const text = rawMessage(error) if (!text) return tip + if (scene === 'generate' || scene === 'edit') { + return mapGenerateEditError(text, tip) + } + if (KNOWN[text]) return KNOWN[text] for (const [key, value] of Object.entries(KNOWN)) { diff --git a/src/views/jingdong/index.vue b/src/views/jingdong/index.vue index cea316e..2ba5dfb 100644 --- a/src/views/jingdong/index.vue +++ b/src/views/jingdong/index.vue @@ -17,7 +17,9 @@ - +
@@ -1143,15 +1145,12 @@ onBeforeUnmount(() => { } .msg__avatar--user { - display: inline-flex; - align-items: center; - justify-content: center; - background: #3a8e4d; - border: 0; - color: #fff; - font-size: 13px; - font-weight: 600; - line-height: 1; + background: #ffffff; + + img { + object-fit: cover; + object-position: center; + } } .msg__body { @@ -1182,7 +1181,7 @@ onBeforeUnmount(() => { } .msg.is-user .msg__bubble { - border-radius: 16px 16px 4px 16px; + border-radius: 16px 4px 16px 16px; } .msg:not(.is-user) .msg__bubble { @@ -1197,7 +1196,7 @@ onBeforeUnmount(() => { width: 100%; box-sizing: border-box; padding: 10px; - background: #f0f1f3; + background: #e9e9eb; box-shadow: none; color: #222;