Files
Neo-ZQYY/apps/miniprogram - 副本/doc/migration-guide.md

25 KiB
Raw Blame History

H5 → 微信小程序迁移实施指南

本文档是"规则化迁移 + AI 辅助 + 视觉验收"的完整实施方案。 适用于将 docs/h5_ui/pages/*.htmlTailwind CSS + 原生 JS迁移为原生微信小程序页面。 试点页面:notes(备注记录)— 中低复杂度,验证流程可行性。


一、核心原则

这是迁移工程,不是生成工程。

  • 规则先行标签映射、样式换算、事件转换有明确规则表AI 按规则执行
  • 原型忠实H5 源码 + computed-styles + 截图是唯一视觉真相,不凭想象
  • 组件化承接TDesign 组件优先,自定义组件按 design.md 接口定义
  • 视觉回归兜底:每个页面转换后必须与 H5 截图逐项对比
  • 增量验证:一个页面走完全流程再推进下一个

二、迁移流水线6 步)

┌─────────────┐    ┌─────────────┐    ┌─────────────┐
│ Step 1       │    │ Step 2       │    │ Step 3       │
│ 输入物冻结   │───▶│ 迁移审计     │───▶│ 规则化转换   │
│ (HTML/CSS/   │    │ (风险点+     │    │ (标签/样式/  │
│  截图/交互)  │    │  替代方案)   │    │  事件/路由)  │
└─────────────┘    └─────────────┘    └─────────────┘
                                            │
┌─────────────┐    ┌─────────────┐          ▼
│ Step 6       │    │ Step 5       │    ┌─────────────┐
│ 验收签收     │◀───│ 差异修复     │◀───│ Step 4       │
│ (通过/打回)  │    │ (截图diff+   │    │ 编译验证     │
│              │    │  定点修复)   │    │ (开发者工具) │
└─────────────┘    └─────────────┘    └─────────────┘

三、Step 1输入物冻结

每个页面转换前AI 必须加载以下材料(缺一不可):

序号 材料 路径 作用
1 H5 源码 docs/h5_ui/pages/<page>.html 结构与样式的唯一真相
2 自定义 CSS docs/h5_ui/css/<page>.css(如有) 非 Tailwind 的自定义样式
3 交互说明 docs/h5_ui/interactions/<page>.md 状态变量、操作响应、页面状态枚举
4 设计 Token docs/h5_ui/design-tokens.json 颜色、间距、圆角、字号、阴影
5 图标映射表 docs/h5_ui/icon-mapping.md 图标处理方案TDesign/自定义/Emoji
6 计算样式 docs/h5_ui/computed-styles.json 中对应 key 精确的 px 数值(如有)
7 截图 docs/h5_ui/screenshots/<page>--*.png 视觉校验基线
8 转换规范 .kiro/steering/miniprogram-h5-conversion.md 强制规则
9 避坑指南 apps/miniprogram/doc/h5-to-miniprogram-pitfalls.md 22 个高频坑点

加载顺序

1. 转换规范 + 避坑指南(规则层)
2. 设计 Token + 图标映射(全局资源层)
3. H5 源码 + 自定义 CSS + 计算样式(页面源码层)
4. 交互说明(行为层)
5. 截图(视觉校验层)

四、Step 2迁移审计

读取 H5 源码后,先输出《迁移审计报告》,不写代码。

审计清单

审计项 输出内容
A. 页面结构 主要区域划分header/list/card/footer组件化边界建议
B. CSS 风险点 不支持的 CSS 特性清单 + 替代方案伪元素、backdrop-filter、clip-path 等)
C. Tailwind 展开 关键元素的 Tailwind 类 → WXSS 属性映射(取 computed-styles 精确值)
D. SVG/图标 内联 SVG 清单 + 处理方式TDesign/导出图片/Emoji
E. JS 交互 DOM 操作 → setData 映射表
F. 外部依赖 CDN 资源Tailwind/Google Fonts的本地化方案
G. 缺失信息 需要用户补充的材料清单

审计报告格式

## <page-name> 迁移审计报告

### A. 页面结构
- 顶部导航栏sticky含返回按钮 + 标题)
- Tab 切换区(客户备注 / 助教备注)
- 备注列表wx:for 渲染,每条含文本 + 标签 + 时间)

### B. CSS 风险点
| 风险点 | 原因 | 影响 | 替代方案 | 验收方式 |
|--------|------|------|---------|---------|
| ... | ... | ... | ... | ... |

### C. 关键样式映射
| 元素 | Tailwind 类 | computed 值 | WXSS |
|------|------------|-------------|------|
| 备注卡片 | bg-white rounded-2xl p-4 shadow-sm | ... | ... |

### D. 图标处理
| 图标 | H5 实现 | 小程序方案 |
|------|---------|-----------|
| 返回箭头 | 内联 SVG | <t-icon name="chevron-left" /> |

### E. 交互映射
| H5 操作 | DOM 实现 | 小程序实现 |
|---------|---------|-----------|
| Tab 切换 | classList.toggle | setData({ activeTab }) + wx:if |

### F. 外部依赖
- Tailwind CDN → 手写 WXSS
- Google Fonts → 系统字体(-apple-system

### G. 缺失信息
- (列出需要用户补充的内容)

用户确认审计报告后,才进入 Step 3。


五、Step 3规则化转换

5.0 SVG 导出规则(强制,在转换前执行)

H5 原型中所有内联 <svg> 必须单独导出为 .svg 文件,小程序中通过 <view> + <image> 引用。

规则

  1. 扫描目标页面 H5 源码中的所有 <svg> 标签
  2. 每个 SVG 导出为独立文件,存放路径:apps/miniprogram/miniprogram/assets/icons/<name>.svg
  3. 命名规则:icon-<用途>.svg(如 icon-wechat.svgicon-clipboard.svgLogo 类用 logo-<名称>.svg
  4. 导出时保留原始 viewBoxfillpath 等属性,确保渲染一致
  5. 小程序中引用方式:<image src="/assets/icons/<name>.svg" mode="aspectFit" />,必须指定宽高
  6. 如果 TDesign 图标库有语义等价的图标(如返回箭头 → chevron-left),优先用 <t-icon>,不导出 SVG
  7. 审计报告的「D. 图标处理」栏必须列出每个 SVG 的处理决策TDesign / 导出 SVG / Emoji

为什么不用 TDesign icon 属性

TDesign <t-button icon="xxx">icon 属性只支持 TDesign 内置图标名。微信 logo 等第三方品牌图标不在 TDesign 图标库中,传入无效名称会导致图标不显示。因此品牌图标、复杂自定义图标一律导出 SVG 文件,用 <image> 引用。

已导出清单

文件名 来源页面 说明
logo-billiard.svg login 台球 Logo已有
icon-wechat.svg login 微信品牌图标
icon-clock-circle.svg reviewing 时钟主图标stroke 风格TDesign 无等价)
icon-forbidden.svg no-permission 禁止符号主图标stroke 风格TDesign 无等价)

每次迁移新页面时,将新导出的 SVG 追加到此清单。

5.1 标签映射(硬性规则)

HTML WXML 说明
<div> <view> 容器
<span> / <p> <text> 文本必须用 <text> 包裹
<a> <navigator>bindtap + wx.navigateTo
<img> <image mode=""> 必须指定 mode 和宽高
<svg> 内联 <image src="xx.svg"><t-icon> 不支持内联 SVG
<ul>/<li> <view wx:for> 无列表语义标签
<button> <t-button> TDesign 优先
<input> <t-input> TDesign 优先
<select> <t-picker> 完全不同的交互
<h1>~<h6> <text> + 样式类 无语义标题标签

严禁在 WXML 中使用 HTML 标签。

5.2 样式转换规则

一屏页面布局模式(强制)

对于 H5 原型中一屏显示完毕、不需要滚动的页面(如 login、reviewing、no-permission 等),必须使用以下布局模式:

.page {
  height: 100vh;              /* 固定一屏高度,不用 min-height */
  display: flex;
  flex-direction: column;
  box-sizing: border-box;     /* padding-top 从 100vh 中扣除 */
  overflow: hidden;           /* 防止内容溢出产生滚动 */
  /* padding-top 由 JS statusBarHeight 动态设置 */
}

