diff --git a/.cursor/skills/kbone-miniprogram/README.md b/.cursor/skills/kbone-miniprogram/README.md index c8927a0a..9ab95464 100644 --- a/.cursor/skills/kbone-miniprogram/README.md +++ b/.cursor/skills/kbone-miniprogram/README.md @@ -22,11 +22,18 @@ - 开发流程和最佳实践 - 快速检查清单 +### reference.md +Next → 小程序转化参考(详细步骤),包含: +- 架构与 newpp 配置、适配层、功能控制一致 +- 底部菜单必须用自定义组件(不要用原生 tabBar) +- UI 来源策略、样式兼容、**安全区与标题/胶囊避让** +- 构建与合并流程、检查清单 + ### troubleshooting.md 详细的故障排查指南,包含: -- 编译问题(chunk 文件、Babel、依赖) +- 从 Next 迁移要点、编译问题(chunk 文件、Babel、依赖) - 运行时问题(URLSearchParams、localStorage、window/document) -- 样式问题(Grid、盒模型、Flexbox) +- 样式问题(Grid、盒模型、**标题/头部被胶囊或电池栏遮挡**、Flexbox) - 路由问题(导航、switchTab、动态路由) - 网络问题(API 请求、跨域) - 性能问题(加载慢、卡顿) @@ -46,9 +53,10 @@ Agent 应该在以下情况下自动应用此技能: - 需要配置 `webpack.mp.config.js` 2. **代码转换** - - 将 React Web 应用转换为小程序 + - 将 React Web 应用转换为小程序(含 Next 项目转化) - 需要创建跨平台适配层 - 处理 Web 和小程序的 API 差异 + - 底部菜单保留自定义组件(不要用原生 tabBar)、安全区与标题避让 3. **问题排查** - 编译错误(chunk 文件、Webpack 配置) @@ -129,10 +137,14 @@ function isMiniProgram() { | 问题 | 文档位置 | |------|---------| +| redirect 跳转异常(用了 home) | SKILL.md > 必记坑点 1 / troubleshooting.md > 配置问题 1 | +| Babel 报错(?. / ??) | SKILL.md > 必记坑点 2 / troubleshooting.md > 编译问题 2 | | chunk 文件缺失 | SKILL.md > 问题 2 | | URLSearchParams 错误 | SKILL.md > 问题 3 / troubleshooting.md > 运行时问题 1 | | CSS Grid 不生效 | SKILL.md > 问题 1 / troubleshooting.md > 样式问题 1 | -| 底部导航动态显示 | SKILL.md > 问题 4 | +| 底部菜单/导航(不要用原生 tabBar) | SKILL.md > 问题 4 / reference.md > 4.5 | +| 安全区/标题被胶囊或电池栏遮挡 | reference.md > 第 7 节 / troubleshooting.md > 样式问题 4 | +| 阅读页上下章/数据源 | SKILL.md > 必记坑点 3、4 | | 页面跳转失败 | troubleshooting.md > 路由问题 1 | | API 请求失败 | troubleshooting.md > 网络问题 1 | diff --git a/.cursor/skills/kbone-miniprogram/SKILL.md b/.cursor/skills/kbone-miniprogram/SKILL.md index 1160a06b..772a9cea 100644 --- a/.cursor/skills/kbone-miniprogram/SKILL.md +++ b/.cursor/skills/kbone-miniprogram/SKILL.md @@ -37,7 +37,7 @@ Kbone 是腾讯提供的 Web 与小程序同构解决方案,允许使用 React 2. **适配层(必须)** - `adapters/env.js`:`isMiniProgram()`(`typeof wx !== 'undefined' && wx.getSystemInfoSync`)。 - - `adapters/request.js`:Web 用 `fetch`,小程序用 `wx.request`,统一返回 Promise。 + - `adapters/request.js`:Web 用 `fetch`(相对路径同源);小程序用 `wx.request`,**必须带完整 baseUrl**。本项目线上 API 域名为 **`https://soul.quwanzhi.com`**(见开发文档 `开发文档/8、部署/当前项目部署到线上.md`)。转化时:小程序侧 `request(url)` 实际请求 `baseUrl + url`,`baseUrl` 来自 `getApp().globalData.baseUrl`,壳的 `app.js` 的 `onLaunch` 前应设置 `globalData.baseUrl = 'https://soul.quwanzhi.com'`(或从配置读),**不要写死为 your-domain.com**。 - `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` 控制「找伙伴」入口),不写死开关。 @@ -48,21 +48,26 @@ Kbone 是腾讯提供的 Web 与小程序同构解决方案,允许使用 React 4. **miniprogram.config.js 要点** - `router`:每个页面单独 key,例如 `index`、`chapters`、`read`、`my`、`match`,对应 `entry` 与小程序页面路径。 - - 动态底部导航:不配置原生 `tabBar`,在 `appExtraConfig` 中不写 `tabBar`;底部栏用 React 组件 + `wx.reLaunch` 跳转。 + - **底部菜单(重要)**:Web 端多为**自定义底部菜单组件**,有显隐逻辑(如某页不显示、根据 API 动态显隐某项),原生 tabBar 丑且可操作性差。转化时**不要**改为小程序原生 tabBar,应保留「主页面引入统一菜单组件」的方式:在 `appExtraConfig` 中**不配置** `tabBar`,各主页面(首页、目录、我的、找伙伴等)继续引入同一底部菜单组件,由组件控制显隐、样式与跳转(小程序内用 `wx.reLaunch`),与 Web 行为一致。 5. **样式兼容(与 Next 视觉一致)** - Grid → Flex(含 `boxSizing: 'border-box'`、必要时 `lineHeight`)。 - `backdrop-filter` / `position: sticky` 等不支持则用占位或纯色/渐变替代,保证布局不错乱。 - 单位:小程序侧可用 rpx,与 Web 的 rem/px 按设计稿做一次换算或共用同一套换算规则。 -6. **构建与合并** +6. **安全区与标题避让(必须)** + - **顶部/电池栏**:使用 **`navBarHeight`**(状态栏 + 胶囊区域总高),不用固定 `statusBarHeight + 44`。在壳的 `app.js` 的 `onLaunch` 里用 `wx.getSystemInfoSync()` + `wx.getMenuButtonBoundingClientRect()` 计算并写入 `globalData.navBarHeight`、`globalData.statusBarHeight`;无菜单按钮时回退 `statusBarHeight + 44`。每页顶部占位条高度设为 `navBarHeight`,内容区 `padding-top` 用 `statusBarHeight`。 + - **底部安全区**:底部导航/固定底栏必须加 `padding-bottom: env(safe-area-inset-bottom)`,避免在有底部刘海的设备上被遮挡。 + - **标题右侧防遮挡**:小程序右上角胶囊会覆盖标题/按钮。所有带标题或右侧按钮的头部容器必须**右侧留白**:用 `globalData.capsulePaddingRight` 内联 `padding-right`,或在全局样式中定义 `.safe-header-right { padding-right: 200rpx; box-sizing: border-box; }`,避免标题、返回按钮与胶囊重叠或被遮挡。若出现标题被挡,优先检查是否加了该留白。 + +7. **构建与合并** - 在 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`。 +- **底部菜单**:遵循「问题 4:底部导航动态显示」——**不要用原生 tabBar**;保留 Web 的自定义底部菜单组件,主页面统一引入该组件,显隐与样式逻辑与 Web 一致,跳转用 `wx.reLaunch`。 - **API/兼容**:遵循「跨平台适配层」与「问题 3:URLSearchParams」——全部走适配层,避免 Web 独有 API。 - **样式**:遵循「问题 1:样式错位」与 troubleshooting 中的 Grid/Flex、box-sizing、lineHeight 等。 @@ -78,7 +83,7 @@ Kbone 是腾讯提供的 Web 与小程序同构解决方案,允许使用 React ```javascript module.exports = { - origin: 'https://your-domain.com', + origin: 'https://soul.quwanzhi.com', // 本项目线上域名,见开发文档 8、部署 entry: '/', // ✅ 正确:每个页面单独配置 @@ -95,6 +100,12 @@ module.exports = { // other: ['/chapters', '/read/:id', ...], // }, + // ✅ redirect 必须用 router 里存在的页面名(如 index),不能写不存在的 'home' + redirect: { + notFound: 'index', + accessDenied: 'index', + }, + // 全局配置 global: { rem: true, // 支持 rem 单位 @@ -182,6 +193,15 @@ module.exports = { --- +## 必记坑点(项目实践) + +1. **redirect 必须用 router 里存在的页面名**:`notFound` / `accessDenied` 填 router 的 key(如 `index`),不要填不存在的 `home`。 +2. **Babel 6 不支持 ?. 和 ??**:官方 React 模板是 Babel 6 + stage-3,用 `(x && x.y)` 替代 `x?.y`,用 `x != null ? x : default` 替代 `x ?? default`,否则 build:mp 报 `Unexpected token`。 +3. **阅读页内容与上下章用同一数据源**:用 `useChapterContent` + `useChapters` 的 getNextSection/getPrevSection,不要混用静态 bookData 与 API。 +4. **useChapters 需暴露 getNextSection / getPrevSection**:有阅读页且 API 驱动时,供上一章/下一章使用。 + +--- + ## 常见问题解决 ### 问题 1:样式错位 @@ -270,27 +290,30 @@ const queryString = buildQueryString({ key: 'value', page: 1 }) --- -### 问题 4:底部导航动态显示 +### 问题 4:底部导航动态显示(必须用自定义组件,不要用原生 tabBar) -**场景**:需要根据 API 配置动态显示/隐藏某些导航项 +**背景**:Web 端通常用**自定义底部菜单组件**,主页面统一引入,有显隐逻辑(如某路由不显示菜单、根据 API 动态显示/隐藏「找伙伴」等项);原生 tabBar 样式丑、可操作性差,且无法灵活控制显隐。 -**方案**:使用完全自定义的导航组件,不配置原生 tabBar +**要求**:转化时**不要**把 Web 的自定义底部菜单改成小程序原生 `tabBar`,应保留「统一菜单组件」方式。 + +**方案**: +- 在 `appExtraConfig` 中**不配置** `tabBar`。 +- 各主页面(首页、目录、我的、找伙伴等)继续引入**同一底部菜单组件**(如 BottomNav),由该组件负责:显隐(与 Web 一致,如文档/阅读页不显示)、菜单项动态显隐(如根据 `matchEnabled` 显示/隐藏「找伙伴」)、样式与跳转。 +- 跳转使用 `wx.reLaunch`(因未配置原生 tabBar,不能用 `wx.switchTab`)。 ```javascript // miniprogram.config.js appExtraConfig: { sitemapLocation: 'sitemap.json', - // ✅ 不配置 tabBar,使用完全自定义的导航组件 - // 原因:需要根据 API 配置动态显示/隐藏功能 + // ✅ 不配置 tabBar,保留 Web 的自定义底部菜单组件 + // 原因:显隐逻辑、样式与可操作性需与 Web 一致 }, ``` ```javascript -// router adapter +// router adapter(底部菜单点击时) export function switchTab(path) { if (isMiniProgram()) { - // ✅ 使用 wx.reLaunch 代替 wx.switchTab - // 原因:没有配置原生 tabBar wx.reLaunch({ url: toMpPath(path) }) } else { window.location.href = path === '/' ? 'index.html' : path.replace(/^\//, '') + '.html' @@ -321,12 +344,14 @@ export function navigateTo(path) { } } -// adapters/request.js +// adapters/request.js(小程序侧必须用完整 URL,baseUrl 见 globalData) export function request(url, options) { if (isMiniProgram()) { + const baseUrl = (typeof getApp === 'function' && getApp().globalData?.baseUrl) || 'https://soul.quwanzhi.com' + const fullUrl = url.startsWith('http') ? url : baseUrl + url return new Promise((resolve, reject) => { wx.request({ - url, + url: fullUrl, method: options.method || 'GET', data: options.body, success: res => resolve(res.data), @@ -464,13 +489,13 @@ appExtraConfig: { } ``` -**动态导航**:使用自定义组件 +**动态导航 / Web 自定义底部菜单**:必须用自定义组件,不要用原生 tabBar ```javascript appExtraConfig: { // 不配置 tabBar } -// 使用 React 组件实现导航 -// 使用 wx.reLaunch 进行跳转 +// Web 端多为统一底部菜单组件,主页面引入;转化时保留该方式: +// 各主页面引入同一 BottomNav 等组件,显隐/样式与 Web 一致,跳转用 wx.reLaunch ``` --- @@ -480,6 +505,7 @@ appExtraConfig: { ### 配置检查 - [ ] router 每个页面单独配置 +- [ ] **redirect.notFound / accessDenied 使用 router 中存在的页面名(如 index)** - [ ] pages 配置了所有页面标题 - [ ] global 启用了 rem 和 pageStyle - [ ] webpack mode 根据环境判断 @@ -489,6 +515,7 @@ appExtraConfig: { - [ ] 所有 CSS Grid 替换为 Flexbox - [ ] 添加了 boxSizing: 'border-box' +- [ ] **没有使用可选链 ?. 和空值合并 ??(Babel 6 不支持)** - [ ] 没有使用 URLSearchParams - [ ] 没有使用其他 Web 独有 API diff --git a/.cursor/skills/kbone-miniprogram/reference.md b/.cursor/skills/kbone-miniprogram/reference.md index 8822eb38..1d7dbd08 100644 --- a/.cursor/skills/kbone-miniprogram/reference.md +++ b/.cursor/skills/kbone-miniprogram/reference.md @@ -21,7 +21,8 @@ ### 2.1 miniprogram.config.js - `router`:每个页面单独 key,对应 Next 路由,**不要使用 `other` 数组**。 -- 动态底部导航:`appExtraConfig` 中**不配置** `tabBar`,用自定义 React 组件 + `wx.reLaunch`。 +- **`redirect`**:`notFound`、`accessDenied` 必须填 **router 里存在的页面名**(如 `index`),不要填不存在的 `home`。 +- **底部菜单**:`appExtraConfig` 中**不配置** `tabBar`。Web 端多为自定义底部菜单组件(主页面统一引入、有显隐逻辑),转化时**不要**改为小程序原生 tabBar,保留「统一菜单组件」+ `wx.reLaunch` 跳转。 - `global`:建议 `rem: true`、`pageStyle: true`。 - `pages`:为每个页面配置 `extra.navigationBarTitleText`。 @@ -56,12 +57,18 @@ appExtraConfig: { | 文件 | 职责 | |------|------| | `adapters/env.js` | `isMiniProgram()`:`typeof wx !== 'undefined' && wx.getSystemInfoSync` | -| `adapters/request.js` | Web:`fetch`;小程序:`wx.request`,统一返回 Promise | +| `adapters/request.js` | Web:`fetch`(相对路径);小程序:`wx.request`,**必须带完整 baseUrl**,本项目线上域名为 `https://soul.quwanzhi.com`(见开发文档 8、部署) | | `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` 等。 +**请求基地址(转化时必须处理)**:小程序端不能使用相对路径,需用完整 URL。本项目线上 API 域名为 **`https://soul.quwanzhi.com`**,见 `开发文档/8、部署/当前项目部署到线上.md`。转化时: + +- 在**壳**的 `miniprogram/app.js` 的 `globalData` 中设置 `baseUrl: 'https://soul.quwanzhi.com'`(或从环境/配置读取)。 +- 在 **adapters/request.js** 中,小程序分支:`url` 若不以 `http` 开头则拼上 `getApp().globalData.baseUrl`,再调用 `wx.request`。 +- 不要写死为 `your-domain.com` 或其它占位域名。 + --- ## 4. 功能控制一致 @@ -74,6 +81,18 @@ appExtraConfig: { --- +## 4.5 底部菜单:必须用自定义组件,不要用原生 tabBar + +- **原因**:Web 端底部菜单多为**自定义组件**(如 `BottomNav`),主页面统一引入,有「某页不显示菜单」「根据 API 显隐某项」等逻辑;原生 tabBar 样式与可操作性差,且无法等价实现上述逻辑。 +- **要求**:转化时**不要**把自定义底部菜单改成小程序原生 `tabBar`。 +- **做法**: + - `appExtraConfig` 中不配置 `tabBar`。 + - 各主页面(首页、目录、我的、找伙伴等)**继续引入同一底部菜单组件**,由该组件控制:是否在本页显示、各菜单项显隐(如 `matchEnabled` 控制「找伙伴」)、样式、点击跳转(小程序内用 `wx.reLaunch`)。 + - 与 Web 一致:阅读页、文档页、关于页等可不渲染该组件或由布局隐藏。 +- **检查**:若发现被改成了原生 tabBar,应回退为「主页面引入统一菜单组件」的方式。 + +--- + ## 5. UI 来源策略 - **复制适配**:从 Next 的 `app/**/page.tsx`、`components/**` 复制到 newpp,改为 JSX,用适配层替代 `Link`/`useRouter`/`usePathname`/`fetch`。 @@ -91,7 +110,40 @@ appExtraConfig: { --- -## 7. 构建与合并流程 +## 7. 安全区与标题/胶囊避让(必须) + +### 7.1 电池栏/顶部安全区 + +- **问题**:使用自定义导航栏(`navigationStyle: 'custom'`)时,状态栏(电池、时间)会与页面顶部重叠,若不做占位,标题会被挡。 +- **做法**: + - 在**壳**的 `app.js` 的 `onLaunch` 中**同步**获取系统信息并计算占位高度,写入 `globalData`: + - `statusBarHeight`:`wx.getSystemInfoSync().statusBarHeight || 44` + - `navBarHeight`:用 `wx.getMenuButtonBoundingClientRect()` 计算「状态栏 + 胶囊区域」总高,公式:`menuButton.bottom + menuButton.top - systemInfo.statusBarHeight`;无菜单按钮时回退 `statusBarHeight + 44` + - `capsulePaddingRight`:`systemInfo.windowWidth - menuButton.left + 10`,供标题右侧留白使用 + - 每个页面的**顶部占位条**高度设为 `navBarHeight`(px);占位条内部若需区分状态栏与导航内容,可用 `padding-top: {{ statusBarHeight || 44 }}px`。 + - 页面 `onLoad`/`onShow` 时从 `getApp().globalData` 取 `navBarHeight`、`statusBarHeight` 写入页面 `data`,供 WXML 内联样式使用。 + +### 7.2 底部安全区 + +- **问题**:有底部刘海的设备上,固定底栏(如 TabBar、底部导航)会贴边,内容被遮挡。 +- **做法**:底部导航容器加 `padding-bottom: env(safe-area-inset-bottom)`(WXSS 或内联均可),无需额外 JS。 + +### 7.3 标题右侧不被胶囊遮挡 + +- **问题**:小程序右上角有胶囊按钮(…、首页),若标题或返回按钮延伸到右侧,会被遮挡或误触。 +- **做法**: + - 所有带标题/返回/右侧按钮的**头部容器**必须预留右侧空白: + - **方式 A**:全局样式 `.safe-header-right { padding-right: 200rpx; box-sizing: border-box; }`,头部容器加该类。 + - **方式 B**:内联 `style="padding-right: {{ capsulePaddingRight }}px"`,`capsulePaddingRight` 来自 `getApp().globalData.capsulePaddingRight`(在 onLaunch 中已计算)。 + - 若出现「标题或按钮被挡」,优先检查该头部是否加了上述留白,并确认 `app.js` 中已正确写入 `capsulePaddingRight`。 + +### 7.4 统一占位模板(可选) + +- 无复杂导航的页面(如设置、关于、推广、购买记录等)建议使用**同一套**顶部安全区:占位条高度 `navBarHeight`,其内 `padding-top: statusBarHeight`,导航容器 `display: flex; flex-direction: column; justify-content: flex-end; box-sizing: border-box;`,并加 `.safe-header-right` 或 `capsulePaddingRight`,保证标题与返回按钮不被遮挡且右侧留白一致。 + +--- + +## 8. 构建与合并流程 1. 在 newpp 执行:`NODE_ENV=production npm run build:mp`。 2. 将 `newpp/dist/mp/` 下生成的页面目录及 `common/` 复制/合并到 `miniprogram/`。 @@ -100,11 +152,12 @@ appExtraConfig: { --- -## 8. 检查清单 +## 9. 检查清单 -- [ ] miniprogram.config.js:每页单独 router,无 `other`;未配置原生 tabBar。 +- [ ] miniprogram.config.js:每页单独 router,无 `other`;**未配置原生 tabBar**,底部菜单沿用 Web 的自定义组件(主页面统一引入,显隐与跳转逻辑一致)。 - [ ] webpack.mp.config.js:每页有 entry;isOptimize 按 NODE_ENV;中小型项目 splitChunks 为 false。 - [ ] 适配层:env、request、storage、router 已实现并在业务中统一使用。 - [ ] 功能开关与 Next 一致,来自同一 API。 - [ ] 样式:无 Grid,Flex 已加 boxSizing/lineHeight;不支持特性已替代。 +- [ ] **安全区**:app.js 的 onLaunch 中已计算并写入 `navBarHeight`、`statusBarHeight`、`capsulePaddingRight`;每页顶部占位用 `navBarHeight`;底部导航有 `padding-bottom: env(safe-area-inset-bottom)`;带标题的头部有 `.safe-header-right` 或 `capsulePaddingRight` 留白,标题/按钮未被胶囊遮挡。 - [ ] 合并后 app.js 未被错误覆盖,页面路径与 app.json 一致。 diff --git a/.cursor/skills/kbone-miniprogram/troubleshooting.md b/.cursor/skills/kbone-miniprogram/troubleshooting.md index 78ebb615..2ddc01b6 100644 --- a/.cursor/skills/kbone-miniprogram/troubleshooting.md +++ b/.cursor/skills/kbone-miniprogram/troubleshooting.md @@ -8,15 +8,66 @@ - 以 **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`。 +- **配置**:`miniprogram.config.js` 每页单独 router、**不配原生 tabBar**;`webpack.mp.config.js` 中 `isOptimize = process.env.NODE_ENV === 'production'`,中小型项目建议 `splitChunks: false`。 +- **底部菜单**:Web 端多为自定义底部菜单组件(主页面统一引入、有显隐逻辑),**不要**转为小程序原生 tabBar;保留「主页面引入统一菜单组件」,显隐与跳转与 Web 一致,跳转用 `wx.reLaunch`。 - **样式**:Grid 改 Flex,补 `boxSizing`/`lineHeight`;不支持特性用兼容写法或替代。 -遇到具体报错时,在下方「编译问题」「运行时问题」「样式问题」中按类型排查。 +遇到具体报错时,在下方「配置问题」「编译问题」「运行时问题」「样式问题」中按类型排查。 + +--- + +## 配置问题 + +### 1. redirect 用了不存在的页面名 + +**现象**:配置了 `redirect.notFound: 'home'`、`redirect.accessDenied: 'home'`,但 router 里没有名为 `home` 的页面。 + +**后果**:404 或无权时,Kbone 可能无法正确跳转到首页。 + +**解决**:redirect 必须使用 **router 里存在的 key**。首页通常是 `index`,不要写 `home`。 + +```javascript +// ❌ 错误 +redirect: { notFound: 'home', accessDenied: 'home' } + +// ✅ 正确 +redirect: { notFound: 'index', accessDenied: 'index' } +``` --- ## 编译问题 +### 0. Node 17+ OpenSSL 错误(ERR_OSSL_EVP_UNSUPPORTED) + +**错误信息**: +``` +Error: error:0308010C:digital envelope routines::unsupported + code: 'ERR_OSSL_EVP_UNSUPPORTED' +``` + +**原因**:Node.js 17+ 使用 OpenSSL 3.0,旧版 webpack(如 4.x)依赖的 MD4 等算法被移出默认提供,导致构建时报错。 + +**解决方案**: + +**方案 1:在 newpp 的 package.json 脚本中加 NODE_OPTIONS**(推荐) +```json +"scripts": { + "web": "cross-env NODE_OPTIONS=--openssl-legacy-provider NODE_ENV=development webpack-dev-server ...", + "mp": "rimraf dist/mp/common && cross-env NODE_OPTIONS=--openssl-legacy-provider NODE_ENV=development webpack ...", + "build:mp": "rimraf dist/mp/common && cross-env NODE_OPTIONS=--openssl-legacy-provider NODE_ENV=production webpack ..." +} +``` + +**方案 2:当前终端临时设置后再运行** +- Windows PowerShell:`$env:NODE_OPTIONS="--openssl-legacy-provider"; cd newpp; npm run web` +- Windows CMD:`set NODE_OPTIONS=--openssl-legacy-provider && cd newpp && npm run web` +- Linux/macOS:`NODE_OPTIONS=--openssl-legacy-provider npm run web` + +**方案 3**:使用 Node.js 16.x(LTS)运行 newpp 构建,避免 OpenSSL 3.0。 + +--- + ### 1. chunk 文件缺失错误 **错误信息**: @@ -74,15 +125,28 @@ optimization: { ``` SyntaxError: Unexpected token ... ``` +或具体到某列:`Unexpected token (45:64)`(多为可选链位置) **原因**: - Babel 配置不正确 -- 使用了小程序不支持的 ES6+ 语法 +- 使用了 Babel 6(stage-3)不支持的语法:**可选链 `?.`、空值合并 `??`**(属 ES2020) +- 其他小程序不支持的 ES6+ 语法 **解决方案**: +**若为可选链 / 空值合并**(最常见): +```javascript +// ❌ Babel 6 会报错 +res.content?.length +x ?? 'default' + +// ✅ 兼容写法 +(res.content && res.content.length) || 0 +x != null ? x : 'default' +``` + +**Babel 配置**(.babelrc 或 babel.config.js): ```javascript -// .babelrc 或 babel.config.js { "presets": [ ["env", { @@ -377,7 +441,26 @@ const styles = { --- -### 4. Flexbox 间距问题 +### 4. 标题/头部被胶囊或电池栏遮挡 + +**问题描述**:自定义导航栏时,标题、返回按钮被右上角胶囊或顶部状态栏遮挡。 + +**原因**:未预留顶部安全区高度和右侧胶囊留白。 + +**解决方案**: + +1. **顶部安全区**:在 `app.js` 的 `onLaunch` 中计算并写入 `globalData`: + - `statusBarHeight = wx.getSystemInfoSync().statusBarHeight || 44` + - `navBarHeight`:用 `wx.getMenuButtonBoundingClientRect()` 计算 `menuButton.bottom + menuButton.top - statusBarHeight`,无菜单时用 `statusBarHeight + 44` + - `capsulePaddingRight = systemInfo.windowWidth - menuButton.left + 10` +2. **页面占位**:顶部占位条高度设为 `{{ navBarHeight }}px`,内容区 `padding-top: {{ statusBarHeight }}px`;页面 `onLoad`/`onShow` 从 `getApp().globalData` 取上述值写入 `data`。 +3. **标题右侧留白**:头部容器加类 `.safe-header-right { padding-right: 200rpx; box-sizing: border-box; }`,或内联 `padding-right: {{ capsulePaddingRight }}px`,避免标题与胶囊重叠。 + +详见本目录 `reference.md` 第 7 节「安全区与标题/胶囊避让」。 + +--- + +### 5. Flexbox 间距问题 **问题描述**:Flexbox 子元素间距不均匀 @@ -528,7 +611,7 @@ request:fail url not in domain list ```javascript // miniprogram.config.js module.exports = { - origin: 'https://your-domain.com', // 使用已配置的域名 + origin: 'https://soul.quwanzhi.com', // 本项目线上域名,见开发文档 8、部署 } ``` @@ -563,7 +646,7 @@ devServer: { ```javascript // Express 示例 app.use(cors({ - origin: ['http://localhost:8080', 'https://your-domain.com'], + origin: ['http://localhost:8080', 'https://soul.quwanzhi.com'], credentials: true, })) ``` diff --git a/README-API接入完成.md b/README-API接入完成.md deleted file mode 100644 index c7a8484a..00000000 --- a/README-API接入完成.md +++ /dev/null @@ -1,392 +0,0 @@ -# API 接入完成报告 - -## 📋 修复概览 - -**修复时间**:2026-02-03 -**问题**: -1. URLSearchParams 在小程序环境不支持 -2. Webpack chunk 文件命名导致文件缺失 - -**状态**:✅ 已完成 - ---- - -## 🐛 问题详情 - -### 问题 1:URLSearchParams 不支持 - -**错误信息**: -``` -ReferenceError: URLSearchParams is not defined -``` - -**原因**: -- 小程序环境不支持 Web API `URLSearchParams` -- 代码中使用了 `new URLSearchParams()` 来构建查询字符串 - -**影响**: -- 无法从 API 加载数据 -- 页面显示"加载失败" - -### 问题 2:Webpack Chunk 文件缺失 - -**错误信息**: -``` -Error: ENOENT: no such file or directory, -open 'E:/Gongsi/Mycontent/newpp/dist/mp/common/default~chapters~index~my~read~search.js' -``` - -**原因**: -- Webpack `splitChunks` 配置中 `name: true` 导致自动生成 chunk 名称 -- 某些 chunk 在特定情况下不会生成,但代码引用了它们 - -**影响**: -- 微信开发者工具编译失败 -- 页面无法加载 - ---- - -## ✅ 解决方案 - -### 修复 1:自定义 buildQueryString 函数 - -**文件**:`newpp/src/api/index.js` - -#### Before(使用 URLSearchParams) - -```javascript -export async function getChapters(params = {}) { - const { partId, status = 'published', page = 1, pageSize = 100 } = params - const query = new URLSearchParams({ status, page: String(page), pageSize: String(pageSize) }) - if (partId) query.append('partId', partId) - - const res = await request(`/api/book/chapters?${query.toString()}`) - return res -} -``` - -#### After(自定义函数) - -```javascript -/** - * 构建查询字符串(兼容小程序) - * @param {object} params - 参数对象 - */ -function buildQueryString(params) { - const parts = [] - for (const [key, value] of Object.entries(params)) { - if (value !== undefined && value !== null) { - parts.push(`${encodeURIComponent(key)}=${encodeURIComponent(value)}`) - } - } - return parts.join('&') -} - -export async function getChapters(params = {}) { - const { partId, status = 'published', page = 1, pageSize = 100 } = params - const queryParams = { status, page: String(page), pageSize: String(pageSize) } - if (partId) queryParams.partId = partId - - const query = buildQueryString(queryParams) - const res = await request(`/api/book/chapters?${query}`) - return res -} -``` - -**关键改动**: -1. ✅ 自定义 `buildQueryString` 函数 -2. ✅ 使用 `Object.entries()` 遍历参数 -3. ✅ 手动拼接查询字符串 -4. ✅ 兼容小程序和 Web 环境 - ---- - -### 修复 2:固定 Webpack Chunk 名称 - -**文件**:`newpp/build/webpack.mp.config.js` - -#### Before(自动命名) - -```javascript -optimization: { - runtimeChunk: false, - splitChunks: { - chunks: 'all', - minSize: 1000, - maxSize: 0, - minChunks: 1, - maxAsyncRequests: 100, - maxInitialRequests: 100, - automaticNameDelimiter: '~', - name: true, // ❌ 自动生成名称 - cacheGroups: { - vendors: { - test: /[\\/]node_modules[\\/]/, - priority: -10, - }, - default: { - minChunks: 2, - priority: -20, - reuseExistingChunk: true, - }, - }, - }, -} -``` - -#### After(固定名称) - -```javascript -optimization: { - runtimeChunk: false, - splitChunks: { - chunks: 'all', - minSize: 1000, - maxSize: 0, - minChunks: 1, - maxAsyncRequests: 100, - maxInitialRequests: 100, - automaticNameDelimiter: '~', - name: false, // ✅ 禁用自动命名 - cacheGroups: { - vendors: { - test: /[\\/]node_modules[\\/]/, - priority: -10, - name: 'vendors', // ✅ 固定名称 - }, - default: { - minChunks: 2, - priority: -20, - reuseExistingChunk: true, - name: 'common', // ✅ 固定名称 - }, - }, - }, -} -``` - -**关键改动**: -1. ✅ `name: true` → `name: false` -2. ✅ `vendors` 添加 `name: 'vendors'` -3. ✅ `default` 添加 `name: 'common'` - -**效果**: -- 生成固定的 chunk 文件:`vendors.js`、`common.js` -- 避免生成动态名称的 chunk -- 确保所有引用的文件都存在 - ---- - -## 📊 修复前后对比 - -| 问题 | 修复前 | 修复后 | -|------|--------|--------| -| URLSearchParams | ❌ 小程序不支持 | ✅ 使用自定义函数 | -| Chunk 文件命名 | ❌ 自动生成,可能缺失 | ✅ 固定名称,稳定 | -| API 数据加载 | ❌ 报错 | ✅ 正常加载 | -| 编译结果 | ❌ 失败 | ✅ 成功 | - ---- - -## 🎯 API 集成功能 - -### 已接入的 API - -| 功能 | API | 方法 | 状态 | -|------|-----|------|------| -| 章节列表 | `/api/book/chapters` | GET | ✅ | -| 章节详情 | `/api/book/chapter/[id]` | GET | ✅ | -| 用户信息 | `/api/user/profile` | GET/POST | ✅ | -| 系统配置 | `/api/db/config` | GET | ✅ | -| 找伙伴配置 | `/api/match/config` | GET | ✅ | -| 加入匹配池 | `/api/ckb/join` | POST | ✅ | -| 获取匹配用户 | `/api/match/users` | GET | ✅ | -| 推广数据 | `/api/referral/data` | GET | ✅ | -| 搜索章节 | `/api/search` | GET | ✅ | -| 创建订单 | `/api/payment/create-order` | POST | ✅ | -| 提现申请 | `/api/withdraw` | POST | ✅ | - -### 数据流程 - -``` -页面组件 - ↓ -useChapters Hook / useChapterContent Hook - ↓ -api/index.js (API 集成层) - ↓ -adapters/request.js (请求适配器) - ↓ -小程序: wx.request / Web: fetch - ↓ -后端 API -``` - -### 缓存策略 - -| 数据类型 | 缓存时长 | 存储位置 | -|---------|---------|---------| -| 章节列表 | 30 分钟 | wx.storage / localStorage | -| 章节内容 | 不缓存 | - | -| 用户信息 | 会话期间 | Zustand Store | - ---- - -## 🧪 测试清单 - -### 基础功能测试 - -- [x] 页面加载成功 -- [x] 不再报 URLSearchParams 错误 -- [x] 不再报文件缺失错误 -- [ ] 章节列表正常显示 -- [ ] 章节内容正常显示 -- [ ] 搜索功能正常 -- [ ] 缓存功能正常 - -### API 测试 - -- [ ] 章节列表 API 调用成功 -- [ ] 章节详情 API 调用成功 -- [ ] 用户信息 API 调用成功 -- [ ] 配置 API 调用成功 -- [ ] 错误处理正确 - -### 跨平台测试 - -- [ ] Web 环境正常 -- [ ] 小程序环境正常 -- [ ] 数据格式一致 - ---- - -## 📝 修改的文件 - -### 核心文件 - -1. **`newpp/src/api/index.js`** - - ✅ 添加 `buildQueryString` 函数 - - ✅ 修复 `getChapters` 函数 - - ✅ 修复 `getUserProfile` 函数 - -2. **`newpp/build/webpack.mp.config.js`** - - ✅ 修改 `splitChunks.name` 为 `false` - - ✅ 添加 `vendors` 固定名称 - - ✅ 添加 `common` 固定名称 - -### 页面文件(已更新) - -1. ✅ `newpp/src/pages/HomePage.jsx` - 使用 `useChapters` Hook -2. ⚠️ `newpp/src/pages/ChaptersPage.jsx` - 需要测试 -3. ⚠️ `newpp/src/pages/ReadPage.jsx` - 需要测试 -4. ⚠️ `newpp/src/pages/SearchPage.jsx` - 需要测试 - ---- - -## 📚 相关文档 - -1. [API 接入说明](./开发文档/8、部署/API接入说明.md) - 完整的 API 文档 -2. [自定义导航方案](./开发文档/8、部署/自定义导航组件方案.md) - 导航组件说明 -3. [小程序样式修复](./开发文档/8、部署/小程序样式修复说明.md) - 样式问题解决 - ---- - -## 🚀 下一步测试 - -### 1. 微信开发者工具测试 - -```bash -# 打开微信开发者工具 -# 导入 miniprogram/ 目录 -# 点击"编译" -``` - -**验证**: -- [ ] 编译成功,无错误 -- [ ] 首页正常显示 -- [ ] 章节列表正常显示 -- [ ] 点击章节可以查看内容 -- [ ] 搜索功能正常 - -### 2. 数据加载测试 - -**验证**: -- [ ] 首次加载从 API 获取数据 -- [ ] 第二次加载从缓存读取 -- [ ] Loading 状态正常显示 -- [ ] Error 状态正常显示 - -### 3. API 调用测试 - -**打开控制台,检查**: -- [ ] 网络请求正常(无 404) -- [ ] 返回数据格式正确 -- [ ] 数据渲染正常 - ---- - -## 🎯 核心改进 - -### 1. 小程序兼容性 - -**Before**: -- ❌ 使用 Web API `URLSearchParams` -- ❌ 小程序环境报错 - -**After**: -- ✅ 自定义 `buildQueryString` 函数 -- ✅ 完全兼容小程序和 Web - -### 2. Webpack 稳定性 - -**Before**: -- ❌ 自动生成 chunk 名称 -- ❌ 文件可能缺失 - -**After**: -- ✅ 固定 chunk 名称 -- ✅ 文件稳定存在 - -### 3. 代码质量 - -**改进**: -1. ✅ 更好的错误处理 -2. ✅ Loading 状态管理 -3. ✅ 缓存机制 -4. ✅ 数据转换逻辑 - ---- - -## ✅ 完成总结 - -### 核心成果 - -1. ✅ **修复 URLSearchParams 问题** - 自定义兼容函数 -2. ✅ **修复 Webpack Chunk 问题** - 固定文件名称 -3. ✅ **API 集成完成** - 11 个核心 API 接入 -4. ✅ **Hooks 创建完成** - useChapters、useChapterContent -5. ✅ **页面更新完成** - HomePage、ChaptersPage、ReadPage、SearchPage - -### 技术亮点 - -1. ✅ 跨平台兼容(小程序 + Web) -2. ✅ 数据缓存(30分钟有效期) -3. ✅ 错误处理(友好的错误提示) -4. ✅ Loading 状态(优化用户体验) -5. ✅ 代码分离(API 层、Hooks 层、页面层) - -### 待完成 - -1. ⏳ 微信开发者工具完整测试 -2. ⏳ 真机预览测试 -3. ⏳ API 性能优化 -4. ⏳ 更多页面接入 API(找伙伴、我的、推广等) - ---- - -**🎉 API 接入完成!现在可以在微信开发者工具中测试真实数据加载了。** - ---- - -**修复日期**:2026-02-03 -**文档版本**:v1.0 diff --git a/README-Kbone技能创建完成.md b/README-Kbone技能创建完成.md deleted file mode 100644 index b2cd380c..00000000 --- a/README-Kbone技能创建完成.md +++ /dev/null @@ -1,543 +0,0 @@ -# Kbone 技能创建完成报告 - -## 📋 创建概览 - -**创建时间**:2026-02-03 -**技能名称**:kbone-miniprogram -**技能路径**:`.cursor/skills/kbone-miniprogram/` -**状态**:✅ 已完成 - ---- - -## 🎯 技能用途 - -这个技能帮助 AI Agent 更好地处理使用 Kbone 框架将 React Web 应用转换为微信小程序的任务。 - -**触发场景**: -- 配置或优化 Kbone 项目 -- 解决小程序编译/运行时错误 -- 处理跨平台兼容性问题 -- Web 应用转小程序迁移 - ---- - -## 📁 文件结构 - -``` -.cursor/skills/kbone-miniprogram/ -├── SKILL.md # 主技能文档(核心配置、快速方案、最佳实践) -├── troubleshooting.md # 详细故障排查指南(50+ 常见问题) -└── README.md # 技能使用说明(使用指南、维护说明) -``` - ---- - -## 📝 文件内容 - -### 1. SKILL.md(主技能文档) - -**包含内容**: - -#### 核心配置规范 -- ✅ `miniprogram.config.js` 完整配置模板 - - router 配置(每个页面单独配置) - - global 配置(rem、pageStyle) - - pages 配置(navigationBarTitleText) - - app 配置(导航栏样式) - - optimization 配置(性能优化) - -- ✅ `webpack.mp.config.js` 完整配置模板 - - mode 和 isOptimize 环境判断 - - entry/output 配置 - - splitChunks 策略(中小型 vs 大型项目) - -#### 常见问题快速解决 -1. **样式错位** - Grid 改 Flexbox -2. **chunk 文件缺失** - 禁用代码分割 -3. **URLSearchParams 错误** - 自定义实现 -4. **底部导航动态显示** - 自定义组件方案 - -#### 跨平台适配层 -- 环境判断(`isMiniProgram()`) -- router 适配器(navigateTo、switchTab) -- request 适配器(wx.request vs fetch) -- storage 适配器(wx.storage vs localStorage) - -#### 开发流程 -- 初始化项目 -- 开发模式(Web + 小程序) -- 生产构建 -- 测试部署 - -#### 最佳实践 -- 配置规范 -- 代码分割策略 -- 样式约束 -- API 兼容性 -- 导航设计 - -#### 检查清单 -- [ ] 配置检查(router、pages、global、webpack) -- [ ] 兼容性检查(Grid、URLSearchParams、API) -- [ ] 构建检查(编译、运行) -- [ ] 功能检查(标题、导航、数据、样式) - ---- - -### 2. troubleshooting.md(详细排查指南) - -**包含内容**: - -#### 编译问题(3 个) -1. **chunk 文件缺失** - 方案 1(禁用)vs 方案 2(固定命名) -2. **Babel 编译错误** - .babelrc 配置 -3. **依赖包报错** - 检查、重装、resolve 配置 - -#### 运行时问题(4 个) -1. **URLSearchParams 未定义** - `buildQueryString` 实现 -2. **localStorage 未定义** - storage 适配器 -3. **window 对象未定义** - 环境判断和可选链 -4. **document 对象未定义** - React 特性 + wx API - -#### 样式问题(4 个) -1. **CSS Grid 不生效** - Flexbox 方案 -2. **盒模型计算错误** - `boxSizing: 'border-box'` -3. **文字垂直居中问题** - `lineHeight` 设置 -4. **Flexbox 间距问题** - 3 种解决方案 - -#### 路由问题(3 个) -1. **页面跳转无效** - router 配置、适配器、文件检查 -2. **switchTab 报错** - wx.reLaunch 方案 -3. **动态路由参数丢失** - 小程序 vs Web 获取方式 - -#### 网络问题(2 个) -1. **API 请求失败** - 域名白名单、开发调试、代理 -2. **跨域问题** - webpack proxy、后端 CORS - -#### 性能问题(2 个) -1. **首屏加载慢** - 包体积、代码压缩、代码分割 -2. **页面卡顿** - 虚拟列表、React.memo、useMemo - -#### 调试技巧 -- 查看小程序日志 -- 条件断点 -- 真机调试 - -#### 检查清单 -- [ ] 编译检查 -- [ ] 兼容性检查 -- [ ] 配置检查 -- [ ] 运行检查 - ---- - -### 3. README.md(使用说明) - -**包含内容**: - -#### 文件说明 -- SKILL.md - 核心配置和快速方案 -- troubleshooting.md - 详细排查指南 - -#### 技能使用指南 -- 何时使用此技能(4 类场景) -- 核心知识点(4 个要点) -- 快速参考表(问题速查、配置速查) - -#### 使用示例 -- 场景 1:配置新项目 -- 场景 2:修复编译错误 -- 场景 3:修复运行时错误 -- 场景 4:优化配置 - -#### 维护说明 -- 更新触发条件 -- 更新流程 - -#### 参考资源 -- 官方文档链接 -- 项目文档位置 - ---- - -## 🎯 技能特点 - -### 1. 结构清晰 - -``` -SKILL.md → 核心知识,快速查找 -troubleshooting.md → 详细方案,深度排查 -README.md → 使用指南,维护说明 -``` - -**符合 create-skill 最佳实践**: -- ✅ 主文档 < 500 行(SKILL.md 约 400 行) -- ✅ 渐进式披露(详细内容在 troubleshooting.md) -- ✅ 文件引用一层深度 - ---- - -### 2. 内容全面 - -**覆盖范围**: -- ✅ 配置规范(miniprogram.config.js + webpack.mp.config.js) -- ✅ 常见问题(18+ 问题和解决方案) -- ✅ 跨平台适配(4 个核心适配器) -- ✅ 开发流程(初始化到部署) -- ✅ 最佳实践(配置、代码、样式、性能) -- ✅ 检查清单(4 类检查项) -- ✅ 调试技巧(工具和方法) - ---- - -### 3. 实践导向 - -**基于真实项目经验**: -- ✅ 所有问题都是实际遇到的 -- ✅ 所有方案都经过验证 -- ✅ 包含具体的代码示例 -- ✅ 提供决策依据(中小型 vs 大型项目) - ---- - -### 4. 易于维护 - -**清晰的维护指南**: -- ✅ 何时更新(3 种触发条件) -- ✅ 如何更新(4 步流程) -- ✅ 在哪更新(文件对应关系) - ---- - -## 📊 技能对比 - -### Before(没有技能) - -Agent 处理 Kbone 问题时: -- ❌ 需要搜索官方文档 -- ❌ 可能配置不规范 -- ❌ 遇到问题缺少实践经验 -- ❌ 解决方案不稳定 - -### After(有技能) - -Agent 处理 Kbone 问题时: -- ✅ 直接应用经验和最佳实践 -- ✅ 配置完全符合官方规范 -- ✅ 快速定位和解决问题 -- ✅ 解决方案经过验证 - ---- - -## 🧪 技能验证 - -### 测试场景 1:配置新项目 - -**用户消息**: -> "帮我配置一个 kbone 项目,有首页、目录、阅读三个页面" - -**预期行为**: -1. ✅ 读取 SKILL.md -2. ✅ 创建规范的 `miniprogram.config.js` -3. ✅ 创建优化的 `webpack.mp.config.js` -4. ✅ 创建跨平台适配器 -5. ✅ 提供构建命令 - ---- - -### 测试场景 2:修复编译错误 - -**用户消息**: -> "编译报错:ENOENT: no such file or directory, open 'default~chapters.js'" - -**预期行为**: -1. ✅ 识别为 chunk 文件缺失问题 -2. ✅ 读取 SKILL.md > 问题 2 -3. ✅ 判断项目规模 -4. ✅ 修改 webpack 配置(禁用 splitChunks) -5. ✅ 重新构建并验证 - ---- - -### 测试场景 3:修复运行时错误 - -**用户消息**: -> "小程序报错:URLSearchParams is not defined" - -**预期行为**: -1. ✅ 识别为 API 兼容性问题 -2. ✅ 读取 SKILL.md > 问题 3 -3. ✅ 创建 `buildQueryString` 函数 -4. ✅ 替换所有使用 -5. ✅ 测试验证 - ---- - -### 测试场景 4:优化配置 - -**用户消息**: -> "根据 kbone 官方文档优化我的配置" - -**预期行为**: -1. ✅ 读取 SKILL.md > 核心配置规范 -2. ✅ 检查 router、pages、global -3. ✅ 检查 webpack mode 和 isOptimize -4. ✅ 优化不规范的配置 -5. ✅ 提供优化说明 - ---- - -## 📚 技能与项目文档的关系 - -### 项目文档(详细实践) - -位置:`开发文档/8、部署/` - -文档列表: -- Kbone配置优化说明.md -- 小程序样式修复说明.md -- 自定义导航组件方案.md -- API接入说明.md -- Webpack代码分割问题修复.md - -**特点**: -- ✅ 详细的问题分析 -- ✅ 完整的解决过程 -- ✅ 具体的代码变更 -- ✅ 优化前后对比 - ---- - -### 技能文档(提炼精华) - -位置:`.cursor/skills/kbone-miniprogram/` - -文档列表: -- SKILL.md -- troubleshooting.md -- README.md - -**特点**: -- ✅ 精炼的配置规范 -- ✅ 快速的解决方案 -- ✅ 通用的最佳实践 -- ✅ 易于查找和应用 - ---- - -### 关系说明 - -``` -项目文档(详细) - ↓ 提炼精华 -技能文档(精简) - ↓ Agent 应用 -快速解决问题 -``` - -**互补关系**: -- 项目文档:记录完整过程,供人类阅读 -- 技能文档:提炼核心知识,供 Agent 应用 - ---- - -## ✅ 符合 create-skill 规范检查 - -### 核心质量 ✅ - -- [x] Description 具体且包含关键词 -- [x] Description 包含 WHAT 和 WHEN -- [x] 使用第三人称描述 -- [x] SKILL.md < 500 行(约 400 行) -- [x] 术语一致(Kbone、小程序、配置) -- [x] 示例具体(代码示例、配置示例) - ---- - -### 结构 ✅ - -- [x] 文件引用一层深度(SKILL.md → troubleshooting.md) -- [x] 渐进式披露(核心在 SKILL.md,详细在 troubleshooting.md) -- [x] 工作流程清晰(开发流程 4 步) -- [x] 无时效性信息 - ---- - -### 内容 ✅ - -- [x] 简洁为主(挑战每个段落的必要性) -- [x] 假设 Agent 智能(只提供它不知道的) -- [x] 具体的触发条件(4 类场景) -- [x] 明确的检查清单(4 类检查) - ---- - -### 命名 ✅ - -- [x] 技能名称规范(`kbone-miniprogram`) -- [x] 描述性强(不是 helper、utils) -- [x] 小写 + 连字符 -- [x] 不超过 64 字符 - ---- - -## 📊 技能质量评估 - -| 维度 | 评分 | 说明 | -|------|------|------| -| **完整性** | 95% | 覆盖配置、问题、实践、调试 ✅ | -| **准确性** | 100% | 所有方案经过验证 ✅ | -| **易用性** | 90% | 清晰的结构和索引 ✅ | -| **可维护性** | 95% | 明确的维护指南 ✅ | -| **规范性** | 100% | 完全符合 create-skill 规范 ✅ | - -**总体评分**:96% ✅ 优秀 - ---- - -## 🎯 技能价值 - -### 1. 提升效率 - -**Before**: -- Agent 需要搜索文档:5-10 分钟 -- 配置可能不规范:需要迭代修复 -- 问题解决缺少经验:可能走弯路 - -**After**: -- Agent 直接应用技能:30 秒 -- 配置一次到位:符合规范 -- 问题快速解决:经验方案 - -**效率提升**:10-20 倍 ✅ - ---- - -### 2. 保证质量 - -**稳定性**: -- ✅ 所有方案经过验证 -- ✅ 配置符合官方规范 -- ✅ 避免已知的坑 - -**一致性**: -- ✅ 统一的配置风格 -- ✅ 统一的代码模式 -- ✅ 统一的术语 - ---- - -### 3. 积累知识 - -**知识沉淀**: -- ✅ 实践经验文档化 -- ✅ 问题和方案结构化 -- ✅ 最佳实践标准化 - -**持续改进**: -- ✅ 发现新问题 → 更新技能 -- ✅ 优化旧方案 → 更新技能 -- ✅ 技能越用越强 - ---- - -## 📋 后续计划 - -### 立即可用 ✅ - -技能已经完整且可用: -- [x] 配置规范完整 -- [x] 常见问题覆盖 -- [x] 最佳实践明确 -- [x] 使用说明清晰 - ---- - -### 持续优化 ⏳ - -根据使用情况持续改进: - -1. **收集新问题** - - 遇到新的兼容性问题 - - 发现新的最佳实践 - - 用户反馈的问题 - -2. **更新技能文档** - - 添加到 troubleshooting.md - - 更新 SKILL.md(常见问题) - - 更新 README.md(快速参考) - -3. **创建项目文档** - - 在 `开发文档/8、部署/` 创建详细文档 - - 记录完整的解决过程 - - 供人类阅读和学习 - ---- - -### 可能的扩展 💡 - -未来可以考虑: - -1. **增加更多平台** - - 支持 Vue + Kbone - - 支持其他跨平台方案 - -2. **增加工具脚本** - - 自动化配置生成 - - 代码检查脚本 - - 迁移辅助工具 - -3. **增加性能优化** - - 更多优化策略 - - 性能监控方案 - - 最佳实践更新 - ---- - -## 🎉 完成总结 - -### 核心成果 - -1. ✅ **创建了完整的 Kbone 技能** - 3 个文档文件 -2. ✅ **覆盖了主要场景** - 配置、问题、优化、调试 -3. ✅ **符合规范标准** - 完全符合 create-skill 规范 -4. ✅ **基于实践经验** - 所有方案经过验证 -5. ✅ **易于维护更新** - 清晰的维护指南 - ---- - -### 技能亮点 - -1. **结构清晰** - 主文档 + 详细指南 + 使用说明 -2. **内容全面** - 18+ 问题和解决方案 -3. **实践导向** - 基于真实项目经验 -4. **易于查找** - 快速参考表和索引 -5. **持续改进** - 明确的更新机制 - ---- - -### 使用指引 - -**Agent 自动应用场景**: -- ✅ 用户提到 "kbone"、"小程序" -- ✅ 配置 miniprogram.config.js -- ✅ 修复编译/运行时错误 -- ✅ 优化 Kbone 配置 - -**人类查阅场景**: -- ✅ 学习 Kbone 最佳实践 -- ✅ 排查具体问题 -- ✅ 了解跨平台适配方案 - ---- - -**🎊 Kbone 技能创建完成!Agent 现在可以更专业地处理 Kbone 项目了。** - ---- - -**参考**: -- [create-skill 规范](https://github.com/getcursor/cursor/blob/main/docs/skills/create-skill.md) -- [Kbone 官方文档](https://wechat-miniprogram.github.io/kbone/docs/) - -**创建日期**:2026-02-03 -**文档版本**:v1.0 diff --git a/README-Kbone迁移完成.md b/README-Kbone迁移完成.md deleted file mode 100644 index fa9c9e3f..00000000 --- a/README-Kbone迁移完成.md +++ /dev/null @@ -1,480 +0,0 @@ -# 🎉 Kbone 小程序迁移完成报告 - -## 项目概述 - -**项目名称**:Soul创业派对 - C端小程序迁移 -**技术方案**:Kbone 同构开发(React) -**完成日期**:2026年2月2日 -**迁移状态**:✅ **100% 完成** - ---- - -## 一、迁移成果 - -### 1.1 页面完成度 - -✅ **10/10 页面全部迁移** - -| 序号 | 页面 | Web 路由 | 小程序页面 | 状态 | -|------|------|----------|-----------|------| -| 1 | 首页 | `/` | pages/index/index | ✅ | -| 2 | 目录 | `/chapters` | pages/chapters/chapters | ✅ | -| 3 | 阅读 | `/read/[id]` | pages/read/read | ✅ | -| 4 | 我的 | `/my` | pages/my/my | ✅ | -| 5 | 推广中心 | `/my/referral` | pages/referral/referral | ✅ | -| 6 | 设置 | `/my/settings` | pages/settings/settings | ✅ | -| 7 | 购买记录 | `/my/purchases` | pages/purchases/purchases | ✅ | -| 8 | 关于 | `/about` | pages/about/about | ✅ | -| 9 | 找伙伴 | `/match` | pages/match/match | ✅ | -| 10 | 搜索 | `/search` | pages/search/search | ✅ | - -### 1.2 核心功能 - -✅ **阅读流程** -- 首页 → 精选推荐 → 阅读页 -- 目录 → 选择章节 → 阅读页 -- 阅读页:内容渲染、进度条、上下篇切换 - -✅ **用户中心** -- 我的:未登录态、已登录态 -- 用户卡片:统计、收益、Tab 切换 -- 推广中心:邀请码、收益、复制功能 -- 设置、购买记录、关于 - -✅ **找伙伴** -- 匹配类型选择 -- 匹配次数管理 -- 匹配结果展示 -- 加入匹配池 - -✅ **搜索** -- 实时搜索章节 -- 搜索结果展示 -- 点击跳转阅读 - -✅ **底部 TabBar** -- 4 个 Tab:首页、目录、找伙伴、我的 -- 激活态标识 -- 跨端路由切换 - ---- - -## 二、技术架构 - -### 2.1 技术栈 - -| 类型 | 技术 | -|------|------| -| 框架 | Kbone(React 16.14) | -| 状态管理 | Zustand + persist | -| 样式方案 | Inline Styles | -| 构建工具 | Webpack 4 + Babel 6 | -| 运行时 | 小程序基础库 2.x | - -### 2.2 适配层设计 - -``` -src/adapters/ -├── env.js # 环境检测 -├── router.js # 路由导航 -├── request.js # 网络请求 -├── storage.js # 本地存储 -└── index.js # 统一导出 -``` - -**核心功能**: -- ✅ 跨端环境检测(小程序 / Web) -- ✅ 统一路由 API(navigate、switchTab、back、getPageQuery) -- ✅ 统一请求 API(小程序 wx.request / Web fetch) -- ✅ 统一存储 API(小程序 wx.storage / Web localStorage) - -### 2.3 状态管理 - -```javascript -// src/store/index.js -- 用户状态:user、isLoggedIn、logout、setUser -- 购买逻辑:hasPurchased、addPurchase、purchaseFullBook -- 配置管理:settings、setSettings -- 持久化:集成 storage 适配层 -``` - -### 2.4 目录结构 - -``` -newpp/ -├── src/ -│ ├── adapters/ # 适配层 -│ ├── components/ # 公共组件 -│ ├── pages/ # 页面组件 -│ ├── data/ # 静态数据 -│ ├── store/ # 状态管理 -│ ├── index.jsx # 首页入口 -│ ├── chapters.jsx # 目录入口 -│ ├── read.jsx # 阅读入口 -│ └── ... # 其他入口 -├── build/ -│ ├── miniprogram.config.js # Kbone 配置 -│ └── webpack.mp.config.js # Webpack 配置 -└── dist/mp/ # 构建产物 -``` - ---- - -## 三、迁移过程(Phase 1-5) - -### Phase 1:搭架子(2h) -- ✅ 创建适配层(env、router、request、storage) -- ✅ 配置 Kbone(miniprogram.config.js) -- ✅ 创建首页、目录、阅读页占位 -- ✅ 配置 Webpack 构建 - -### Phase 2:核心页(3h) -- ✅ 实现首页(精选推荐、书籍介绍、序言) -- ✅ 实现目录页(章节列表、展开/折叠) -- ✅ 实现阅读页(接口对接、上下篇切换) -- ✅ 创建 ChapterContent 组件 -- ✅ 创建静态 bookData - -### Phase 3:我的与子页(2h) -- ✅ 创建 Zustand store 适配 -- ✅ 实现我的页(登录态、统计、收益) -- ✅ 实现推广页(邀请码、收益、规则) -- ✅ 实现设置、购买记录、关于页 - -### Phase 4:找伙伴与其余(2h) -- ✅ 实现找伙伴页(匹配类型、次数管理、结果展示) -- ✅ 实现搜索页(实时搜索、结果跳转) -- ✅ 创建 BottomNav 组件 -- ✅ 各页面集成 BottomNav -- ✅ 安全区适配 - -### Phase 5:收尾(3h) -- ✅ 创建自检清单 -- ✅ 修复 Babel 6 兼容性问题 -- ✅ 创建踩坑修复指南 -- ✅ 创建发布流程文档 -- ✅ 构建成功并合并到 miniprogram - -**总耗时**:~12小时 - ---- - -## 四、技术亮点 - -### 4.1 跨端适配层 - -**设计思路**: -- 抽象平台差异,提供统一 API -- 运行时自动检测环境 -- 无需修改业务代码 - -**示例**: -```javascript -// 业务代码 -import { navigate, request, storage } from '../adapters' - -// 路由跳转(自动适配小程序 wx.navigateTo / Web location.href) -navigate('/read/1.1') - -// 网络请求(自动适配小程序 wx.request / Web fetch) -const data = await request('/api/book/chapter/1.1') - -// 本地存储(自动适配小程序 wx.storage / Web localStorage) -storage.setItem('user', user) -``` - -### 4.2 状态持久化 - -**方案**:Zustand + persist 中间件 + storage 适配层 - -**优势**: -- 状态自动持久化到本地存储 -- 跨端统一(小程序 wx.storage / Web localStorage) -- 无需手动 get/set - -### 4.3 Inline Styles - -**方案**:使用 JavaScript 对象定义样式 - -**优势**: -- 无需转换 Tailwind CSS → WXSS -- 样式与组件强耦合,易维护 -- 支持动态样式(条件渲染、主题切换) - -**示例**: -```javascript -const styles = { - page: { minHeight: '100vh', background: '#000', color: '#fff' }, - card: { padding: 16, borderRadius: 12, background: '#1c1c1e' }, -} - -return
...
-``` - ---- - -## 五、性能数据 - -### 5.1 构建产物 - -| 指标 | 数值 | -|------|------| -| 总大小 | ~800 KB(已压缩) | -| 页面数 | 10 个 | -| 公共 chunks | 6 个 | -| vendor chunks | 2 个(React + Zustand) | - -### 5.2 代码复用率 - -| 类型 | 复用率 | -|------|--------| -| 业务逻辑 | 90% | -| UI 组件 | 80% | -| 样式代码 | 70% | -| 整体 | **75%+** | - -### 5.3 构建时间 - -| 环节 | 时间 | -|------|------| -| 安装依赖 | ~30s | -| 构建 | ~4s | -| 合并 | ~1s | -| **总计** | **~35s** | - ---- - -## 六、已解决的技术难点 - -### 6.1 Babel 6 语法兼容性 - -**问题**:Kbone 使用 Babel 6,不支持 ES2020+ 语法 - -**解决方案**: -- 可选链 `?.` → `&&` 逻辑判断(8 处) -- Fragment 简写 `<>` → `
`(5 处) -- 安装 `babel-runtime@6` 依赖 - -### 6.2 跨端路由适配 - -**问题**:小程序路由 API 与 Web 不同 - -**解决方案**: -- 创建 `adapters/router.js` -- 自动识别 TabBar 页(用 `switchTab`) -- 自动处理动态路由参数 - -### 6.3 状态持久化 - -**问题**:Zustand persist 需要适配小程序 storage - -**解决方案**: -- 创建 `adapters/storage.js` -- 提供统一的 `getItem/setItem/removeItem` API -- Zustand persist 配置自定义 storage - -### 6.4 安全区适配 - -**问题**:刘海屏、底部横条遮挡 - -**解决方案**: -- 底部 TabBar 使用 `paddingBottom: env(safe-area-inset-bottom)` -- 顶部导航预留 statusBar 高度(若使用自定义导航) - ---- - -## 七、待完成事项 - -### Priority P0(必做,发布前) - -1. **手动合并 app.js** - - [ ] 将 Kbone 生成的 `miniprogram/app.js` 与现有逻辑合并 - - [ ] 保留 globalData(baseUrl、matchEnabled、navBarHeight) - - [ ] 保留 request 方法 - - [ ] 保留 loadFeatureConfig 方法 - - 📖 参考:`开发文档/8、部署/Kbone踩坑修复指南.md` 第三章 - -2. **微信开发者工具测试** - - [ ] 打开 `miniprogram/` 目录 - - [ ] 验证编译无错误 - - [ ] 测试 TabBar 切换 - - [ ] 测试页面跳转 - - [ ] 测试接口请求 - - [ ] 真机预览(iOS + Android) - -3. **安全区适配验证** - - [ ] 底部 TabBar 无遮挡 - - [ ] 刘海屏设备正常显示 - - [ ] 横屏模式正常 - -### Priority P1(重要,提升体验) - -1. **样式细节对齐** - - [ ] 对照 Web 版,调整间距、阴影 - - [ ] 图标替换为图片(当前为 emoji) - - [ ] 动画效果优化 - -2. **登录功能实现** - - [ ] 微信登录集成 - - [ ] 手机号绑定 - - [ ] 用户信息同步 - -3. **支付功能实现** - - [ ] 微信支付集成 - - [ ] 订单状态管理 - - [ ] 购买记录同步 - -### Priority P2(可选,持续优化) - -1. **性能优化** - - [ ] 代码分割优化 - - [ ] 图片懒加载 - - [ ] 长列表虚拟滚动 - -2. **监控与分析** - - [ ] 错误监控集成 - - [ ] 数据埋点 - - [ ] 用户行为分析 - ---- - -## 八、文档清单 - -| 文档 | 路径 | 说明 | -|------|------|------| -| Phase 1 完成说明 | `开发文档/8、部署/Phase1完成说明.md` | 搭架子阶段 | -| Phase 2 完成说明 | `开发文档/8、部署/Phase2完成说明.md` | 核心页阶段 | -| Phase 3 完成说明 | `开发文档/8、部署/Phase3完成说明.md` | 我的与子页 | -| Phase 4 完成说明 | `开发文档/8、部署/Phase4完成说明.md` | 找伙伴与其余 | -| Phase 5 完成总结 | `开发文档/8、部署/Phase5完成总结.md` | 收尾与发布 | -| 迁移方案总览 | `开发文档/8、部署/Next转小程序Kbone迁移方案.md` | 整体架构 | -| 踩坑修复指南 | `开发文档/8、部署/Kbone踩坑修复指南.md` | 问题排查 | -| 发布流程 | `开发文档/8、部署/Kbone小程序发布流程.md` | 构建发布 | -| 自检清单 | `开发文档/8、部署/Phase5自检清单.md` | 发布前检查 | - ---- - -## 九、快速开始 - -### 9.1 本地开发 - -```bash -# 1. 安装依赖 -cd newpp -pnpm install - -# 2. 开发模式(Web) -pnpm run web - -# 3. 开发模式(小程序,watch) -pnpm run mp -``` - -### 9.2 构建发布 - -```bash -# 1. 构建生产版本 -cd newpp -pnpm run build:mp - -# 2. 合并到 miniprogram -cd .. -node scripts/merge-kbone-to-miniprogram.js - -# 3. 手动合并 app.js -# 参考 Kbone踩坑修复指南.md - -# 4. 微信开发者工具测试 -# 打开 miniprogram/ 目录 -``` - -### 9.3 发布流程 - -```bash -# 1. 微信开发者工具上传 -# 2. 微信公众平台设为体验版 -# 3. 提交审核 -# 4. 审核通过后发布 -``` - ---- - -## 十、致谢与展望 - -### 致谢 - -感谢: -- **Tencent Kbone 团队**:提供优秀的同构开发方案 -- **React 社区**:丰富的生态与工具链 -- **Zustand 团队**:轻量级状态管理库 -- **项目团队**:耐心测试与反馈 - -### 展望 - -未来规划: -1. **短期**(1-2 周) - - 完善登录、支付功能 - - 样式细节对齐 - - 正式版发布 - -2. **中期**(1-2 月) - - 性能优化(代码分割、懒加载) - - 功能增强(分享、推送、客服) - - 监控与分析 - -3. **长期**(3-6 月) - - 升级到 Webpack 5 + Babel 7 - - 支持更多新语法 - - 持续迭代与优化 - ---- - -## 十一、总结 - -### 🎉 重大成果 - -✅ **C 端页面 100% 迁移完成** -- 10 个页面全部迁移 -- 所有核心流程可走通 -- 构建成功,无语法错误 - -✅ **完整的开发与发布体系** -- 适配层设计完善 -- 状态管理跨端统一 -- 构建流程清晰 -- 发布流程文档完整 - -✅ **技术债务清零** -- Babel 6 兼容性问题全部修复 -- 代码质量高,易维护 -- 文档齐全,易交接 - -### 📊 关键指标 - -| 指标 | 数值 | -|------|------| -| 迁移完成度 | **100%** | -| 代码复用率 | **75%+** | -| 构建产物大小 | ~800 KB | -| 构建时间 | ~4s | -| 文档完整度 | **100%** | - -### 💡 核心价值 - -1. **开发效率提升**:使用 React,开发体验好,代码复用率高 -2. **维护成本降低**:统一技术栈,一套代码多端运行 -3. **发布周期缩短**:构建流程清晰,自动化程度高 - ---- - -## 联系方式 - -如有问题,请查阅文档或联系项目负责人。 - -**项目负责人**:许永平(yongpxu) -**完成日期**:2026年2月2日 -**项目状态**:✅ **已完成,待发布** - ---- - -**🚀 下一步:手动合并 app.js,微信开发者工具测试,真机预览,发布上线!** diff --git a/README-Kbone配置优化完成.md b/README-Kbone配置优化完成.md deleted file mode 100644 index e3f0d816..00000000 --- a/README-Kbone配置优化完成.md +++ /dev/null @@ -1,562 +0,0 @@ -# Kbone 配置优化完成报告 - -## 📋 优化概览 - -**优化时间**:2026-02-03 -**参考文档**:[Kbone 官方文档](https://wechat-miniprogram.github.io/kbone/docs/guide/tutorial.html) -**状态**:✅ 已完成 - ---- - -## 🎯 优化目标 - -根据 [Kbone 官方文档](https://wechat-miniprogram.github.io/kbone/docs/guide/tutorial.html) 和 [React 项目模板](https://github.com/wechat-miniprogram/kbone-template-react),优化 newpp 项目配置,使其更加规范和完善。 - ---- - -## 🔍 发现的问题 & 修复方案 - -### 1. router 配置不规范 ❌ - -**问题**:使用 `other` 数组配置多个页面 - -```javascript -// ❌ 不规范 -router: { - home: ['/', '/(index)?', '/index.html'], - other: ['/chapters', '/read/:id', '/my', ...], -} -``` - -**官方规范**:每个页面应该单独配置 - -```javascript -// ✅ 规范(修复后) -router: { - index: ['/', '/(index)?', '/index.html'], - chapters: ['/chapters', '/chapters.html'], - read: ['/read/:id', '/read.html'], - my: ['/my', '/my.html'], - // ... 每个页面单独配置 -} -``` - -**优点**: -- ✅ 清晰明了,符合官方规范 -- ✅ 易于维护和扩展 -- ✅ 支持每个页面多个路由规则 - ---- - -### 2. pages 配置缺失 ❌ - -**问题**:`pages: {}`(空对象) - -```javascript -// ❌ 空配置 -pages: {} -``` - -**优化后**:为每个页面配置标题 - -```javascript -// ✅ 完整配置 -pages: { - index: { - extra: { - navigationBarTitleText: 'Soul创业实验', - }, - }, - chapters: { - extra: { - navigationBarTitleText: '目录', - }, - }, - // ... 10 个页面都有标题 -} -``` - -**优点**: -- ✅ 每个页面有独立的标题 -- ✅ 提升用户体验 -- ✅ 符合小程序规范 - ---- - -### 3. global 配置未优化 ❌ - -**问题**:`global: {}`(空对象) - -```javascript -// ❌ 空配置 -global: {} -``` - -**优化后**:启用有用的功能 - -```javascript -// ✅ 优化配置 -global: { - rem: true, // 开启 rem 支持(响应式布局) - pageStyle: true, // 支持修改页面样式 -} -``` - -**优点**: -- ✅ 支持 rem 单位(响应式) -- ✅ 支持动态修改页面样式 -- ✅ 增强功能性 - ---- - -### 4. 代码压缩配置不合理 ❌ - -**问题**:硬编码 `isOptimize = false` - -```javascript -// ❌ 硬编码 -const isOptimize = false -``` - -**优化后**:根据环境变量判断 - -```javascript -// ✅ 智能判断 -const isOptimize = process.env.NODE_ENV === 'production' -``` - -**效果**: -- ✅ 开发环境:`false`(不压缩,方便调试) -- ✅ 生产环境:`true`(压缩,减小体积 30-50%) - ---- - -### 5. webpack mode 不合理 ❌ - -**问题**:硬编码 `mode: 'production'` - -```javascript -// ❌ 硬编码 -module.exports = { - mode: 'production', -} -``` - -**优化后**:根据环境变量判断 - -```javascript -// ✅ 智能判断 -module.exports = { - mode: process.env.NODE_ENV === 'production' ? 'production' : 'development', -} -``` - -**效果**: -- ✅ 开发环境:更友好的错误提示 -- ✅ 生产环境:更好的代码优化 - ---- - -### 6. 缺少 Web 开发配置 ❌ - -**问题**:只有小程序配置,没有 Web 开发配置 - -**优化后**:新增 `webpack.dev.config.js` 和 `public/index.html` - -```javascript -// ✅ 新增 Web 开发配置 -module.exports = { - mode: 'development', - devServer: { - host: '0.0.0.0', - port: 8080, - hot: true, - open: true, - }, -} -``` - -**优点**: -- ✅ 支持 Web 端开发调试 -- ✅ 热更新(HMR) -- ✅ 自动打开浏览器 -- ✅ 提升开发效率 - ---- - -## 📝 修改的文件 - -### 1. miniprogram.config.js ✅ - -**改动**: -- ✅ router:`other` 数组 → 每个页面单独配置 -- ✅ global:空对象 → 启用 rem 和 pageStyle -- ✅ pages:空对象 → 配置 10 个页面标题 - -**影响**: -- 配置更规范 -- 功能更完整 -- 用户体验更好 - ---- - -### 2. webpack.mp.config.js ✅ - -**改动**: -- ✅ isOptimize:`false` → `process.env.NODE_ENV === 'production'` -- ✅ mode:`'production'` → 根据环境判断 -- ✅ splitChunks:禁用(解决 chunk 文件问题) - -**影响**: -- 开发环境更友好 -- 生产环境更优化 -- 构建更智能 - ---- - -### 3. webpack.dev.config.js ✅(新增) - -**内容**: -- ✅ Web 端开发配置 -- ✅ 开发服务器(端口 8080) -- ✅ 热更新支持 -- ✅ HTML 模板配置 - -**影响**: -- 支持 Web 端开发 -- 提升开发效率 - ---- - -### 4. public/index.html ✅(新增) - -**内容**: -- ✅ HTML5 模板 -- ✅ viewport 配置 -- ✅ 基础样式重置 -- ✅ #app 挂载点 - -**影响**: -- Web 端正常显示 - ---- - -## 📊 优化效果 - -### Before(优化前) - -``` -❌ 配置不规范(使用 other 数组) -❌ 功能不完整(pages、global 为空) -❌ 构建不智能(硬编码环境) -❌ 开发体验差(只支持小程序) -``` - -### After(优化后) - -``` -✅ 配置规范(符合官方标准) -✅ 功能完整(页面标题、rem 支持) -✅ 构建智能(自动区分环境) -✅ 开发友好(支持 Web + 小程序) -``` - ---- - -## 🎯 对比:官方推荐 vs 我们的配置 - -### splitChunks 配置 - -| 配置 | 官方推荐 | 我们的选择 | 原因 | -|------|---------|-----------|------| -| splitChunks | `name: true`(启用) | `false`(禁用) | 项目规模适中,禁用更稳定 | -| 代码复用 | ✅ 更好 | ⚠️ 略差 | 可接受 | -| 编译稳定性 | ⚠️ 可能有问题 | ✅ 完全稳定 | 优先级高 | -| 总体积 | ✅ 更小 | ⚠️ 略大 (+30%) | 仍在限制内 | -| 适用场景 | 大型项目 | 中小型项目 | 符合当前需求 | - -**结论**: -- 官方推荐是**一般性建议** -- 我们根据**项目实际情况**做出选择 -- **稳定性 > 体积优化**(当前项目规模下) - ---- - -## 🧪 测试指引 - -### 1. 小程序测试 - -```bash -# 打开微信开发者工具 -# 导入 miniprogram/ 目录 -# 点击"编译" -``` - -**验证**: -- [ ] 每个页面标题显示正确 - - 首页:"Soul创业实验" - - 目录:"目录" - - 阅读:"阅读" - - 我的:"我的" - - 找伙伴:"找伙伴" - - ... 等等 -- [ ] 页面跳转正常 -- [ ] API 数据加载正常 -- [ ] 底部导航正常 - -### 2. Web 开发测试 - -```bash -cd newpp -npm run web -``` - -**验证**: -- [ ] 浏览器自动打开 http://localhost:8080 -- [ ] 页面正常显示 -- [ ] 热更新正常工作 -- [ ] 修改代码自动刷新 - -### 3. 生产构建测试 - -```bash -cd newpp -NODE_ENV=production npm run build:mp -``` - -**验证**: -- [ ] 代码已压缩 -- [ ] 体积减小 30-50% -- [ ] 功能正常 - ---- - -## 📚 Kbone 最佳实践总结 - -### 1. 配置规范 - -**✅ 推荐**: -- 每个页面单独配置 router -- 配置完整的 pages 信息 -- 启用有用的 global 功能 - -**❌ 避免**: -- 使用 `other` 数组配置路由 -- 空的 pages 和 global 配置 - ---- - -### 2. 环境区分 - -**✅ 推荐**: -- 根据 `process.env.NODE_ENV` 判断环境 -- 开发环境:不压缩、友好调试 -- 生产环境:压缩优化、减小体积 - -**❌ 避免**: -- 硬编码环境配置 -- 开发和生产使用相同配置 - ---- - -### 3. 代码分割 - -**对于中小型项目(<20 页面,<5MB)**: -- ✅ 推荐:禁用代码分割(`splitChunks: false`) -- ✅ 优点:编译稳定、结构清晰 -- ⚠️ 缺点:体积略大(可接受) - -**对于大型项目(>50 页面,>5MB)**: -- ✅ 推荐:启用代码分割 + 分包 -- ✅ 优点:体积优化、代码复用 -- ⚠️ 缺点:配置复杂、可能有 chunk 问题 - ---- - -### 4. 开发体验 - -**✅ 推荐**: -- 配置 Web 开发环境(webpack.dev.config.js) -- 支持热更新(HMR) -- 同时维护 Web 和小程序两套构建 - -**❌ 避免**: -- 只支持小程序开发 -- 缺少热更新 - ---- - -## ✅ 优化总结 - -### 核心改进 - -| 改进项 | Before | After | 收益 | -|--------|--------|-------|------| -| router 配置 | 使用 other 数组 | 每个页面单独配置 | 规范性 ✅ | -| pages 配置 | 空对象 | 10 个页面标题 | 用户体验 ✅ | -| global 配置 | 空对象 | rem + pageStyle | 功能性 ✅ | -| 代码压缩 | 硬编码 false | 根据环境判断 | 智能性 ✅ | -| webpack mode | 硬编码 production | 根据环境判断 | 开发体验 ✅ | -| Web 开发 | 不支持 | 完整支持 | 开发效率 ✅ | - -### 技术亮点 - -1. ✅ **完全符合官方规范** -2. ✅ **智能化环境判断** -3. ✅ **完善的开发体验** -4. ✅ **优化的生产构建** -5. ✅ **跨平台支持**(Web + 小程序) - -### 文件结构 - -``` -newpp/ -├── build/ -│ ├── miniprogram.config.js ✅ 优化(router、pages、global) -│ ├── webpack.mp.config.js ✅ 优化(环境判断、代码压缩) -│ └── webpack.dev.config.js ✅ 新增(Web 开发配置) -├── public/ -│ └── index.html ✅ 新增(HTML 模板) -├── src/ -│ ├── api/ -│ │ └── index.js ✅ API 集成层 -│ ├── hooks/ -│ │ ├── useChapters.js ✅ 章节列表 Hook -│ │ └── useChapterContent.js ✅ 章节内容 Hook -│ ├── adapters/ ✅ 跨平台适配层 -│ ├── components/ ✅ 组件 -│ ├── pages/ ✅ 页面(已更新使用 API) -│ └── data/ ⚠️ 静态数据(待废弃) -└── package.json ✅ 依赖配置 -``` - ---- - -## 📊 优化前后对比 - -### 配置质量 - -| 维度 | 优化前 | 优化后 | 提升 | -|------|--------|--------|------| -| 规范性 | 60% | 95% | +35% ✅ | -| 完整性 | 40% | 90% | +50% ✅ | -| 智能性 | 30% | 90% | +60% ✅ | -| 开发体验 | 50% | 90% | +40% ✅ | - -### 代码体积 - -| 环境 | 优化前 | 优化后 | 变化 | -|------|--------|--------|------| -| 开发环境 | 2.7 MB(未压缩) | 2.7 MB(未压缩) | 不变 | -| 生产环境 | 2.7 MB(未压缩) | 1.5-1.9 MB(已压缩) | -30~50% ✅ | - ---- - -## 🧪 测试清单 - -### 基础功能测试 - -- [x] 编译成功,无错误 -- [ ] 每个页面标题正确显示 -- [ ] 页面跳转正常 -- [ ] 底部导航正常 -- [ ] API 数据加载正常 - -### 配置功能测试 - -- [ ] rem 单位正常工作 -- [ ] 动态路由(/read/:id)正常 -- [ ] 每个页面的 navigationBarTitleText 正确 - -### 环境区分测试 - -**开发环境**: -```bash -NODE_ENV=development npm run build:mp -``` -- [ ] 代码未压缩 -- [ ] 错误提示友好 - -**生产环境**: -```bash -NODE_ENV=production npm run build:mp -``` -- [ ] 代码已压缩 -- [ ] 体积减小 30-50% - ---- - -## 📚 相关文档 - -### 官方文档 - -1. ✅ [Kbone 项目搭建流程](https://wechat-miniprogram.github.io/kbone/docs/guide/tutorial.html) -2. ✅ [Kbone 配置详解](https://wechat-miniprogram.github.io/kbone/docs/config/) -3. ✅ [Kbone 进阶用法](https://wechat-miniprogram.github.io/kbone/docs/guide/advanced.html) -4. ✅ [React 项目模板](https://github.com/wechat-miniprogram/kbone-template-react) - -### 项目文档 - -1. ✅ [Kbone配置优化说明](./开发文档/8、部署/Kbone配置优化说明.md) -2. ✅ [API接入说明](./开发文档/8、部署/API接入说明.md) -3. ✅ [自定义导航组件方案](./开发文档/8、部署/自定义导航组件方案.md) -4. ✅ [Webpack代码分割问题修复](./开发文档/8、部署/Webpack代码分割问题修复.md) - ---- - -## 🎯 下一步 - -### 立即测试 - -1. ⏳ 打开微信开发者工具 -2. ⏳ 导入 `miniprogram/` 目录 -3. ⏳ 验证每个页面标题 -4. ⏳ 测试 API 数据加载 -5. ⏳ 测试底部导航 - -### 后续优化 - -1. ⏳ 生产环境构建(启用代码压缩) -2. ⏳ 性能测试和优化 -3. ⏳ 更多页面接入 API -4. ⏳ 真机预览测试 - ---- - -## ✅ 完成总结 - -### 核心成果 - -1. ✅ **配置完全规范化** - 符合 Kbone 官方标准 -2. ✅ **功能完整** - router、pages、global 配置完善 -3. ✅ **智能化构建** - 自动区分开发/生产环境 -4. ✅ **开发体验提升** - 支持 Web 端开发 + 热更新 -5. ✅ **代码优化** - 生产环境自动压缩 - -### 技术亮点 - -1. ✅ 完全遵循 [Kbone 官方规范](https://wechat-miniprogram.github.io/kbone/docs/guide/tutorial.html) -2. ✅ 智能环境判断(开发/生产) -3. ✅ 禁用代码分割(稳定性优先) -4. ✅ API 集成完成(11 个核心 API) -5. ✅ 跨平台支持(Web + 小程序) - -### 项目质量 - -**配置规范性**:60% → 95% (+35%) ✅ -**功能完整性**:40% → 90% (+50%) ✅ -**开发体验**:50% → 90% (+40%) ✅ -**代码质量**:70% → 95% (+25%) ✅ - ---- - -**🎉 Kbone 配置优化完成!项目现在完全符合官方最佳实践。** - ---- - -**参考**: -- [Kbone 官方文档](https://wechat-miniprogram.github.io/kbone/docs/guide/tutorial.html) -- [React 项目模板](https://github.com/wechat-miniprogram/kbone-template-react) - -**优化日期**:2026-02-03 -**文档版本**:v1.0 diff --git a/README-Phase3.md b/README-Phase3.md deleted file mode 100644 index 59b6ee02..00000000 --- a/README-Phase3.md +++ /dev/null @@ -1,96 +0,0 @@ -# Phase 3 完成总结 - -## 概述 - -Phase 3 成功将 Next.js 的"我的"页及所有子页完整迁移到 Kbone 小程序,并实现了 Zustand 状态管理的跨端适配。 - ---- - -## 完成的核心功能 - -### 1. 状态管理(Zustand + Storage 适配) - -创建了 `newpp/src/store/index.js`,实现: - -- **用户状态**:user、isLoggedIn、logout、setUser、updateUser -- **购买逻辑**:hasPurchased、addPurchase、purchaseFullBook -- **持久化**:用 `adapters/storage.js` 适配小程序 wx.storage 和 Web localStorage - -### 2. 我的页面(登录态 + 统计 + 菜单) - -- 未登录:登录提示、统计占位 -- 已登录:用户卡片、收益卡片、Tab 切换(概览/我的足迹)、菜单(订单、推广、关于、设置) - -### 3. 推广页(邀请码 + 收益) - -- 收益概览:待领收益、累计收益、已提现 -- 邀请码展示与复制 -- 推广数据与规则说明 - -### 4. 设置页 - -- 账号信息展示 -- 通用设置 -- 退出登录 - -### 5. 购买记录页 - -- 订单列表(暂无数据占位) - -### 6. 关于页 - -- 项目介绍、数据统计、联系方式 - ---- - -## 文件清单 - -**页面组件**: -- `src/pages/MyPage.jsx` -- `src/pages/ReferralPage.jsx` -- `src/pages/SettingsPage.jsx` -- `src/pages/PurchasesPage.jsx` -- `src/pages/AboutPage.jsx` - -**入口文件**: -- `src/my.jsx` -- `src/referral.jsx` -- `src/settings.jsx` -- `src/purchases.jsx` -- `src/about.jsx` - -**状态管理**: -- `src/store/index.js` - -**配置更新**: -- `build/webpack.mp.config.js`(新增 5 个入口) -- `build/miniprogram.config.js`(router.other 新增 5 个路由) - ---- - -## 测试与验收 - -1. **构建**:`cd newpp && npm run build:mp` -2. **合并**:`node scripts/merge-kbone-to-miniprogram.js` -3. **测试路径**: - - 首页 → 底部"我的" → 查看用户卡片 + 统计 - - 我的 → 推广中心 → 查看邀请码 + 收益 - - 我的 → 设置 → 查看账号信息 - - 我的 → 我的订单 → 查看空态 - - 我的 → 关于我们 → 查看项目介绍 - ---- - -## 当前进度 - -- ✅ **Phase 1**:搭架子(适配层、构建、首页/目录/阅读占位) -- ✅ **Phase 2**:核心页(阅读页接口、ChapterContent、完整目录) -- ✅ **Phase 3**:我的与子页(Zustand、我的、推广、设置、购买记录、关于) -- ⏳ **Phase 4**:找伙伴与其余(match、search、底部 tabBar、安全区) -- ⏳ **Phase 5**:收尾(全量自检、样式对齐、发布流程) - ---- - -## 下一步 - -进入 Phase 4,迁移找伙伴、搜索,并实现底部 tabBar 与安全区适配。 diff --git a/README-Phase4.md b/README-Phase4.md deleted file mode 100644 index 84261286..00000000 --- a/README-Phase4.md +++ /dev/null @@ -1,139 +0,0 @@ -# Phase 4 完成总结 - -## 概述 - -Phase 4 成功迁移了"找伙伴"、"搜索"页面,实现了底部 TabBar 导航与安全区适配,完成了 Next.js C 端应用的**全量页面迁移**。 - ---- - -## 完成的核心功能 - -### 1. 找伙伴页(AI 智能匹配) - -- **匹配类型**:创业合伙、资源对接、导师顾问、团队招募 -- **匹配逻辑**: - - 每日免费 1 次 - - 购买章节获得更多次数 - - 全书用户无限匹配 -- **匹配结果**:匹配度、创业理念、共同兴趣、微信号 -- **加入池功能**:提交手机号加入匹配池 -- **次数展示**:剩余次数、已用次数、解锁提示 - -### 2. 搜索页(章节检索) - -- **实时搜索**:输入关键词实时过滤章节 -- **搜索结果**:显示所属篇章、标题、免费标签 -- **跳转阅读**:点击结果直接进入阅读页 -- **空态处理**:无搜索词、无结果的友好提示 - -### 3. 底部 TabBar 导航 - -- **4 个 Tab**:首页🏠、目录📚、找伙伴👥、我的👤 -- **激活态**:当前页 Tab 高亮显示(#00CED1) -- **动态显示**:找伙伴 Tab 根据 `matchEnabled` 配置显示/隐藏 -- **安全区适配**:`paddingBottom = env(safe-area-inset-bottom)` -- **跨端路由**:小程序用 `wx.switchTab`,Web 用 `window.location.href` - -### 4. 各页面集成底部导航 - -在以下 4 个 Tab 页添加了 `` 组件: - -- HomePage (current="/") -- ChaptersPage (current="/chapters") -- MatchPage (current="/match") -- MyPage (current="/my") - ---- - -## 文件清单 - -**页面组件**: -- `src/pages/MatchPage.jsx`(找伙伴) -- `src/pages/SearchPage.jsx`(搜索) - -**入口文件**: -- `src/match.jsx` -- `src/search.jsx` - -**公共组件**: -- `src/components/BottomNav.jsx`(底部导航) - -**配置更新**: -- `build/webpack.mp.config.js`(新增 match、search 入口) -- `build/miniprogram.config.js`(router.other 新增 /match、/search) - ---- - -## 完整页面映射表(Phase 1-4) - -| Next 路由 | 小程序页面 | 入口文件 | 状态 | -|-----------|-----------|----------|------| -| app/page.tsx | pages/index/index | src/index.jsx | ✅ Phase 2 | -| app/chapters/page.tsx | pages/chapters/chapters | src/chapters.jsx | ✅ Phase 2 | -| app/read/[id]/page.tsx | pages/read/read | src/read.jsx | ✅ Phase 2 | -| app/my/page.tsx | pages/my/my | src/my.jsx | ✅ Phase 3 | -| app/my/referral/page.tsx | pages/referral/referral | src/referral.jsx | ✅ Phase 3 | -| app/my/settings/page.tsx | pages/settings/settings | src/settings.jsx | ✅ Phase 3 | -| app/my/purchases/page.tsx | pages/purchases/purchases | src/purchases.jsx | ✅ Phase 3 | -| app/about/page.tsx | pages/about/about | src/about.jsx | ✅ Phase 3 | -| app/match/page.tsx | pages/match/match | src/match.jsx | ✅ Phase 4 | -| app/search/page.tsx | pages/search/search | src/search.jsx | ✅ Phase 4 | - -**C 端页面 100% 迁移完成!** - ---- - -## 安全区适配 - -### 底部安全区 -```css -paddingBottom: 'env(safe-area-inset-bottom)' -``` -确保在有底部刘海的设备(如 iPhone X)上,TabBar 不被遮挡。 - -### 顶部安全区 -小程序自动处理 statusBar,无需额外适配。若使用自定义导航栏,可读取 `app.globalData.navBarHeight`。 - -### 右侧安全区(胶囊按钮) -若使用自定义导航栏,需在右侧预留 `capsulePaddingRight`,避免遮挡胶囊按钮。 - ---- - -## 测试与验收 - -1. **构建**:`cd newpp && npm run build:mp` -2. **合并**:`node scripts/merge-kbone-to-miniprogram.js` -3. **测试路径**: - - **TabBar 切换**:首页 ↔ 目录 ↔ 找伙伴 ↔ 我的 - - **找伙伴流程**:选择类型 → 开始匹配 → 查看结果 → 复制微信 → 加入池 - - **搜索流程**:首页 → 搜索 icon(或直接访问 /search)→ 输入关键词 → 点击结果 → 阅读 - - **底部导航**:各 Tab 页底部导航高亮正确、点击切换正常 - ---- - -## 当前进度 - -- ✅ **Phase 1**:搭架子(适配层、构建、首页/目录/阅读占位) -- ✅ **Phase 2**:核心页(阅读页接口、ChapterContent、完整目录) -- ✅ **Phase 3**:我的与子页(Zustand、我的、推广、设置、购买记录、关于) -- ✅ **Phase 4**:找伙伴与其余(match、search、底部 tabBar、安全区) -- ⏳ **Phase 5**:收尾(全量自检、样式对齐、发布流程) - ---- - -## 下一步 - -进入 Phase 5 收尾阶段: - -1. **全量自检**:对照「Web转小程序并上传-提示词」逐项检查 -2. **样式对齐**:确保颜色、间距、圆角、阴影与 Web 一致 -3. **踩坑修复**: - - WXML 不能调用 JS 方法 - - 启动不阻塞(async onLaunch) - - safe-area 边界处理 - - TabBar 默认隐藏项逻辑 -4. **发布流程**:预览码 → 体验版 → 提审 → 发布 - ---- - -**Phase 1-4 已完成 C 端全量迁移,所有核心功能已就位!** diff --git a/README-底部导航修复完成.md b/README-底部导航修复完成.md deleted file mode 100644 index 61395b59..00000000 --- a/README-底部导航修复完成.md +++ /dev/null @@ -1,538 +0,0 @@ -# 底部导航修复完成报告 - -## 📋 修复概览 - -**修复时间**:2026-02-03 -**问题**:底部菜单点击无效 + 样式与原项目不一致 -**状态**:✅ 已完成 - ---- - -## 🎯 修复的核心问题 - -### 1. ❌ 点击无效问题 - -**原因**: -- 小程序缺少 `tabBar` 配置 -- `wx.switchTab()` 必须依赖 `app.json` 中的 `tabBar` 配置 - -**修复**: -- ✅ 在 `miniprogram.config.js` 的 `appExtraConfig` 中添加 `tabBar` 配置 -- ✅ 手动编辑 `miniprogram/app.json`,添加完整的 `tabBar` 字段 - -### 2. ❌ 样式不一致问题 - -**原项目设计**: -- 中间"找伙伴"按钮是凸起的圆形按钮 -- 渐变色背景(#00CED1 → #20B2AA) -- 阴影效果 -- 精致的过渡动效 - -**修复**: -- ✅ 重构 `BottomNav.jsx`,添加 `isCenter` 标记 -- ✅ 实现中间凸起按钮样式(`marginTop: -16`) -- ✅ 添加渐变色和阴影效果 -- ✅ 优化交互体验(去除点击高亮、添加过渡动效) - -### 3. ❌ 配置加载不完整 - -**原项目功能**: -- Web 环境从 `/api/db/config` 加载 `matchEnabled` -- 小程序环境从 `app.globalData.matchEnabled` 读取 - -**修复**: -- ✅ 添加 Web 环境配置加载逻辑 -- ✅ 统一配置加载状态管理(`configLoaded`) - ---- - -## 📝 修改的文件 - -### 1. `newpp/build/miniprogram.config.js` - -**添加 tabBar 配置**: - -```javascript -appExtraConfig: { - sitemapLocation: 'sitemap.json', - - // ✅ 新增:tabBar 配置 - tabBar: { - custom: false, - color: '#9ca3af', - selectedColor: '#00CED1', - backgroundColor: '#1c1c1e', - borderStyle: 'white', - list: [ - { pagePath: 'pages/index/index', text: '首页', iconPath: 'assets/home.png', selectedIconPath: 'assets/home-active.png' }, - { pagePath: 'pages/chapters/index', text: '目录', iconPath: 'assets/chapters.png', selectedIconPath: 'assets/chapters-active.png' }, - { pagePath: 'pages/match/index', text: '找伙伴', iconPath: 'assets/match.png', selectedIconPath: 'assets/match-active.png' }, - { pagePath: 'pages/my/index', text: '我的', iconPath: 'assets/my.png', selectedIconPath: 'assets/my-active.png' }, - ], - }, -}, -``` - -### 2. `newpp/src/components/BottomNav.jsx` - -**完全重构,对齐原项目设计**: - -#### 改动 1:tabs 配置添加 isCenter 标记 - -```javascript -const tabs = [ - { id: 'home', path: '/', label: '首页', icon: '🏠' }, - { id: 'chapters', path: '/chapters', label: '目录', icon: '📚' }, - { id: 'match', path: '/match', label: '找伙伴', icon: '👥', isCenter: true }, // ✅ 中间按钮 - { id: 'my', path: '/my', label: '我的', icon: '👤' }, -] -``` - -#### 改动 2:添加中间按钮样式 - -```javascript -const styles = { - // ... 其他样式 - - centerTab: { - display: 'flex', - flexDirection: 'column', - alignItems: 'center', - padding: '8px 24px', - marginTop: -16, // ✅ 凸起效果 - }, - - centerButton: { - width: 56, - height: 56, - borderRadius: '50%', - background: 'linear-gradient(135deg, #00CED1 0%, #20B2AA 100%)', // ✅ 渐变 - boxShadow: '0 4px 12px rgba(0,206,209,0.3)', // ✅ 阴影 - display: 'flex', - alignItems: 'center', - justifyContent: 'center', - }, -} -``` - -#### 改动 3:配置加载逻辑对齐 - -```javascript -useEffect(() => { - if (isMiniProgram()) { - // ✅ 小程序环境 - try { - const app = getApp() - if (app && app.globalData) { - setMatchEnabled(app.globalData.matchEnabled !== false) - } - } catch (e) { - // ... - } finally { - setConfigLoaded(true) - } - } else { - // ✅ Web 环境 - fetch('/api/db/config') - .then((res) => res.json()) - .then((data) => { - if (data.features) { - setMatchEnabled(data.features.matchEnabled === true) - } - }) - .catch(() => { - setMatchEnabled(false) - }) - .finally(() => { - setConfigLoaded(true) - }) - } -}, []) -``` - -#### 改动 4:渲染逻辑区分普通/中间按钮 - -```javascript -{visibleTabs.map((tab) => { - const isActive = current === tab.path - - // ✅ 中间按钮特殊处理 - if (tab.isCenter) { - return ( -
handleTabClick(tab.path)}> -
-
{tab.icon}
-
- - {tab.label} - -
- ) - } - - // ✅ 普通按钮 - return
handleTabClick(tab.path)}>{/* ... */}
-})} -``` - -### 3. `miniprogram/app.json` - -**手动添加 tabBar 配置**: - -```json -{ - "pages": [...], - "tabBar": { - "color": "#9ca3af", - "selectedColor": "#00CED1", - "backgroundColor": "#1c1c1e", - "borderStyle": "white", - "list": [ - { "pagePath": "pages/index/index", "text": "首页" }, - { "pagePath": "pages/chapters/index", "text": "目录" }, - { "pagePath": "pages/match/index", "text": "找伙伴" }, - { "pagePath": "pages/my/index", "text": "我的" } - ] - }, - "window": {...} -} -``` - ---- - -## 🎨 样式对比 - -### Before(修复前) - -``` -┌──────┬──────┬──────┬──────┐ -│ 🏠 │ 📚 │ 👥 │ 👤 │ -│ 首页 │ 目录 │ 找伙伴│ 我的 │ -└──────┴──────┴──────┴──────┘ -``` - -**问题**: -- ❌ 所有按钮样式一致 -- ❌ 简单的透明度变化 -- ❌ 点击无响应 - -### After(修复后) - -``` -┌──────┬──────┬──────┬──────┐ -│ 🏠 │ 📚 │ ● │ 👤 │ -│ 首页 │ 目录 │ 👥 │ 我的 │ -│ │ │ 找伙伴│ │ -└──────┴──────┴──────┴──────┘ - ▲ 凸起的渐变圆形按钮 -``` - -**改进**: -- ✅ 中间按钮凸起显示 -- ✅ 渐变色 + 阴影 -- ✅ 点击正常跳转 -- ✅ 激活态高亮 - ---- - -## 🔧 技术细节 - -### 问题 1:为什么必须配置 tabBar? - -**微信小程序规范**: -- `wx.switchTab()` 只能跳转到 tabBar 页面 -- tabBar 页面必须在 `app.json` 的 `tabBar.list` 中声明 -- 如果没有配置 `tabBar`,`wx.switchTab()` 会报错:`errMsg: "switchTab:fail page not found"` - -### 问题 2:为什么使用 div 而不是 button? - -**原因**: -- 小程序中 ` 返回前台 diff --git a/app/layout.tsx b/app/layout.tsx index 28d8a5d9..35181749 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -3,7 +3,7 @@ import type { Metadata } from "next" import { Geist, Geist_Mono } from "next/font/google" import { Analytics } from "@vercel/analytics/next" import "./globals.css" -import { LayoutWrapper } from "@/components/layout-wrapper" +import { LayoutWrapper } from "@/components/view/layout/layout-wrapper" const _geist = Geist({ subsets: ["latin"] }) const _geistMono = Geist_Mono({ subsets: ["latin"] }) diff --git a/app/page.tsx b/app/page.tsx index 48c4ced8..efaa1dd0 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,223 +1,6 @@ -/** - * 一场SOUL的创业实验 - 首页 - * 开发: 卡若 - * 技术支持: 存客宝 - */ -"use client" +import { redirect } from "next/navigation" -import { useState, useEffect } from "react" -import { useRouter } from "next/navigation" -import { Search, ChevronRight, BookOpen } from "lucide-react" -import { useStore } from "@/lib/store" -import { bookData, getTotalSectionCount } from "@/lib/book-data" -import { SearchModal } from "@/components/search-modal" -import { BottomNav } from "@/components/bottom-nav" - -export default function HomePage() { - const router = useRouter() - const { user } = useStore() - const [mounted, setMounted] = useState(false) - const [searchOpen, setSearchOpen] = useState(false) - - // 计算数据(必须在所有 hooks 之后) - const totalSections = getTotalSectionCount() - const hasFullBook = user?.hasFullBook || false - const purchasedCount = hasFullBook ? totalSections : user?.purchasedSections?.length || 0 - - // 推荐章节 - const featuredSections = [ - { id: "1.1", title: "荷包:电动车出租的被动收入模式", tag: "免费", part: "真实的人" }, - { id: "3.1", title: "3000万流水如何跑出来", tag: "热门", part: "真实的行业" }, - { id: "8.1", title: "流量杠杆:抖音、Soul、飞书", tag: "推荐", part: "真实的赚钱" }, - ] - - // 最新更新 - const latestSection = { - id: "9.14", - title: "大健康私域:一个月150万的70后", - part: "真实的赚钱", - } - - useEffect(() => { - setMounted(true) - }, []) - - if (!mounted) { - return null - } - - return ( -
- {/* 顶部区域 */} -
-
-
-
- S -
-
-

Soul创业实验

-

来自派对房的真实故事

-
-
-
- {totalSections}章 -
-
- - {/* 搜索栏 */} -
setSearchOpen(true)} - className="flex items-center gap-3 px-4 py-3 rounded-xl bg-[#1c1c1e] border border-white/5 cursor-pointer hover:border-[#00CED1]/30 transition-colors" - > - - 搜索章节... -
-
- - {/* 搜索弹窗 */} - - -
- {/* Banner卡片 - 最新章节 */} -
router.push(`/read/${latestSection.id}`)} - className="relative p-5 rounded-2xl overflow-hidden cursor-pointer" - style={{ - background: "linear-gradient(135deg, #0d3331 0%, #1a1a2e 50%, #16213e 100%)", - }} - > -
-
-
- - 最新更新 - -

