第六轮深度测试:7个并行agent覆盖全部11章,修复21处前端问题
Ch1-2: 用户名注册邀请码错误改为友好文案(原为原始TypeError泄露给用户); 企业注册页扫描图标补上点击反馈。 Ch3-4: 发名片消息补传typeHint(此前被后端误分类成text,搜索/历史摘要显示成JSON); 文件卡片按mime大类区分图标颜色(此前所有文件都是同一个灰色通用图标); 表情面板补上专属退格/发送按钮(浅蓝禁用/深蓝启用两态);订正一处与截图不符的代码注释。 Ch5-6: 群聊设置页"群成员"补上可点击的完整成员列表页(复用MemberPicker字母分组); 群文件页去掉无依据的硬编码"/10GB"容量上限显示。 Ch7: 收藏点击图片/视频缩略图补上预览;"查找聊天记录-链接"tab补上定位回原消息; 文件下载页返回入口按来源tab纠正;批量转发成功计数改为按目标会话去重 (原公式混用"收藏条数"和"目标会话数"两个维度,混选多条收藏转发时可能显示负数); 收藏/视频tab切换补上URL路由同步。 Ch8: topic.onInfo在通话期间补上事件转发(此前通话中"对方正在输入"和已读回执静默失效); 主叫等待接听页补上"正在等待对方接受邀请…"文案;语音通话最小化胶囊的还原按钮移出 拖拽区域(原来嵌在draggable内部,指针捕获可能吞掉点击);修复中等窗宽(641-750px)下 本地摄像头预览遮挡"切换摄像头"按钮的CSS重叠。 Ch9: 部门树展开时跳过child_count=0的注定空请求;月报字数上限从硬编码1000改为读取 后端真实的10000;发起会议补上"结束时间需晚于开始时间"的本地校验和更具体的报错; 日报/月报统计接口补上department_id透传(此前包装函数注释断言后端会忽略,实际已支持)。 Ch10-11: URL风险检测补上g标志(原来一条消息只检查第一个链接,第二个链接哪怕是高危 也不会被拦截);补上"安全教育"设置入口(原功能完整实现但零入口,需手改hash才能打开); 修改密码按钮补上busy态反馈;青少年模式PIN页补上"忘记独立密码"入口(后端已支持重置, 前端未接);"关于我们"补上"联系我们"行。 7个agent同时对全部11章存量后端问题条目逐一复核,结论详见待整理的后端问题清单更新 (Ch1-2发现一条P0级后端问题:验证码固定值"123456",已记录未修复,不在此commit修改 im_be仓库范围内)。 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
d42591284f
commit
f42b8536e5
@@ -64,7 +64,7 @@ export const GROUP_MANAGEMENT_VIEWS = [
|
||||
'group-list', 'group-empty', 'group-invite', 'group-events', 'group-join-requests',
|
||||
'group-new-request', 'group-announcement', 'group-summary', 'group-announcement-detail',
|
||||
'group-settings', 'group-profile', 'group-avatar', 'group-nickname', 'group-description',
|
||||
'group-my-nickname', 'group-role-message', 'group-qr', 'group-governance', 'group-admins',
|
||||
'group-my-nickname', 'group-members', 'group-role-message', 'group-qr', 'group-governance', 'group-admins',
|
||||
'group-admin-add', 'group-admin-success', 'group-admin-partial', 'group-admin-removed',
|
||||
'group-mute-add', 'group-muted', 'group-owner-select', 'group-owner-confirm', 'group-files',
|
||||
'group-file-upload'
|
||||
@@ -455,14 +455,14 @@ function GroupSettings({topic, myUserId, tinode, onToast}) {
|
||||
<span><strong>{group.name}</strong><small>{group.description || '群主很懒,还没有群简介'}</small></span>
|
||||
<Icon>chevron_right</Icon>
|
||||
</button>
|
||||
{/* 参考图 docs/images/群聊设置.png:一行"群成员"标题(右侧带人数),下面是等宽网格,最后一格
|
||||
是"添加"。这里刻意不把标题行右侧做成可点链接、也没有参考图里那个"删除"格——本分支没有
|
||||
通用的群成员列表页,也没有移除成员的页面,与其跳到语义不对的页面或放一个点了没用的格子,
|
||||
不如先只展示信息,缺口另行记录。 */}
|
||||
{/* 参考图 docs/images/群聊设置.png:"群成员"标题行右侧是可点的"查看{N}群成员 >",现在真的有
|
||||
落地页了(GroupMembers,复用 MemberPicker 同款字母分组/搜索,只读不可多选)。参考图里最后
|
||||
一格的"删除"(移除成员)没有做——那是一个独立的、需要产品先确认交互细节的成员管理功能,
|
||||
不属于这条"看不到全部成员"缺口本身。 */}
|
||||
<section className="group-member-section">
|
||||
<div className="group-member-section-title">
|
||||
<strong>群成员</strong><small>共 {group.member_count} 位成员</small>
|
||||
</div>
|
||||
<button type="button" className="group-member-section-title" onClick={() => navigate('group-members', topic)}>
|
||||
<strong>群成员</strong><small>共 {group.member_count} 位成员</small><Icon>chevron_right</Icon>
|
||||
</button>
|
||||
<div className="group-member-grid">
|
||||
{members.slice(0, 9).map(item => <button type="button" key={item.user_id} onClick={() => navigateToProfile(topic, item.user_id, item)}>
|
||||
<Avatar item={item} icon="person" /><small>{item.group_nickname || item.display_name}</small>
|
||||
@@ -735,6 +735,38 @@ function AdminList({topic, onToast}) {
|
||||
</div>;
|
||||
}
|
||||
|
||||
// 参考图 群聊设置.png 里"群成员"标题行是可点的("查看{N}群成员 >"),但 GroupSettings 一直没有
|
||||
// 对应的落地页——群成员超过预览网格的9人就完全看不到其余成员。复用 MemberPicker 已经在用的
|
||||
// 字母分组+搜索逻辑,去掉多选/提交,只读展示每个人的角色。
|
||||
function GroupMembers({topic, onToast}) {
|
||||
const [members, setMembers] = React.useState(() => (topic && readCachedData('group-members:' + topic)) || null);
|
||||
const [query, setQuery] = React.useState('');
|
||||
const [loadError, setLoadError] = React.useState(false);
|
||||
React.useEffect(() => {
|
||||
if (!topic) return;
|
||||
getGroupMembers(topic).then(data => {
|
||||
const items = data.items || [];
|
||||
writeCachedData('group-members:' + topic, items);
|
||||
setMembers(items);
|
||||
}).catch(() => { onToast('加载失败'); setLoadError(true); });
|
||||
}, [topic]);
|
||||
const visible = (members || []).filter(m => (m.group_nickname || m.display_name || '').includes(query));
|
||||
const groups = groupBy(visible, m => alphaInitial(m.group_nickname || m.display_name));
|
||||
const roleLabel = role => role === 'owner' ? '群主' : role === 'admin' ? '管理员' : '';
|
||||
return <div className="group-page">
|
||||
<Header title="群成员" back="group-settings" backTopic={topic} />
|
||||
<Search value={query} onChange={setQuery} placeholder="搜索群成员" />
|
||||
<main className="group-member-picker">
|
||||
{members === null ? <p className="group-inline-empty">{loadError ? '成员列表加载失败' : '加载中…'}</p> : groups.map(([letter, list]) => <React.Fragment key={letter}>
|
||||
<div className="group-section-title">{letter}</div>
|
||||
{list.map(item => <button type="button" key={item.user_id} onClick={() => navigateToProfile(topic, item.user_id, item)}>
|
||||
<Avatar item={item} icon="person" /><span><strong>{item.group_nickname || item.display_name}</strong>{roleLabel(item.role) ? <small>{roleLabel(item.role)}</small> : null}</span>
|
||||
</button>)}
|
||||
</React.Fragment>)}
|
||||
</main>
|
||||
</div>;
|
||||
}
|
||||
|
||||
// Shared alphabetically-grouped multi-select picker for admin-add / mute-add (same visual component,
|
||||
// three purposes, per docs/images/添加管理员.png vs .../添加管理员-设置成功(1).png which turned out to
|
||||
// actually be the mute-add variant of the identical layout).
|
||||
@@ -986,7 +1018,9 @@ function GroupFiles({topic, tinode, onToast}) {
|
||||
<button type="button" onClick={() => pick('video')}><Icon>play_circle_outline</Icon>上传视频</button>
|
||||
</div> : null}
|
||||
<Search value={queryInput} onChange={setQueryInput} placeholder="搜索文件名或者上传者" />
|
||||
<p className="group-inline-hint">共{(files || []).length}个文件(已使用{gb(usedBytes)}GB/10GB)</p>
|
||||
{/* "/10GB" 分母曾经是前端编的——群文件相关接口(含治理/设置)全仓库没有任何配额字段下发,
|
||||
显示一个无依据的上限数字比不显示更容易误导人,改成只报告已用量。 */}
|
||||
<p className="group-inline-hint">共{(files || []).length}个文件(已使用{gb(usedBytes)}GB)</p>
|
||||
<main className="group-file-list">
|
||||
{uploading ? <article className="uploading">
|
||||
<span className="group-file-type" style={{backgroundColor: '#8a8f98'}}><Icon>upload_file</Icon></span>
|
||||
@@ -1108,6 +1142,7 @@ export default function GroupManagementView({view, hashParams, myUserId, tinode,
|
||||
else if (view === 'group-nickname') content = <GroupNameEditor topic={topic} onToast={setToast} />;
|
||||
else if (view === 'group-description') content = <GroupDescriptionEditor topic={topic} onToast={setToast} />;
|
||||
else if (view === 'group-my-nickname') content = <MyNicknameEditor topic={topic} myUserId={myUserId} onToast={setToast} />;
|
||||
else if (view === 'group-members') content = <GroupMembers topic={topic} onToast={setToast} />;
|
||||
else if (view === 'group-qr') content = <GroupQr topic={topic} onToast={setToast} />;
|
||||
else if (view === 'group-governance') content = <Governance topic={topic} tinode={tinode} onToast={setToast} />;
|
||||
else if (['group-admins', 'group-admin-success', 'group-admin-partial', 'group-admin-removed'].includes(view)) content = <AdminList topic={topic} onToast={setToast} />;
|
||||
|
||||
Reference in New Issue
Block a user