fix(admin): 重建 watch 补 slug 源(防缺组租户切换草稿泄漏)

c6bd542 拆分后的分组 watch 只盯组 props 引用——seed 租户快照可能缺组
(如无 ai/contact/misc 组),两家租户都缺时切租户该组 prop 走
undefined→undefined 引用不变、watch 不触发,上一家租户的草稿留在当前
租户界面(dirty 也残留)。旧的全量耦合顺带防住了它,拆分后暴露。

修复:六个 pane 的重建 watch 源统一加 () => props.slug 兜底——slug 变化
即强制重建(缺组时空槽归位、dirty 归 false);单卡保存路径不受影响
(组引用照常换新触发)。覆盖 CredsPane 三卡 + WallPane / PromptsPane /
ContactFormPane / EngineFormPane / MiscFormPane(均已声明 slug prop,
index.vue 挂载处均已传 activeSlug,零接线改动)。

顺手:CredsPane 测试态注释仍提已删除的 resetAll → 改提三卡 watch;
计划文档 Task 15 段落末补 Batch 1 遗留打磨项处置注记(色列宽度/头像
提示样式经 Task 16 走查目检后定夺)。

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
浅念
2026-10-14 21:40:29 +08:00
parent c6bd54229d
commit 1df1fc7922
7 changed files with 31 additions and 15 deletions
@@ -114,8 +114,8 @@
const baseline = ref(JSON.stringify(contactToForm(undefined)))
watch(
() => props.group,
(group) => {
[() => props.slug, () => props.group],
([, group]) => {
const next = contactToForm(group)
Object.assign(form, next)
baseline.value = JSON.stringify(next)
+19 -5
View File
@@ -442,7 +442,7 @@
const pushBaseline = ref(JSON.stringify(normalizePushCredsGroup(undefined).form))
const aiBaseline = ref(JSON.stringify(normalizeAiCredsGroup(undefined).form))
// 测试态(声明先于下方 immediate watch 的 resetAll——避免 TDZ 引用)
// 测试态(声明先于下方 immediate 的三卡 watch——避免 TDZ 引用)
const emailTestTo = ref('')
const emailTesting = ref(false)
const emailTestResult = ref<{
@@ -494,10 +494,24 @@
// 分组 watch(替代旧 array-source 全量 resetAll):父层 onPaneSaved 对快照做
// {...snapshot, [group]: config} 展开——只有被保存组引用换新,兄弟组引用不动
// → 只重建该卡;切租户/回滚时快照先置 null 再整份换新,三组引用齐变 → 三卡
// 全量重置(保持既有行为)
watch(() => props.emailGroup, resetEmailCard, { immediate: true })
watch(() => props.pushGroup, resetPushCard, { immediate: true })
watch(() => props.aiGroup, resetAiCard, { immediate: true })
// 全量重置(保持既有行为)。watch 源额外带 slug:seed 租户快照可能缺组(如
// 无 ai 组),两家都缺时组 prop undefined→undefined 引用不变、watch 不触发,
// 上一家租户的草稿会留在当前租户界面——slug 变化兜底强制三卡重建
watch(
[() => props.slug, () => props.emailGroup],
([, group]) => resetEmailCard(group),
{ immediate: true }
)
watch(
[() => props.slug, () => props.pushGroup],
([, group]) => resetPushCard(group),
{ immediate: true }
)
watch(
[() => props.slug, () => props.aiGroup],
([, group]) => resetAiCard(group),
{ immediate: true }
)
/** 未保存判定:三表单任一偏离基线,或密钥三态(输入非空 = 覆盖新值 /
* cleared = 显式清除)——密钥输入框基线恒空串,只能独立判定 */
@@ -133,8 +133,8 @@
const baseline = ref(JSON.stringify(engineToForm(undefined)))
watch(
() => props.group,
(group) => {
[() => props.slug, () => props.group],
([, group]) => {
const next = engineToForm(group)
Object.assign(form, next)
baseline.value = JSON.stringify(next)
@@ -103,8 +103,8 @@
const baseline = ref(JSON.stringify(miscToForm(undefined)))
watch(
() => props.group,
(group) => {
[() => props.slug, () => props.group],
([, group]) => {
const next = miscToForm(group)
Object.assign(form, next)
baseline.value = JSON.stringify(next)
@@ -198,8 +198,8 @@
const baseline = ref(JSON.stringify(normalizePromptsGroup(undefined)))
watch(
() => props.group,
(group) => {
[() => props.slug, () => props.group],
([, group]) => {
const next = normalizePromptsGroup(group)
Object.assign(draft, next)
baseline.value = JSON.stringify(next)
+2 -2
View File
@@ -244,8 +244,8 @@
const baseline = ref<WallEditModel | null>(null)
watch(
() => props.group,
(group) => {
[() => props.slug, () => props.group],
([, group]) => {
model.value = initWallEdit(group)
baseline.value = model.value
? (JSON.parse(JSON.stringify(model.value)) as WallEditModel)
@@ -1195,6 +1195,8 @@ watch(activeSlug, async () => {
- [ ] **Step 2: 逐页轻改**——原则(spec §7):空态从「暂无数据」改具体动作指引(会话页「暂无会话——客户开始聊天后会出现在这里」/统计页「暂无数据——产生会话后展示分析」/平台设置存储表空「暂无设置项——从上方推荐键开始」);提示文案去术语(「线索」统一、错误 toast 主语明确)。**只改文案与空态,不动布局/逻辑**;每页改完 dev 目检
- [ ] **Step 3: Commit**——`git commit -m "polish(admin): 各页文案与空态轻量打磨"`
> 处置注记(Task 15 收尾):色列宽度与头像提示样式打磨经 Task 16 走查目检后定夺(视觉可接受则结项,否则列入后续)。
### Task 16: 全局回归 + 验收
- [ ] **Step 1: 前端全量测试**——`cd admin && pnpm test`,预期全绿且总数 ≥ 改造前(既有用例随退役迁移、新增只增不减;当前基线 164)