# microi-mobile-app-quality 详细参考 1

> 按需读取；本文件由 SKILL.md 的原章节无损拆分。

<!-- microi-progressive:chunk id=microi-mobile-app-quality-008 sha256=8cc961b2ba44763893f3cc09fbfca36668061b6bd75fc8f066904a04622948ff -->
## 4. 重要按钮必须带图标

醒目的主操作必须使用打磨过的图标加文字按钮。

要求：
- 登录、去登录、提交、保存、确认、接单、报修、生成报告、上传照片以及首屏主操作必须包含图标。
- 按钮必须有可见的按下态、加载态、禁用态，并有足够触控高度。
- 主按钮应使用项目品牌渐变或品牌纯色，阴影克制，文字对比度安全。
- `open-type="getPhoneNumber"` 这类小程序原生按钮必须样式化为 `mci-btn`，并移除默认边框。

禁止：
- 首屏、空状态、登录页或固定底栏里出现纯文字主按钮。
- 按钮文字没有垂直居中。
- 异步操作按钮没有加载反馈。

验收：
- 截图检查空状态、登录页和表单提交页。
- 确认主操作有图标、合适的加载文案和按下反馈。

<!-- /microi-progressive:chunk -->
<!-- microi-progressive:chunk id=microi-mobile-app-quality-009 sha256=47248f77c0a41a7fd64f6a255d8c9af356b6768e5167c73a48d60b86f1ab9899 -->
## 4.1 模块列表优先使用声明式业务卡片

Microi.Client 的标准模块移动端不应把 PC 表格字段机械纵向堆叠。优先在
`sys_menu.ViewSchema` 配置 `Scene=Card, Device=Mobile`：

- `AvatarTextField`：头像首字或业务简称；没有头像时才显示序号。
- `TitleField`：唯一主标题，最多两行，不能被右侧金额挤成单字列。
- `TopFields/StatusFields`：顶部状态、分类、阶段标签，使用少量语义色。
- `SubtitleFields`：客户、负责人、供应商等一至两项关联信息。
- `RightFields`：金额、未付、库存等高辨识度数值，支持 `Prefix/Suffix/Tone/Color`。
- `Fields/MetaFields`：正文和编号、创建人、日期等弱化信息。
- `BottomFields`：联系人、跟进、合同、附件等可行动的计数或标签。

要求：

- 卡片内边距通常 12 至 14px，标题、弱信息、金额形成清晰三级层次；不能全卡同字号同颜色。
- 选择框、更多、底部操作和浮动主按钮的触控区域至少 40 至 44px。
- 选中态用边框/底色/勾选状态表达，不通过 `transform` 位移导致列表跳动。
- 只配置字段值和样式时使用 ViewSchema；复杂 HTML 才使用字段 `V8TmpEngineTable`，仍须净化。
- 未配置 Card 视图时兼容 `MobileListFields/CardTitleTagFields/CardBottomTagFields`，不得白屏。
- 卡片引用字段必须进入查询列；关联计数由列表接口批量返回，禁止每张卡片再次请求。

验收：

- 以 375x812、390x844、430x932 至少三种视口检查长标题、空值、大金额、多标签和选中态。
- 检查顶部/右侧/底部多字段与模板字段均能显示，滚动到底后浮动按钮不遮挡最后一张卡。
- 批量选择后出现底部操作条；取消选择、执行按钮、更多菜单均可单手点击。

<!-- /microi-progressive:chunk -->
<!-- microi-progressive:chunk id=microi-mobile-app-quality-010 sha256=979733873bd74cd95b0c84988a8d3709f673a813dc212076e4f02e8b376fb492 -->
## 5. 首屏文字和浮层不得重叠

移动端首屏常组合大首屏区域和悬浮快捷面板。这个布局必须视觉检查，因为过大的中文标题和激进的负边距容易造成难看的换行或遮挡主按钮。

