Files
Neo-ZQYY/_DEL/h5-to-mp-bridge.md
2026-03-15 10:15:02 +08:00

62 KiB
Raw Permalink Blame History

H5 原型 → 微信小程序迁移桥接规范

版本v1.0 基于:docs/h5_ui/ 原型代码穷举审阅 + Tailwind CSS v3 CDN 默认配置 + 微信 WXSS rpx 官方定义 适用:本项目所有 H5 原型页面到小程序页面的迁移工作


0. 尺寸换算基础(核心依据)

0.1 Tailwind CSS 在 412px 设备上的渲染行为

本项目 H5 原型使用 <script src="https://cdn.tailwindcss.com"></script>Tailwind CSS v3 CDN Play 模式)。

Tailwind v3 默认主题中,大量 spacing、font-size、border-radius token 主要使用 rem 体系(默认 spacing scale 中除 00pxpx1px其余均为 rem但 Tailwind 并非所有尺寸都基于 rem仍可能出现 px、百分比w-1/2)、分数、视口单位(min-h-screen)、自定义 arbitrary 值(h-[38px])等。

在浏览器默认根字号 1rem = 16px 且页面未主动改写 html { font-size } 的前提下,这些 rem 体系的 utility 会计算成固定 CSS px 值。这个值不随设备宽度变化——在 320px、375px、412px 的设备上,p-41rem)始终渲染为 16px

原型的 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 意味着:在浏览器按 width=device-width 建立布局视口、且该设备当前等效布局宽度为 412 CSS px 的前提下,页面宽度按 412 CSS px 参与布局。

因此:Tailwind 默认 rem 体系的 utility class 在任何设备上都会计算为固定的 CSS px 值,与设备布局宽度无关。但百分比、视口单位、自定义值等非 rem 单位仍会随设备变化。

0.2 微信 rpx 的精确定义

来源:微信官方 WXSS 文档

rpxresponsive pixel可以根据屏幕宽度进行自适应。规定屏幕宽为 750rpx。

换算公式:

1rpx = 屏幕宽度px / 750
1px  = 750 / 屏幕宽度px × rpx

官方示例:

设备 屏幕宽度 1rpx = ?px 1px = ?rpx
iPhone 5 320px 0.42px 2.34rpx
iPhone 6 375px 0.5px 2rpx
iPhone 6 Plus 414px 0.552px 1.81rpx

0.3 本项目的换算链路

验收基准设备:等效宽度 412px

完整链路:Tailwind class → CSS 计算值(px) → rpx

rpx = CSS_px × (750 / 412)
    = CSS_px × 1.82039...

但这里有一个关键问题:rpx 是响应式单位px 是固定单位

在 412px 设备上:750rpx = 412px,所以 1rpx ≈ 0.549px 在 375px 设备上:750rpx = 375px,所以 1rpx = 0.5px 在 320px 设备上:750rpx = 320px,所以 1rpx ≈ 0.427px

而 H5 原型中 Tailwind 的 p-4 = 16px 在所有设备上都是 16px。如果我们把它转成 29rpx16 × 1.82),那么:

  • 在 412px 设备上29rpx = 29 × 0.549 = 15.93px ≈ 16px ✓
  • 在 375px 设备上29rpx = 29 × 0.5 = 14.5px ✗(比原型小 9.4%
  • 在 320px 设备上29rpx = 29 × 0.427 = 12.38px ✗(比原型小 22.6%

这意味着:用 rpx 无法在所有设备上精确还原 H5 原型的固定 px 值。rpx 的本质是按屏宽缩放适合保持比例px 的本质是固定绝对尺寸,适合贴近 H5 原型的绝对视觉值。二者不能在所有设备上同时做到完全等价。

0.4 本项目的尺寸单位策略

H5 原型是移动端单栏布局,没有使用响应式断点。在 412px 设备上验收。

用 rpx 可以在不同宽度设备上保持相对比例一致;用固定 px 更接近 H5 原型的绝对尺寸视觉。二者不能在所有设备上同时做到完全等价。

推荐策略:以 rpx 为主、px 为辅的混合单位方案。

rpx 适用场景(跟屏宽强相关的尺寸):

  • 页面外层容器宽度、横向间距、卡片宽度、栅格
  • 吸顶区高度、块级间距
  • 大尺寸元素的 padding / margin

px 适用场景(需要绝对精度或极小尺寸):

  • 1px 发丝线边框(border: 1px solid
  • 阴影的模糊半径和扩散半径(box-shadow 的 blur/spread
  • 极小的装饰元素2px 以下的点、线)
  • icon 实际绘制尺寸12/14/16/18/20px 等常见图标尺寸)
  • 绝对定位微调badge 偏移 top/right
  • 弹层阴影、蒙层模糊参数
  • 评分星星、勾选框、状态点等小控件
  • 自定义导航栏中的状态栏补偿(通常结合 wx.getSystemInfoSync() 动态计算,不是纯 rpx 公式能解决)

按实际效果决定(不一刀切):

  • 文本字号、图标尺寸、圆角、局部 padding

理由:

  1. 小程序的主要使用场景是手机,屏幕宽度在 320-428px 之间rpx 的等比缩放在这个范围内视觉差异可接受
  2. 微信官方推荐使用 rpx 做适配,但并未要求所有样式都必须用 rpxWXSS 也支持直接写 px
  3. 412px 是验收基准,在该设备上 rpx 换算后的值与原型一致
  4. 固定 px 在小屏设备上可能导致内容溢出或布局挤压,但在细节尺寸上能保持质感
  5. 全面 rpx 在小尺寸上容易出现"比例上没错,但质感不对"的问题

换算公式rpx 场景使用):

rpx = H5_CSS_px × (750 / 412)
    ≈ H5_CSS_px × 1.8204

取整规则:

  • 常规布局值:四舍五入到整数 rpx
  • 高频出现的设计 token沉淀成统一变量表如 §1.1 换算表),保持全项目一致
  • 对视觉特别敏感的值:以真机截图比对微调,不强行套公式

1. Tailwind Spacing → rpx 完整换算表

Tailwind v3 的 spacing scale 基于 0.25rem = 4px 步进。以下是本项目原型中实际使用的所有 spacing 值的精确换算。这些 rpx 值已在已迁移页面中使用,作为项目统一 token后续页面应保持一致。

1.1 Tailwind spacing scale默认

Tailwind 值 rem CSS px rpx项目 token 常见用途
0.5 0.125rem 2px 4rpx gap-0.5, p-0.5, mt-0.5
px 1px 1px 2rpx py-px, w-px
1 0.25rem 4px 8rpx gap-1, p-1, mt-1, mb-1
1.5 0.375rem 6px 12rpx gap-1.5, p-1.5, mt-1.5
2 0.5rem 8px 14rpx gap-2, p-2, mt-2, mb-2
2.5 0.625rem 10px 18rpx gap-2.5, p-2.5, mb-2.5
3 0.75rem 12px 22rpx gap-3, p-3, mb-3, pl-3
3.5 0.875rem 14px 26rpx p-3.5, h-3.5, w-3.5
4 1rem 16px 30rpx gap-4, p-4, px-4, mb-4
5 1.25rem 20px 36rpx p-5, px-5, mb-5, pb-5
6 1.5rem 24px 44rpx gap-6, p-6, px-6, mb-6
7 1.75rem 28px 52rpx h-7, w-7
8 2rem 32px 58rpx px-8, mb-8, pb-8, h-8
9 2.25rem 36px 66rpx h-9, w-9
10 2.5rem 40px 72rpx h-10, w-10, pb-10
11 2.75rem 44px 80rpx h-11, w-11, ml-11
12 3rem 48px 88rpx h-12, w-12, pb-12, left-12
14 3.5rem 56px 102rpx h-14, w-14
16 4rem 64px 116rpx h-16, w-16
20 5rem 80px 146rpx h-20, w-20, top-20
24 6rem 96px 174rpx h-24, w-24
28 7rem 112px 204rpx h-28, w-28
32 8rem 128px 232rpx h-32, w-32
40 10rem 160px 292rpx bottom-40, top-40
64 16rem 256px 466rpx h-64
72 18rem 288px 524rpx w-72

