# 访前准备 Tab 归档同步机制 + 浏览器实测验证（2026-08-22 实测）

## 核心结论：归档是"前端拉清单"模式，不是服务端推送

打开访前准备 Tab（`switchTab('visitPrep')`）→ `vpLoadArchive()` 异步执行：

1. `fetch('../数据/file_manifest.json?v=vc2707')` 读文件清单
2. 筛选 `name` 含 `_拜访卡` / `_市场调研卡` / `_调研_` / `_访前背调` / `_拜访作战卡` 的条目
3. 销售员隔离：从文件名提取客户简称 → 与 `getVisibleCustomers()`（admin 返回全部 `mcpCustomers`）双向包含 + 2字滑动窗口模糊匹配
4. 渲染到 `#vpArchive`

**所以只要两件事就绪，打开 Tab 即自动显示，无需手动推送：**
- 清单已部署（`regenerate_manifest.sh` → `deploy_visit_card.sh` 会顺带部署 `数据/file_manifest.json`）
- 客户在 `数据/mcp_customers.json` 可见名单里（admin 全见；sales 按 owner/operators 隔离）

## "处理中的请求" vs "已生成的拜访卡" 是两套数据源

| 区域 | 数据源 | 跨设备一致 |
|------|--------|:--:|
| ⏳ 处理中的请求 | localStorage `bior_visit_queue`（按 `q.salesperson===user.name` 过滤） | ❌ 浏览器本地 |
| ✅ 已生成的拜访卡 | `../数据/file_manifest.json`（fetch 清单） | ✅ |

服务端队列 `数据/visit_requests/*.json`（`POST /api/visit-card` 写入）与前端 localStorage 队列**不互通**。`vpGenerate()` 提交时同时写 localStorage + POST 服务端。

## getCustShort() 算法（客户全称 → 文件名简称）

依次 strip：`股份有限公司` / `有限责任公司` / `有限公司` → 再循环 strip `科技`/`生物`/`医学`/`医药`/`医疗`/`诊断`/`集团`/`技术`/`工程`/`制药`。

例：`迈克生物股份有限公司` → `迈克生物` → `迈克`（很短的简称，前缀匹配即可命中文件名）。

## 双斜杠 URL bug（`ts.net//客户管理`）

`vpLoadArchive()` 拼链接：`'https://macbook-air-2.tail6864e8.ts.net/' + path`，而 manifest 的 `path` 带前导 `/`（`/客户管理/...`）→ 结果 `ts.net//客户管理`（双斜杠）。实测 HTTP 200（服务器容忍），不影响点开，但严格是拼路径 bug。修复：`path.replace(/^\//,'')`。

## 浏览器实测验证步骤（验证某客户卡片是否同步）

```javascript
// 1. 登录（CloudBase index.html 当前用 <select id="loginRole"> + <input id="loginPwd">）
document.getElementById('loginRole').value = '刘新元';   // admin
document.getElementById('loginPwd').value = '<密码>';     // 查 user_roles 或问用户
document.getElementById('loginBtn').click();
// 2. 等 4s（doLogin 异步查 CloudBase user_roles）
// 3. 切访前准备 Tab + 等 vpLoadArchive 异步 fetch 完成
switchTab('visitPrep');   // 等 5s
// 4. 检查归档区
document.getElementById('vpArchive').innerText;  // 应含目标客户名
```

要点：
- 登录成功后 `state.user` 是对象 `{name, role, dept, tabs}`（不是字符串）；判断用 `state.user.role`
- **第一次检查可能 `NO vpArchive element`**——`vpLoadArchive` 是异步 fetch，切换后立即查会落空，`sleep 5s` 再查
- 归档区链接指向 Tailscale `https://macbook-air-2.tail6864e8.ts.net/...`，用 `curl -o /dev/null -w "%{http_code}"` 验证 HTTP 200
- macOS Chrome 远程调试首次会弹 "Allow remote debugging?" → 需先 `~/.cache/uv/archive-v0/*/bin/browser-harness mac-approve`（否则 `new_tab` 返回 `about:blank`）

## 关联 Pitfall

- #58 访前准备拜访卡生成（数据源交叉 + `getVisibleCustomers()`）
- #60 `file_manifest.json` 格式陷阱（对象非数组、name 无扩展名）
- #62 存档区客户名模糊匹配（简称 vs 全称）
- `references/manifest-and-funnel-pitfalls.md`（归档检测 + 请求队列双写全记录）