要求：
- 首屏标题必须使用能适配真实中文文案的字号，在常见 375px 和 430px 手机宽度下都要可读。
- 两行中文标题要有足够行高；紧凑业务首屏内不要使用过大的展示字。
- 使用悬浮快捷面板时，首屏底部要给操作区预留内边距，负边距只能轻微覆盖装饰空间。
- 首屏主按钮必须完整可见且可点击，包括阴影和圆角底边。

禁止：
- 首屏标题换行成难看的单字或双字第二行。
- 悬浮面板覆盖登录、报告或提交按钮。
- 通过隐藏按钮或缩小触控区域来解决重叠。

验收：
- 在 375px 和 430px 宽度截图首屏。
- 检查首屏标题、主/次按钮和后续悬浮面板是否裁切或重叠。

<!-- /microi-progressive:chunk -->
<!-- microi-progressive:chunk id=microi-mobile-app-quality-011 sha256=47a21d62bc04740ed19eea3f7c2c816e4c73b9054de63beb16df12468c6a4363 -->
## 5.1 未登录/授权提示必须在可用内容区居中

未登录、未授权、无权限等提示模块不能贴在页面顶部。页面上方有 hero/header，下方有 tabBar 或固定底栏时，提示卡片和“立即登录/去授权”按钮必须在剩余可用内容区上下左右居中。

要求：
- 复用统一组件，例如 `MciAuthPrompt` / `mci-auth-prompt`，不要每个页面复制一套未登录提示样式。
- 页面容器使用 `flex-direction: column` 时，未登录提示外层必须 `flex: 1`、`display:flex`、`align-items:center`、`justify-content:center`，并考虑底部安全区。
- 如果组件放在自定义组件根节点内，业务页仍要提供外层居中 wrapper，避免小程序自定义组件根节点不参与父级 flex 导致卡片贴顶。
- 未登录提示的按钮文字和图标必须在按钮内上下左右居中。

禁止：
- 未登录提示卡片紧贴 header 下方，只在横向居中、纵向不居中。
- 因 tabBar、刘海屏、安全区或固定底栏导致提示卡片视觉中心偏上。

验收：
- 截图检查工作台、消息、我的等未登录态页面，卡片和主按钮必须在 header 与 tabBar/底栏之间的可用区域居中。
- 375px、430px、iOS 刘海屏/灵动岛和 Android 状态栏场景均不得出现贴顶或按钮文字偏移。

<!-- /microi-progressive:chunk -->
<!-- microi-progressive:chunk id=microi-mobile-app-quality-012 sha256=22c9781f49c49419f30df95fac1694cbd1b94bdec99215bb7446ad655b111053 -->
## 5.2 自定义导航页面必须通过安全区与微信胶囊门禁

`navigationStyle: custom` 代表应用接管了系统导航区域，页面壳必须同时负责状态栏、刘海/灵动岛、微信右上角胶囊和底部手势区，不能把这一责任留给业务页面自行估算。

要求：
- 统一页面壳读取 `uni.getWindowInfo()`，旧运行时回退 `uni.getSystemInfoSync()`；顶部至少使用 `statusBarHeight`，底部使用 `safeAreaInsets.bottom` 或 `screenHeight - safeArea.bottom`。
- CSS `env(safe-area-inset-*)` 只能作为 H5 兜底，不能作为微信小程序唯一实现。真实值应注入 `--mci-safe-top`、`--mci-safe-bottom` 等共享变量。
- 微信小程序必须读取 `getMenuButtonBoundingClientRect()` 并为顶部栏预留胶囊右侧宽度；标题、登录、分享、状态按钮与返回按钮都不能和胶囊相交。
- 全屏弹层或工作台的多按钮头部必须纳入同一门禁。若按钮组不能完整放在胶囊左侧，标题和按钮组整体布局到胶囊底边以下；不得让关闭按钮被原生“更多/关闭”覆盖。
- 底部导航、fixed 提交栏、底部弹层和正文滚动区必须消费同一个底部安全变量，正文还要预留完整固定栏高度。
- 审计 `pages.json` 的全部页面：每一个自定义导航路由都必须使用统一安全页面壳。首页通过不代表详情页、表单页、管理页已经通过。