1.2 Arbitrary spacing 值

Tailwind class CSS 值 rpx
h-[38px] / w-[38px] 38px 70rpx
top-[44px] 44px 80rpx
min-h-[2.5rem] 40px 72rpx
max-h-[70vh] 70vh 70vh保留 vh

2. Tailwind 字号 → WXSS font-size + line-height 完整换算表

2.1 核心事实

Tailwind v3 的字号类(text-xstext-sm 等)同时设置 font-sizeline-height。这是 Tailwind 的内置行为,不是可选的。

例如 text-sm 生成的 CSS 是:

font-size: 0.875rem;    /* 14px */
line-height: 1.25rem;   /* 20px */

如果 WXSS 只写了 font-size 而没写 line-height,小程序会使用默认行高(约 1.2 倍字号),导致每行文字的垂直空间不足,整体高度偏矮。

强制规则:每个 Tailwind 字号类转 WXSS 时,必须同时写 font-sizeline-height

2.1.1 微信小程序 text 组件的 line-height 限制 ⚠️

关键发现:微信小程序的 <text> 组件不能直接设置 line-height,必须通过外层 <view> 设置。

正确做法:

/* 全局设置 */
page {
  line-height: 1.5; /* Tailwind 默认行高 */
}

view {
  line-height: inherit; /* view 继承 page 的 line-height */
}

/* text 会自动继承外层 view 的 line-height不需要额外设置 */

局部覆盖:

.section-title {
  font-size: 26rpx;
  line-height: 36rpx; /* 在 view 的 class 上设置text 会继承 */
  font-weight: 600;
}
<view class="section-title">
  <text>标题文本</text>
</view>

错误做法:

/* ❌ 直接在 text 上设置 line-height 无效 */
text {
  line-height: 36rpx; /* 不会生效 */
}

/* ❌ 添加 display: inline-block 也无效 */
text {
  display: inline-block;
  line-height: 36rpx; /* 仍然不会生效 */
}

原因:微信小程序的 <text> 是特殊的内联组件,直接设置 line-height 不生效,必须在外层 <view> 上设置。

验证方法:在开发者工具的 Computed 面板中text 元素不会显示 line-height 属性,但外层 view 的 height 值会包含行高效果。

2.2 标准字号类换算表

Tailwind v3 默认字号定义来源Tailwind CSS v3 源码 defaultTheme.js

Tailwind class font-size (rem) font-size (px) line-height (rem) line-height (px) WXSS font-size WXSS line-height
text-xs 0.75rem 12px 1rem 16px 22rpx 30rpx
text-sm 0.875rem 14px 1.25rem 20px 26rpx 36rpx
text-base 1rem 16px 1.5rem 24px 30rpx 44rpx
text-lg 1.125rem 18px 1.75rem 28px 32rpx 52rpx
text-xl 1.25rem 20px 1.75rem 28px 36rpx 52rpx
text-2xl 1.5rem 24px 2rem 32px 44rpx 58rpx
text-3xl 1.875rem 30px 2.25rem 36px 54rpx 66rpx

2.3 Arbitrary 字号换算表

原型中使用的 arbitrary 字号(text-[Npx])没有 Tailwind 内置的 line-height 绑定。浏览器会使用默认行高(通常为 normal,约 1.2 倍字号)。

Tailwind class font-size (px) 默认 line-height (px, ×1.2) WXSS font-size WXSS line-height
text-[9px] 9px ~10.8px 16rpx 20rpx
text-[10px] 10px ~12px 18rpx 22rpx
text-[11px] 11px ~13.2px 20rpx 24rpx
text-[12px] 12px ~14.4px 22rpx 26rpx
text-[13px] 13px ~15.6px 24rpx 28rpx

注意arbitrary 字号在原型中使用频率极高(text-[10px] 157 次、text-[11px] 230 次),是本项目的主力小字号。

2.4 font-weight 映射

Tailwind class CSS font-weight WXSS
font-normal 400 font-weight: 400
font-medium 500 font-weight: 500
font-semibold 600 font-weight: 600
font-bold 700 font-weight: 700

注意:font-medium 是 500 不是 600。 这是常见错误。

2.5 其他文本属性

Tailwind class CSS WXSS
text-center text-align: center text-align: center
text-right text-align: right text-align: right
text-left text-align: left text-align: left
truncate overflow: hidden; text-overflow: ellipsis; white-space: nowrap 同左
line-clamp-2 -webkit-line-clamp: 2; display: -webkit-box; -webkit-box-orient: vertical; overflow: hidden 同左(小程序支持)
whitespace-nowrap white-space: nowrap white-space: nowrap
leading-normal line-height: 1.5 line-height: 1.5
leading-relaxed line-height: 1.625 line-height: 1.625
leading-snug line-height: 1.375 line-height: 1.375
tracking-wide letter-spacing: 0.025em letter-spacing: 0.025em
underline text-decoration: underline text-decoration: underline
line-through text-decoration: line-through text-decoration: line-through

3. Tailwind 颜色 → WXSS 颜色完整映射

3.1 项目自定义颜色tailwind.config.theme.extend.colors

每个 HTML 页面的 <script> 中定义了相同的自定义颜色扩展:

Tailwind 名称 色值 WXSS 变量
primary #0052d9 var(--color-primary)
primary-light #ecf2fe var(--color-primary-light)
success #00a870 var(--color-success)
warning #ed7b2f var(--color-warning)
error #e34d59 var(--color-error)
gray-1 #f3f3f3 var(--color-gray-1)
gray-2 #eeeeee var(--color-gray-2)
gray-3 #e7e7e7 var(--color-gray-3)
gray-4 #dcdcdc var(--color-gray-4)
gray-5 #c5c5c5 var(--color-gray-5)
gray-6 #a6a6a6 var(--color-gray-6)
gray-7 #8b8b8b var(--color-gray-7)
gray-8 #777777 var(--color-gray-8)
gray-9 #5e5e5e var(--color-gray-9)
gray-10 #4b4b4b var(--color-gray-10)
gray-11 #393939 var(--color-gray-11)
gray-12 #2c2c2c var(--color-gray-12)
gray-13 #242424 var(--color-gray-13)

3.2 Tailwind 内置颜色(原型中实际使用的)

以下是原型中使用的 Tailwind 内置颜色的精确色值Tailwind v3 默认 palette

Tailwind 色阶 50 100 200 300 400 500 600 700 800 900
red #fef2f2 #fee2e2 #fecaca #fca5a5 #f87171 #ef4444 #dc2626 #b91c1c #991b1b #7f1d1d
orange #fff7ed #ffedd5 #fed7aa #fdba74 #fb923c #f97316 #ea580c #c2410c
amber #fffbeb #fef3c7 #fde68a #fcd34d #fbbf24 #f59e0b #d97706 #b45309
yellow #fefce8 #fef9c3 #fef08a #fde047 #facc15 #eab308 #ca8a04 #a16207
green #f0fdf4 #dcfce7 #bbf7d0 #86efac #4ade80 #22c55e #16a34a #15803d
emerald #ecfdf5 #d1fae5 #6ee7b7 #059669 #047857
teal #f0fdfa #99f6e4 #5eead4 #2dd4bf #14b8a6
cyan #cffafe #22d3ee #06b6d4 #0e7490
sky #7dd3fc #38bdf8 #0ea5e9
blue #eff6ff #dbeafe #93c5fd #60a5fa #3b82f6 #1d4ed8
indigo #a5b4fc #818cf8
violet #c4b5fd #a78bfa
purple #f3e8ff #a855f7 #9333ea #7e22ce
fuchsia #f0abfc #e879f9
pink #fce7f3 #f9a8d4 #f472b6 #ec4899 #db2777 #be185d
rose #fda4af #fb7185