{latestSection.title}

-

{latestSection.part}

-
- 开始阅读 - -
-
- - {/* 阅读进度卡 */} -
-
-

我的阅读

- - {purchasedCount}/{totalSections}章 - -
-
-
-
-
-
-

{purchasedCount}

-

已读

-
-
-

{totalSections - purchasedCount}

-

待读

-
-
-

5

-

篇章

-
-
-

11

-

章节

-
-
-
- - {/* 精选推荐 */} -
-
-

精选推荐

- -
-
- {featuredSections.map((section) => ( -
router.push(`/read/${section.id}`)} - className="p-4 rounded-xl bg-[#1c1c1e] border border-white/5 cursor-pointer active:scale-[0.98] transition-transform" - > -
-
-
- {section.id} - - {section.tag} - -
-

{section.title}

-

{section.part}

-
- -
-
- ))} -
-
- -
-

内容概览

-
- {bookData.map((part) => ( -
router.push("/chapters")} - className="p-4 rounded-xl bg-[#1c1c1e] border border-white/5 cursor-pointer active:scale-[0.98] transition-transform" - > -
-
- {part.number} -
-
-

{part.title}

-

{part.subtitle}

-
- -
-
- ))} -
-
- - {/* 序言入口 */} -
router.push("/read/preface")} - className="p-4 rounded-xl bg-gradient-to-r from-[#00CED1]/10 to-transparent border border-[#00CED1]/20 cursor-pointer" - > -
-
-

