# 2026-09-23
## 登录页「记住密码」多账号下拉选择(已完成)
- 需求:登录页勾选记住密码后,把企业名+账号+密码存本地;之后企业名/账号均可下拉选择(同时支持手输新值)
- 实现:
- `src/hooks/web/useCache.ts`:新增 `CACHE_KEY.SavedAccounts = 'savedAccounts'`
- `src/utils/auth.ts`:新增 `SavedAccountType` + `getSavedAccounts / addSavedAccount / removeSavedAccount`,密码复用 `encrypt/decrypt`(RSA)加密存 localStorage(web-storage-cache,有效期 365 天,上限 20 条,同 tenantName+username 去重覆盖并置顶)
- `src/views/Login/components/LoginForm.vue`:
- 企业名、账号两个 `el-input` 改为 `el-select`(`filterable + allow-create + default-first-option`,仍可手输新值)
- 账号下拉项显示 `账号(企业名)`,右侧删除图标可单条移除
- 选企业名 → 自动带出该企业下最近记住的账号+密码;选账号 → 自动带出企业名+密码(优先匹配当前企业名)
- 登录成功:勾选记住 → `addSavedAccount`;未勾选 → 从列表移除该账号
- `src/locales/zh-CN.ts` / `en.ts`:新增 `login.rememberPassword`(记住密码)
- 已跑 `vue-tsc --noEmit`,改动文件无类型报错
## ⚠️ 补做:真正生效的登录页是 `src/views/Login/Login.vue`(不是 components/LoginForm.vue)
- **事由**:在 `components/LoginForm.vue` 改完后用户截图反馈「没看到新功能」
- **根因**:本项目真实登录路由渲染的是 `views/Login/Login.vue` 的自定义手写表单(原生 `` + `.inputM/.inputS` 样式 + 「使用谷歌浏览器」页脚);
`components/LoginForm.vue` / MobileForm / QrCodeForm / RegisterForm 在 Login.vue 里**全是注释掉的**(`` 注释),改了不生效
- **排查手法**:用户截图里的特征文案(`账号登录` / `使用谷歌浏览器` / `欢迎来到颐年`)→ 在项目里 Grep,直接定位真正的组件
- **教训**:改登录页前先确认实际挂载的组件;本项目多个 Login 入口并存,别默认 `LoginForm.vue`
- Login.vue 侧实现:企业名/账号输入框 `@focus/@input` 弹自绘 `.account-dropdown` 面板(失焦 150ms 延时关闭,选项用 `@mousedown.prevent` 以免 blur 早于 click);
密码框下新增「记住密码」el-checkbox(登录按钮 marginTop 由 40px 收为 12px,卡片 500px 高度不变);登录成功分支同样 addSavedAccount / removeSavedAccount
- 共用同一套 `src/utils/auth.ts` 的 SavedAccounts 存储(跨两个登录页互通)
## 🐛 上述功能上线后两个 BUG 的修复(务必记住这两个坑)
1. **密码被清空 / 回填后是乱码**
- 根因:`src/utils/auth.ts` 的 `setLoginForm()` **原地改写传入对象**(`loginForm.password = encrypt(...)`)。
`handleLogin` 里先 `setLoginForm(loginData.loginForm)` 再 `addSavedAccount(...)` → 存进去的是「密文再加密」→ 读时解不开 → 密码空
- 修法:① `setLoginForm` 改为 `{ ...loginForm, password: encrypt(...) }` 不再改原对象;
② SavedAccounts 的增删改统一基于**原始密文列表** `getSavedAccountsRaw()`,禁止「先全解密再全加密」(会把解密失败的条目永久写成空);
③ 解密结果仍像密文(≥40 位 base64)时再解一层,自动修复历史二次加密数据
2. **自绘下拉面板自动关闭**
- 根因:面板 `position:absolute` 在 `overflow:auto` 滚动容器内会撑出滚动条→重排→input 失焦→`blur` 关闭;且原本用 blur+150ms 延时关闭太脆
- 修法:面板 `Teleport to="body"` + `position:fixed` + 输入框 `getBoundingClientRect()` 定位;
**关闭只判 `document.mousedown` 是否落在面板/输入框外**,完全不依赖 focus/blur;scroll/resize 只重新定位
3. 选中空的记住密码不再覆盖用户已输入的内容(`if (matched.password)` 守卫)
- `vue-tsc --noEmit` 已过
## 交互约定
- 回填策略:选企业名 → 带出该企业最近记住的账号+密码;选账号 → 带出企业名+密码;两者都加非空守卫
## 后续微调(同日)
- 登录下拉改为**打开即展示全部**记住的企业名/账号,不再按输入内容过滤(用户明确要求)
- 临时医嘱单打印页 `short-doctor-order/Print.vue`:字体全部改黑色(标题/字段标签/表头),红色仅保留线条(标题双红线、表格边框);
长期医嘱单 Print.vue 本就是黑色;execute-doctor-order 无红色字体