3.3 透明度变体

原型大量使用 Tailwind 的透明度修饰符(/N),如 bg-primary/10text-white/60

WXSS 映射方式:使用 rgba() 或直接写带透明度的色值。

透明度后缀 alpha 值 示例
/5 0.05 bg-primary/5rgba(0, 82, 217, 0.05)
/10 0.10 bg-primary/10rgba(0, 82, 217, 0.10)
/15 0.15 bg-white/15rgba(255, 255, 255, 0.15)
/20 0.20 bg-white/20rgba(255, 255, 255, 0.20)
/25 0.25 bg-white/25rgba(255, 255, 255, 0.25)
/30 0.30 bg-primary/30rgba(0, 82, 217, 0.30)
/40 0.40 bg-primary/40rgba(0, 82, 217, 0.40)
/50 0.50 bg-black/50rgba(0, 0, 0, 0.50)
/60 0.60 text-white/60rgba(255, 255, 255, 0.60)
/70 0.70 text-white/70rgba(255, 255, 255, 0.70)
/80 0.80 bg-white/80rgba(255, 255, 255, 0.80)
/85 0.85 text-white/85rgba(255, 255, 255, 0.85)
/90 0.90 text-white/90rgba(255, 255, 255, 0.90)
/95 0.95 bg-white/95rgba(255, 255, 255, 0.95)

4. Tailwind 布局类 → WXSS 完整映射

4.1 Flexbox

Tailwind class CSS WXSS 频率
flex display: flex display: flex 1530
flex-col flex-direction: column flex-direction: column 14
flex-1 flex: 1 1 0% flex: 1 358
flex-[2] flex: 2 flex: 2 1
flex-shrink-0 flex-shrink: 0 flex-shrink: 0 527
shrink-0 flex-shrink: 0 flex-shrink: 0 72
flex-wrap flex-wrap: wrap flex-wrap: wrap 26
items-center align-items: center align-items: center 1432
items-start align-items: flex-start align-items: flex-start 56
items-end align-items: flex-end align-items: flex-end 9
items-baseline align-items: baseline align-items: baseline 8
items-stretch align-items: stretch align-items: stretch 1
justify-between justify-content: space-between justify-content: space-between 395
justify-center justify-content: center justify-content: center 341
justify-end justify-content: flex-end justify-content: flex-end 55
justify-start justify-content: flex-start justify-content: flex-start 3

4.2 Grid

Tailwind class CSS WXSS 频率
grid display: grid display: grid 48
grid-cols-2 grid-template-columns: repeat(2, minmax(0, 1fr)) grid-template-columns: repeat(2, minmax(0, 1fr)) 8
grid-cols-3 grid-template-columns: repeat(3, minmax(0, 1fr)) grid-template-columns: repeat(3, minmax(0, 1fr)) 11
grid-cols-4 grid-template-columns: repeat(4, minmax(0, 1fr)) grid-template-columns: repeat(4, minmax(0, 1fr)) 29

4.3 Gap间距

Tailwind class CSS WXSS
gap-0.5 gap: 2px gap: 4rpx
gap-1 gap: 4px gap: 8rpx
gap-1.5 gap: 6px gap: 12rpx
gap-2 gap: 8px gap: 14rpx
gap-2.5 gap: 10px gap: 18rpx
gap-3 gap: 12px gap: 22rpx
gap-4 gap: 16px gap: 30rpx
gap-5 gap: 20px gap: 36rpx
gap-6 gap: 24px gap: 44rpx
gap-x-4 column-gap: 16px column-gap: 30rpx
gap-y-1 row-gap: 4px row-gap: 8rpx

4.4 定位

Tailwind class CSS WXSS 频率
relative position: relative position: relative 64
absolute position: absolute position: absolute 56
fixed position: fixed position: fixed 23
sticky position: sticky 见 §6.3 兼容性说明 11
inset-0 inset: 0 top: 0; right: 0; bottom: 0; left: 0 14
top-0 top: 0 top: 0 10
bottom-0 bottom: 0 bottom: 0 33
left-0 left: 0 left: 0 8
right-0 right: 0 right: 0 35
top-1/2 top: 50% top: 50% 5
left-1/2 left: 50% left: 50% 5
-translate-x-1/2 transform: translateX(-50%) transform: translateX(-50%) 5
-translate-y-1/2 transform: translateY(-50%) transform: translateY(-50%) 5
z-10 z-index: 10 z-index: 10 13
z-20 z-index: 20 z-index: 20 3
z-50 z-index: 50 z-index: 50 12
z-[100] z-index: 100 z-index: 100 5

4.5 尺寸

Tailwind class CSS WXSS
w-full width: 100% width: 100%
h-full height: 100% height: 100%
w-1/2 width: 50% width: 50%
min-w-0 min-width: 0 min-width: 0
min-h-screen min-height: 100vh min-height: 100vh
max-w-sm max-width: 24rem (384px) max-width: 700rpx
max-w-xs max-width: 20rem (320px) max-width: 582rpx

固定尺寸参照 §1.1 spacing 换算表。

4.6 显示与溢出

Tailwind class CSS WXSS
block display: block display: block
inline-block display: inline-block display: inline-block
hidden display: none display: none
overflow-hidden overflow: hidden overflow: hidden
overflow-x-auto overflow-x: auto overflow-x: auto
overflow-y-auto overflow-y: auto overflow-y: auto

4.7 space-y子元素间距

space-y-N 通过给子元素(除第一个外)添加 margin-top 实现。WXSS 中需要手动实现。

Tailwind class CSS 效果 WXSS 实现
space-y-0 子元素间距 0 子元素 margin-top: 0
space-y-1 子元素间距 4px 子元素 margin-top: 8rpx(首个除外)
space-y-1.5 子元素间距 6px 子元素 margin-top: 12rpx(首个除外)
space-y-2 子元素间距 8px 子元素 margin-top: 14rpx(首个除外)
space-y-2.5 子元素间距 10px 子元素 margin-top: 18rpx(首个除外)
space-y-3 子元素间距 12px 子元素 margin-top: 22rpx(首个除外)
space-y-4 子元素间距 16px 子元素 margin-top: 30rpx(首个除外)

实现方式:在 WXSS 中用 .parent > view + view { margin-top: Nrpx; } 或对每个子元素单独加 class。小程序支持 >+ 选择器。

4.8 divide-y分割线

Tailwind class CSS 效果 WXSS 实现
divide-y 子元素之间 1px 上边框 子元素 border-top: 1px solid (首个除外)
divide-gray-100 分割线颜色 #f3f4f6 border-color: #f3f4f6

5. Tailwind 装饰类 → WXSS 完整映射

5.1 圆角

Tailwind class CSS WXSS
rounded-sm border-radius: 2px border-radius: 4rpx
rounded border-radius: 4px border-radius: 8rpx
rounded-lg border-radius: 8px border-radius: 14rpx
rounded-xl border-radius: 12px border-radius: 22rpx
rounded-2xl border-radius: 16px border-radius: 30rpx
rounded-3xl border-radius: 24px border-radius: 44rpx
rounded-full border-radius: 9999px border-radius: 50%border-radius: 999rpx
rounded-t border-top-left/right-radius: 4px border-top-left-radius: 8rpx; border-top-right-radius: 8rpx
rounded-t-3xl border-top-left/right-radius: 24px border-top-left-radius: 44rpx; border-top-right-radius: 44rpx
rounded-b-2xl border-bottom-left/right-radius: 16px border-bottom-left-radius: 30rpx; border-bottom-right-radius: 30rpx
rounded-tl-sm border-top-left-radius: 2px border-top-left-radius: 4rpx
rounded-tr-sm border-top-right-radius: 2px border-top-right-radius: 4rpx