自动化验收：
- 在微信开发者工具至少选择一台 iPhone 刘海/灵动岛机型和一台 Android 机型，逐页访问 `pages.json` 全路由并截图。
- 断言首个可交互元素位于状态栏下方，逐个读取顶部按钮与胶囊的矩形并确认不相交，底部导航/按钮位于手势条上方，最后一条滚动内容可完整显示。
- 发现任意页面被遮挡时，必须修复共享页面壳并重跑全路由；禁止只给当前截图页面增加固定 padding。

<!-- /microi-progressive:chunk -->
<!-- microi-progressive:chunk id=microi-mobile-app-quality-013 sha256=f4e1f01f3dde22120ecba9131b5c738118c0e4aa8131ea83978f3e1d6b4db289 -->
## 5.3 全屏工具页必须遵守返回状态栈

- AI 助手、扫码工作台、全屏预览等独占视口功能需要手机侧滑返回时，优先使用独立路由承载；普通 `position:fixed` 蒙层不能冒充页面历史。
- 返回事件按“键盘/确认框 -> 内部抽屉/筛选 -> 当前全屏页 -> 底层业务页”的顺序消费。关闭按钮与手机返回手势必须得到一致结果。
- 从 Tab 页进入后，第一次返回只能关闭全屏工具页并回到原 Tab；路由栈异常或分享直达时才兜底回首页，禁止退出小程序或跳过原页面。
- 自动化至少覆盖关闭按钮、Android 返回键/`onBackPress`、微信侧滑返回三条路径，并验证返回后原页面和滚动状态仍然可用。

<!-- /microi-progressive:chunk -->
<!-- microi-progressive:chunk id=microi-mobile-app-quality-014 sha256=eaf8ac0e9edadf382a08951d6c43b9ab561dd2a8ee02fc80048fc843e4656033 -->
## 5.4 微信浮动入口必须通过真实事件桥门禁

- UniApp 自定义组件中的浮动按钮、拖拽助手和悬浮客服不得只做 H5 点击测试。必须在微信运行时找到真实组件节点，派发 `touchstart/touchend`，并断言页面栈、弹层状态或业务动作确实变化。
- `touch* / tap` 上的 `.stop/.prevent` 会编译成 `catchtouch* / catchtap`；禁止在可拖拽组件上整组滥用。若出现“看得见但点不动”，先检查生成 WXML 是 `catch*` 还是 `bind*`，再检查事件方法与路由，不要用构建成功替代交互验收。
- 短触与拖动应通过明确位移阈值区分。短触在 `touchend` 完成主动作，拖动只更新位置并持久化，导航失败必须给用户可见反馈。
- 自动化需增加对照按钮：同页普通按钮可点击、浮动入口也可点击，才能确认不是自动化连接或页面整体失效。

<!-- /microi-progressive:chunk -->
<!-- microi-progressive:chunk id=microi-mobile-app-quality-015 sha256=3a65e2da022d493b7d950450a10186d33652ec4225c359d9b3800ef431458525 -->
## 6. 后台菜单必须规划为至少两级

真实业务系统的后台菜单不能简单堆成一批一级菜单。

要求：
- 先创建父级菜单分组，再把子级 CRUD 模块放到对应分组下。
- 相关页面超过三个的业务模块必须有父级目录菜单。
- 建议分组示例：
  - 客户中心：客户、站点、联系人、客户账号绑定。
  - 设备中心：设备台账、设备模板、维保参数。
  - 维保运营：计划、工单、服务记录、维修申请。
  - 报告中心：巡检报告、阅读日志、打印/分享模板。
  - 系统配置：字典、任务、集成设置。
- 使用 Manifest/MCP 时，必须显式包含父模块和子模块。子模块必须设置 `ParentId`。
- dry-run 计划必须列出最终菜单树，不能只列平铺菜单名。
- 如果用户要求通过 MCP 修复已有后台，要真正执行远端工作：读取 `sys_menu`，创建缺失的父级 `SecondMenu` 行，更新现有子菜单 `ParentId` / `Sort`，给管理员角色授权新父菜单，然后回读菜单树。
- 当用户明确要求修改当前 MCP 租户时，不要只把规则写进 skill 就停下。

