fix(web): 第9轮审查批次1——认证/图标CSS/顶号竞态/消息核心一批问题

认证域:
- 找回密码验证码请求存在竞态:请求发出后如果用户改了手机号,旧请求
  返回时仍会用旧手机号的challenge�covers新手机号的输入框,现在按
  发起请求时的手机号快照判断,作废过期响应
- 切换企业码时"退出登录"确认框可被绕过:判断依据只有内存里的
  myUserId,直接刷新在/tenant页会让这个值变回null,导致确认框不弹
  就静默销毁一个其实还有效的会话;现在同时检查sessionStorage里的
  token
- 企业码不存在错误文案缺后半句",请核实后重试",和其它两处文案对齐
- 营业执照上传补上前端体积/类型校验(仿照问题反馈页截图校验的写法),
  不再无条件发一次注定被拒的上传请求
- 找回密码页移动端"重置密码"标题重复渲染两次,桌面版标题补上
  min-[900px]:block hidden
- 企业码输入框补maxLength=64

图标CSS:Material Icons的.im-icon规则漏抄了Google原始样式表的两行
(-webkit-font-feature-settings/-webkit-font-smoothing),补齐让
"1:1 copy"真正成立

顶号踢人 vs 已有401登出的竞态:两条路径会同时清空同一个token,如果
401先触发,用户会被直接甩回登录页看不到"账号已在其他设备登录"的
提示——forceReauth()现在先等一小段时间看kickedInfo是否被顶号流程
设置,设置了就让顶号流程自己的按钮触发跳转,避免抢跑

通话信令健壮性:sendCallRinging/Accept/Hangup底层用的{note}包没有id
字段,连接断开时会同步抛异常而不是走Promise reject——之前hangUp()
在这类异常后直接中断,导致后续的leaveCall()清理和UI收尾全部被跳过。
统一加了try/catch兜底

消息核心:
- 名片消息/通话结果消息(非Drafty的扁平自定义对象)之前可以被引用/
  复制/转发,会把这些对象错误地喂给Drafty的解析函数,产生格式错误
  的内容——现在这两类消息只保留收藏/撤回/删除/多选
- MessagesLayout.tsx自己独立计算的peerName没有对slf(自聊天)做
  isSelf特殊处理,会显示裸topic id"slf"而不是昵称,和已经修过的
  ConversationView头部不一致,现在补齐同样的处理
- 发送失败图标/文案错用了danger-text(撤回删除等破坏性操作的语义
  token),改成语义正确的unread token;录音条文字被连带染红,只保留
  指示点是红色
- 群成员数徽标形状(应为4px圆角矩形不是胶囊)和颜色(应为text-tertiary-4
  不是text-tertiary-3)都和设计稿不符,已修正

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Your Name
2026-09-16 12:33:23 +08:00
co-authored by Claude Sonnet 5
parent fb1fda689c
commit ab96ae1fee
14 changed files with 118 additions and 33 deletions
+9 -2
View File
@@ -25,8 +25,14 @@ export function TenantEntryPage() {
if (!code.trim() || submitting) return;
// Reachable by navigating back here while already logged in (no route guard keeps an
// authenticated user out of /tenant) -- resetTinode() below would otherwise silently tear down
// that live session with no warning at all.
if (myUserId && !(await requestConfirm({ message: '切换企业将退出当前登录,确定继续吗?' }))) return;
// that live session with no warning at all. `myUserId` alone isn't enough: it's an in-memory
// Zustand value that resets to null on any fresh mount of this module (a page reload while
// sitting on /tenant, or opening this URL directly in a new tab) even though the session token
// is still sitting in sessionStorage and would otherwise restore fine -- confirmed by reading
// RequireAuth.tsx, which is the ONLY place that re-populates `myUserId` from that token, and it
// never mounts for this route. Checking the token directly closes that gap.
const hasLiveSession = myUserId || sessionStorage.getItem('im-session-token');
if (hasLiveSession && !(await requestConfirm({ message: '切换企业将退出当前登录,确定继续吗?' }))) return;
setSubmitting(true);
setError('');
try {
@@ -88,6 +94,7 @@ export function TenantEntryPage() {
value={code}
onChange={(e) => setCode(e.target.value)}
placeholder="请输入企业码"
maxLength={64}
className="w-full bg-transparent text-[15px] outline-none"
/>
</label>