From 44c53429d95070a2544299835f7359c67ec0c352 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B5=85=E5=BF=B5?= Date: Tue, 20 Oct 2026 22:30:44 +0800 Subject: [PATCH] =?UTF-8?q?feat(admin):=20=E4=BB=8E=E7=BD=91=E7=AB=99?= =?UTF-8?q?=E5=AF=BC=E5=85=A5=E5=AF=B9=E8=AF=9D=E6=A1=86=E4=B8=8E=E5=AE=A1?= =?UTF-8?q?=E6=A0=B8=E9=A1=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Task 9 装配:site-import/index.vue 单文件两形态——导入对话框(URL 启动/3s 轮询五键/phase 文案/failed retry 复活形态/7.5 分钟窗口/404 过期终态/放弃确认)与审核页 fullscreen dialog(品牌表单+头像 dataURL 预览与跳过勾选/四件套 tabs 等宽编辑+UTF-8 字节计数/墙尾巴 三语单行/联系方式两开关/warnings 分级黄条)。 保存序列执行器按 buildSaveSequence 依次调既有端点(每步 GET 在该步 执行时取,avatar 上传写的版本不丢);wall 步消费 Task 9 备忘新增的 wallPoolDispatch 纯函数(保存时新鲜 GET 池判断逐语言替换/追加, append 提示「原池含人工编辑句」),contact 步读-改-写仅覆写两开关。 部分失败停当前组,重试跳过已完成组。 两入口接线:租户详情抽屉(超管,停用禁用)与配置中心页头(两角色, slug=当前租户上下文),completed 后各自刷新。 测试 201→206(wallPoolDispatch 五例 TDD);pnpm build 零错误; dts 行尾漂移按纪律还原。 Co-Authored-By: Claude Code --- admin/src/views/config/center/index.vue | 27 + .../views/site-import/__tests__/model.test.ts | 57 + admin/src/views/site-import/index.vue | 1238 +++++++++++++++++ admin/src/views/site-import/model.ts | 52 +- admin/src/views/tenants/list/index.vue | 25 + 5 files changed, 1398 insertions(+), 1 deletion(-) create mode 100644 admin/src/views/site-import/index.vue diff --git a/admin/src/views/config/center/index.vue b/admin/src/views/config/center/index.vue index 2c7c573..1607968 100644 --- a/admin/src/views/config/center/index.vue +++ b/admin/src/views/config/center/index.vue @@ -65,6 +65,15 @@

按左侧分组编辑——保存后立即生效并产生新版本,历史可随时回滚

+ + 从网站导入 + 当前版本 v{{ version }}
+ + + + + diff --git a/admin/src/views/site-import/model.ts b/admin/src/views/site-import/model.ts index 9d4e8e5..799d46f 100644 --- a/admin/src/views/site-import/model.ts +++ b/admin/src/views/site-import/model.ts @@ -14,7 +14,8 @@ * (有则 dataURL→Blob→POST brand/avatar)→ PUT config/brand(读-改- * 写)→ PUT config/prompts → PUT config/wall(读-改-写,仅尾巴句)→ * PUT config/contact。本模块只构造步骤描述(kind/label/note),实际 - * 请求编排与部分失败重试在 Task 9 的 index.vue。 + * 请求编排与部分失败重试在 Task 9 的 index.vue;墙步的池形态分派由 + * wallPoolDispatch 纯函数承担(Task 9 备忘——保存时新鲜 GET 快照判断)。 */ import type { ApiError } from '@/api/client' @@ -218,6 +219,55 @@ export function buildSaveSequence(draft: SiteImportDraft): SaveStep[] { return steps } +// ---- 墙池形态分派(Task 9——保存时以新鲜 GET 快照判断替换/追加) ------------------------ + +/** 分派结果:mode=append 时 UI 提示「原池含人工编辑句,已追加而非替换」; + * pools 按语言给出目标池,null = 不触该语言(es 尾巴缺失的合法降级, + * 载荷保持 GET 快照原值) */ +export interface WallDispatch { + mode: 'replace' | 'append' + pools: Record<'en' | 'zh' | 'es', string[] | null> +} + +/** 现池语言槽防御归一:非数组值按空池对待(字符串项过滤) */ +function asStringList(value: unknown): string[] { + return Array.isArray(value) + ? value.filter((s): s is string => typeof s === 'string') + : [] +} + +/** + * deflections_contact 池形态分派(spec §3.2 / §5,Task 9 备忘升为纯函数): + * 保存时以**新鲜 GET** 的现池为判断基准,逐语言分派—— + * - 池内 >1 句 = 含人工编辑句 → 追加到末项(原句保留); + * - ≤1 句(出厂单句/空/缺键)→ 整句替换为新尾巴; + * - es 新尾巴 null → pools.es 为 null(不触该语言池)。 + * 任一语言走了追加 → mode='append'(调用方展示追加提示文案)。 + */ +export function wallPoolDispatch( + currentPools: Record, + newTails: { en: string; zh: string; es: string | null } +): WallDispatch { + let appended = false + const pools = {} as WallDispatch['pools'] + const current = currentPools ?? {} + for (const lang of ['en', 'zh', 'es'] as const) { + const tail = newTails[lang] + if (tail === null) { + pools[lang] = null + continue + } + const sentences = asStringList(current[lang]) + if (sentences.length > 1) { + appended = true + pools[lang] = [...sentences, tail] + } else { + pools[lang] = [tail] + } + } + return { mode: appended ? 'append' : 'replace', pools } +} + // ---- warnings 分级 ------------------------------------------------------------------- /** diff --git a/admin/src/views/tenants/list/index.vue b/admin/src/views/tenants/list/index.vue index aae9f60..8d26dde 100644 --- a/admin/src/views/tenants/list/index.vue +++ b/admin/src/views/tenants/list/index.vue @@ -121,6 +121,17 @@ {{ detail.status === 'active' ? '启用' : '停用' }} + + 从网站导入 + 绑定 + + +