修复全页面走查发现的 P0 前端问题(第一批)
八个 agent 对 11 章逐页对照设计稿+后端源码后,先修其中"功能不可用/崩溃/谎报成功"级别的:
崩溃与数据错误
- content-library-view: Highlight 按 [start,len] 数组解构后端返回的 {start,end} 对象,
对象不可迭代会直接抛 TypeError,任何文本搜索命中都让整个结果区渲染崩掉。服务端偏移还叠加
两个问题(Go 字节下标 vs JS UTF-16;偏移基准是全文但 snippet 会被开窗截断),改为在展示
文本上本地匹配关键词。
- content-library-view: firstAttachment 用 Drafty.attachments,SDK 里硬过滤 tp=='EX'
只认文件附件,图片(IM)/视频(VD)/语音(AU) 一个都取不到——收藏缩略图、语音播放源、聊天记录
图片视频宫格全部拿不到资源。改用 Drafty.entities 按类型分派(chat-message.jsx 已有先例)。
- message-center-view: 系统通知的机器码兜底会把真实数据吞掉(日报提醒的 summary 就是日期
字符串 "2026-08-20",会议通知的 summary 是标题),改为查表命中才替换。
- message-center-view: 会话列表漏了 avatar 字段,每行都只能落到兜底图标;同文件
frequentContacts() 本来就是对的,照它补上。
功能不可用
- auth-onboarding-view: 首次开启青少年模式是死路——设完密码丢弃了服务端返回的
enable_action_token,从不调 enableYouthMode,用户走完短信验证+设密码后模式仍是关的。
现在拿着这个 token 把最后一步做完。
- group-management-view: "投诉/举报"是个 onToast('暂未开放') 死桩,而 target_type='group'
这条链路早就通了。接上真实举报页。
- tinode-web: 刷新/深链会被弹回首页。两个原因叠加:初始化白名单还是上游 Tinode 的
['cred','reset','register'];handleLoginSuccessful 无条件把 hash 改写成 workbench,
刷新触发的自动登录也走这条。抽出 SIDE_PANEL_VIEWS 常量复用,并区分登录流程与自动登录。
- widgets/meta-message: deleted 分支从未实现,撤回一条消息后时间线上什么都不剩。
谎报成功 / 判定错误
- tinode-web + personal-settings-view: 改密码不返回 Promise,服务端拒绝时照样弹"密码已更新"。
- group-management-view: 禁言批量读 result.succeeded,但该接口返回的是 {topic, items},
succeeded 恒为 undefined,不管成没成都弹"设置成功"。两个接口结构不同,分开判定。
- workplace-collaboration-view: 会议取消状态写成 'cancelled',后端是 'canceled' 单 l,
会议取消后"取消会议"按钮还挂着。
样式与显示
- base.css: 删掉旧版 .organization-department 的 padding-left !important,它压过按层级算出的
内联缩进,导致部门行全平、只有人员行缩进。
- base.css: 补 .youth-password-gate(原来 0 条规则),PIN 框缺 position:relative 和居中。
- security-support-view: shield_lock 不在经典 Material Icons 里(只有 Symbols),会显示成
字面文本;.risk-info-page 也是 0 条规则,整页渲染成裸项目符号列表。改用 verified_user 并
补上 .risk-dialog。
- contact-management-view: 好友资料页性别直接输出后端枚举,显示成 "male"。
- workplace-collaboration-view: 层级标注两条路径两种格式(树形深三层变"3级"、搜索硬传"三"),
统一成一个 helper。
已记录不在前端修的后端缺口:recallMessage 的 version 只能传 1,因为所有消息读取接口
(messagePayload/messageSearchPayload/mediaMessagePayload/favoriteSnapshot)都不下发 version,
冲突错误里也不回传真实值,被编辑过的消息撤回必然失败。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
c4189646f4
commit
5a07bb4e3b
@@ -396,7 +396,17 @@ function GroupSettings({topic, myUserId, tinode, onToast}) {
|
||||
const t = tinode ? tinode.getTopic(topic) : null;
|
||||
(t ? t.delMessagesAll(false) : Promise.resolve()).then(() => onToast('聊天记录已清除')).catch(err => onToast(err.message || '操作失败'));
|
||||
}}><span>清空聊天记录</span><Icon>chevron_right</Icon></button>
|
||||
<button type="button" onClick={() => onToast('举报入口暂未开放')}><span>投诉/举报</span><Icon>chevron_right</Icon></button>
|
||||
{/* target_type='group' 这条链路早就是通的(tinode-web.jsx 的 handleReportTopic 就在用),
|
||||
这里原来一直是个"暂未开放"的死桩。跟 contact-management-view.jsx 的好友举报入口一样,
|
||||
举报页要的是 target_type/target_id/target_name 三个参数,本文件的 navigate 辅助函数
|
||||
只会写 topic,所以直接拼 hash。 */}
|
||||
<button type="button" onClick={() => {
|
||||
let hash = HashNavigation.setUrlSidePanel('', 'abuse-report-reasons');
|
||||
hash = HashNavigation.addUrlParam(hash, 'target_type', 'group');
|
||||
hash = HashNavigation.addUrlParam(hash, 'target_id', topic);
|
||||
hash = HashNavigation.addUrlParam(hash, 'target_name', (group && group.name) || topic);
|
||||
HashNavigation.navigateTo(hash);
|
||||
}}><span>投诉/举报</span><Icon>chevron_right</Icon></button>
|
||||
</section>
|
||||
<button type="button" className="group-danger" onClick={leave}>退出群组</button>
|
||||
</main>
|
||||
@@ -659,8 +669,19 @@ function MemberPicker({type, topic, onToast}) {
|
||||
setBusy(true);
|
||||
const call = mute ? muteGroupMembers(topic, selected.map(user_id => ({user_id}))) : addGroupAdmins(topic, selected);
|
||||
call.then(result => {
|
||||
const failed = result && result.succeeded ? result.succeeded.filter(s => !s.ok) : [];
|
||||
setToast(failed.length ? `成功设置 ${selected.length - failed.length} 人,${failed.length} 人设置失败` : '设置成功');
|
||||
// 两个批量接口的响应结构不一样,不能用同一套判定:
|
||||
// - 添加管理员 POST .../admins/batch 返回 succeeded[],每项带 ok/reason(im_http.go:5377-5392)
|
||||
// - 添加禁言 POST .../mutes 返回 {topic, items},只有成功的才进 items,
|
||||
// 非法 uid 会被静默 continue(im_http.go:5486-5488),根本没有 succeeded 字段
|
||||
// 原来两条路都读 succeeded,禁言那条恒为 undefined → failed 恒为空 → 不管实际成没成都弹"设置成功"。
|
||||
let failedCount;
|
||||
if (mute) {
|
||||
const done = (result && Array.isArray(result.items)) ? result.items.length : selected.length;
|
||||
failedCount = Math.max(selected.length - done, 0);
|
||||
} else {
|
||||
failedCount = (result && Array.isArray(result.succeeded)) ? result.succeeded.filter(s => !s.ok).length : 0;
|
||||
}
|
||||
setToast(failedCount ? `成功设置 ${selected.length - failedCount} 人,${failedCount} 人设置失败` : '设置成功');
|
||||
window.setTimeout(() => navigate(back, topic), 900);
|
||||
}).catch(err => onToast(err.message || '设置失败')).finally(() => setBusy(false));
|
||||
};
|
||||
@@ -867,8 +888,10 @@ function GroupDescriptionEditor({topic, onToast}) {
|
||||
return <div className="group-page">
|
||||
<Header title="群简介" back="group-profile" backTopic={topic} right="发布" onRight={publish} />
|
||||
<main className="group-edit-text">
|
||||
<textarea autoFocus value={value} maxLength={225} placeholder="请输入群简介内容" onChange={e => setValue(e.target.value)} />
|
||||
<small>{value.length}/225</small>
|
||||
{/* 上限跟后端硬限对齐(im_http.go:4667 超过 200 rune 直接 400 field=description)。
|
||||
参考图标的是 0/225,但那个数字后端不接受——按 225 放行会让用户写满再被服务端打回。 */}
|
||||
<textarea autoFocus value={value} maxLength={200} placeholder="请输入群简介内容" onChange={e => setValue(e.target.value)} />
|
||||
<small>{value.length}/200</small>
|
||||
</main>
|
||||
</div>;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user