5.2 阴影

Tailwind class CSS WXSS
shadow-sm box-shadow: 0 1px 2px 0 rgba(0,0,0,0.05) box-shadow: 0 2rpx 4rpx 0 rgba(0,0,0,0.05)
shadow-lg box-shadow: 0 10px 15px -3px rgba(0,0,0,0.1), 0 4px 6px -4px rgba(0,0,0,0.1) box-shadow: 0 18rpx 28rpx -6rpx rgba(0,0,0,0.1), 0 8rpx 12rpx -8rpx rgba(0,0,0,0.1)
shadow-xl box-shadow: 0 20px 25px -5px rgba(0,0,0,0.1), 0 8px 10px -6px rgba(0,0,0,0.1) box-shadow: 0 36rpx 46rpx -10rpx rgba(0,0,0,0.1), 0 14rpx 18rpx -12rpx rgba(0,0,0,0.1)

带颜色的阴影(原型中使用的):

Tailwind class WXSS
shadow-primary/20 box-shadow: 0 2rpx 4rpx 0 rgba(0,82,217,0.2)
shadow-primary/30 box-shadow: 0 2rpx 4rpx 0 rgba(0,82,217,0.3)
shadow-error/30 box-shadow: 0 2rpx 4rpx 0 rgba(227,77,89,0.3)
shadow-warning/30 box-shadow: 0 2rpx 4rpx 0 rgba(237,123,47,0.3)
shadow-gray-200/50 box-shadow: 0 2rpx 4rpx 0 rgba(229,231,235,0.5)
shadow-orange-500/30 box-shadow: 0 2rpx 4rpx 0 rgba(249,115,22,0.3)
shadow-pink-500/30 box-shadow: 0 2rpx 4rpx 0 rgba(236,72,153,0.3)
shadow-teal-500/30 box-shadow: 0 2rpx 4rpx 0 rgba(20,184,166,0.3)

5.3 边框

Tailwind class CSS WXSS
border border: 1px solid border: 1px solid
border-0 border: 0 border: 0
border-2 border: 2px solid border: 2px solid
border-b border-bottom: 1px solid border-bottom: 1px solid
border-t border-top: 1px solid border-top: 1px solid
border-r border-right: 1px solid border-right: 1px solid
border-l-2 border-left: 2px solid border-left: 2px solid
border-transparent border-color: transparent border-color: transparent

边框颜色:直接使用 §3 中的颜色映射,加上 border-color: xxx

5.4 渐变

原型大量使用渐变背景(bg-gradient-to-br 190 次、bg-gradient-to-r 62 次)。

Tailwind class CSS WXSS
bg-gradient-to-r background-image: linear-gradient(to right, ...) background: linear-gradient(to right, ...)
bg-gradient-to-br background-image: linear-gradient(to bottom right, ...) background: linear-gradient(to bottom right, ...)
bg-gradient-to-b background-image: linear-gradient(to bottom, ...) background: linear-gradient(to bottom, ...)
from-X 渐变起始色 渐变第一个色值
to-X 渐变结束色 渐变最后一个色值

示例:bg-gradient-to-br from-blue-400 to-blue-500 → WXSS: background: linear-gradient(to bottom right, #60a5fa, #3b82f6)

5.5 透明度与模糊

Tailwind class CSS WXSS 小程序兼容性
opacity-30 opacity: 0.3 opacity: 0.3
opacity-55 opacity: 0.55 opacity: 0.55
opacity-60 opacity: 0.6 opacity: 0.6
opacity-90 opacity: 0.9 opacity: 0.9
backdrop-blur-sm backdrop-filter: blur(4px) 见 §6.2 ⚠️ 部分支持
backdrop-blur-md backdrop-filter: blur(12px) 见 §6.2 ⚠️ 部分支持
backdrop-blur-lg backdrop-filter: blur(16px) 见 §6.2 ⚠️ 部分支持
blur-xl filter: blur(24px) filter: blur(24px)

5.6 其他装饰

Tailwind class CSS WXSS
object-cover object-fit: cover mode="aspectFill"image 组件属性)
ring-2 box-shadow: 0 0 0 2px box-shadow: 0 0 0 2px
appearance-none appearance: none 不需要(小程序无浏览器默认样式)
outline-none outline: none 不需要
resize-none resize: none 不需要(小程序 textarea 默认不可拖拽)
cursor-pointer cursor: pointer 不需要(触屏无光标)

6. CSS 特性兼容性H5 → 小程序

6.1 完全支持(直接迁移)

以下 CSS 特性在小程序 WXSS 中完全支持,可直接使用:

CSS 特性 说明
display: flex / grid / block / none 布局核心
position: relative / absolute / fixed 定位
box-shadow 阴影
border-radius 圆角
linear-gradient() 线性渐变
radial-gradient() 径向渐变
opacity 透明度
transform: translate / scale / rotate 变换
transition 过渡动画
animation + @keyframes 关键帧动画
::before / ::after 伪元素
overflow: hidden / auto 溢出控制
text-overflow: ellipsis 文本省略
-webkit-line-clamp 多行省略
calc() 计算函数
var() CSS 变量
z-index 层级
white-space 空白处理
word-break / word-wrap 换行控制
filter: blur() 模糊滤镜
clip-path 裁剪路径
will-change 性能提示
font-variant-numeric 数字字体变体

6.2 部分支持(需要降级方案)

backdrop-filter: blur()(原型使用 35 次)

小程序对 backdrop-filter 的支持取决于基础库版本和设备:

  • iOS基础库 2.9.0+ 通常支持
  • Android部分机型不支持或性能差

降级策略

/* 优先尝试 backdrop-filter */
.blur-bg {
  background: rgba(255, 255, 255, 0.80);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
}
/* 如果不生效,半透明背景本身也能接受 */

对于原型中的三种 blur 级别:

H5 降级 WXSS
backdrop-blur-smblur 4px background: rgba(255,255,255,0.85) + 可选 backdrop-filter: blur(4px)
backdrop-blur-mdblur 12px background: rgba(255,255,255,0.90) + 可选 backdrop-filter: blur(12px)
backdrop-blur-lgblur 16px background: rgba(255,255,255,0.92) + 可选 backdrop-filter: blur(16px)

position: sticky(原型使用 11 次)

小程序支持 position: sticky,但有限制:

  • 必须在页面自然滚动中使用(不能在 scroll-view 内部)
  • top 值必须明确指定
  • 多层 sticky 嵌套容易出问题

迁移规则

  1. 页面级 sticky如筛选栏吸顶直接用 position: sticky; top: Nrpx
  2. scroll-view 内的 sticky改用 JS 监听 bindscroll + 条件渲染
  3. 多层 sticky只保留最外层内层改为固定布局

env(safe-area-inset-*)(原型使用 9 次)

小程序不支持 env() CSS 函数。

替代方案:

// JS 获取安全区信息
const systemInfo = wx.getSystemInfoSync()
const statusBarHeight = systemInfo.statusBarHeight  // 状态栏高度 px
const safeAreaBottom = systemInfo.screenHeight - systemInfo.safeArea.bottom  // 底部安全区 px
/* WXSS 中通过 style 动态绑定 */
/* WXML: <view style="padding-top: {{statusBarHeight}}px"> */

color-mix()(原型 CSS 中使用)

小程序不支持 color-mix()。替代方案:预计算混合后的色值,直接写 rgba()

6.3 不支持(必须替代)