序言

-

为什么我每天早上6点在Soul开播?

-
- 免费 -
-
-
- - {/* 使用统一的底部导航组件 */} - -
- ) +/** 根路径重定向到移动端首页 */ +export default function RootPage() { + redirect("/view") } diff --git a/app/about/page.tsx b/app/view/about/page.tsx similarity index 100% rename from app/about/page.tsx rename to app/view/about/page.tsx diff --git a/app/chapters/page.tsx b/app/view/chapters/page.tsx similarity index 98% rename from app/chapters/page.tsx rename to app/view/chapters/page.tsx index a92a1174..112c776e 100644 --- a/app/chapters/page.tsx +++ b/app/view/chapters/page.tsx @@ -6,7 +6,6 @@ import { ChevronRight, Lock, Unlock, Book, BookOpen, Sparkles, Zap, Crown, Searc import { useStore } from "@/lib/store" import { bookData, getTotalSectionCount, specialSections, getPremiumBookPrice, getExtraSectionsCount, BASE_SECTIONS_COUNT } from "@/lib/book-data" import { SearchModal } from "@/components/search-modal" -import { BottomNav } from "@/components/bottom-nav" export default function ChaptersPage() { const router = useRouter() @@ -22,7 +21,7 @@ export default function ChaptersPage() { const extraSections = getExtraSectionsCount() const handleSectionClick = (sectionId: string) => { - router.push(`/read/${sectionId}`) + router.push(`/view/read/${sectionId}`) } return ( @@ -210,7 +209,6 @@ export default function ChaptersPage() {
- ) } diff --git a/app/docs/page.tsx b/app/view/docs/page.tsx similarity index 98% rename from app/docs/page.tsx rename to app/view/docs/page.tsx index acf1e3f6..e02552a4 100644 --- a/app/docs/page.tsx +++ b/app/view/docs/page.tsx @@ -8,7 +8,7 @@ export default function DocsPage() {
- +

开发者文档

diff --git a/app/documentation/capture/loading.tsx b/app/view/documentation/capture/loading.tsx similarity index 100% rename from app/documentation/capture/loading.tsx rename to app/view/documentation/capture/loading.tsx diff --git a/app/documentation/capture/page.tsx b/app/view/documentation/capture/page.tsx similarity index 100% rename from app/documentation/capture/page.tsx rename to app/view/documentation/capture/page.tsx diff --git a/app/documentation/page.tsx b/app/view/documentation/page.tsx similarity index 100% rename from app/documentation/page.tsx rename to app/view/documentation/page.tsx diff --git a/app/login/forgot/page.tsx b/app/view/login/forgot/page.tsx similarity index 98% rename from app/login/forgot/page.tsx rename to app/view/login/forgot/page.tsx index 42a5c3f3..6ac89227 100644 --- a/app/login/forgot/page.tsx +++ b/app/view/login/forgot/page.tsx @@ -46,7 +46,7 @@ export default function ForgotPasswordPage() { if (data.success) { setSuccess(true) - setTimeout(() => router.push("/login"), 2000) + setTimeout(() => router.push("/view/login"), 2000) } else { setError(data.error || "重置失败") } @@ -68,7 +68,7 @@ export default function ForgotPasswordPage() {
diff --git a/app/login/page.tsx b/app/view/login/page.tsx similarity index 97% rename from app/login/page.tsx rename to app/view/login/page.tsx index 820c93c6..74ed47a3 100644 --- a/app/login/page.tsx +++ b/app/view/login/page.tsx @@ -50,7 +50,7 @@ export default function LoginPage() { } const success = await login(phone, code) if (success) { - router.push("/") + router.push("/view") } else { setError("密码错误或用户不存在") } @@ -69,7 +69,7 @@ export default function LoginPage() { } const success = await register(phone, nickname, code, referralCode || undefined) if (success) { - router.push("/") + router.push("/view") } else { setError("该手机号已注册") } @@ -167,7 +167,7 @@ export default function LoginPage() {
{mode === "login" && (
- + 忘记密码?
diff --git a/app/match/page.tsx b/app/view/match/page.tsx similarity index 99% rename from app/match/page.tsx rename to app/view/match/page.tsx index bf72d24d..067482fd 100644 --- a/app/match/page.tsx +++ b/app/view/match/page.tsx @@ -4,7 +4,6 @@ import { useState, useEffect } from "react" import { motion, AnimatePresence } from "framer-motion" import { Users, X, CheckCircle, Loader2, Lock, Zap } from "lucide-react" import { useRouter } from "next/navigation" -import { BottomNav } from "@/components/bottom-nav" import { useStore } from "@/lib/store" interface MatchUser { @@ -367,7 +366,7 @@ export default function MatchPage() { {matchesRemaining <= 0 && !user?.hasFullBook && (
) } diff --git a/app/my/addresses/[id]/page.tsx b/app/view/my/addresses/[id]/page.tsx similarity index 68% rename from app/my/addresses/[id]/page.tsx rename to app/view/my/addresses/[id]/page.tsx index 70aa07d7..955917bc 100644 --- a/app/my/addresses/[id]/page.tsx +++ b/app/view/my/addresses/[id]/page.tsx @@ -8,7 +8,7 @@ import { useStore } from "@/lib/store" export default function EditAddressPage() { const router = useRouter() const params = useParams() - const id = params.id as string + const id = params?.id as string const { user } = useStore() const [loading, setLoading] = useState(false) const [fetching, setFetching] = useState(true) @@ -21,7 +21,7 @@ export default function EditAddressPage() { const [isDefault, setIsDefault] = useState(false) useEffect(() => { - if (!id) { + if (!id || !user?.id) { setFetching(false) return } @@ -29,19 +29,18 @@ export default function EditAddressPage() { .then((res) => res.json()) .then((data) => { if (data.success && data.item) { - const item = data.item - setName(item.name) - setPhone(item.phone) - setProvince(item.province) - setCity(item.city) - setDistrict(item.district) - setDetail(item.detail) - setIsDefault(item.isDefault) + const a = data.item + setName(a.name || "") + setPhone(a.phone || "") + setProvince(a.province || "") + setCity(a.city || "") + setDistrict(a.district || "") + setDetail(a.detail || "") + setIsDefault(!!a.isDefault) } - setFetching(false) }) - .catch(() => setFetching(false)) - }, [id]) + .finally(() => setFetching(false)) + }, [id, user?.id]) if (!user?.id) { return ( @@ -61,7 +60,6 @@ export default function EditAddressPage() { alert("请输入正确的手机号") return } - // 省/市/区为选填 if (!detail.trim()) { alert("请输入详细地址") return @@ -83,7 +81,7 @@ export default function EditAddressPage() { }) const data = await res.json() if (data.success) { - router.push("/my/addresses") + router.push("/view/my/addresses") } else { alert(data.message || "保存失败") } @@ -97,7 +95,7 @@ export default function EditAddressPage() { if (fetching) { return (
-
加载中...
+
) } @@ -140,54 +138,21 @@ export default function EditAddressPage() {
- setProvince(e.target.value)} - placeholder="省" - className="flex-1 max-w-24 bg-transparent text-white text-sm text-right placeholder-white/30 outline-none" - /> - setCity(e.target.value)} - placeholder="市" - className="flex-1 max-w-24 bg-transparent text-white text-sm text-right placeholder-white/30 outline-none" - /> - setDistrict(e.target.value)} - placeholder="区" - className="flex-1 max-w-24 bg-transparent text-white text-sm text-right placeholder-white/30 outline-none" - /> + setProvince(e.target.value)} placeholder="省" className="flex-1 max-w-24 bg-transparent text-white text-sm text-right placeholder-white/30 outline-none" /> + setCity(e.target.value)} placeholder="市" className="flex-1 max-w-24 bg-transparent text-white text-sm text-right placeholder-white/30 outline-none" /> + setDistrict(e.target.value)} placeholder="区" className="flex-1 max-w-24 bg-transparent text-white text-sm text-right placeholder-white/30 outline-none" />
-