diff --git a/.cursor/skills/kbone-miniprogram/SKILL.md b/.cursor/skills/kbone-miniprogram/SKILL.md
index 442f5ff0..1160a06b 100644
--- a/.cursor/skills/kbone-miniprogram/SKILL.md
+++ b/.cursor/skills/kbone-miniprogram/SKILL.md
@@ -17,6 +17,59 @@ Kbone 是腾讯提供的 Web 与小程序同构解决方案,允许使用 React
---
+## Next 项目 → 小程序 最优转化方式
+
+适用于:**现有 Next.js 前端 + newpp(Kbone 模板)**,以**适配**方式转化,保持功能与视觉一致。
+
+### 架构约定
+
+| 角色 | 说明 |
+|------|------|
+| **newpp** | Kbone 构建源:多入口 React 应用,`npm run mp` 输出到 `dist/mp/common/` |
+| **miniprogram** | 小程序壳:`app.js`/`app.json`/`custom-tab-bar` 等,构建后**合并** newpp 的页面与 common |
+| **Next 项目根目录** | Web 端:`app/`、`components/`、Tailwind;与 newpp 共享业务逻辑时可被 alias 或复制 |
+
+### 转化步骤概要
+
+1. **以 newpp 为唯一 Kbone 构建源**
+ - 在 `newpp/build/miniprogram.config.js` 中按 Next 路由配置 `router`(每页单独配置,不用 `other`)。
+ - 在 `newpp/build/webpack.mp.config.js` 中为每个页面配置 `entry`,且 `isOptimize = process.env.NODE_ENV === 'production'`。
+
+2. **适配层(必须)**
+ - `adapters/env.js`:`isMiniProgram()`(`typeof wx !== 'undefined' && wx.getSystemInfoSync`)。
+ - `adapters/request.js`:Web 用 `fetch`,小程序用 `wx.request`,统一返回 Promise。
+ - `adapters/storage.js`:Web 用 `localStorage`,小程序用 `wx.getStorageSync`/`setStorageSync`。
+ - `adapters/router.js`:Web 用 `window.location` 或 Next `router`,小程序用 `wx.navigateTo`/`wx.reLaunch`;路径转小程序页面路径 `toMpPath(path)`。
+ - **功能控制一致**:与 Next 端相同,用同一套 API(如 `/api/db/config`)和条件渲染(如 `features.matchEnabled` 控制「找伙伴」入口),不写死开关。
+
+3. **UI 与页面来源**
+ - **方案 A(推荐起步)**:在 newpp 内维护页面与组件,从 Next 的 `app/**/page.tsx`、`components/**` 复制并改为 JSX + 适配器调用(去掉 Next 专属的 `Link`/`useRouter`/`usePathname`,改用适配层)。
+ - **方案 B**:在 newpp 的 webpack 中配置 `resolve.alias`,直接引用项目根目录的共享组件目录(需保证组件仅用 React + 适配层,无 `window`/`document`/Next 专属 API)。
+
+4. **miniprogram.config.js 要点**
+ - `router`:每个页面单独 key,例如 `index`、`chapters`、`read`、`my`、`match`,对应 `entry` 与小程序页面路径。
+ - 动态底部导航:不配置原生 `tabBar`,在 `appExtraConfig` 中不写 `tabBar`;底部栏用 React 组件 + `wx.reLaunch` 跳转。
+
+5. **样式兼容(与 Next 视觉一致)**
+ - Grid → Flex(含 `boxSizing: 'border-box'`、必要时 `lineHeight`)。
+ - `backdrop-filter` / `position: sticky` 等不支持则用占位或纯色/渐变替代,保证布局不错乱。
+ - 单位:小程序侧可用 rpx,与 Web 的 rem/px 按设计稿做一次换算或共用同一套换算规则。
+
+6. **构建与合并**
+ - 在 newpp 执行 `NODE_ENV=production npm run build:mp`,将 `dist/mp/common/` 及 mp-plugin 生成的页面目录合并到 `miniprogram/`。
+ - 合并时保留 miniprogram 壳的 `app.js` 全局数据与生命周期,只覆盖/新增 Kbone 生成的页面与 common 资源。
+
+### 与 Kbone 规则的关系
+
+- **router**:遵循本技能「核心配置规范」——每页单独配置、不用 `other`。
+- **底部导航**:遵循「问题 4:底部导航动态显示」——不配置原生 tabBar,用自定义组件 + `wx.reLaunch`。
+- **API/兼容**:遵循「跨平台适配层」与「问题 3:URLSearchParams」——全部走适配层,避免 Web 独有 API。
+- **样式**:遵循「问题 1:样式错位」与 troubleshooting 中的 Grid/Flex、box-sizing、lineHeight 等。
+
+详细排查与示例见本目录下 `troubleshooting.md` 以及项目 `开发文档/8、部署/`。
+
+---
+
## 核心配置规范
### 1. miniprogram.config.js 配置
diff --git a/.cursor/skills/kbone-miniprogram/reference.md b/.cursor/skills/kbone-miniprogram/reference.md
new file mode 100644
index 00000000..8822eb38
--- /dev/null
+++ b/.cursor/skills/kbone-miniprogram/reference.md
@@ -0,0 +1,110 @@
+# Next → 小程序 转化参考(详细步骤)
+
+与 `SKILL.md` 中「Next 项目 → 小程序 最优转化方式」对应,用于按步骤执行转化。
+
+---
+
+## 1. 架构与目录约定
+
+| 目录/产物 | 作用 |
+|-----------|------|
+| `newpp/` | Kbone 源码:多入口 React,构建输出到 `newpp/dist/mp/` |
+| `miniprogram/` | 小程序壳:`app.js`、`app.json`、`custom-tab-bar`、空白页面占位等 |
+| Next 根目录 `app/`、`components/` | Web 端页面与组件;可被 newpp 复制或通过 alias 引用 |
+
+构建后:将 `newpp/dist/mp/` 下的页面与 `common/` 合并进 `miniprogram/`,保留壳的 `app.js` 全局逻辑。
+
+---
+
+## 2. newpp 配置
+
+### 2.1 miniprogram.config.js
+
+- `router`:每个页面单独 key,对应 Next 路由,**不要使用 `other` 数组**。
+- 动态底部导航:`appExtraConfig` 中**不配置** `tabBar`,用自定义 React 组件 + `wx.reLaunch`。
+- `global`:建议 `rem: true`、`pageStyle: true`。
+- `pages`:为每个页面配置 `extra.navigationBarTitleText`。
+
+示例(按实际路由调整):
+
+```javascript
+router: {
+ index: ['/', '/(index)?', '/index.html'],
+ chapters: ['/chapters', '/chapters.html'],
+ read: ['/read/:id', '/read.html'],
+ my: ['/my', '/my.html'],
+ match: ['/match', '/match.html'],
+},
+appExtraConfig: {
+ sitemapLocation: 'sitemap.json',
+ // 不配置 tabBar
+},
+```
+
+### 2.2 webpack.mp.config.js
+
+- `entry`:每个页面一个入口,例如 `index`、`chapters`、`read`、`my`、`match`,指向 newpp 内对应入口 JSX。
+- `isOptimize`:`const isOptimize = process.env.NODE_ENV === 'production'`,生产构建时再压缩。
+- `optimization.splitChunks`:中小型项目建议 `splitChunks: false`,避免 chunk 缺失;大型项目再考虑固定命名 cacheGroups。
+
+---
+
+## 3. 适配层(必须)
+
+在 newpp 内(或共享到根目录再被引用)实现:
+
+| 文件 | 职责 |
+|------|------|
+| `adapters/env.js` | `isMiniProgram()`:`typeof wx !== 'undefined' && wx.getSystemInfoSync` |
+| `adapters/request.js` | Web:`fetch`;小程序:`wx.request`,统一返回 Promise |
+| `adapters/storage.js` | Web:`localStorage`;小程序:`wx.getStorageSync`/`setStorageSync` |
+| `adapters/router.js` | Web:`window.location` 或 Next Router;小程序:`wx.navigateTo`/`wx.reLaunch`,路径用 `toMpPath(path)` 转成小程序页面路径 |
+
+业务代码**只**通过适配层访问请求、存储、路由,不直接使用 `window`、`document`、`localStorage`、`URLSearchParams` 等。
+
+---
+
+## 4. 功能控制一致
+
+与 Next 端保持一致:
+
+- 功能开关来自同一 API,例如 `/api/db/config` 的 `features.matchEnabled`。
+- 底部导航「找伙伴」等入口根据该配置条件渲染,不在小程序端写死。
+- 支付方式、营销二维码等同样由 API 配置驱动,两端逻辑一致。
+
+---
+
+## 5. UI 来源策略
+
+- **复制适配**:从 Next 的 `app/**/page.tsx`、`components/**` 复制到 newpp,改为 JSX,用适配层替代 `Link`/`useRouter`/`usePathname`/`fetch`。
+- **Alias 引用**:在 newpp 的 webpack `resolve.alias` 中指向项目根目录的共享组件目录,保证该目录内仅用 React + 适配层,无 Next 专属 API。
+
+---
+
+## 6. 样式兼容(与 Next 视觉一致)
+
+- **Grid → Flex**:所有 Grid 布局改为 Flex,并加 `boxSizing: 'border-box'`、必要时 `lineHeight`。
+- **backdrop-filter / sticky**:不支持则用纯色、渐变或占位,保证布局不错位。
+- **单位**:小程序侧可用 rpx,与 Web 的 rem/px 按设计稿统一换算。
+
+详见 `troubleshooting.md` 中的「样式问题」小节。
+
+---
+
+## 7. 构建与合并流程
+
+1. 在 newpp 执行:`NODE_ENV=production npm run build:mp`。
+2. 将 `newpp/dist/mp/` 下生成的页面目录及 `common/` 复制/合并到 `miniprogram/`。
+3. 保留 miniprogram 壳的 `app.js`(全局 data、onLaunch、onShow 等),仅覆盖或新增 Kbone 生成的页面与 common。
+4. 用微信开发者工具打开 `miniprogram/` 根目录,编译并测试。
+
+---
+
+## 8. 检查清单
+
+- [ ] miniprogram.config.js:每页单独 router,无 `other`;未配置原生 tabBar。
+- [ ] webpack.mp.config.js:每页有 entry;isOptimize 按 NODE_ENV;中小型项目 splitChunks 为 false。
+- [ ] 适配层:env、request、storage、router 已实现并在业务中统一使用。
+- [ ] 功能开关与 Next 一致,来自同一 API。
+- [ ] 样式:无 Grid,Flex 已加 boxSizing/lineHeight;不支持特性已替代。
+- [ ] 合并后 app.js 未被错误覆盖,页面路径与 app.json 一致。
diff --git a/.cursor/skills/kbone-miniprogram/troubleshooting.md b/.cursor/skills/kbone-miniprogram/troubleshooting.md
index 314764ad..78ebb615 100644
--- a/.cursor/skills/kbone-miniprogram/troubleshooting.md
+++ b/.cursor/skills/kbone-miniprogram/troubleshooting.md
@@ -1,5 +1,20 @@
# Kbone 常见问题排查指南
+## 从 Next 项目迁移到小程序
+
+**先读**:本技能 `SKILL.md` 中的「Next 项目 → 小程序 最优转化方式」章节。
+
+**要点**:
+- 以 **newpp** 为 Kbone 构建源,**miniprogram** 为壳,构建后合并产物。
+- 必须做 **适配层**:`env`、`request`、`storage`、`router`;功能开关与 Next 一致,用同一 API(如 `/api/db/config` 的 `features.matchEnabled`)。
+- **UI**:在 newpp 内复制并适配 Next 的页面/组件,或通过 webpack alias 引用根目录共享组件(无 Next 专属 API)。
+- **配置**:`miniprogram.config.js` 每页单独 router、不配原生 tabBar;`webpack.mp.config.js` 中 `isOptimize = process.env.NODE_ENV === 'production'`,中小型项目建议 `splitChunks: false`。
+- **样式**:Grid 改 Flex,补 `boxSizing`/`lineHeight`;不支持特性用兼容写法或替代。
+
+遇到具体报错时,在下方「编译问题」「运行时问题」「样式问题」中按类型排查。
+
+---
+
## 编译问题
### 1. chunk 文件缺失错误
diff --git a/.cursor/skills/web-to-miniprogram-conversion/SKILL.md b/.cursor/skills/web-to-miniprogram-conversion/SKILL.md
deleted file mode 100644
index ba89ce12..00000000
--- a/.cursor/skills/web-to-miniprogram-conversion/SKILL.md
+++ /dev/null
@@ -1,159 +0,0 @@
----
-name: web-to-miniprogram-conversion
-description: Ensures complete 1:1 style migration and visual parity when converting Next.js to WeChat Mini Program (Kbone). Covers style compatibility mapping,补齐兼容 for Grid/backdrop/sticky/shadow/selectors, and visual parity checklist. Use when migrating Web to miniprogram, doing style conversion, or when the user asks for 100% style migration or visual consistency.
----
-
-# Web 转小程序转化过程技能
-
-本技能提供「Web 前端转微信小程序」时的注意事项与清单,与 Kbone 配置互补使用。
-
-**前置**:Kbone 搭建与配置见 [.cursor/skills/kbone-miniprogram/SKILL.md](../kbone-miniprogram/SKILL.md)。本文聚焦**转化过程**中的样式、动态 UI、请求、存储、路由等细节。
-
----
-
-## 样式迁移目标与原则
-
-**目标**:Next 转小程序过程中,实现 **完整、1:1、100% 样式迁移**,小程序端与 Web 端**视觉一致**。
-
-**原则**:
-- 两端框架 CSS 能力有差异时,**不做删减**,而是做**补齐兼容**:用小程序支持的写法实现**等价的视觉效果**(颜色、字号、间距、圆角、对齐、安全区等一致)。
-- 凡无法用原生 CSS 完全复刻的(如 backdrop-filter、多阴影、部分选择器),用**视觉等价替代**(如毛玻璃→同色半透明、多阴影→单阴影或边框)并记录在 [reference.md - 样式兼容映射表](reference.md#web小程序-样式兼容映射表)。
-- 验收标准:同一页面在 Web 与小程序上并排对比,**字体、颜色、间距、圆角、对齐、安全区**等肉眼无差异。
-
----
-
-## 一、样式转化与补齐兼容
-
-### 1.1 布局:Grid → Flex(视觉等价)
-
-- 小程序对 CSS Grid 支持不完整,易错位。
-- **补齐兼容**:所有 `grid` / `grid-cols-*` 改为 Flex,**保持视觉一致**:
- - 父:`display: flex`,`flexDirection: 'row'`(多列)或 `'column'`(多行);`flexWrap: 'wrap'` 若需换行。
- - 子:`flex: 1` 平分,或 `flex: '0 0 25%'` 等固定比例;**宽度/比例与 Web 上列数一致**(如 4 列即每项约 25%)。
- - **gap**:小程序支持则用 `gap`;不支持则用子项 `margin`(如 `marginRight`/`marginBottom`)或父 `padding`,**数值与 Web 一致**(如 gap-3 → 12px 或 24rpx)。
-- 涉及:底部导航、统计卡片、菜单列表、弹窗内多列、Dialog 居中(用 `flex` + `alignItems: 'center'` + `justifyContent: 'center'`)等。
-
-### 1.2 必加属性(保证视觉一致)
-
-- **box-sizing**:有 padding/border 的容器**必须**加 `boxSizing: 'border-box'`(或 Tailwind `box-border`),避免撑破布局导致错位、溢出。
-- **line-height**:图标+文字、多行文案处**显式**设 `lineHeight`(如 1、1.2、1.5),与 Web 一致,避免垂直错位。
-- **width**:需要占满的块加 `width: '100%'` 或 `flex: 1`,避免窄屏下宽度不一致。
-
-### 1.3 需替换或降级的样式(视觉等价补齐)
-
-| 类型 | Web 常见用法 | 小程序补齐兼容(视觉一致) |
-|------|--------------|----------------------------|
-| 毛玻璃 | `backdrop-filter` / `backdrop-blur-*` | 取 Web 背景主色 + 透明度:如 `backgroundColor: 'rgba(0,0,0,0.95)'` 或 `'rgba(28,28,30,0.95)'`,**与 Web 视觉效果接近**,不删该区域。 |
-| 吸顶 | `position: sticky` | `position: 'fixed'`,`top: 0`,`left/right: 0`,**同高同背景**;页面内容区加 `paddingTop` 与顶栏高度一致,避免被遮挡。 |
-| 安全区 | `env(safe-area-inset-*)` | 小程序支持则保留;构建后确认 WXSS 未丢失;`.safe-top`/`.safe-bottom` 在小程序端用相同 `padding` 或类实现,**数值一致**。 |
-| 多阴影 | `box-shadow: a, b, c` | Skyline 仅支持单阴影:保留主阴影或用 `border` + 单 `boxShadow` 近似,**颜色与扩散与 Web 主阴影一致**。 |
-| 伪类间距 | `:last-child { margin }` | 改为给最后一项加 class 或内联 `marginBottom: 0`,其余项统一 `marginBottom`,**间距数值与 Web 一致**。 |
-
-### 1.4 Tailwind / CSS 变量
-
-- Tailwind v4 的 `@theme inline`、`@layer` 等需确认 Kbone 构建链支持;`:root` 的 CSS 变量(如 oklch)一般可用,若有问题再在构建侧替换为固定值。
-- 单位:若 Kbone 配置 `global.rem: true`,Tailwind 的 rem 会按小程序规范转换。
-
-### 1.5 样式补充(选择器、单位、Skyline、动画)
-
-- **WXSS 选择器**:仅支持 `.class`、`#id`、`element`、`element, element`、`::before`/`::after`;**不支持**通配 `*`、属性选择器 `[attr]`、伪类 `:hover`/`:not()`/`:first-child` 等(Skyline 高版本起部分伪类支持,需看基础库)。
-- **单位**:竖屏以 **rpx** 为主(规定屏幕宽 750rpx);字体建议 rpx 或页面设 `page` 初始字号,避免 px 随系统字体变化;横屏可用 **vmin**(100rpx ≈ 100vmin/7.5)。
-- **Skyline 渲染**:若启用 Skyline,默认 `display: flex`、`box-sizing: border-box`、`flex-direction: column`,与 Web 不同;可配置 `defaultDisplayBlock: true`、`defaultContentBox: true` 对齐 Web。`overflow: scroll` 不支持,需用 `scroll-view`;`position: sticky` 用组件 `sticky-header`/`sticky-section` 替代;`backdrop-filter` 有多项限制(多函数、与 opacity 混用、blur 表现不一致);`box-shadow` 不支持多阴影叠加。
-- **动画与性能**:CSS 动画(transform、keyframes)会影响性能,宜少用;静态样式写 class,动态写 style,避免全部塞进 style。
-- 更多见 [reference.md - 样式补充(联网核查)](reference.md#样式补充联网核查)。
-
-### 1.6 视觉一致验收
-
-- **单位统一**:Web 用 rem/px 的数值,小程序侧用 rpx 或 rem(Kbone 开启 rem 时)时,**换算后与 Web 视觉一致**(如 16px → 32rpx 或 1rem 按基准换算)。
-- **颜色**:CSS 变量、hex、rgba 等在小程序侧**原样保留或替换为同值**(oklch 若不支持则转为 rgb/hex)。
-- **字体与行高**:`fontSize`、`fontWeight`、`lineHeight` 与 Web 一致;图标+文字对齐用 `lineHeight` 与 `alignItems: 'center'` 保证。
-- **间距与圆角**:padding、margin、borderRadius 数值与 Web 一致(px→rpx 按 750 基准换算)。
-- **对齐**:flex 的 `alignItems`、`justifyContent` 与 Web 一致;多列时子项 `flex: 1` 或比例与 Web 列数一致。
-- 完整检查项见 [reference.md - 视觉一致检查清单](reference.md#视觉一致检查清单);兼容映射见 [reference.md - Web→小程序 样式兼容映射表](reference.md#web小程序-样式兼容映射表)。
-
----
-
-## 二、不能「配置化」的 UI(API 控制)
-
-以下均需**自定义组件/页面 + 请求同一套 API**,不能依赖 app.json 的静态配置。
-
-### 2.1 底部导航
-
-- **Tab 项动态显隐**(如按 `features.matchEnabled` 显示「找伙伴」):必须用**自定义底部栏**,在组件内请求配置接口,根据返回渲染项;跳转用 `wx.reLaunch` 等,不配置原生 tabBar。
-- **按页面隐藏底部栏**:无 `usePathname()`,需按「当前页面路径」判断(各页是否渲染 BottomNav,或在公共组件内维护不展示的页面列表)。
-
-### 2.2 功能开关
-
-- 找伙伴/推广/搜索/关于等若由后端 `feature_config` 控制:各页面/组件请求配置后按 `features.*` 条件渲染;小程序端与 Web 同一套逻辑,仅请求方式走适配层。
-
-### 2.3 配置驱动的内容
-
-- 匹配类型·次数·价格、书籍章节·价格、支付方式展示:凡从后端配置来的,均在**页面内请求接口再渲染**,不能写死在小程序配置里。
-
----
-
-## 三、请求与域名
-
-- **CORS**:小程序请求 API **不涉及 CORS**(CORS 是浏览器策略);服务端无需为小程序加 CORS 头。
-- **合法域名**:小程序必须在微信公众平台配置「请求合法域名」,否则 `wx.request` 报 `url not in domain list`;本地调试可设 `urlCheck: false`。
-- **baseURL**:小程序内需使用完整 API 地址(如 `https://soul.quwanzhi.com`),相对路径 `fetch('/api/xxx')` 会失败;适配层用 `getApp().globalData.baseUrl + path` 或等价方式。
-
----
-
-## 四、路由与导航
-
-- 所有跳转走**适配层**:小程序用 `wx.navigateTo` / `wx.reLaunch` / `wx.redirectTo` / `wx.navigateBack`,Web 用 location/history;不用 Next.js `Link` / `useRouter`。
-- 动态参数:`/read/[id]` 在小程序为 `/pages/read/read?id=xxx`,参数从 `getCurrentPages()[].options` 或适配层 `getPageQuery()` 获取。
-- 底部 Tab 因使用自定义导航,用 `wx.reLaunch`,不用 `wx.switchTab`。
-
----
-
-## 五、存储与持久化
-
-- **localStorage**:全部走适配层,小程序端用 `wx.getStorageSync` / `wx.setStorageSync`(如 match 页联系方式、匹配次数、自动提现配置等)。
-- **Zustand persist**:需为 persist 提供自定义 storage(小程序环境用 wx 的 storage),否则登录态、设置等无法持久化。
-
----
-
-## 六、登录与支付
-
-- **微信登录**:若小程序用微信登录,走 `wx.login` 取 code 再调后端换 session;与 Web 手机号/密码逻辑不同,需分支或适配。
-- **支付**:小程序内用 `wx.requestPayment`;「展示哪些支付方式」仍可由现有 `settings.paymentMethods` 等 API 配置控制。
-
----
-
-## 七、环境与 API 兼容
-
-- **window / document**:使用前加 `if (!isMiniProgram())` 或 `window?.xxx`,避免报错。
-- **URLSearchParams**:小程序不支持,需自实现 `buildQueryString` 或 polyfill。
-- **标题**:用 `wx.setNavigationBarTitle`,不用 `document.title`。
-
----
-
-## 八、第三方库
-
-- **framer-motion**:Kbone 不支持,改用 CSS 动画或 `wx.createAnimation`,或移除动画。
-- **Radix UI**:部分依赖 DOM/Portal,小程序可能不可用;用内联样式 + 自写组件替代(Dialog、Tabs、Select 等)。
-
----
-
-## 九、构建与包体
-
-- 主包 ≤ 2MB,总分包 ≤ 20MB;必要时分包或减依赖。
-- 中小项目建议 `splitChunks: false`,避免动态 chunk 缺失。
-- Kbone 产出需与现有 miniprogram 壳合并(custom-tab-bar、globalData、project.config 等)。
-
----
-
-## 十、测试与发布
-
-- 开发阶段可勾选「不校验合法域名」;体验版/真机须配置合法域名,且关闭 `urlCheck: false`。
-- 真机重点测:请求、支付、登录、存储、底部栏显隐、各功能开关。
-
----
-
-## 参考
-
-- **样式 1:1 迁移**:[reference.md - Web→小程序 样式兼容映射表](reference.md#web小程序-样式兼容映射表)、[视觉一致检查清单](reference.md#视觉一致检查清单)、[补齐兼容实施步骤](reference.md#补齐兼容实施步骤1-1-样式迁移)。
-- 完整转化清单与更多细节见 [reference.md](reference.md)。
-- Kbone 配置与故障排查见 [../kbone-miniprogram/](../kbone-miniprogram/)。
diff --git a/.cursor/skills/web-to-miniprogram-conversion/reference.md b/.cursor/skills/web-to-miniprogram-conversion/reference.md
deleted file mode 100644
index 573fc8a5..00000000
--- a/.cursor/skills/web-to-miniprogram-conversion/reference.md
+++ /dev/null
@@ -1,223 +0,0 @@
-# Web 转小程序转化过程 - 详细清单
-
-与 SKILL.md 配套使用,提供完整检查项与说明。**目标**:样式 1:1、100% 迁移,视觉与 Web 一致;有差异处做补齐兼容。
-
----
-
-## Web→小程序 样式兼容映射表
-
-| Web(Next/CSS) | 小程序能力 | 补齐兼容(视觉一致) |
-|-----------------|------------|----------------------|
-| `display: grid` / `grid-cols-N` | Grid 支持不完整 | 改为 `display: flex`,子项 `flex: 1` 或 `flex: 0 0 (100/N)%`,**列数、间距与 Web 一致** |
-| `gap: 12px` | 部分支持 gap | 用子项 `marginRight`/`marginBottom` 或父 padding,**数值与 Web 一致**(12px→24rpx 等) |
-| `backdrop-filter` / `backdrop-blur-*` | 支持差/限制多 | 取 Web 背景主色 + 透明度:`backgroundColor: 'rgba(r,g,b,0.95)'`,**视觉接近毛玻璃** |
-| `position: sticky` | 不支持或仅组件 | `position: fixed` + 内容区 `paddingTop: 顶栏高度`,**顶栏高度、背景与 Web 一致** |
-| `box-shadow: 多值` | 仅单阴影 | 保留主阴影或 `border` + 单 `boxShadow`,**颜色、模糊、偏移与 Web 主阴影一致** |
-| `:hover` / `:last-child` 等伪类 | 不支持或部分 | 用 class/内联样式:如最后一项加 `marginBottom: 0`,其余统一 margin,**间距数值一致** |
-| `env(safe-area-inset-*)` | 支持 | 保留;确认构建后 WXSS 未丢失;`.safe-top`/`.safe-bottom` 等价实现,**数值一致** |
-| `box-sizing: content-box`(默认) | Skyline 默认 border-box | 需与 Web 一致时配置 `defaultContentBox: true` 或显式设 `boxSizing: 'content-box'` |
-| `rem` / `px` | rpx / rem | 统一基准:750 宽→1px=2rpx;rem 由 Kbone 转换;**换算后视觉一致** |
-| oklch / 复杂颜色 | 部分支持 | 不支持的用 rgb/hex 替换,**色值一致** |
-| 多 `background-image` | 仅单图 | 保留主图或合并为单图,**视觉近似** |
-
----
-
-## 视觉一致检查清单
-
-每页迁移后按下列项与 Web 并排对比,确保**肉眼无差异**。
-
-### 全局
-
-- [ ] 页面背景色与 Web 一致
-- [ ] 安全区(顶部/底部/左右)与 Web 一致(有无留白、留白多少)
-- [ ] 全局字体大小、行高、字重与 Web 一致
-
-### 布局
-
-- [ ] 多列数量与 Web 一致(如 4 列、3 列)
-- [ ] 列间距、行间距与 Web 一致(gap 或 margin 换算正确)
-- [ ] 顶栏/底栏高度、内边距与 Web 一致
-- [ ] 吸顶区域(若有)与 Web 视觉一致(fixed + 占位)
-
-### 组件与模块
-
-- [ ] 卡片/列表项:圆角、内边距、边框、背景与 Web 一致
-- [ ] 按钮:高度、圆角、字号、背景/边框与 Web 一致
-- [ ] 图标+文字:垂直对齐、间距与 Web 一致(lineHeight、alignItems)
-- [ ] 弹窗/遮罩:背景透明度、圆角与 Web 一致(毛玻璃→同色半透明)
-
-### 文字与颜色
-
-- [ ] 标题/正文字号、颜色、字重与 Web 一致
-- [ ] 链接/强调色与 Web 一致
-- [ ] 占位符、禁用态颜色与 Web 一致
-
-### 阴影与装饰
-
-- [ ] 主阴影(单阴影或近似)与 Web 主阴影颜色、模糊、偏移一致
-- [ ] 装饰性 blur 可保留或简化,**不改变布局与主色**
-
-### 验收通过标准
-
-- 同一页面在 Web 与小程序并排对比,**字体、颜色、间距、圆角、对齐、安全区**无肉眼差异;有差异的已记录为「视觉等价替代」并符合上表。
-
----
-
-## 补齐兼容实施步骤(1:1 样式迁移)
-
-1. **扫一遍 Web 样式**:列出所有 Grid、backdrop、sticky、多阴影、伪类间距、单位(rem/px)。
-2. **按映射表替换**:用 [Web→小程序 样式兼容映射表](#web小程序-样式兼容映射表) 逐项改为小程序等价写法,**数值与 Web 一致**(单位换算:750 宽下 1px≈2rpx)。
-3. **必加属性**:有 padding/border 的容器加 `boxSizing: 'border-box'`;图标+文字处加 `lineHeight`;需占满的加 `width: '100%'` 或 `flex: 1`。
-4. **Skyline 若启用**:与 Web 不一致时配置 `defaultDisplayBlock`、`defaultContentBox`;overflow 用 `scroll-view`;sticky 用组件或 fixed+占位。
-5. **验收**:按 [视觉一致检查清单](#视觉一致检查清单) 与 Web 并排对比,逐项打勾。
-
----
-
-## 一、样式转化清单
-
-### Grid 需改为 Flex 的典型位置(视觉等价)
-
-- 首页「我的阅读」统计区(grid-cols-4)→ flex,子项 flex: 1,**4 列间距与 Web 一致**
-- 我的页统计卡片、菜单区(grid-cols-3)→ flex,子项 flex: 1,**3 列间距一致**
-- 找伙伴页统计/列表(grid-cols-4)→ 同上
-- 弹窗内多列(grid-cols-2 / grid-cols-3)→ flex,**列数、gap 一致**
-- 目录/章节列表多列、书籍介绍多列 → flex,**比例与间距一致**
-- Dialog 居中:用 `display: flex; align-items: center; justify-content: center` 替代 `display: grid`,**居中效果一致**
-
-### 需替换或降级的样式(视觉等价补齐)
-
-- `backdrop-blur-*`:底部导航、顶栏、弹窗遮罩、glass-card → **取 Web 背景主色 + 透明度**(如 rgba(28,28,30,0.95)),视觉接近毛玻璃
-- `position: sticky`:各页顶栏 → fixed + 内容区 paddingTop 占位,**顶栏高度、背景与 Web 一致**
-- 装饰性 `blur-3xl` 等:可保留(对布局无影响);性能差再考虑简化,**不改变主色与布局**
-- 多阴影:保留主阴影或单阴影+边框,**颜色、模糊与 Web 主阴影一致**
-
-### 安全区
-
-- `env(safe-area-inset-*)` 小程序支持;确认构建后 WXSS 中未丢失
-- `.safe-top` / `.safe-bottom` 类需在小程序端有等价实现,**padding 数值与 Web 一致**
-
----
-
-## 样式补充(联网核查)
-
-以下基于微信官方 WXSS / Skyline 文档与社区实践整理,转化时可按需核对。
-
-### WXSS 选择器
-
-| 支持 | 不支持或部分支持 |
-|------|-------------------|
-| `.class`、`#id`、`element`、`element, element`、`::before`、`::after` | 通配 `*`、属性选择器 `[attr]` |
-| (Skyline 8.0.49+)`:first-child`、`:last-child`;(8.0.50+)`:nth-child`、`:not`、`:only-child`、`:empty` | 传统 WXSS 下伪类如 `:hover`、`:not()`、`:first-child` 等可能不支持,需以 JS 控制状态替代 |
-
-- 依赖 `:last-child`、`:not()` 等做间距/边框时,改为给子项加 class 或内联样式(如 `marginRight`、`borderBottom`)。
-
-### 单位与适配
-
-- **rpx**:规定屏幕宽 750rpx;设计稿 750px 可直接 1:1 转 rpx。竖屏布局、字体建议用 rpx,避免 px 随系统字体缩放。
-- **横屏**:rpx 以宽度为基准,横屏会变;若支持横屏,可用 **vmin**(如 `100rpx ≈ 100vmin/7.5`)或动态计算。
-- **rem**:Kbone 可开 `global.rem: true`,Tailwind 的 rem 会按小程序规范转换;与 rpx 二选一或统一策略。
-
-### Skyline 渲染引擎差异(若启用)
-
-- **默认值**:`display: flex`、`box-sizing: border-box`、`flex-direction: column`、`position: relative`,与 Web 不同;布局错乱时可配置:
- - `rendererOptions.skyline.defaultDisplayBlock: true` → 默认 block
- - `rendererOptions.skyline.defaultContentBox: true` → 默认 content-box
-- **不支持**:通配 `*`、属性选择器;`overflow: scroll`(用 `scroll-view`);单独 `overflow-x`/`overflow-y`;`position: sticky`(用 `sticky-header`/`sticky-section`);inline / inline-block 布局(或仅 text 内部分支持)。
-- **限制**:`fixed` 的 top/left/bottom/right 不支持 `auto`;z-index 无 Web 层叠上下文,只对兄弟节点生效;`box-shadow` 不支持多阴影叠加;`backdrop-filter` 不支持多函数、drop-shadow、url,与 opacity 混用有问题,blur 部分场景不一致;SVG 不支持 rgba,可用 fill-opacity。
-- **Font-face**:仅支持 ttf。
-
-### 内联样式与性能
-
-- 官方建议:**静态样式写 class**,**style 只放动态样式**,避免全部塞进 style 影响解析与渲染。
-- Kbone 若大量使用内联 style,需权衡;可把不变部分抽成 class,动态部分再合并进 style。
-
-### 动画
-
-- CSS 动画(transform、keyframes)会拉高渲染开销,宜少用、简化。
-- Skyline 可用 Worklet 动画;传统 view 动画可用 `wx.createAnimation`。
-
-### Kbone 样式相关
-
-- 尽量用 **HTML 标签**(如 ``、`
`)而非小程序内置组件,减少包裹层对样式的影响。
-- 用 webpack `DefinePlugin` 注入 `process.env.isMiniprogram`,便于样式与逻辑按端分支。
-
----
-
-## 二、API 控制 UI 清单(不能配置化)
-
-| 场景 | 当前实现 | 小程序做法 |
-|------|----------|------------|
-| 底部 Tab 数量与「找伙伴」显隐 | `/api/db/config` → `features.matchEnabled` | 自定义底部栏 + 同一配置接口 + `wx.reLaunch` |
-| 按页面隐藏底部栏 | `usePathname()` 判断 | 按当前页面路径或各页是否挂载 BottomNav |
-| 找伙伴/推广/搜索/关于开关 | `feature_config.*`(若接上) | 各页请求配置后条件渲染 |
-| 匹配类型·次数·价格 | 可改为请求 `match_config` | 页面内请求并渲染 |
-| 书籍章节·价格 | 可改为请求 `book_config`/`price_config` | 页面内请求并渲染 |
-| 支付方式展示 | `settings.paymentMethods` | 同一配置,支付调起用 `wx.requestPayment` |
-
----
-
-## 三、构建与配置清单
-
-- [ ] miniprogram.config.js:router 每页单独配置,无 other 数组
-- [ ] webpack entry 与 router 键名一致
-- [ ] global.rem / pageStyle 按需开启
-- [ ] 主包 ≤ 2MB;必要时分包
-- [ ] splitChunks:中小项目建议 false
-- [ ] 合并脚本:dist/mp 与 miniprogram 壳合并(custom-tab-bar、globalData、project.config)
-
----
-
-## 四、路由与导航清单
-
-- [ ] 所有跳转走适配层(navigateTo / reLaunch / redirectTo / navigateBack)
-- [ ] 底部 Tab 用 reLaunch(因自定义导航)
-- [ ] 动态参数用 query:/read/[id] → ?id=xxx,从 getCurrentPages()[].options 或 getPageQuery() 取
-- [ ] 无 usePathname;按页面路径或各页挂载逻辑控制底部栏显隐
-
----
-
-## 五、请求与域名清单
-
-- [ ] 小程序不涉及 CORS;服务端无需为小程序加 CORS 头
-- [ ] 公众平台配置「请求合法域名」(如 https://soul.quwanzhi.com)
-- [ ] 请求 baseURL 为完整域名;适配层拼接 baseUrl + path
-- [ ] 本地调试可 urlCheck: false;发布前恢复并确认域名
-
----
-
-## 六、存储与持久化清单
-
-- [ ] 所有 localStorage 走适配层(wx.getStorageSync / wx.setStorageSync)
-- [ ] Zustand persist 提供自定义 storage(小程序用 wx storage)
-- [ ] match 页:联系方式、当日匹配次数等 key 在适配层统一实现
-
----
-
-## 七、登录与支付清单
-
-- [ ] 微信登录:wx.login + code 换 session,与 Web 逻辑分支或适配
-- [ ] 支付:小程序内用 wx.requestPayment;展示哪些方式仍由 API 配置控制
-
----
-
-## 八、环境与 API 兼容清单
-
-- [ ] window/document 使用前判断或可选链
-- [ ] URLSearchParams 用自实现或 polyfill
-- [ ] 页面标题用 wx.setNavigationBarTitle
-
----
-
-## 九、第三方库清单
-
-- [ ] framer-motion:移除或改为 CSS / wx.createAnimation
-- [ ] Radix UI:用内联样式 + 自写组件替代(Dialog、Tabs、Select 等)
-- [ ] 其他依赖 DOM/BOM 的库:条件加载或替换
-
----
-
-## 十、测试与发布清单
-
-- [ ] 开发阶段:不校验合法域名可开启
-- [ ] 体验版/真机:合法域名已配置,urlCheck 已关闭
-- [ ] 真机验证:请求、支付、登录、存储、底部栏显隐、功能开关
diff --git a/app/my/page.tsx b/app/my/page.tsx
index b9d40f39..1de3622b 100644
--- a/app/my/page.tsx
+++ b/app/my/page.tsx
@@ -10,7 +10,7 @@ import { getFullBookPrice, getTotalSectionCount } from "@/lib/book-data"
export default function MyPage() {
const router = useRouter()
- const { user, isLoggedIn, logout, getAllPurchases, settings, updateUser } = useStore()
+ const { user, isLoggedIn, logout, getAllPurchases, settings, updateUser, refreshUserProfile } = useStore()
const [showAuthModal, setShowAuthModal] = useState(false)
const [mounted, setMounted] = useState(false)
const [activeTab, setActiveTab] = useState<"overview" | "footprint">("overview")
@@ -46,6 +46,13 @@ export default function MyPage() {
loadConfig()
}, [])
+ // 每次进入「我的」页都刷新可提现金额等用户资料
+ useEffect(() => {
+ if (mounted && isLoggedIn && user?.id && refreshUserProfile) {
+ refreshUserProfile()
+ }
+ }, [mounted, isLoggedIn, user?.id])
+
// 绑定账号
const handleBind = async () => {
if (!bindValue.trim()) {
diff --git a/lib/store.ts b/lib/store.ts
index e036d5cb..d973cd61 100644
--- a/lib/store.ts
+++ b/lib/store.ts
@@ -193,6 +193,8 @@ interface StoreState {
getLiveQRCodeUrl: (qrId: string) => string | null
exportData: () => string
fetchSettings: () => Promise
+ /** 从服务端刷新当前用户资料(含可提现金额等),进入「我的」页时调用 */
+ refreshUserProfile: () => Promise
}
const initialSettings: Settings = {
@@ -542,6 +544,31 @@ export const useStore = create()(
}
},
+ refreshUserProfile: async () => {
+ const { user } = get()
+ if (!user?.id) return
+ try {
+ const res = await fetch(`/api/user/profile?userId=${encodeURIComponent(user.id)}`)
+ const json = await res.json()
+ if (!json.success || !json.data) return
+ const d = json.data
+ set((state) =>
+ state.user
+ ? {
+ user: {
+ ...state.user,
+ earnings: typeof d.earnings === 'number' ? d.earnings : parseFloat(d.earnings) || state.user.earnings,
+ pendingEarnings: typeof d.pendingEarnings === 'number' ? d.pendingEarnings : parseFloat(d.pendingEarnings) || state.user.pendingEarnings,
+ referralCount: typeof d.referralCount === 'number' ? d.referralCount : Number(d.referralCount) || state.user.referralCount,
+ },
+ }
+ : state
+ )
+ } catch (e) {
+ console.warn('[Store] refreshUserProfile failed', e)
+ }
+ },
+
deleteUser: (userId: string) => {
if (typeof window === "undefined") return
const users = JSON.parse(localStorage.getItem("users") || "[]") as User[]