.hero { flex: 1; }            /* 主内容区域占满剩余空间,内部垂直居中 */
.bottom-area { /* 固定高度,不参与 flex 伸缩 */ }

关键点:

  • height: 100vh + box-sizing: border-box → padding-top状态栏从总高度中扣除不会导致底部溢出
  • 主内容区域用 flex: 1 自适应剩余空间,内部用 justify-content: center 垂直居中
  • 底部操作区固定高度,不设 flex
  • 如果页面内容可能超过一屏(如 apply 的表单页),改用 min-height: 100vh + 允许滚动

状态栏适配(强制)

所有 navigationStyle: "custom" 的页面,必须:

  1. TS 中 onLoad 获取 wx.getSystemInfoSync().statusBarHeight
  2. WXML 中 .pagestyle="padding-top: {{statusBarHeight}}px;"
  3. WXSS 中 .pagebox-sizing: border-box;(确保 padding 不增加总高度)
  4. 禁止使用 env(safe-area-inset-top)(部分机型不生效)

rpx 换算

H5 px 值 × 2 = rpx 值(基于 375px → 750rpx
Tailwind spacing: 1 unit = 4px = 8rpx

Tailwind → WXSS 速查表

Tailwind WXSS
p-4 padding: 32rpx;
px-4 padding-left: 32rpx; padding-right: 32rpx;
m-3 margin: 24rpx;
gap-3 gap: 24rpx;
space-y-3 子元素 margin-top: 24rpx;(首个除外)
rounded-2xl border-radius: 32rpx;
text-sm font-size: 28rpx;
text-base font-size: 32rpx;
text-xs font-size: 24rpx;
font-medium font-weight: 500;
font-semibold font-weight: 600;
leading-relaxed line-height: 1.625;
shadow-sm box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.05);
flex display: flex;
flex-col flex-direction: column;
items-center align-items: center;
justify-between justify-content: space-between;
flex-1 flex: 1;
min-h-screen min-height: 100vh;
sticky top-0 position: sticky; top: 0;
z-10 z-index: 10;
bg-white background-color: #ffffff;
bg-gray-1 background-color: #f3f3f3;
text-gray-13 color: #242424;
text-gray-6 color: #a6a6a6;
border-b border-gray-2 border-bottom: 2rpx solid #eeeeee;
backdrop-blur-sm 不支持,改为 rgba() 半透明