禁止：
- 把所有生成模块直接创建到根菜单。
- 把客户主数据、工单、报告、日志和设置混在同一级。

验收：
- MCP 生成后回读 `sys_menu` 并确认菜单深度。
- 最终回复必须说明通过 MCP 写入的真实菜单树，以及执行过的权限刷新。

<!-- /microi-progressive:chunk -->
<!-- microi-progressive:chunk id=microi-mobile-app-quality-016 sha256=808d3a4d3e7736566e1dd23c287a9316ea851c3ca212ffe283f49e565a7e0a3d -->
## 7. 移动端页面需要动效，但动效必须有用

移动端产品不应像静态后台表单。

要求：
- 页面首屏、面板、卡片和重要操作区使用克制的入场动画。
- 点击目标要有按下反馈。
- 骨架屏应有轻微流光或脉冲。
- 装饰动效幅度要小，不能干扰任务完成。
- 支持时尊重减少动态效果偏好。

禁止：
- 整个应用没有交互反馈。
- 在密集业务列表上使用重度循环动画。
- 动效导致布局位移或文字重叠。

验收：
- 浏览器或设备检查确认卡片、面板、按钮或骨架屏有可见但克制的动效。
- 没有动画导致横向溢出、文字裁切或固定栏抖动。

<!-- /microi-progressive:chunk -->
<!-- microi-progressive:chunk id=microi-mobile-app-quality-017 sha256=15d415351b8e3a7700d56cab2b39079494b06265b7d648636d1e511db1892161 -->
## 8. 登录页必须是直接登录界面

登录页不能强迫用户先在两个身份标签之间切换才能登录。

要求：
- H5/App 提供一个账号或手机号 + 密码表单。除非后端明确支持并要求第二条路径，否则不要把独立手机号客户登录表单和账号密码表单并列展示。
- 微信小程序默认使用 `<button open-type="getPhoneNumber">` 的手机号授权登录。账号密码登录可以作为次级兜底，但必须折叠或弱化，不能和手机号授权并列成第二套完整登录系统。
- 微信小程序手机号快捷登录必须把返回的手机号 `code` 传给后端；当后端需要 OpenId/UnionId 时，还必须调用 `uni.login()` 获取新的 `LoginCode`。
- H5/App 兜底只有在后端支持手机号登录时才提供手动手机号输入。
- 登录文案要清楚：账号/手机号 + 密码是一条路径；微信手机号授权是小程序默认路径。
- 登录页不要展示当前租户、OsClient、移动端构建版本、API host、调试版本块等内部实现信息。

禁止：
- 除非用户明确要求，否则把员工/客户身份标签作为主登录模型。
- 同屏展示两套完整登录系统，例如“账号 + 密码”和“手机号输入登录”并列。
- 假设前端可以从 `getPhoneNumber` 直接读取微信手机号；现代微信返回的是 code。
- 目标是微信小程序时，只把手机号登录实现成文本输入。
- 向终端用户展示租户、版本或调试块。

验收：
- 实现前检查标准参考 `microi.uniapp/src/pages/login/index.vue`。
- 测试账号登录路径和手机号登录按钮渲染。
- 构建 H5 和微信小程序目标。

<!-- /microi-progressive:chunk -->

## 平台方通用 App 的登录连接器例外

“登录页不要展示 ApiBase / OsClient”仍是 H5、小程序与客户专属 App 的默认规则。只有用户明确要求、并由平台方提交一个聚合多租户的通用 `APP-PLUS` 安装包时，才允许把平台连接器作为登录页次级折叠卡片；必须保留清晰的当前连接摘要、固定协议下拉、HTTP 风险提示、带图标的应用按钮、连接 loading/disabled 状态，并在展开后通过滚动和安全区保证小屏设备不裁切登录操作。连接器不能伪装成调试面板，也不能展示 Token、密码或其它秘密。