H5 CSS 特性 原型使用场景 小程序替代方案
env(safe-area-inset-*) 顶部/底部安全区 JS wx.getSystemInfoSync() 动态获取
:hover 伪类 悬停高亮45 次) 不需要(触屏无 hover如需按压反馈用 hover-class 属性
:focus 伪类 输入框聚焦样式11 次) bindfocus 事件 + setData 切换 class
:active 伪类 按压缩放1 次) hover-class 属性或 bindtouchstart/end
group-hover:* 父元素 hover 时子元素变化1 次) 不需要(触屏无 hover
:first-child / :last-child 首尾元素特殊样式 wx:if + index 判断,或手动加 class
:checked 复选框选中态 setData + 条件 class
history.back() 返回上一页 wx.navigateBack()
window.scrollY 滚动位置 onPageScroll(e)e.scrollTop
scrollIntoView() 滚动到元素 scroll-viewscroll-into-view 属性
navigator.clipboard 复制文本 wx.setClipboardData()
navigator.vibrate 触觉反馈 wx.vibrateShort()
localStorage / sessionStorage 本地存储 wx.setStorageSync() / wx.getStorageSync()
confirm() / alert() 对话框 wx.showModal() / wx.showToast()

6.4 hover-class 属性说明

小程序的 view 组件支持 hover-class 属性,可以实现按压态效果:

<view hover-class="pressed" hover-stay-time="100">
  点击我
</view>
.pressed {
  opacity: 0.7;
  /* 或 background-color 变化 */
}

原型中 hover:bg-primary/545 次)和 hover:bg-gray-1004 次)统一改为 hover-class


7. HTML 标签 → WXML 组件映射

7.1 基础标签映射

H5 标签 WXML 组件 说明
<div> <view> 通用容器
<span> <text> 行内文本。注意:<text> 内只能嵌套 <text>,不能嵌套 <view>
<p> <view><text> 段落
<img> <image> 图片。必须指定 mode 属性
<a> <navigator><view bindtap> 链接/跳转
<button> <button><view bindtap> 按钮
<input> <input> 输入框
<textarea> <textarea> 多行输入
<svg> <image src="xxx.svg"> 小程序不支持内联 SVG
<ul> / <ol> / <li> <view> 列表容器
<h1>~<h6> <view> + 样式 class 标题
<label> <label><view> 表单标签
<select> <picker> 选择器

7.2 image 组件的 mode 属性

原型中 <img> 使用 object-cover 的场景,对应小程序 <image mode="aspectFill">

H5 CSS image mode
object-fit: cover mode="aspectFill"
object-fit: contain mode="aspectFit"
无特殊设置 mode="widthFix"(宽度撑满,高度自适应)

7.3 SVG 处理规则

原型中大量使用内联 SVG图标、装饰图形。小程序不支持内联 SVG。

处理方式:

  1. 将 SVG 导出为独立 .svg 文件,放入 assets/icons/
  2. <image src="/assets/icons/xxx.svg" mode="aspectFit"> 引用
  3. 高频图标考虑使用 iconfont 或 TDesign 内置图标

7.4 事件属性映射

H5 事件 WXML 事件 说明
onclick bindtap 点击
onchange bindchange 值变化
oninput bindinput 输入
onscroll bindscrollscroll-viewonPageScroll(页面) 滚动
ontouchstart bindtouchstart 触摸开始
ontouchmove bindtouchmove 触摸移动
ontouchend bindtouchend 触摸结束
oncontextmenu bindlongpress 长按

bind 前缀事件会冒泡,catch 前缀事件会阻止冒泡:

  • bindtap:点击事件,冒泡
  • catchtap:点击事件,阻止冒泡(等同于 e.stopPropagation()
  • catchtouchmove:阻止触摸移动冒泡(常用于遮罩层阻止背景滚动)

8. JS DOM 操作 → 小程序数据驱动 完整映射

本章是整份文档的核心。H5 原型中所有 JS 交互都基于直接操作 DOM小程序必须改为数据驱动模式。

8.1 总体原则

H5 模式:用户操作 → JS 查询 DOM → 修改 DOM 属性/样式/内容 → 视觉变化 小程序模式:用户操作 → 事件处理函数 → setData 更新数据 → WXML 自动重渲染 → 视觉变化

禁止在小程序中使用的 H5 API(原型中全部出现过):

  • document.getElementById() — 175 处
  • document.querySelector() / querySelectorAll() — 多处
  • element.classList.add/remove/toggle/contains — 109 处
  • element.style.xxx = yyy — 47 处
  • element.innerHTML / textContent — 38 处
  • document.createElement() / appendChild() — 18 处
  • element.getAttribute() — 1 处

8.2 模式 A显示/隐藏切换(最高频,~80 处)

H5 原型中最常见的 DOM 操作模式。

H5 写法

// 显示弹窗
document.getElementById('modal').classList.remove('hidden')
document.getElementById('modal').classList.add('flex')
// 隐藏弹窗
document.getElementById('modal').classList.add('hidden')
document.getElementById('modal').classList.remove('flex')

小程序写法

// JS
Page({
  data: { showModal: false },
  openModal() { this.setData({ showModal: true }) },
  closeModal() { this.setData({ showModal: false }) }
})
<!-- WXML -->
<view wx:if="{{showModal}}" class="modal-overlay" catchtouchmove bindtap="closeModal">
  <view class="modal-content" catchtap>
    <!-- 弹窗内容 -->
  </view>
</view>

原型中的具体实例(穷举):

状态变量 控制对象 出现页面
showModal 通用弹窗/确认框 task-detail 系列, notes
showFilter / showFilterOverlay 筛选下拉面板 board-finance, board-customer, board-coach
showToc / showTocDropdown 目录导航浮层 board-finance, board-customer, board-coach
showContextMenu 长按上下文菜单 task-list
showToast Toast 提示 多个页面
showNoteModal 备注弹窗 notes, task-detail 系列
showAbandonModal 放弃客户确认框 task-detail 系列
showTipOverlay 帮助提示浮层 board-finance
showCopied 复制成功提示 chat, chat-history

8.3 模式 BTab/状态切换(~40 处)

H5 写法

function switchTab(tabName) {
  document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'))
  document.getElementById('tab-' + tabName).classList.add('active')
  document.querySelectorAll('.tab-content').forEach(c => c.classList.add('hidden'))
  document.getElementById('content-' + tabName).classList.remove('hidden')
}

小程序写法

Page({
  data: { activeTab: 'basic' },
  onTabChange(e) {
    this.setData({ activeTab: e.currentTarget.dataset.tab })
  }
})
<view class="tab {{activeTab === 'basic' ? 'tab--active' : ''}}"
      data-tab="basic" bindtap="onTabChange">基础课</view>
<view class="tab {{activeTab === 'vip' ? 'tab--active' : ''}}"
      data-tab="vip" bindtap="onTabChange">VIP课</view>

<view wx:if="{{activeTab === 'basic'}}">基础课内容</view>
<view wx:if="{{activeTab === 'vip'}}">VIP课内容</view>

原型中的具体实例

状态变量 选项值 出现页面
activeTab (看板) 'finance' / 'customer' / 'coach' board-*
activeTab (任务详情) 'basic' / 'vip' / 'incentive' task-detail 系列
activeTab (业绩) 'income' / 'service' performance
selectedTime 'thisMonth' / 'lastMonth' / 'last3Months' board-*
selectedArea 'all' / 'area1' / 'area2' board-*
selectedSort 'default' / 'amount' / 'count' board-customer, board-coach

8.4 模式 C展开/收起(~20 处)

H5 写法

function toggleExpand(id) {
  const el = document.getElementById(id)
  el.classList.toggle('hidden')
  const btn = document.getElementById(id + '-btn')
  btn.textContent = el.classList.contains('hidden') ? '展开更多' : '收起'
}

小程序写法

Page({
  data: { expandedSections: {} },
  toggleSection(e) {
    const key = e.currentTarget.dataset.key
    this.setData({ [`expandedSections.${key}`]: !this.data.expandedSections[key] })
  }
})
<view bindtap="toggleSection" data-key="detail">
  <text>{{expandedSections.detail ? '收起' : '展开更多'}}</text>
</view>
<view wx:if="{{expandedSections.detail}}">
  <!-- 展开内容 -->
</view>

8.5 模式 Dstyle 直接操作 → 动态 style 绑定47 处)