颜色值参照 design-tokens.json

primary: #0052d9    primary-light: #ecf2fe
success: #00a870    warning: #ed7b2f    error: #e34d59
gray-1 ~ gray-13: 见 design-tokens.json

不支持的 CSS 替代方案

CSS 特性 替代方案
backdrop-filter: blur() background: rgba(255,255,255,0.95);
* 通配符选择器 逐个元素设置
::before / ::after 额外 <view> 元素模拟(小程序部分支持伪元素,复杂场景用 DOM
远程 @font-face wx.loadFontFace() 或系统字体
clip-path 改为图片或 CSS 渐变近似
blur-xlTailwind 去掉或改为纯色

5.3 事件转换规则

H5 小程序 说明
onclick="fn()" bindtap="fn" 不能传参
onclick="fn(id)" data-id="{{id}}" bindtap="fn" dataset 传参
addEventListener 不支持 只能声明式绑定
event.target.value e.detail.value 取值路径不同
event.preventDefault() catchtap catch 前缀阻止冒泡
classList.toggle('active') setData({ active: !this.data.active }) + class="{{active ? 'on' : ''}}"
innerHTML = '...' setData({ content: '...' }) + WXML 数据绑定
history.back() wx.navigateBack()
window.location.href wx.navigateTo({ url: '...' })
localStorage.setItem wx.setStorageSync
alert() / confirm() wx.showToast() / wx.showModal()

5.4 路由转换规则

场景 小程序 API 说明
普通页面跳转 wx.navigateTo 保留当前页,页面栈 +1
TabBar 页面跳转 wx.switchTab 必须用 switchTabnavigateTo 会报错
替换当前页 wx.redirectTo 关闭当前页
清空页面栈 wx.reLaunch 登录/登出场景
返回上一页 wx.navigateBack 页面栈 -1

TabBar 页面task-list、board-finance、my-profile

5.5 转换执行顺序

1. 创建页面 4 文件骨架(.wxml / .wxss / .ts / .json
2. 在 .json 中注册 usingComponentsTDesign 组件 + 自定义组件)
3. 转换 WXML 结构HTML 标签 → WXML 标签,保持层级一致)
4. 转换 WXSS 样式Tailwind → 手写 WXSS取 computed-styles 精确值)
5. 转换 TS 逻辑DOM 操作 → setData事件绑定 → bindtap
6. 设置 Mock 数据(贴近真实 API 格式,标记 TODO
7. 处理三态loading / empty / normal / error

六、Step 4编译验证

在微信开发者工具中检查:

检查项 合格标准
WXML 编译 无编译错误(特别注意 .toFixed() 等 JS 方法不能在 WXML 中使用)
WXSS 编译 无警告(检查不支持的选择器)
控制台 无 JS 运行时错误
图片加载 无 404/500 错误(所有 /assets/ 引用的文件必须存在)
组件注册 无 "component not found" 警告
路由跳转 无 "navigateTo:fail" 错误

七、Step 5差异修复

7.1 截图对比

用户在微信开发者工具中截图,与 docs/h5_ui/screenshots/<page>--*.png 逐项对比。

7.2 差异追踪表

差异描述 位置 严重度 可能原因 修复方案 修复代价
卡片圆角偏小 备注卡片 P1 rpx 换算错误 改为 32rpx
标签颜色偏差 tag-coach P1 颜色值不准确 取 computed-styles 精确值
间距不一致 列表间距 P0 space-y-3 未正确转换 改为 margin-top: 24rpx

7.3 修复原则

  • 只输出需要改动的文件和改动段落diff 风格),不整文件重贴
  • 优先使用 flex/盒模型的确定性方案,不用"碰运气"的魔法数
  • rpx 换算统一,严禁同一类间距混用 rpx 和 px
  • 每次修复后重新编译验证,防止修 A 坏 B

