联系人页改版调研(2026-09-13)
CONTACTS PAGE
状态:🟢 现行 · 最后核对 2026-09-13 起因:用户反馈**「联系人页面有点太丑了」,并直觉指出「一些圆角似乎不太符合我们技术终端的理念」**。 ⚠️ 结论:那个直觉是对的,而且根因是一条明确可定位的、没做完的设计迁移。
相关:zoot-teardown(ZOOT 前端拆解)· visual-redesign(视觉语言的定义)· 模块使用率与简化候选(
deep_chat84% 最普及)
0. 一句话
联系人页不是"丑",是"用错了语言":
它用 2026-06 那代的 iOS 圆角卡片(clip-path:none + --radius-md=16px)
而项目从 2026-08-26 起已转向 PRTS 终端(硬边 + 斜切 + 发丝线)
—— 主界面这两个列表没跟上那次迁移。
⚠️ 而素材约束(只有 160×160 头像)决定了"更漂亮的卡片"现在做不了 —— 所以真正该做的不是"美化",是换形态 + 跟上语言。
1. 素材约束(先说清天花板)
实测(服务器 frontend/assets/):
| 目录 | 数量 | 尺寸 | 体积 |
|---|---|---|---|
avatars/operators/ |
1182 | 全部 160×160 | 15 M |
sprites/ |
32 | 180×360 | 2.4 M |
operator-bg/ |
3 | 720×1280 | 984 K |
backgrounds/ |
8 | 场景图 | 5.5 M |
⚠️ 干员头像只有 160×160,每干员 3~5 个变体({id}_2.webp …,
见 avatars/operators/_variants.json)。这是全部可用的干员图像。
⚠️ build_operator_bg.py(生成立绘 banner)需要 sprites/{opId}.png,
而只有 32 个干员有 → 它没法批量跑。
(一处我曾判断错的地方:我一度以为 app/prompts/sp 是立绘目录 —— sp 是潜能,不是立绘。)
所以要说服结论:
| 方案 | 可行性 |
|---|---|
| ✅ 横排行(48px 头像 + 文字) | 现实。160 缩到 48 完全够 |
| ✅ 大网格(现状,160 当内容) | 能用,但卡片越大越暴露素材小 |
| ❌ 纵向立绘卡(半身/全身) | 要新素材管线,现在做不了 |
⚠️ 这正好反过来说明现状为什么别扭:它把 160 的方图当大图用(竖卡里占满宽度), 而 160 撑不起"大图"。
2. 触控门槛(一条别照抄的)
⚠️ ZOOT 的 .contact-avatar 是 40×40 —— 低于本项目自己定的门槛。
STATUS.md 记着 2026-09-10 那轮审计的成果:
「触控目标 < 44×44:21 个 → 0」。
| 头像尺寸 | 判定 | |
|---|---|---|
ZOOT .contact-avatar |
40×40 | ❌ 不合格 |
你现在 .list-item-avatar |
48×48 | ✅ 合规 |
改版时别把 48 改小。
3. ZOOT 的做法(实测它的 renderContacts())
ZOOT 是 pywebview 桌面应用,script.js 46k 行 / style.css 27k 行。
[🔍 contact-search] [ 干员 | 外部 | 群聊 ] ← contact-filter-tabs
[标签多选 activeFilters] [信赖排序 trustSort ▲▼]
★ 星标干员 ← starredOps 单独一个分区,在字母分组之前
[头像40] 名字(粗体)
contact-tags(小字副行)
A ← sticky 字母头(contact-letter,position:sticky)
[头像40] 阿米娅 …
[头像40] …
B …
它的四个关键决定
| 决定 | 代码依据 |
|---|---|
| 分组按名字拼音首字母 | grouped[firstChar] + updateContactIndex(sortedLetters, …) |
| 横排行 + 40px 圆头像 | .contact-item{display:flex;align-items:center} / .contact-avatar{border-radius:50%} |
| 名字为主 + 标签副行 | .contact-name{font-weight:bold} / .contact-tags{font-size:.8em} |
| 星标置顶 | starredOps 在字母分组之前渲染 |
它有三个你现在没有的维度
trustSort(按信赖升/降序) —— 「谁跟我最熟」是一等排序activeFilters(标签多选筛选)contactFiltertab(干员 / 外部 / 群聊)
⚠️ 而它的索引条是对的 —— 因为它按名字分组,A-Z 直接对应 "我要找的那个人叫什么"。你的索引条按阵营分组,所以对不上(见 §5)。
4. 外部调研
4.1 PRTS 微件:干员筛选 —— 玩家为 300+ 干员做的答案
筛选维度(**每一维都是多选 + 全选/清除**,不是单选下拉):
职业 8 · 稀有度 1-6 · 位置 2 · 性别 3 · 资历 3 · 获取途径 6 · 词缀 14
势力 40+ · 出身地 26 · 种族 36
六维(物理强度/战场机动/生理耐受/战术规划/战斗技巧/源石技艺适应性)各 5 档
排序方式:实装顺序 · 名称升/降 · **稀有度升/降** · **极简模式**
分页:每页 50 / 100 / 200 / 500
可以把筛选结果**复制成链接分享**
三个可借的点:
| 点 | 为什么 |
|---|---|
极简模式 |
名字暗示"只出头像"的密集模式 —— 正好治"卡片太大" |
| 每一维多选(不是一个下拉单选) | 用户会想"近卫或狙击" |
| 筛选状态可分享(URL 化) | 你现在分享一个筛选结果只能靠截图 |
4.2 移动端筛选 UX 研究(2026)
同构问题("怎么在几百个东西里找到我要的"):
· 组内 OR、组间 AND(用户期望的默认逻辑)
· 每个筛选项旁显示**命中数量** → 避免点进空结果
· ⚠️ **横向工具条不适合移动端**:「用户扫视时容易漏掉,且放不下复杂维度」
→ 移动端应改用**全屏弹层(modal)**
· ⚠️ 长列表**别用无限滚动**(返回时丢位置)→ 用"加载更多"
· 78% 的移动端筛选体验被评为「差到中等」
⚠️ 第 3 条直接约束你的方案:要加筛选,不能做成横向一排 chip。
4.3 游戏 UI:卡片型 vs 列表型
页面没抓到正文(只保留 URL 语义): 卡片型「好看、信息少」vs 列表型「能扫、信息多」 —— 这正是本页要做的取舍。
5. ⚠️ 根因:一条没做完的设计迁移
5.1 你的理念有文档依据
docs/visual-redesign.md:
第 13 行:…(iOS 时代的)「圆角 / 动效 / 文字透明度」;**圆角 16px**、system-ui、低饱和实色…
第 50 行:几何(明日方舟)| 圆角 16px,六边形只在蚀刻章 → **斜切角、角括号、细线几何**
第 60 行:把写死的 **iOS 值**提成 token
"圆角 16px" 被明确列为要摆脱的 iOS 遗留。
5.2 而 --radius-md 就是 16px,且是故意调的
--radius-sm: 10px; --radius-md: 16px; --radius-lg: 22px;
来自 2026-06-25 9cb18111:
fix: 连续圆角——token上调25%替代错误的clip-path
- --radius-sm: 8→10px, --radius-md: 12→16px, --radius-lg: 16→22px
- 删除上次无效的 clip-path: inset(0 round 0px) 代码
+ squircle 效果通过 border-radius token 增大 25% 实现
那一刻是刻意做圆角的(squircle 路线)。后来的终端模式推翻了它。
5.3 时间线 —— 两代语言并存
| 时间 | 提交 | 语言 |
|---|---|---|
| 06-25 | bf798d7f / 9cb18111 |
iOS 连续圆角(squircle,radius → 16px) |
| 08-25 | af42e5bc 主界面卡片化 |
⚠️ 沿用上一代:clip-path:none + radius-md |
| 08-26 | abda2975 终端模式 Phase 1 |
硬边(PC 外框 + 状态栏 + Tab 硬边化) |
| 之后 | 2e21b9d2 Phase 2 |
气泡终端化(硬边 + 边线 + 系统通知条) |
| 之后 | a789eb66 Phase 5.3 |
骨架屏线框 + 光标闪烁 |
主界面在"终端化"开始的前一天被卡片化了 —— 用旧语言做的,然后旧语言被抛弃。
5.4 审计:终端模式覆盖 26 条规则,没覆盖主界面列表
已终端化:状态栏 · tab bar · settings · 气泡 · time-divider · reply-quote ·
skeleton · initiative-search · 系统通知(.rl-card)
未终端化:#messages-list .list-item ← 显式 clip-path:none + radius 16
.contact-group-body .list-item ← 显式 clip-path:none + radius 16
而基础 .list-item 本来是斜切的:
.list-item {
/* 裸切角浮条(同 dock 指示器/设置页斜切浮条) */
clip-path: polygon(0 0, calc(100% - var(--cut-sm)) 0, 100% var(--cut-sm), 100% 100%, 0 100%);
}
联系人页与消息列表是 app 里唯一两处显式关掉斜切角、换成 iOS 圆角的列表。
5.5 ⚠️ 但有一处要更正:头像不算违规
.list-item-avatar { width:48px; height:48px; border-radius: var(--radius-full); } /* 圆 */
.list-item-avatar img { object-fit: cover; border-radius: 4px; } /* 方 + 4px */
圆形头像是图标惯例,不算"圆角惰性"。 别把头像一起"硬边化"。
6. 建议
6.1 修法(一条路,两种力度)
最小:往 body[data-terminal="true"] 那 26 条规则里再加两条,
把那两个列表的 border-radius 归 0、恢复斜切。
彻底:不要再"卡片化",改成终端行:
[48px 圆头像] 名字 [信赖 / 未读]
职业 · 阵营 · 上次聊于 3 天前
───────────────────────────────────── ← 发丝线分隔,不是卡片
斜切角 + 发丝线 + 无圆角,与 dock / 气泡 / 设置页统一。
⚠️ 而这条路同时解决"错配":横排行的信息密度天然比竖卡高 —— 顺带解决"卡片太大、信息太少"(见 §1 的素材约束)。
6.2 三条优先级(按代价)
| # | 做什么 | 依据 | 代价 |
|---|---|---|---|
| 1 | 分组维度可切:阵营 / 职业 / 稀有度 / 最近 | PRTS 的核心就是多维多选;你现在只有阵营 | 小 |
| 2 | 索引条跟着维度走,或去掉 | ZOOT 的索引能用是因为它按名字分组;你的按阵营,语义上就是错的 | 小 |
| 3 | 加"最近聊过 / 在等你的"置顶 | ZOOT 的 starredOps 置顶 + trustSort |
小~中 |
6.3 ⚠️ 一个三家都没有的机会
ZOOT 的联系人列表里没有"谁在等你" —— 它有星标、有信赖排序, 但没有未读 / 待回。PRTS 也没有(它是百科)。
而你有:initiative_messages.json(52% 用户在用、1991 条)、未读、
以及 agent_loop 算出的 scene/action(见 与ZOOT的差距分析 §2.6)。
如果把第一个分区改成:
● 在等你的(2) ← 有未读主动消息
● 今天找过你的 ← 有互动
★ 特别关心
其他(按职业 / 阵营)
那它就从"图鉴"变成"有人气的地方" —— ZOOT 和 PRTS 都做不到, 因为它们没有你那套主动消息 + 存在感模拟。
6.4 加筛选时的一条硬约束
⚠️ 移动端用全屏弹层,不做横向 chip 条(§4.2 的实测结论)。 你已经有 5 个 tab + 1 个搜索框,再横排一排 chip 会挤掉列表本身。