H5 写法

element.style.top = scrollY + 'px'
element.style.opacity = '0'
element.style.transform = 'translateY(-10px)'

小程序写法

Page({
  data: { dynamicTop: 0, fadeOpacity: 1 },
  onScroll(e) {
    this.setData({ dynamicTop: e.scrollTop })
  }
})
<view style="top: {{dynamicTop}}px; opacity: {{fadeOpacity}}; transform: translateY({{offsetY}}px)">

注意:频繁 setData 驱动 style 变化会导致性能问题。对于动画场景,优先使用 WXSS transition / animation,或 wx.createAnimation()

8.6 模式 E内容动态更新38 处)

H5 写法

document.getElementById('total').textContent = '¥12,345'
document.getElementById('list').innerHTML = items.map(i => `<div>${i.name}</div>`).join('')

小程序写法

Page({
  data: { total: '¥12,345', items: [] },
  loadData() {
    this.setData({
      total: '¥12,345',
      items: [{ name: '台桌' }, { name: '酒水' }]
    })
  }
})
<text>{{total}}</text>
<view wx:for="{{items}}" wx:key="name">
  <text>{{item.name}}</text>
</view>

8.7 模式 F表单验证样式~10 处)

H5 写法

if (!value) {
  input.classList.add('ring-2', 'ring-error/30', 'border-error/40')
  errorMsg.classList.remove('hidden')
} else {
  input.classList.remove('ring-2', 'ring-error/30', 'border-error/40')
  errorMsg.classList.add('hidden')
}

小程序写法

Page({
  data: { errors: {} },
  validate(field, value) {
    this.setData({ [`errors.${field}`]: !value })
  }
})
<input class="input {{errors.phone ? 'input--error' : ''}}" bindinput="onPhoneInput" />
<text wx:if="{{errors.phone}}" class="error-text">请输入手机号</text>

8.8 模式 G滚动联动~15 处)

原型中看板页的筛选栏隐藏/显示、吸顶标题切换、section 感知都依赖滚动事件。

H5 写法

let lastScrollY = 0
window.addEventListener('scroll', () => {
  const currentY = window.scrollY
  if (currentY > lastScrollY && currentY > 100) {
    filterBar.classList.add('filter-bar-hidden')
  } else {
    filterBar.classList.remove('filter-bar-hidden')
  }
  lastScrollY = currentY
})

小程序写法

Page({
  data: { filterBarVisible: true, stickyTitle: '' },
  _lastScrollTop: 0,
  _throttleTimer: null,

  onPageScroll(e) {
    // 节流:避免每帧 setData
    if (this._throttleTimer) return
    this._throttleTimer = setTimeout(() => {
      this._throttleTimer = null
    }, 100)

    const scrollTop = e.scrollTop
    const goingDown = scrollTop > this._lastScrollTop
    this._lastScrollTop = scrollTop

    // 只在状态真正变化时 setData
    const shouldHide = goingDown && scrollTop > 100
    if (shouldHide !== !this.data.filterBarVisible) {
      this.setData({ filterBarVisible: !shouldHide })
    }
  }
})

关键规则

  1. 滚动事件处理函数中必须做节流100ms 以上)
  2. 只在状态真正变化时调用 setData,避免无意义的重渲染
  3. 不要在滚动事件中更新大量数据或触发复杂计算
  4. 吸顶标题切换:用 wx.createIntersectionObserver() 监听 section 进出视口,比手动计算 scrollTop 更高效

8.9 模式 H长按菜单task-list 页面)

H5 写法

let pressTimer = null
let startX, startY
card.addEventListener('touchstart', (e) => {
  startX = e.touches[0].clientX
  startY = e.touches[0].clientY
  pressTimer = setTimeout(() => showContextMenu(e), 500)
})
card.addEventListener('touchmove', (e) => {
  if (Math.abs(e.touches[0].clientX - startX) > 10 ||
      Math.abs(e.touches[0].clientY - startY) > 10) {
    clearTimeout(pressTimer)
  }
})
card.addEventListener('touchend', () => clearTimeout(pressTimer))

小程序写法

Page({
  data: { contextMenu: { visible: false, taskId: null } },
  onLongPress(e) {
    const taskId = e.currentTarget.dataset.id
    this.setData({
      contextMenu: { visible: true, taskId }
    })
  },
  closeContextMenu() {
    this.setData({ 'contextMenu.visible': false })
  }
})
<view wx:for="{{tasks}}" wx:key="id"
      bindlongpress="onLongPress" data-id="{{item.id}}">
  <!-- 任务卡片内容 -->
</view>

<!-- 上下文菜单(建议改为底部 action sheet -->
<view wx:if="{{contextMenu.visible}}" class="menu-overlay" bindtap="closeContextMenu">
  <view class="action-sheet" catchtap>
    <view class="action-item" bindtap="onMenuAction" data-action="abandon">放弃客户</view>
    <view class="action-item" bindtap="onMenuAction" data-action="note">添加备注</view>
  </view>
</view>

建议:原型中的锚点浮出菜单改为底部 action sheet兼容性和可维护性更高。

8.10 模式 I评分拖动notes 页面)

H5 写法

stars.forEach((star, index) => {
  star.addEventListener('touchstart', () => setRating(index + 1))
  star.addEventListener('touchmove', (e) => {
    const rect = container.getBoundingClientRect()
    const x = e.touches[0].clientX - rect.left
    const newRating = Math.ceil(x / (rect.width / 5))
    setRating(Math.max(1, Math.min(5, newRating)))
  })
})

小程序写法

Component({
  properties: { value: { type: Number, value: 0 } },
  methods: {
    onStarTap(e) {
      const score = e.currentTarget.dataset.score
      this.triggerEvent('change', { score })
    },
    onTouchMove(e) {
      // 用 selectorQuery 获取容器位置
      const query = this.createSelectorQuery()
      query.select('.star-container').boundingClientRect((rect) => {
        const x = e.touches[0].clientX - rect.left
        const score = Math.ceil(x / (rect.width / 5))
        this.triggerEvent('change', { score: Math.max(1, Math.min(5, score)) })
      }).exec()
    }
  }
})

注意createSelectorQuery 是异步的,频繁调用会有延迟。建议在 touchstart 时缓存 recttouchmove 中直接用缓存值计算。

8.11 模式 J复制到剪贴板chat 页面)

H5 写法

navigator.clipboard.writeText(text).then(() => {
  copyBtn.classList.add('copied')
  setTimeout(() => copyBtn.classList.remove('copied'), 2000)
})

小程序写法

Page({
  onCopy(e) {
    const text = e.currentTarget.dataset.text
    wx.setClipboardData({
      data: text,
      success() {
        wx.showToast({ title: '已复制', icon: 'success', duration: 1500 })
      }
    })
  }
})

8.12 模式 K页面导航

H5 写法

window.location.href = 'task-detail.html?id=123'
history.back()

小程序写法

// 普通页面跳转
wx.navigateTo({ url: '/pages/task-detail/task-detail?id=123' })

// 返回上一页
wx.navigateBack()

// TabBar 页面跳转task-list、board-finance、my-profile
wx.switchTab({ url: '/pages/task-list/task-list' })

// 重定向(替换当前页)
wx.redirectTo({ url: '/pages/login/login' })

关键规则TabBar 页面(在 app.jsontabBar.list 中定义的)必须用 wx.switchTab,用 navigateTo 会静默失败。

8.13 模式 L定时器Toast 自动隐藏等)

H5 写法

toast.classList.remove('hidden')
setTimeout(() => toast.classList.add('hidden'), 2000)