八、Step 6验收签收

逐项验收清单

验收项 怎么看 合格标准 常见失败表现
布局结构 对比截图整体布局 区域划分、层级关系一致 元素错位、层级混乱
间距系统 对比元素间距 与 H5 截图一致±4rpx 容差) 间距过大/过小
字体系统 对比字号、字重、行高 与 design-tokens 一致 字号偏差、行高不对
颜色 对比背景色、文字色、边框色 与 design-tokens 一致 颜色偏差
圆角 对比卡片、按钮圆角 与 design-tokens 一致 圆角过大/过小
阴影 对比卡片阴影 有阴影且不突兀 无阴影或阴影过重
图标 对比图标位置、大小、颜色 TDesign 图标正确显示 图标缺失或错位
交互完整性 按交互说明逐项操作 所有操作有正确响应 点击无反应、状态不切换
三态处理 切换 loading/empty/error 三种状态均有对应 UI 缺少空状态或加载态
安全区 刘海屏设备检查 内容不被刘海遮挡 顶部内容被裁切

九、试点页面notes备注记录

选择理由

  • 中低复杂度:简单列表 + Tab 切换 + 标签样式
  • 覆盖核心转换场景Tailwind → WXSS、事件绑定、列表渲染、三态处理
  • 有完整材料HTML + CSS + 交互说明 + 截图
  • 验证周期短:预计 1 轮即可完成

需要加载的材料

docs/h5_ui/pages/notes.html
docs/h5_ui/css/notes.css
docs/h5_ui/interactions/notes.md
docs/h5_ui/design-tokens.json
docs/h5_ui/icon-mapping.md
docs/h5_ui/screenshots/notes--*.png
docs/h5_ui/computed-styles.jsonnotes key如有

预期产出

miniprogram/pages/notes/notes.wxml
miniprogram/pages/notes/notes.wxss
miniprogram/pages/notes/notes.ts
miniprogram/pages/notes/notes.json

十、页面迁移优先级

按模块分组,每组内按复杂度从低到高排列:

批次 页面 复杂度 说明
试点 notes 验证流程
第 1 批 login, apply, reviewing, no-permission 认证流程(已有实现,需重写)
第 2 批 task-list, task-detail 中-高 任务模块核心
第 3 批 task-detail-callback, task-detail-priority, task-detail-relationship 任务详情变体
第 4 批 performance, performance-records 绩效模块
第 5 批 board-finance, board-customer, board-coach 看板模块(筛选+吸顶+复杂布局)
第 6 批 customer-detail, customer-service-records, coach-detail 详情模块
第 7 批 chat, chat-history 对话模块
第 8 批 my-profile 个人中心

