# 贴票台 · 界面优化方案 > 依据当前代码(`reimburse/` 下 26 个 Swift 视图文件)整理,只提优化建议,不改动代码。 > 应用结构:顶层 TabView(`项目管理 AdminView` + `本地贴票 ContentView`),本地贴票内含 `材料扫描 ScanPage`、`人工配对 PairPage`、`已核对 MatchedPage` 三个页面,以及报销单模板、收款信息、报销单预览等弹窗。 --- ## 一、现状盘点 ### 信息架构 | 层级 | 内容 | 现状评价 | |---|---|---| | 顶层 Tab | 项目管理 / 本地贴票 | 应用名为"贴票台"且主功能是本地贴票,但项目管理排在第一个 Tab,与产品定位略有错位 | | 本地贴票 | 侧栏 3 页 + 顶栏统计 + 工具栏 3 按钮 | 结构清晰,导航合理 | | 弹窗 | 报销模板(1000×700)、收款信息(宽 580)、个人报销单(870×620)、预览(动态)、材料预览(1000×700) | 尺寸规格不统一,部分固定尺寸在小屏上有溢出风险 | ### 已做好的地方(应保留) - `MaterialCard` 卡片 + Canvas 贝塞尔连线的配对交互,直观且具辨识度; - `MatchExplanationPopover` 的 100 分制证据评分明细,透明度高; - 拖拽导入的 dropDestination 高亮反馈、`.help()` 悬停提示、`accessibilityLabel` 覆盖较全; - Table + 分组 Picker + 跨分组保留勾选的选择模型,业务逻辑扎实。 --- ## 二、设计系统层面(全局问题) ### 2.1 颜色语义不统一 ⭐ 高优先级 - **现状**:teal 同时承担「品牌色、强调色、成功色、进行中状态」四种语义(侧栏图标、付款金额、匹配满分、通知横幅全是 teal);警告用 orange、错误用 red、AdminView 里成功又用 green。同一语义在两个 Tab 中颜色不同。 - **建议**: 1. 建立语义色枚举(如 `AppTheme.success / warning / danger / accent`),收敛到一处定义; 2. teal 保留为品牌/强调色;成功态用系统 green、警告 orange、危险 red,与项目管理端对齐; 3. 「自动匹配 ≥90 分」与「人工确认」目前用 teal / blue 区分,建议改为统一的徽章组件(形状+图标+色),而非仅靠文字前缀。 - 金额显示可遵循国内财务习惯:发票与付款两列总额(topbar)目前一灰一 teal,对比弱,建议付款列加图标或用同色不同字重,避免用户误读为涨跌。 ### 2.2 深色模式适配缺失 ⭐ 高优先级 - **现状**:`MaterialThumbnail` 用 `Color.white` 背景、`SpreadsheetPreview / ExpenseTemplateManager` 大量 `.background(.white)`、预览区 `.black.opacity(0.04)`。深色模式下会出现大面积刺眼的白色块与文字对比不足。 - **建议**:表格/缩略图区保留白底是合理的(还原纸张观感),但外层容器、边框、占位图标应改用 `Color(nsColor: .textBackgroundColor)` 或材质色;至少验证 `Window` 在深色外观下所有页面无纯白大色块、无低对比 caption。 ### 2.3 排版与间距 token 化 - **现状**:`padding(28) / (26) / (22) / (20)`、`cornerRadius 16 / 13 / 12 / 10 / 9 / 8`、字号 `.title2 / .title3 / .system(size: 34 / 44 / 42)` 混用,页面间无对应关系。 - **建议**:定义间距(4 的倍数:8/12/16/24/28)与圆角(8 卡片内、12 卡片、16 面板)两档规格;页面主标题统一为 `.title2.bold()`(当前 PairPage 是 title3、MatchedPage 是 title2)。 ### 2.4 组件重复实现 - `AdminView` 内自建了 `statusBadge / sectionCard / searchField / emptyState`,`ContentView` 一侧有 `Panel / EmptyPanel`,两套体系风格接近但参数不同。 - **建议**:抽出共享组件库(Badge、Card、SearchField、EmptyState、StatNumber),两个 Tab 复用,减少后续维护分叉。 --- ## 三、页面级优化 ### 3.1 材料扫描 ScanPage | 问题 | 建议 | 优先级 | |---|---|---| | 空工作区时仍显示全部统计面板(全是 0),首屏信息密度高但价值低 | 无材料时展示「导入引导」大空状态:仅保留拖拽卡片 + 模板卡片,统计面板在导入后再出现 | P1 | | 单列滚动太长:导入卡、模板卡、警告、3 个统计卡、2 个金额面板、分类汇总、底部计数,关键动作「前往人工配对」沉底 | ① 统计卡片改为一行 4 列紧凑指标条;②「前往人工配对」在有待配对材料时上浮为醒目入口(如导入完成后的成功横幅内嵌跳转按钮);③ 分类汇总与金额概览合并为一个「金额」面板 | P1 | | 拖拽高亮 `opacity 0→1` 突变 | 加 `withAnimation(.easeInOut(duration: 0.15))`,并让图标/文字随 targeted 状态做位移或缩放微动效 | P2 | | warnings 面板无法关闭、数量多时占据大量空间 | 逐条可关闭 + 超过 3 条折叠为「N 条提醒」;每条加对应的跳转动作(如跳转到识别失败的材料) | P2 | | 三个统计卡数字 `size: 34` 硬编码,视觉与「金额概览」面板重复 | 数字统一走 `StatNumber` 组件(`.rounded` 数字字体 + tabular),金额面板可考虑用小图表(分类占比条)替代纯文本 | P3 | ### 3.2 人工配对 PairPage | 问题 | 建议 | 优先级 | |---|---|---| | 材料多时无法定位:无搜索、无排序切换、无金额/日期过滤 | 每栏顶部加搜索框(文件名/商户/金额)+ 排序菜单(日期/金额/文件名),复用 AdminView 的 `searchField` 样式 | P0 | | 已选 N 张发票 × N 张付款为全连接,选 5×5 时 25 条虚线交叉难以阅读 | 连线改为:hover/聚焦某卡片时只高亮与它相关的连线;或选中的卡片显示序号,连线带序号标签 | P1 | | 只有 Return 键可操作选中,无方向键导航 | 利用现有 `.focusable()` 补充 ↑↓ 移动焦点、Space 选中,形成完整键盘流;说明文字中标注快捷键 | P1 | | 缩略图固定 180pt 网格,看不清细节需逐个开预览 | 增加缩放滑杆(紧凑/标准/大图三档),或双击卡片直接放大预览后可从预览中「选中此材料」 | P2 | | 确认配对按钮位于右上角,与底部的选中信息分离 | 选中数 >0 时在两栏之间显示居中的「确认配对(N×M)」悬浮按钮,动线更短 | P2 | | 空状态文案两栏雷同 | 左栏空时提示「所有发票已处理或已核对」,右栏提示对应付款文案,并可放「撤销上一组」入口 | P3 | ### 3.3 已核对 MatchedPage | 问题 | 建议 | 优先级 | |---|---|---| | 无搜索/过滤:几十组材料时找特定金额/商户困难 | 表格上方加搜索框(金额、商户、日期),支持过滤后再勾选 | P0 | | `MaterialCell` 只列前 2 个文件名链接,「还有 N 份」不可展开 | 点击单元格弹出 popover 缩略图网格,与 PairPage 的卡片视觉统一;>2 份时显示「+N」叠图效果 | P1 | | 底部三行 caption 说明文字(勾选规则、PPT 版式)常驻 | 收敛为一个「导出说明」帮助按钮(`questionmark.circle`)弹出;常用信息已在按钮禁用态与选中计数中体现 | P2 | | 分组多时 segmented Picker 挤压变形 | 分类 ≥5 个时改用 Menu + 下拉,或横向滚动 chips | P2 | | 「操作」列撤销仅图标按钮,与分类 Picker 挤在一列 | 撤销改为 hover 行时出现的 trailing 按钮或右键菜单;分类修改也可考虑双击单元格弹出,减少列宽 | P2 | | 表格无小计:分组切换后看不到当前分组金额合计 | 表格底部(或分组标题旁)显示「本分组合计:发票 X · 付款 Y」 | P2 | | 评分 <90 分为橙色但没有聚合入口(「哪些组需要人工复核」) | 增加「需关注」过滤(评分 <90 或识别失败),配合 P0 搜索框;侧栏「已核对」徽标可显示低分组数量 | P1 | ### 3.4 个人报销单 ExpenseSheet 与预览 | 问题 | 建议 | 优先级 | |---|---|---| | 固定 `frame(width: 870, height: 620)`,小屏(MacBook Air 13" 缩放)可能顶出屏幕 | 改为 `min` 上限策略(参照 `ExpensePreviewSheet` 的 `min(1200, visibleFrame - 80)` 写法) | P1 | | 费用行用途 TextField 无校验反馈(空值、重复用途不易发现) | 空用途时行内灰字提示;重复用途合并提示 | P3 | | 签字岗位 minus.circle 无确认,误触即删 | 删除加轻量确认或支持撤销 toast;输入框加默认岗位快选 Menu | P3 | | `expenseSelectionValid` 失效只显示一行红字,动作指引弱 | 失效时禁用预览按钮并把红字放进醒目横幅,附「返回重新选择」按钮 | P2 | | 预览弹窗已较好;「返回修改」与「确认并保存」并排但视觉主次不明 | 确认按钮保持 prominent 即可;建议保存成功后用横幅告知保存路径并提供「在访达中显示」 | P2 | ### 3.5 报销模板管理 ExpenseTemplateManager / ExpenseTemplateFields | 问题 | 建议 | 优先级 | |---|---|---| | 模板字段调整(列字母/单元格地址)要求用户手工填 "C" "C23",认知门槛高 | ① 输入框支持点选:在左侧表格预览上点击单元格即回填地址(SwiftUI 可用 onTapGesture + 布局信息);② 输入时实时校验字母/地址格式并给出示例反馈 | P1 | | 「查看单元格地址与原文」DisclosureGroup 是纯文本行,对照困难 | 与左侧预览联动:hover 某行时高亮预览中对应行;保留行号列 | P3 | | 未识别完整时只有一行橙色警告,确认按钮置灰原因不直观 | 每个缺失必填项(用途/金额列)在对应输入框旁标橙点,按钮旁注明「还差 N 项」 | P2 | | 模板卡片(ExpenseTemplateSummary)与导入卡片视觉相似,都是 teal 虚线框 | 模板卡片改为普通卡片 + 右上「模板」徽标,虚线拖拽样式只留给导入场景,减少误拖 | P3 | ### 3.6 收款信息 PayeeProfileSheet - 现状已经较好(SecureField + 显隐切换 + 删除确认)。可补充:保存成功后 toast 确认;`PayeeProfileSummary` 中「尾号 XXXX」处加复制按钮。 - 优先级 P3。 ### 3.7 项目管理 AdminView | 问题 | 建议 | 优先级 | |---|---|---| | 单文件 1364 行、50+ 个 `@State`,六个页签全部内联 | 拆分为独立 View 文件(`AdminExpensesTab` 等);这不是纯界面问题,但直接影响后续所有 UI 迭代效率 | P1(技术债) | | 自绘 tab 条(navigationItem)与 macOS 惯例不符,窗口宽度不足时六个标签挤压 | 采用 macOS 15 可用的 `TabView` 新样式或保留但允许横向滚动 + 更多收纳进 Menu | P2 | | 登录页固定 400 宽输入区 + ScrollView,视觉重心偏上 | 居中卡片式布局;服务地址输入记忆历史地址下拉;微信扫码过期自动刷新提示 | P2 | | 报销审批表无分页指示、无列排序 | Table 补充排序(金额/日期);行数多时显示计数 | P2 | | 错误横幅红色整条较突兀 | 收敛为顶部统一通知组件(同本地贴票端的 notice 样式),成功/警告/错误三态 | P2 | --- ## 四、全局交互与反馈 1. **统一通知体系**(P1):目前成功提示是自定义 teal 横幅、错误是 `alert`、Admin 是红横幅。建议统一为三态通知条(成功/警告/错误),支持多条堆叠与自动消失(错误不自动消失),并复用 macOS 15 的 `SymbolEffect`。 2. **菜单栏与快捷键**(P2):已有 ⌘O 导入。建议补充:⌘Z 撤销最近核对组(复用 store.undo)、⌘F 聚焦搜索(配合 P0 搜索项)、⌘1/2/3 切换三个页面、⌘, 打开设置类弹窗(模板/收款信息)。 3. **空状态体系**(P1):EmptyPanel 目前只有图标+标题+说明,可按页面补一个行动按钮(如「选择文件夹」「前往人工配对」),形成闭环。 4. **进度反馈**(P2):busy 时是整页 `disabled` + 顶部进度条;扫描大目录时建议在 Dock 图标显示进度(NSApp.dockTile.progress),并保留页面滚动可读。 5. **危险操作确认**(P2):「清空重置」的 confirmationDialog 文案已很详细,但「恢复内置模板」「删除收款信息」等破坏性操作可统一加「需要输入确认/二次点击」模式中的轻量档:按钮文字改为具体后果(如「移除本机模板副本」)。 --- ## 五、性能相关(影响体感的界面项) | 问题 | 建议 | 优先级 | |---|---|---| | `MaterialPreview` 用 `NSImage(contentsOf:)` 在主线程同步解码整图,大图/PDF 首页可能卡顿 | 改为 `task` 异步加载 + 占位 ProgressView;预览窗口打开先显示缩略图再换全图 | P1 | | `Workspace.sortedMatches`、`matches(for:)`、`groups`、表格行的 `index(of:)` 都是每次重算的 computed 属性,匹配组多时 Table 滚动会重复计算 | 在 Store 层缓存排序结果(matches 变更时失效);`index(of:)` 用 `Dictionary(id→index)` 替代 | P2 | | ScanPage 分类汇总对每个 category 全量 filter(7 × N) | 一次遍历 reduce 成字典再渲染 | P3 | | `MaterialThumbnail` 700px 缩略图生成已异步(好);但 `task(id: previewURL)` 取消旧任务的行为依赖 detached task,建议显式检查 `Task.isCancelled` | 补充取消检查,避免快速滚动时旧图覆盖新图 | P2 | --- ## 六、无障碍与本地化细节 - caption / caption2 + `.tertiary` 的组合在浅色模式下对比度偏低(如「支持图片和 PDF · PDF 处理首页」),建议 tertiary 只用于辅助信息,主说明用 secondary。 - `MatchConfidenceCell` 的颜色(teal/orange)承载了语义,建议在 accessibilityLabel 中补充「评分高/需关注」文字(目前已有百分比,可再补状态词)。 - PairPage 连线是 Canvas 绘制,VoiceOver 完全不可见;建议在读出的标签中描述配对关系(已有 accessibilityLabel 基础,补充「已与 N 张付款连线」)。 --- ## 七、实施路线图 | 阶段 | 内容 | 预期收益 | |---|---|---| | **P0(先做)** | PairPage / MatchedPage 搜索与排序;低分组「需关注」过滤 | 解决材料量大时的核心查找痛点 | | **P1** | 颜色语义统一 + 深色模式适配;语义色/间距 token 化;空状态行动按钮;ScanPage 信息层级重组;MaterialPreview 异步加载;AdminView 拆分 | 视觉一致性与小屏/深色体验 | | **P2** | 统一通知组件;键盘快捷键;表格 MaterialCell 缩略图 popover;分组小计;模板字段可视化点选;拖拽动效;进度 Dock 反馈 | 效率与细节打磨 | | **P3** | 统计卡组件化、金额小图表、模板卡片徽标、签字岗位快选 | 锦上添花 | > 约束提醒:以上均为界面层建议,实施时须保持 README 中列明的业务口径不变(PPT 版式、金额计算口径、勾选保留规则等),尤其 P1 中 ScanPage 重组不要改变「再次导入替换工作区」的确认流程。