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 @@
按左侧分组编辑——保存后立即生效并产生新版本,历史可随时回滚
+
+
+
+
+
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' ? '启用' : '停用' }}
+
+ 从网站导入
+
绑定
+
+
+