十一、需要用户补充的内容

P0阻塞试点

材料 状态 说明
notes 页面截图 ⚠️ 待确认 确认 docs/h5_ui/screenshots/notes--*.png 是否存在
notes 的 computed-styles ⚠️ 待确认 确认 computed-styles.json 中是否有 notes key

P1提升还原度

材料 状态 说明
Banner 背景图片 缺失 icon-mapping.md 规划了用 Playwright 截取 Banner但实际图片未导出
AI 图标图片 缺失 icon-ai-float.png / icon-ai-inline.png / icon-ai-badge.png 需要从 H5 截取
其他页面的 computed-styles ⚠️ 部分缺失 当前仅 5 个页面有数据,其余 19 个缺失

P2后续批次需要

材料 状态 说明
home-settings 交互说明 缺失
ai-icon-demo 交互说明 缺失

十二、与现有文档的关系

文档 职责 本指南的关系
miniprogram-h5-conversion.mdsteering 强制转换规则 本指南引用其规则,不重复
h5-to-miniprogram-pitfalls.md 避坑清单 本指南引用其坑点,不重复
h5-input-material-guide.md 输入材料准备规范 本指南引用其格式要求
howtodo.md 6 阶段工作流提示词 本指南是其具体化实施版本
design.mdspec 组件接口 + 数据模型 本指南引用其组件定义

十三、实战踩坑记录

本节记录迁移过程中实际遇到的坑和解决方案,按发现时间倒序排列。每次迁移新页面遇到新坑时追加。

P1WXML 中不能调用 JS 方法

  • 触发页面:所有页面
  • 现象:{{price.toFixed(2)}} 编译报错
  • 原因WXML 模板表达式不支持 JS 方法调用(.toFixed().map().filter() 等)
  • 解决:创建 WXS 模块 utils/format.wxs,在 WXML 中 <wxs src="..." module="fmt" />,用 {{fmt.toFixed(price, 2)}}

P2TabBar 页面不能用 navigateTo

  • 触发页面task-list、board-finance、my-profile
  • 现象:navigateTo:fail 静默失败
  • 原因TabBar 页面必须用 wx.switchTabnavigateTo / redirectTo 均无效
  • 解决:跳转前判断目标是否 TabBar 页面,是则用 switchTab