小程序写法

Page({
  showToast(text) {
    this.setData({ showToast: true, toastText: text })
    setTimeout(() => {
      this.setData({ showToast: false })
    }, 2000)
  }
})

或直接使用微信 API

wx.showToast({ title: '操作成功', icon: 'success', duration: 2000 })

9. 动画与过渡迁移

9.1 CSS transition → WXSS transition直接迁移

原型中的 transition 定义可以直接迁移到 WXSS时间和缓动函数不变其中涉及的 px 值按 §0.4 策略决定是否换算为 rpx。

H5 transition WXSS transition
transition: all 0.2s ease transition: all 0.2s ease
transition: all 0.3s ease transition: all 0.3s ease
transition: opacity 0.2s ease transition: opacity 0.2s ease
transition: transform 0.15s ease transition: transform 0.15s ease
transition: opacity 0.15s ease, transform 0.15s ease transition: opacity 0.15s ease, transform 0.15s ease
transition: transform 220ms ease, opacity 220ms ease, max-height 220ms ease 同左
transition: background 0.2s, opacity 0.2s transition: background 0.2s, opacity 0.2s
transition: width 0.6s ease-out transition: width 0.6s ease-out
transition: clip-path 0.12s ease transition: clip-path 0.12s ease

9.2 @keyframes → WXSS @keyframes直接迁移

原型中的关键帧动画可以直接迁移,其中涉及的 px 值按 §0.4 策略决定是否换算为 rpx。

动画名 用途 迁移方式
ai-shimmer AI 标识微光扫过12s/14s 周期) 直接复制,已在 app.wxss 中实现
ai-pulse AI 标识呼吸脉冲3s 周期) 直接复制,已在 app.wxss 中实现
float 浮动装饰动画3s/4s 周期) 直接复制
pulse-soft 柔和脉冲装饰2s/3s 周期) 直接复制
pulse-glow 奖励闪烁2s 周期) 直接复制
shake 抖动反馈0.5s 直接复制
stampDown 红戳盖章0.5scubic-bezier 直接复制
texture-shift Banner 纹理位移20s 周期) 直接复制
filterBarDrop 筛选栏下滑出现 直接复制

9.3 Tailwind transition 类

Tailwind class CSS WXSS
transition-all transition-property: all; transition-timing-function: cubic-bezier(0.4,0,0.2,1); transition-duration: 150ms transition: all 150ms cubic-bezier(0.4,0,0.2,1)
transition-colors transition-property: color, background-color, border-color, text-decoration-color, fill, stroke; ...150ms transition: color 150ms, background-color 150ms, border-color 150ms
transition-transform transition-property: transform; ...150ms transition: transform 150ms cubic-bezier(0.4,0,0.2,1)
duration-200 transition-duration: 200ms 覆盖 duration 为 200ms

9.4 JS 驱动动画的替代

原型中有少量通过 JS 连续修改 style 实现的动画。在小程序中:

  1. 优先用 WXSS transition/animation性能最好
  2. 其次用 wx.createAnimation()
  3. 避免用连续 setData 逐帧更新

wx.createAnimation() 示例:

const animation = wx.createAnimation({
  duration: 300,
  timingFunction: 'ease'
})
animation.opacity(0).translateY(-10).step()
this.setData({ fadeAnimation: animation.export() })
<view animation="{{fadeAnimation}}">内容</view>

9.5 @media (prefers-reduced-motion: reduce)

原型中有减少动画偏好的媒体查询。小程序 WXSS 支持 @media 查询,可以直接迁移:

@media (prefers-reduced-motion: reduce) {
  .animated-element {
    animation: none;
    transition: none;
  }
}

10. CSS 变量迁移

10.1 原型中定义的 CSS 变量

变量名 用途 定义位置
--ai-from AI 图标渐变起始色 ai-icons.css
--ai-to AI 图标渐变结束色 ai-icons.css
--ai-from-deep AI 图标深色起始 ai-icons.css
--ai-to-deep AI 图标深色结束 ai-icons.css
--ai-pulse-r/g/b AI 脉冲动画 RGB 分量 ai-icons.css
--banner-color-start/mid/end Banner 渐变三色 banner.css
--star-color 评分星星颜色 notes.css
--texture-opacity 纹理透明度 banner.css

10.2 小程序 CSS 变量支持

小程序 WXSS 支持 CSS 变量(var()),在 page 选择器中定义全局变量:

/* app.wxss */
page {
  --color-primary: #0052d9;
  --ai-from: #667eea;
  /* ... */
}

组件内可以通过 class 覆盖变量值(与 H5 行为一致):

.ai-color-red {
  --ai-from: #e74c3c;
  --ai-to: #f39c9c;
}

注意color-mix() 函数在小程序中不支持。原型中使用 color-mix() 的地方需要预计算为 rgba() 值。


11. 自定义 CSS 类 → WXSS 迁移指引

原型中有 212 个非 Tailwind 的自定义类,定义在各页面的 <style> 标签和独立 CSS 文件中。这些类的迁移策略是:逐个检查其 CSS 定义,按 §0.4 混合单位策略决定 px 值是否换算为 rpx将不兼容的 CSS 特性按 §6 替换。

11.1 全局自定义类(跨页面复用)

以下类在多个页面中出现,应提取到 app.wxss 或公共样式文件中:

类名 用途 出现频率 迁移要点
safe-area-top 顶部安全区 padding 9 页面 改为 JS 动态获取 statusBarHeight
banner-bg Banner 渐变背景 10 页面 直接迁移 linear-gradientpx→rpx
texture-aurora Banner 纹理动画 10 页面 直接迁移 @keyframes
card-section 卡片区块容器 5 页面 px→rpx
card-header / card-header-content / card-header-emoji / card-header-title / card-header-desc 卡片头部组件 5 页面 px→rpx
section-title 区块标题 17 处 px→rpx
compare-up / compare-down / compare-data 环比数据标签 85+70+9 处 px→rpx颜色直接映射
page-label 页面标签 22 处 px→rpx
phone-frame / phone-screen 手机框架容器 22 处 仅 index.html 用,小程序不需要
ai-inline-icon / ai-title-badge / ai-title-badge-icon AI 图标系统 16+22+22 处 已在 app.wxss 中实现
show 显示状态 74 处 改为 wx:if 或条件 class
active 激活状态 32 处 改为条件 class {{xxx ? 'active' : ''}}

11.2 页面专属自定义类

以下类仅在特定页面中使用,应放在对应页面的 WXSS 文件中:

类名 页面 用途
task-card / task-name / task-desc / task-tag-wrap / task-tag-text task-list 任务卡片
customer-card / customer-item board-customer, customer-detail 客户卡片
coach-card board-coach, coach-detail 助教卡片
record-card / record-item / record-table / record-date / record-time / record-type / record-duration / record-income performance-records 业绩明细
svc-record / svc-table / svc-date / svc-type / svc-duration / svc-income / svc-drinks customer-service-records 服务记录
message-bubble / chat-container / chat-item / copy-btn / copied chat, chat-history 聊天界面
note-card-wrap / note-expand-btn / note-indicator / note-tag / note-rating-* notes 备注系统
nr-star / nr-filled / nr-empty / star-rating / star / star-fill / star-empty notes 评分星级
context-menu / context-overlay / ctx-item task-list 长按菜单
modal-overlay / modal-card / modal-submit-btn / modal-error notes, task-detail 弹窗
toc-dropdown / toc-overlay / toc-item / toc-item-emoji / toc-item-text board-* 目录导航
filter-overlay / filter-dropdown board-* 筛选面板
date-divider / dd-date / dd-line / dd-stats performance-records, customer-service-records 日期分割线
dim-container board-customer 维度容器
assistant-badge / assistant-tag / assistant-sep / assistant-normal / assistant-abandoned / assistant-assignee / assistant-badge-drop / assistant-badge-follow board-coach, coach-detail 助教标签系统
speech-bubble task-detail 对话气泡
stamp-badge / stamp-text / stamp-animate / red-stamp task-detail 红戳动画
tier-badge / tier-progress / tier-fill / tier-segment performance 等级进度条
income-tab / income-tier performance 收入层级
service-card customer-service-records 服务卡片
info-card customer-detail 信息卡片
option-card home-settings 选项卡片
checkbox-custom / radio-checked apply, home-settings 自定义表单控件
field-error / error-border / btn-disabled apply 表单验证
voice-btn chat 语音按钮
progress-sm / ruler-bar / ruler-strip performance 进度条
tip-overlay / tip-toast / tip-toast-content board-finance 帮助提示
summary-header / summary-content / summary-gradient board-customer 摘要区
float-animation login, reviewing 浮动装饰

11.3 主题色类

类名 用途 迁移方式
theme-blue / theme-blue-light / theme-coral / theme-dark-gold / theme-orange / theme-pink / theme-red / theme-teal Banner 主题色 通过 CSS 变量 --banner-color-start/mid/end 控制
blue / green / orange / pink / purple / red / teal 标签/徽章颜色 直接映射为对应色值

11.4 状态类

类名 用途 小程序迁移
show 显示元素 wx:if="{{visible}}" 或条件 class
active 激活态 class="{{isActive ? 'active' : ''}}"
selected 选中态 class="{{isSelected ? 'selected' : ''}}"
pinned 置顶态 class="{{isPinned ? 'pinned' : ''}}"
completed 完成态 class="{{isCompleted ? 'completed' : ''}}"
abandoned 放弃态 class="{{isAbandoned ? 'abandoned' : ''}}"
current 当前项 class="{{isCurrent ? 'current' : ''}}"
high-priority 高优先级 class="{{isHighPriority ? 'high-priority' : ''}}"

12. 浏览器 API → 微信小程序 API 完整映射

12.1 导航

H5 API 小程序 API 说明
window.location.href = url wx.navigateTo({ url }) 普通页面跳转
window.location.replace(url) wx.redirectTo({ url }) 替换当前页
history.back() wx.navigateBack() 返回上一页
wx.switchTab({ url }) TabBar 页面跳转(必须用这个)
wx.reLaunch({ url }) 关闭所有页面,打开指定页面

12.2 滚动

H5 API 小程序 API 说明
window.scrollY onPageScroll(e)e.scrollTop 页面滚动位置
window.scrollTo(0, y) wx.pageScrollTo({ scrollTop: y }) 滚动到指定位置
element.scrollIntoView() scroll-viewscroll-into-view="{{id}}" 滚动到指定元素
element.getBoundingClientRect() wx.createSelectorQuery().select('.cls').boundingClientRect() 获取元素位置(异步)

12.3 存储

H5 API 小程序 API 说明
localStorage.setItem(k, v) wx.setStorageSync(k, v) 同步写入
localStorage.getItem(k) wx.getStorageSync(k) 同步读取
localStorage.removeItem(k) wx.removeStorageSync(k) 同步删除
localStorage.clear() wx.clearStorageSync() 清空全部
sessionStorage.* 无直接等价 用页面 data 或 app.globalData 替代

12.4 剪贴板

H5 API 小程序 API
navigator.clipboard.writeText(text) wx.setClipboardData({ data: text })
navigator.clipboard.readText() wx.getClipboardData()

12.5 触觉反馈

H5 API 小程序 API
navigator.vibrate(15) wx.vibrateShort({ type: 'light' })
navigator.vibrate(100) wx.vibrateLong()

12.6 对话框

H5 API 小程序 API
alert(msg) wx.showModal({ title: '', content: msg, showCancel: false })
confirm(msg) wx.showModal({ title: '提示', content: msg })
wx.showToast({ title: msg, icon: 'success' })
wx.showActionSheet({ itemList: [...] })

12.7 系统信息

需求 小程序 API
状态栏高度 wx.getSystemInfoSync().statusBarHeight
屏幕宽度 wx.getSystemInfoSync().windowWidth
安全区域 wx.getSystemInfoSync().safeArea
底部安全区高度 screenHeight - safeArea.bottom
胶囊按钮位置 wx.getMenuButtonBoundingClientRect()

12.8 定时器

H5 API 小程序 API 说明
setTimeout(fn, ms) setTimeout(fn, ms) 完全相同
setInterval(fn, ms) setInterval(fn, ms) 完全相同
clearTimeout(id) clearTimeout(id) 完全相同
clearInterval(id) clearInterval(id) 完全相同
requestAnimationFrame(fn) 不推荐使用 用 WXSS animation 或 wx.createAnimation() 替代

13. WXSS 选择器支持范围

微信官方文档明确列出的支持选择器:

选择器 示例 支持
.class .card
#id #header
element view
element, element view, text
::after view::after
::before view::before

实测额外支持(非官方文档,但可用):

选择器 示例 支持
后代选择器 .parent .child
子选择器 .parent > .child
相邻兄弟 .item + .item
通用兄弟 .item ~ .item
属性选择器 [data-type="vip"] ⚠️ 部分支持

不支持或不推荐

  • 复杂的后代/兄弟选择器链(性能差)
  • :nth-child() / :nth-of-type()(不稳定)
  • :hover(触屏无意义)
  • :focus(用事件替代)
  • :first-child / :last-child(用条件 class 替代更可靠)

建议:样式选择器尽量简单,优先使用 class 选择器。复杂的结构关系用 WXML 条件渲染 + 显式 class 替代。


14. 迁移检查清单(逐页面执行)

每个页面迁移完成后,按以下清单逐项检查:

14.1 结构检查

  • 所有 <div> 已替换为 <view>
  • 所有 <span> 已替换为 <text>(且 <text> 内无 <view> 嵌套)
  • 所有 <img> 已替换为 <image>,且指定了 mode 属性
  • 所有内联 SVG 已导出为文件,用 <image> 引用
  • <a> 标签残留(改为 <navigator>bindtap
  • 页面 JSON 中 navigationStyle 配置正确

14.2 样式检查

  • 无 Tailwind class 残留(所有 utility 已转为 WXSS
  • 布局尺寸(容器宽度、间距、卡片)已按 §0.4 策略换算为 rpx
  • 细节尺寸发丝线、阴影、小图标、badge 偏移)按 §0.4 策略保留 px 或按效果决定
  • 所有字号同时写了 font-sizeline-height§2
  • font-medium 对应 500不是 600
  • env(safe-area-inset-*) 残留(改为 JS 动态获取)
  • cursor: pointer 残留
  • 渐变色值正确from/to 色值已查表确认)
  • 透明度值正确(/N 后缀已转为 rgba()

14.3 交互检查

  • document.querySelector 等 DOM 操作残留
  • classList.add/remove 残留
  • element.style.xxx = 残留
  • 所有显示/隐藏改为 wx:if 或条件 class + setData
  • 所有 Tab 切换改为 setData + 条件渲染
  • 所有页面跳转改为 wx.navigateTo / wx.switchTab / wx.navigateBack
  • TabBar 页面跳转使用 wx.switchTab(不是 navigateTo
  • 滚动事件有节流处理
  • 弹窗遮罩有 catchtouchmove 阻止背景滚动

14.4 性能检查

  • 无连续 setData 驱动动画(改用 WXSS transition/animation
  • 滚动事件处理函数中只更新必要的状态位
  • 长列表考虑分页或虚拟列表
  • 无不必要的 setData(状态未变化时不调用)

14.5 间距校验规则

  • 相邻元素间距确认归属(上方 padding-bottom 还是下方 margin-top不能两端都加
  • space-y-N 的间距值正确(space-y-2 = 8px = 14rpx不是 4px
  • gap-N 的间距值正确(gap-1 = 4px = 8rpx不是 2px