P3图片 500 错误(资源文件不存在)

  • 触发页面:所有引用 /assets/images/*.png/assets/icons/icon-ai-*.png 的页面
  • 现象:控制台大量 500 错误
  • 原因:代码引用了不存在的图片文件
  • 解决:用 CSS 渐变、emoji 文本、<t-icon> 替代所有不存在的图片引用;仅保留确实存在的文件(如 logo-billiard.svg

P4env(safe-area-inset-top) 部分机型不生效

  • 触发页面notes、login所有 navigationStyle: "custom" 的页面)
  • 现象iPhone 刘海屏顶部内容被状态栏遮挡,部分安卓机型也有此问题
  • 原因:env(safe-area-inset-top) 在部分机型/基础库版本下返回 0
  • 解决TS 中 onLoad 获取 wx.getSystemInfoSync().statusBarHeightWXML 中动态设置 style="padding-top: {{statusBarHeight}}px;"
  • 标准模式:见 5.2 节「状态栏适配」

P5statusBarHeight padding 导致一屏页面底部溢出

  • 触发页面login所有一屏不滚动的页面
  • 现象:底部按钮和协议文字被推到屏幕外
  • 原因:min-height: 100vh + padding-top: Xpx = 实际高度 > 100vh
  • 解决:改为 height: 100vh + box-sizing: border-boxpadding 从总高度中扣除
  • 标准模式:见 5.2 节「一屏页面布局模式」

P6TDesign Button icon 属性不支持自定义图标

  • 触发页面login
  • 现象:<t-button icon="logo-wechat"> 微信图标不显示
  • 原因TDesign icon 属性只接受内置图标名,微信 logo 不在内置库中
  • 解决:放弃 t-button,改为原生 <view> + <image src="/assets/icons/icon-wechat.svg"> 手动组合按钮
  • 规则:品牌图标、复杂自定义图标一律导出 SVG 文件,用 <image> 引用(见 5.0 节)

P7TDesign Button 默认样式覆盖自定义 WXSS

  • 触发页面login
  • 现象:按钮圆角过大(接近胶囊形)、禁用态颜色不是预期的 #dcdcdc
  • 原因TDesign Button 内部样式优先级高于外部 t-class 覆盖,!important 也不一定生效
  • 解决:对于需要高度定制的按钮,直接用原生 <view> 实现,完全绕开 TDesign 样式干扰
  • 原则TDesign 组件适合"接近默认样式"的场景;与原型差异大时,原生实现更可控

P8小程序 WXSS 不支持 ::before / ::after 伪元素

  • 触发页面reviewing
  • 现象:尝试用 ::before 添加背景图案层,编译无报错但不渲染
  • 原因:微信小程序 WXSS 不支持 CSS 伪元素
  • 解决:用实际的 <view class="bg-pattern"> 替代伪元素absolute 定位覆盖全屏
  • 规则:所有需要伪元素的场景(装饰层、分隔线、角标等),一律用 WXML <view> 实现

P9小程序 WXSS 不支持 url("data:image/svg+xml,...") 内联 SVG

  • 触发页面reviewing
  • 现象H5 用 background-image: url("data:image/svg+xml,...") 实现十字纹背景,小程序中不渲染
  • 原因:小程序 WXSS 的 background-image 不支持 data URI仅支持网络图片和 base64 图片)
  • 解决:用 repeating-linear-gradient 组合模拟纹理图案;或用实际图片文件
  • 规则H5 中的 SVG 背景图案,迁移时优先用 CSS 渐变模拟;效果不佳时导出为 PNG/base64

P10小程序不支持 filter: blur() CSS 属性

  • 触发页面reviewing
  • 现象H5 用 blur-xlTailwind实现图标背景光晕的模糊效果小程序中无效
  • 原因:微信小程序 WXSS 不支持 filter 属性
  • 解决:用更大尺寸的 radial-gradient 模拟模糊扩散效果(扩大元素尺寸 + 调整渐变衰减曲线)
  • 参数参考:原始 256rpx → 扩大到 320rpxgradient 从 0.18 → 0.06 → transparent 三段衰减

P11Tailwind max-w-sm 在小程序中的换算需考虑容器 padding

  • 触发页面reviewing
  • 现象:进度卡片 max-w-sm = 384px = 768rpx 直接换算后卡片过宽
  • 原因H5 中 max-w-sm 受外层 px-832px padding约束实际可用宽度 ≈ 320px小程序中 content 区域的 padding 和 max-width 叠加效果不同
  • 解决:实测调整 max-width 值(本例最终为 550rpx不能机械换算
  • 规则:涉及 max-w-* 的元素,迁移后必须在真机/模拟器中目测确认宽度,按实际效果微调

P12H5 Tailwind 颜色变体需逐一核对(如 bg-amber-300bg-warning

  • 触发页面reviewing
  • 现象:装饰点 dot-2 在 H5 中用 bg-amber-300#fcd34d迁移时误用了 bg-warning#ed7b2f
  • 原因Tailwind 的 amber/orange/yellow 色系有多个变体,不能一律映射为 design-tokens 中的 warning
  • 解决:逐个检查 H5 中的颜色类名,查 Tailwind 色板取精确 hex 值
  • 规则:迁移时遇到非 design-tokens 定义的 Tailwind 颜色,必须查 Tailwind 官方色板确认精确值

附录:相关文档索引

  • 转换规范:.kiro/steering/miniprogram-h5-conversion.md
  • 避坑指南:apps/miniprogram/doc/h5-to-miniprogram-pitfalls.md
  • 输入材料指南:apps/miniprogram/doc/h5-input-material-guide.md
  • 工作流提示词:apps/miniprogram/doc/howtodo.md
  • 设计文档:.kiro/specs/p52-miniapp-fe-all-pages/design.md
  • 设计 Tokendocs/h5_ui/design-tokens.json
  • 图标映射:docs/h5_ui/icon-mapping.md
  • H5 原型:docs/h5_ui/pages/
  • 交互说明:docs/h5_ui/interactions/
  • 截图:docs/h5_ui/screenshots/